Facebook Instagram Twitter Vimeo Youtube
Sign in
  • Home
  • latest tech
    • PRIVACY AND SECURITY
  • Featured
    • SOFTWARE
  • Social Media
  • Mobiles
  • others
    • Games
    • Uncategorized
  • Contact Us for Quality content, blog posts, and links at techmistake.com
Sign in
Welcome!Log into your account
Forgot your password?
Password recovery
Recover your password
Search
Tech Mistake Find new tech and its mistake
  • Home
  • latest tech
    • PRIVACY AND SECURITY
  • Featured
    • SOFTWARE
  • Social Media
  • Mobiles
  • others
    • Games
    • Uncategorized
  • Contact Us for Quality content, blog posts, and links at techmistake.com

    Running UITests with Facebook login in iOS

    By
    admin
    -
    October 2, 2018
    0
    858
    Facebook
    Twitter
    Pinterest
    WhatsApp
      Source: Google

      Tech mistake |Today I’m trying to run some UITest on my app, which uses Facebook login. And here are some of my notes on it.

      Challenges

      • The challenges with Facebook is it uses Safari controller, we we deal mostly with web viewfor now. Starting from iOS 9+, Facebook decided to use safariinstead of native facebook appto avoid app switching. You can read the detail here Building the Best Facebook Login Experience for People on iOS 9
      • It does not have wanted accessibilityIdentifier or accessibilityLabel
      • The webview content may change in the future ?

      Create a Facebook test user

      Luckily, you don’t have to create your own Facebook user to test. Facebook supports test users that you can manage permissions and friends, very handy

      When creating the test user, you have the option to select language. That will be the displayed language in Safari web view. I choose Norwegian ?? for now

      Click the login button and show Facebook login

      Here we use the default FBSDKLoginButton

      var showFacebookLoginFormButton: XCUIElement {
        return buttons["Continue with Facebook"]
      }

      And then tap it

      app.showFacebookLoginFormButton.tap()

      Check login status

      When going to safari Facebook form, user may have already logged in or not. So we need to handle these 2 cases. When user has logged in, Facebook will say something like “you have already logged in” or the OK button.

      The advice here is to put breakpoint and po app.staticTexts, po app.buttons to see which UI elements are at a certain point.

      You can check for the static text, or simply just the OK button

      var isAlreadyLoggedInSafari: Bool {
        return buttons["OK"].exists || staticTexts["Du har allerede godkjent Blue Sea."].exists
      }

      Wait and refresh

      But Facebook form is a webview, so its content is a bit dynamic. And UITest seems to cache content for fast query, so before checking staticTexts, we need to wait and refresh the cache

      app.clearCachedStaticTexts()

      This is the wait function

      extension XCTestCase {
        func wait(for duration: TimeInterval) {
          let waitExpectation = expectation(description: "Waiting")
          let when = DispatchTime.now() + duration
          DispatchQueue.main.asyncAfter(deadline: when) {
            waitExpectation.fulfill()
          }
          // We use a buffer here to avoid flakiness with Timer on CI
          waitForExpectations(timeout: duration + 0.5)
        }
      }

      Wait for element to appear

      But a more solid approach would be to wait for element to appear. For Facebook login form, they should display a Facebook label after loading. So we should wait for this element

      extension XCTestCase {
        /// Wait for element to appear
        func wait(for element: XCUIElement, timeout duration: TimeInterval) {
          let predicate = NSPredicate(format: "exists == true")
          let _ = expectation(for: predicate, evaluatedWith: element, handler: nil)
          // Here we don't need to call `waitExpectation.fulfill()`
          // We use a buffer here to avoid flakiness with Timer on CI
          waitForExpectations(timeout: duration + 0.5)
        }
      }

      And call this before you do any further inspection on elements in Facebook login form

      wait(for: app.staticTexts["Facebook"], timeout: 5)

      If user is logged in

      After login, my app shows the main controller with a map view inside. So a basic test would be to check the existence of that map

      if app.isAlreadyLoggedInSafari {
        app.okButton.tap()
        handleLocationPermission()
        // Check for the map
        XCTAssertTrue(app.maps.element(boundBy: 0).exists)
      }

      Handle interruption

      You know that when showing the map with location, Core Location will ask for permission. So we need to handle that interruption as well. You need to ensure to call it early before the alert happens

      fileprivate func handleLocationPermission() {
        addUIInterruptionMonitor(withDescription: "Location permission", handler: { alert in
          alert.buttons.element(boundBy: 1).tap()
          return true
        })
      }

      There is another problem, this monitor won’t be called. So the workaround is to call app.tap() again when the alert will happen. In my case, I call app.tap() when my map has been shown for 1,2 seconds, just to make sure app.tap() is called after alert is shown

      For a more detailed guide, please read #48

      If user is not logged in

      In this case, we need to fill in email and password. You can take a look at the The full source codesection below. When things don’t work or po does not show you the elements you needed, it’s probably because of caching or you need to wait until dynamic content finishes rendering.

      You need to wait for element to appear

      Tap on the text field

      You may get Neither element nor any descendant has keyboard focus, here are the workaround

      • If you test on Simulator, make sure Simulator -> Hardware -> Keyboard -> Connect Hardware Keyboard is not checked
      • wait a bit after tap
      app.emailTextField.tap()

      Clear all the text

      The idea is to move the caret to the end of the textField, then apply each delete key for each character, then type the next text

      extension XCUIElement {
        func deleteAllText() {
          guard let string = value as? String else {
            return
          }
          let lowerRightCorner = coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9))
          lowerRightCorner.tap()
          let deletes = string.characters.map({ _ in XCUIKeyboardKeyDelete }).joined(separator: "")
          typeText(deletes)
        }
      }

      Change language

      For my case, I want to test in Norwegian, so we need to find the Norwegianoption and tap on that. It is identified as static text by UI Test

      var norwegianText: XCUIElement {
        return staticTexts["Norsk (bokmål)"]
      }
      wait(for: app.norwegianText, timeout: 1)
      app.norwegianText.tap()

      The email text field

      Luckily, email text field is detected by UI Test as text field element, so we can query for that. This uses predicate

      var emailTextField: XCUIElement {
        let predicate = NSPredicate(format: "placeholderValue == %@", "E-post eller mobil")
        return textFields.element(matching: predicate)
      }

      The password text field

      UI Test can’t seem to identify the password text field, so we need to search for it by coordinate

      var passwordCoordinate: XCUICoordinate {
        let vector = CGVector(dx: 1, dy: 1.5)
        return emailTextField.coordinate(withNormalizedOffset: vector)
      }

      This is the document for func coordinate(withNormalizedOffset normalizedOffset: CGVector) -> XCUICoordinate

      Creates and returns a new coordinate with a normalized offset.
      The coordinate’s screen point is computed by adding normalizedOffset multiplied by the size of the element’s frame to the origin of the element’s frame.

      Then type the password

      app.passwordCoordinate.tap()
      app.typeText("My password")

      We should not use app.passwordCoordinate.referencedElement because it will point to email text field ❗️ ?

      Run that test again

      Go to Xcode -> Product -> Perform Actions -> Test Again to run the previous test again

      Here are the full source code

      One more thing

      Thanks to the helpful feedback on my article Original story https://github.com/onmyway133/blog/issues/44, here are some more ideas

      • To look for password text fields, we can actually use secureTextFieldsinstead of coordinate
      • The wait function should be made as an extension to XCUIElement so other element can use that. Or you can just use the old expectation style, which does not involve a hardcoded interval value.

      Where to go from here

      I found these guides to cover many aspects of UITests, worth taking a look

      The article was originally published here.

      • TAGS
      • Facebook
      • Facebook Login In Ios
      • iOS
      • Running Uitest
      • Running UITests with Facebook login in iOS
      • Uitest
      Facebook
      Twitter
      Pinterest
      WhatsApp
        Previous articleHow to Track Sleep: Apple Watch Edition
        Next articleA Gentle Introduction to tmux
        admin
        admin
        http://forupon.com/
        I am Muhammad Asif Sidiq and working as blogger in tech mistake and For upon which is the best company. I want to share our experience on your website through quality article… View our Guest Posting Sites which will give you good free back links.
        © Newspaper WordPress Theme by TagDiv