Tuesday, January 14, 2014

Struggles and successes with ios-driver

or how to get scrolling, the inspector and different SDK versions to work with ios-driver

I've been writing some code for Mobile Safari that is sensitive to scrolling, viewport (scaling) and browser chrome visibility (basically I want to show a as-fullscreen-as-possible popup that uses gestures). It ended up being a lot more code than anticipated (hah, what else is new) so I really wanted some tests for it.

My requirements for the testing environment are thus: runs Mobile Safari (not just UIWebView), can simulate touch gestures and native scroll, can test on iPhone, iPad; iOS6.1 and iOS7 with different orientations and resolutions - note that programmatic scrolling through the browser javascript doesn't hide the browser chrome on iOS7. The only thing that I found that can support these is ios-driver (a Selenium driver for iOS simulator and devices).

Getting everything working together turned out to be somewhat involved, somewhat under-documented and somewhat buggy:

Note: all code examples are in python. We at Brightside are a python shop.

The combination of versions I got to work was XCode 5.0.2/4.6.3 on OS-X 10.9.1 using the 'refactor' branch of ios-driver and maven from macports.

Ios-driver is actually not just a browser-driver: it can drive the browser through WebKit remote debugging and the native app through UI Automation. You must switch the driver between the two modes for things to work. This is done through driver.switch_to_window("Native") and driver.switch_to_window("Web"). E.g.,:

  • UI Automation (touch, native widgets) uses Native mode
  • URL navigation uses Web mode
  • You must switch the driver to Native mode before trying to access the Inspector
  • You can run javascript code in either, but in Native mode you are talking to the UI Automation javascript environment and in Web mode to the browser

Note that since UI Automation needs to be enabled for the app you are driving, ios-driver can't run against Mobile Safari on a (non-jailbroken) device. It automatically modifies the bundle on the simulator.

Scroll gestures are in theory really simple: driver.execute_script("UIATarget.localTarget().dragFromToForDuration({ x: 10, y: 250 }, { x: 10, y: 50 }, 0.5);"). However, they don't work on iOS7 (Or at least I couldn't get them to work, and neither have other people, see for example the workaround in Subliminal). They work great with iOS6 though.

There are a couple of ways to scroll on iOS7. You can scroll the scrollview with something like driver.execute_script("UIATarget.localTarget().frontMostApp().mainWindow().scrollViews()[0].scrollViews()[0].scrollDown()"), but this (in my experience) scrolls all the way to the bottom. You can also scroll to specific native elements with e = driver.find_element_by_name("your div's text goes here") and driver.execute_script("arguments[0].scrollToVisible();", e) (this I couldn't get to work with iOS6, it gave 'stale element' errors).

Switching orientation is really easy: driver.execute_script("UIATarget.localTarget().setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);").

The ios-driver documentation talks (amongst the TODOs) about being able to use different SDK/simulator versions based on the desired capabilities the client asks for. I didn't get any of this to work (the best I managed was to get 6.1 Safari installed on the 7.0 simulator, which does not work). What does work is installing XCode 4.6 side-by-side with 5.0 and running DEVELOPER_DIR=/Applications/Xcode4.6.app/Contents/Developer/ java -jar ios-server-0.6.5-jar-with-dependencies.jar -simulators. Sadly Instruments 4.6 will prompt you for access every time on OS-X 10.9.

Ios-driver doesn't support getting the browser console logs as such. I wrote a shim that replaces window.console and stores the logged messages in an array. I then use messages = driver.execute_script("return window.bside_get_messages ? window.bside_get_messages() : [];") to get them to the client.

Thanks to the ios-driver developers! I got everything I wanted working in the end. Hope these notes prove useful to the next person trying to do the same.

No comments: