Saturday, February 6, 2016

OS X Setup and iOS Device Configuration for Mobile Web Test Automation Using Free iOS Provisioning Profile

Requirements:

  • OS X Yosemite 10.10.5
  • Xcode 7.2.1
  • iOS Device (9.2.1)
  • Homebrew
  • Git
  • SafariLauncher
  • Appium (1.4.16)
  • Apple ID with *Provisioning Profile (iOS Development)
*Note: Free iOS Development Provisioning Profile can be created via Xcode 7

I. Create iOS Development Provisioning Profile for SafariLauncher

  • Clone https://github.com/budhash/SafariLauncher.git
  • Open the project in Xcode
    2016-02-06_1915
  • Open Xcode > Preferences > Accounts
  • Add an Apple ID
  • Click the apple id and click View Details
  • Create iOS Development Signing Identities
ios_development_account

II. Create Provisioning Profile for SafariLauncher

  • Using the same xcode project, provide ‘Bundle Identifier’ value.
    i.e.
    com.github.blago.SafariLauncher
  • Fix the issue with Provisioning Profile
    2016-02-06_1551
  • After fixing the issue, provide deployment information
    2016-02-06_1227
  • For Xcode 7.2.1, click Build Settings and edit Code Signing section
    2016-02-06_1230
  • Build and run SafariLauncher on the iOS Device
    2016-02-06_1258
  • Trust the developer of the app in the iOS Device.
    Go to Settings > General > Device Management
    Screenshot 2016.02.06 13.35.13

III. Build SafariLauncher for Real Devices (using appium source)

  • Clone https://github.com/appium/appium.git
    • SHA-1 hash used: 54c2b4e6500eddbca4bb3047116ceb8d177f7294
    • git checkout 54c2b4e6500eddbca4bb3047116ceb8d177f7294
  • Go to the appium directory (clone) and edit reset.sh then provide the BUNDLE_ID used in the provisioning profile
    2016-02-06_1247
  • Run the command inside the appium directory
    ./reset.sh --ios --real-safari --verbose
    
    
    Sample Output
    2016-02-06_1244


IV. Install Homebrew Packages 

  • Do the following command
    brew update
    brew install libimobiledevice libplist libtasn1 usbmuxd openssl ideviceinstaller
    brew install ios-webkit-debug-proxy
    brew install node


V. iOS Device Configuration

  • Enable UI Automation (Settings > Developer)
    Screenshot 2016.02.06 13.06.04
  • Turn on Safari Web Inspector  (Settings > Safari > Advanced)
    Screenshot 2016.02.06 13.07.15


VI. Appium Server Configuration

  • Start ios_webkit_debug_proxy
    ios_webkit_debug_proxy -c <iOS_SERIAL_NUMBER>:27753
    
    
  • Start the sever inside the appium directory
    node . -U <iOS_SERIAL_NUMBER> --app 'safari' --command-timeout 120
    
    
    Note:
    iOS_SERIAL_NUMBER can be found in
    About This Mac > Overview > System Report > Hardware > USB
    2016-02-06_1317

VII. Code Configuration


 String hubUrl = "http://127.0.0.1:4723/wd/hub";  
 String iOSBrowserName = "Safari";  
 String iOSDeviceName = "<iOS_SERIAL_NUMBER>";  
 String iOSVersion = 9.2;  

 DesiredCapabilities capabilities = new DesiredCapabilities();  
 capabilities.setCapability("deviceName", iOSDeviceName);  
 capabilities.setCapability("platformName", "iOS");  
 capabilities.setCapability("platformVersion", iOSVersion);  
 capabilities.setCapability("browserName", iOSBrowserName);  
 WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities); 


VIII. Possible Runtime Errors

  • FBSOpenApplicationErrorDomain error

    info: [debug] [INST STDERR] Instruments Trace Error : Target failed to run: The operation couldn’t be completed. (FBSOpenApplicationErrorDomain error 3.) : Failed to launch process with bundle identifier ‘com.github.blago.SafariLauncher'

    Solution:

    Trust the developer who signed the SafariLauncher app (Settings > General > Device Management)
    Screenshot 2016.02.06 13.35.13
  • ideviceinstaller error

    Error: Command failed: /bin/sh -c ideviceinstaller

    Solution:

    Select the target device in Xcode for the SafariLauncher then restart the appium node.2016-02-06_1620
    2016-02-06_1626

IX. Sample Appium Server Output with iOS Device Screen

  • On standby, waiting for test to run
    2016-02-06_1740
  • Test executed, SafariLauncher started on iOS device.
    2016-02-06_1734
  • Actual test being executed, application under test is m.facebook.com
    2016-02-06_1735

Click here to watch the execution.

Thanks for visiting my blog!

32 comments:

  1. Bernard, Thank you! Thank you!!!
    I spent the whole weekend trying to make this work. Then I found your article...
    Very grateful to you for putting all this together in a clear way.
    Cheers
    Omer Shatil

    ReplyDelete
  2. Hi Bernard,
    Getting the below Error,Will you please help me.

    An App ID with Identifier 'com.github.blago.SafariLauncher' is not available. Please enter a different string.

    ReplyDelete
    Replies
    1. The free ios provisioning started with Xcode 7... It might work.

      Provide your own bundle id for the app. That one was already used and was provided as an example.

      Delete
  3. Thank you, it is works. Can I know how to do it in simulator, since there is no serial number for simulator.

    ReplyDelete
    Replies
    1. Inside the appium directory start the node using:
      node . --app 'safari'

      If there are errors such as the one below...

      error: uncaughtException: Cannot find module 'rimraf'

      Just do the following command inside the appium directory:
      npm install rimraf

      Some modules are: adm-zip, mkdirp

      For the script setup:
      DesiredCapabilities capabilities = new DesiredCapabilities();
      capabilities.setCapability("deviceName", "iPhone 6");
      capabilities.setCapability("platformName", "iOS");
      capabilities.setCapability("platformVersion", "9.2");
      capabilities.setCapability("browserName", "safari");
      driver = new RemoteWebDriver(new URL(hubUrl), capabilities);
      driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
      driver.get(url);

      With XCode 7.2.1, available devices are:
      ["iPad 2 (9.2) [5938A9F7-A0D2-49C7-8F15-F07B34F58FBC]","iPad Air (9.2) [ADDFBAFE-4F28-44C0-A492-8A1E4D699EA0]","iPad Air 2 (9.2) [E041DAAA-FF81-449B-A8DE-DE73CC032803]","iPad Pro (9.2) [1B693D22-DDA3-4B59-A399-BDC48469D626]","iPad Retina (9.2) [94F3009C-B287-44B7-AF18-54402F9D7D3B]","iPhone 4s (9.2) [4A7C33D1-9A4C-432F-B6A0-6CE0DB6951D8]","iPhone 5 (9.2) [90CA0C5A-E732-408E-A60F-C52D74D66081]","iPhone 5s (9.2) [EE817852-113F-41F9-85EB-D69852E00A6F]","iPhone 6 (9.2) [D15DB589-368A-482F-B4F0-CDE525A7C380]","iPhone 6 Plus (9.2) [63BC22C8-47DF-4DA8-AE62-A779B5CA6629]","iPhone 6s (9.2) [1EACC8D5-B07F-4906-A491-84412E5473F8]","iPhone 6s (9.2) + Apple Watch - 38mm (2.1) [C393DB9E-0C9E-4F29-8576-FBD119955BCE]","iPhone 6s Plus (9.2) [0F3B4E66-1E12-4A38-90BD-D7A7071E760C]","iPhone 6s Plus (9.2) + Apple Watch - 42mm (2.1) [1306CD4C-E745-483E-8782-4A04F390B019]"]

      Delete
    2. Thanks for reply, how about the ios_webkit_debug_proxy, it is not working, because error come with this line
      new RemoteWebDriver(new URL(hubUrl), capabilities);

      Delete
    3. debug proxy is no longer needed if you're starting an iOS simulator. That is needed for the real device.

      You need to define the hub url:
      String hubUrl = "http://127.0.0.1:4723/wd/hub"

      Delete
    4. WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities);

      Delete
    5. Ya, I already defined before that. I mean the error is come from the hubUrl

      Delete
    6. Which error?
      Paste the error here: http://pastebin.com

      Delete
    7. Just
      Exception in thread "main" org.openqa.selenium.UnsupportedCommandException: That URL did not map to a valid JSONWP resource

      Delete
    8. If the appium node is successfully started, there shouldn't be any problem with this:
      String hubUrl = "http://127.0.0.1:4723/wd/hub";
      WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities);

      Paste this in your browser to check if the node is started.
      http://127.0.0.1:4723/wd/hub/status

      Other than that, you can try tweaking the values to localhost instead of 127.0.0.1



      Delete
  4. Working fine.Great article.

    is it possible to excute the script on Multiple Devices at the same time?(Real Devices)

    ReplyDelete
    Replies
    1. Yes, familiarize yourself with using Selenium Grid and TestNG.

      Here's an example of a testng suite:
      https://github.com/SrinivasanTarget/AppiumIOSParallel/blob/master/testng.xml

      For the selenium grid config, you'll need a nodeconfig.json file.
      you can refer to this:
      https://github.com/appium/appium/issues/273

      REMOTE_DEBUGGER_PORT=27753
      node appium --session-override --nodeconfig /path/to/iOSDevice1.json --show-ios-log --tmp /tmp/iOSDevice1/ -p 4623 --bootstrap-port 4464 --webkit-debug-proxy-port 27753 --full-reset --native-instruments-lib --safari > logs/iOSDevice1.txt &

      REMOTE_DEBUGGER_PORT=27754
      node appium --session-override --nodeconfig /path/to/iOSDevice2.json --show-ios-log --tmp /tmp/iOSDevice2/ -p 4523 --bootstrap-port 4465 --webkit-debug-proxy-port 27754 --full-reset --native-instruments-lib --safari > logs/iOSDevice2.txt &

      REMOTE_DEBUGGER_PORT=27753
      ./ios-webkit-debug-proxy-launcher.js -c DEVICE_1_SERIAL:27753 -d > logs/log_iwdpl.txt &

      REMOTE_DEBUGGER_PORT=27754
      ./ios-webkit-debug-proxy-launcher.js -c DEVICE_2_SERIAL:27754 -d > logs/log_iwdpl2.txt

      Delete
  5. Hi Bernard,

    Thanks for such a wonderful article. It has helped a lot. But, I'm facing a problem. I'm able to launch safari using SafariLauncher, but the URL is not entered in the safari browser. It seems as if the control is not passed from SafariLauncher app to safari browser. Please help here. Also, safariLauncher is opened if I pass the path of .app of safariLauncher in App Path. If I select the 'Mobile Safari' option in appium and pass 'Safari' as browserName, then error is thrown with message 'A new session could not be created.' I have a understanding the safari browser will be launched via SafariLauncher only by our scripts. Pls correct me if I'm wrong and help me with the solution.

    Thanks,
    Prashant

    ReplyDelete
    Replies
    1. Are you using appium GUI? since you stated that you're selecting 'Mobile Safari'. Start the appium via terminal using node, after building it from source using:
      ./reset.sh --ios --real-safari --verbose

      Safari is launched via SafariLauncher, generated by the command above.

      If you encounter 'A new session could not be created.', you can always restart the appium node via Ctrl+C command in the terminal.

      Delete
    2. Thanks Bernard. Yes, I was trying with the appium GUI. Also, I figured out the issue I was facing. The issue was that the control was not being passed from SafariLauncher app to safari browser. I did some research on it and got the solution to use context switching and it worked. Alternatively, we can set the 'autoWebview' desired Capabilities to 'true' to overcome this. Both of the above solution worked for me. Please let me know if my understanding is not right.

      Thanks,
      Prashant

      Delete
  6. Hello Bernard,

    Is it possible to update your example and blog to use the latest Appium 1.5.3?

    As the latest Appium 1.5.3 has removed the reset.sh script I am at a lost.

    Thanks,

    -----John.

    ReplyDelete
  7. With iphone5s I'm getting the following error (on Appium 1.5.3):

    org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: We exceeded the number of retries allowed for instruments to successfully start; failing launch (WARNING: The server did not provide any stacktrace information)
    Command duration or timeout: 34.20 seconds
    Build info: version: '3.0.0-beta2', revision: '2aa21c1', time: '2016-08-02 15:03:28 -0700'
    System info: host: 'ip-10-207-203-234.ec2.internal', ip: '10.207.203.234', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.6', java.version: '1.8.0_102'
    Driver info: io.appium.java_client.ios.IOSDriver

    Can you please help me with this. Appium Inspector is working fine

    ReplyDelete
    Replies
    1. You don't really have to use the appium java_client package for Mobile Safari.

      Remote WebDriver with DesiredCapabilites will suffice as indicated in the Code Configuration section of the blog.

      String hubUrl = "http://127.0.0.1:4723/wd/hub";
      String iOSBrowserName = "safari";
      String iOSDeviceName = "";
      String iOSVersion = “9.2”;

      DesiredCapabilities capabilities = new DesiredCapabilities();
      capabilities.setCapability("deviceName", iOSDeviceName);
      capabilities.setCapability("platformName", "iOS");
      capabilities.setCapability("platformVersion", iOSVersion);
      capabilities.setCapability("browserName", iOSBrowserName);
      WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities);

      Delete
  8. Hello bernard, I am still wondering how were you able to display the Real Iphone to the laptop screen?

    ReplyDelete
    Replies
    1. QuickTime Player

      see https://youtu.be/ZZ_UFqN12Rs

      Delete
  9. Is it possbile to do this on windows laptop connected to IOS devices

    ReplyDelete
    Replies
    1. You obviously need OSX for this to work with iOS devices.

      Delete
  10. Hi Mr Lago, after cloning appium, I can't find reset.sh file. Please!

    ReplyDelete
  11. reset.sh is no longer present in the appium's master branch (v 1.6 beta 2)

    This is applicable for appium v1.4.16, iOS 9.2 and Xcode 7.2.1.
    If you still want to try it with v1.4.16 in the clone directory of appium, run the ff command:
    git checkout 54c2b4e6500eddbca4bb3047116ceb8d177f7294

    This was the revision used for this blog as indicated in section III.

    I haven't tried the latest version of appium since I am no longer working on any iOS mobile safari automation.

    If you have iOS 9.3, you will have to use Xcode 7.3.1.
    If you have iOS 10+, you will have to use Xcode 8. But, this configuration will no longer be applicable since Apple deprecated UIAutomation from Xcode.

    See
    https://discuss.appium.io/t/ios9-uiautomation-what-is-appium-approach-to-uiautomation-deprecation-by-apple/7319
    https://github.com/appium/appium/issues/6597

    ReplyDelete
    Replies
    1. Thanks for your reply. I rollback appium v1.4.0 and I am using it. But, I face an error. I can't navigate an URL. Thank you!
      This is my code:
      String myURL = "http://reputa.vn";
      String hubUrl = "http://127.0.0.1:4723/wd/hub";
      String iOSDeviceName = "86f9ee569fb6a89ef223b49e1986c182d175c703";
      String iOSVersion = "9.3";
      DesiredCapabilities capabilities = new DesiredCapabilities();
      capabilities.setCapability("deviceName", iOSDeviceName);
      capabilities.setCapability("platformName", "iOS");
      capabilities.setCapability("platformVersion", iOSVersion);
      capabilities.setCapability("app","/Users/kakalot/Desktop/SafariLauncher.app");
      WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities);
      driver.get(myURL);
      driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);

      Appium's log:

      info: --> POST /wd/hub/session {"desiredCapabilities":{"app":"/Users/kakalot/Desktop/SafariLauncher.app","platformVersion":"9.3","platformName":"iOS","deviceName":"86f9ee569fb6a89ef223b49e1986c182d175c703"}}

      info: Client User-Agent string: Apache-HttpClient/4.5.2 (Java/1.8.0_60)
      info: *************************************
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: **** NEW SESSION ***
      info: *************************************

      info: [debug] Using local app from desired caps: /Users/kakalot/Desktop/SafariLauncher.app
      info: [debug] Creating new appium session 67c6bb44-6ef5-41ac-a7c6-976dba134122

      {"id":""},"enabled":true,"valid":true,"visible":false,"hint":"","path":"/0/1/4/0/0/11","x":0,"y":893,"width":70,"height":72},">":{"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"iOS","platform":"MAC","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"warnings":{},"desired":{"app":"/Users/kakalot/Desktop/SafariLauncher.app","platformVersion":"9.3","platformName":"iOS","deviceName":"86f9ee569fb6a89ef223b49e1986c182d175c703"},"app":"/Users/kakalot/Desktop/SafariLauncher.app","platformVersion":"9.3","platformName":"iOS","deviceName":"86f9ee569fb6a89ef223b49e1986c182d175c703"},"sessionId":"67c6bb44-6ef5-41ac-a7c6-976dba134122"}
      info: --> POST /wd/hub/session/67c6bb44-6ef5-41ac-a7c6-976dba134122/url {"url":"http://reputa.vn"}

      Delete
  12. Here's the latest post using appium 1.6.0 and iPhone 6 Simulator
    (iOS 10 and Xcode 8)
    http://bernardlago.blogspot.com/2016/10/running-mobile-web-tests-on-iphone-6.html

    ReplyDelete
  13. Hi, Thanks for such a wonderful post!! Are you planning to create a blog for Real Device IOS automation with Appium for latest versions of IOS & Xcode or your current blog will support real ios for the latest versions too??

    ReplyDelete
    Replies
    1. I am just using the "FREE" provisioning profile, I am limited to running it on simulators.
      http://bernardlago.blogspot.com/2016/10/running-mobile-web-tests-on-iphone-6.html

      For real devices, you'll need a development team and provisionining profile to build and code-sign WebDriverAgent - which is needed to be installed on the device.
      https://github.com/appium/appium-xcuitest-driver#real-devices

      Delete
  14. with an app on your phone, and also continue to prolific on your tablet or desktop computer, and it is now becoming more and more common. originally us mobile app developer

    ReplyDelete