diff --git a/docs/en/advanced-concepts/hybrid.md b/docs/en/advanced-concepts/hybrid.md index 275e6aaeb..b2fda29a9 100644 --- a/docs/en/advanced-concepts/hybrid.md +++ b/docs/en/advanced-concepts/hybrid.md @@ -2,17 +2,12 @@ One of the core principles of Appium is that you shouldn't have to change your app to test it. In line with that methodology, it is possible to test -hybrid web apps (e.g., the "UIWebView" elements in an iOS app) the same* way +hybrid web apps (e.g., the "UIWebView" elements in an iOS app) the same way you can with Selenium for web apps. There is a bit of technical complexity required so that Appium knows whether you want to automate the native aspects of the app or the web views, but thankfully, we can stay within the WebDriver protocol for everything. -* We're working on filling out the methods available in web view contexts. -[Join us in our quest!](http://appium.io/get-involved.html) - -### Automating hybrid iOS apps - Here are the steps required to talk to a web view in your Appium test: 1. Navigate to a portion of your app where a web view is active @@ -31,13 +26,139 @@ Here are the steps required to talk to a web view in your Appium test: native portion of the app, simply call `context` again with the native context id to leave the web frame. -### Execution against a real iOS device +```javascript +// javascript +// assuming we have an initialized `driver` object for an app +driver + .contexts().then(function (contexts) { // get list of available views. Returns array: ["NATIVE_APP","WEBVIEW_1"] + return driver.context(contexts[1]); // choose the webview context + }) + + // do some web testing + .elementsByCss('.green_button').click() + + .context('NATIVE_APP') // leave webview context + + // do more native stuff here if we want + + .quit() // stop webdrivage +``` + +```java +// java +// assuming we have an initialized `driver` object for an app +driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); + +Set contextNames = driver.getContextHandles(); +for (String contextName : contextNames) { + System.out.println(contextNames); //prints out something like NATIVE_APP \n WEBVIEW_1 +} +driver.context(contextNames.toArray()[1]); // set context to WEBVIEW_1 + +//do some web testing +String myText = driver.findElement(By.cssSelector(".green_button")).click(); + +driver.context("NATIVE_APP"); + +// do more native testing if we want + +driver.quit(); +``` + +```ruby +# ruby +# assuming we have an initialized `driver` object for an app +@driver = Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => SERVER_URL) + +# I switch to the last window because its always the webview in our case, in other cases you may need to specify a window number +# View the appium logs while running @driver.window_handles to figure out which window is the one you want and find the associated number +# Then switch to it using @driver.switch_to_window("6") + +Given(/^I switch to webview$/) do + webview = @driver.contexts.last + @driver.switch_to.context(webview) +end + +Given(/^I switch out of webview$/) do + @driver.switch_to(@driver.contexts.first) +end + +# Now you can use CSS to select an element inside your webview + +And(/^I click a webview button $/) do + @driver.find_element(:css, ".green_button").click +end +``` + +```python +# python +# assuming we have an initialized `driver` object for an app + +# switch to webview +webview = driver.contexts.last +driver.switch_to.context(webview) + +# do some webby stuff +driver.find_element(:css, ".green_button").click + +# switch back to native view +driver.switch_to(driver.contexts.first) + +# do more native testing if we want + +driver.quit() +``` + +```php +// php +// assuming we have an initialized `driver` object in an AppiumTestCase + +public function testThings() +{ + $expected_contexts = array( + 0 => 'NATIVE_APP', + 1 => 'WEBVIEW_1' + ); + + $contexts = $this->contexts(); + $this->assertEquals($expected_contexts, $contexts); + + $this->context('WEBVIEW_1'); + $context = $this->context(); + $this->assertEquals('WEBVIEW_1', $context); + + // do webby stuff + + $this->context('NATIVE_APP'); + + // do mobile stuff +} +``` + +### Automating hybrid Android apps + +Appium comes with built-in hybrid support via Chromedriver. Appium also uses +Selendroid under the hood for webview support on devices older than 4.4. (In +that case, you'll want to specify `"automationName": "selendroid"` as a desired +capability). + +Make sure +[setWebContentsDebuggingEnabled](http://developer.android.com/reference/android/webkit/WebView.html#setWebContentsDebuggingEnabled(boolean)) is set to true as described in the [remote debugging docs](https://developer.chrome.com/devtools/docs/remote-debugging#configure-webview). + +Once you've set your desired capabilities and started an appium session, follow the generalized instructions above. + +### Automating hybrid iOS apps To interact with a web view appium establishes a connection -using a remote debugger. When executing the examples below against a -simulator this connection can be established directly as the simulator and -the appium server are on the same machine. When executing against a real -device appium is unable to access the web view directly. Therefore the +using a remote debugger. When executing against a +simulator this connection is established directly as the simulator and +the appium server are on the same machine. + +Once you've set your desired capabilities and started an appium session, follow the generalized instructions above. + +### Execution against a real iOS device + +When executing against a real iOS device appium is unable to access the web view directly. Therefore the connection has to be established through the USB lead. To establish this connection we use the [ios-webkit-debugger-proxy](https://github.com/google/ios-webkit-debug-proxy). @@ -77,208 +198,4 @@ allow a connection to be established. Turn it on by going to **settings > safari > advanced**. Please be aware that the web inspector was **added as part of iOS 6** and was not available previously. -```javascript -// javascript -// assuming we have an initialized `driver` object working on the UICatalog app -return driver - .elementByName('Web, Use of UIWebView') // find button to nav to view - .click() // nav to UIWebView - .contexts().then(function (contexts) { // get list of available views - return driver.context(contexts[1]); // choose what is probably the webview context - }).elementsByCss('.some-class').then(function (els) { // get webpage elements by css - els.length.should.be.above(0); // there should be some! - return els[0]; - }).text() // get text of the first element - .should.become("My very own text") // it should be extremely personal and awesome - .context('NATIVE_APP') // leave webview context - // do more native stuff here if we want - .quit() // stop webdrivage - .done(); // end promise chain (may not be needed) -``` - -```java -// java -DesiredCapabilities capabilities = new DesiredCapabilities(); -capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); -capabilities.setCapability("platformVersion", "7.1"); -capabilities.setCapability("platformName", "iOS"); -capabilities.setCapability("deviceName", "iPhone Simulator"); -capabilities.SetCapability("app", app); - -driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); - -WebElement button = driver.findElement(By.id("buttonStartWebview")); -button.click(); -Thread.sleep(6000); -Set contextNames = driver.getContextHandles(); -for (String contextName : contextNames) { - System.out.println(contextName); - if (contextName.contains("WEBVIEW")){ - driver.context(contextName); - } -} -WebElement inputField = driver.findElement(By.id("name_input")); -inputField.sendKeys("Some name"); -``` - -```ruby -# ruby -TEST_NAME = "Example Ruby Test" -SERVER_URL = "http://127.0.0.1:4723/wd/hub" -APP_PATH = "https://dl.dropboxusercontent.com/s/123456789101112/ts_ios.zip" -capabilities = - { - 'platformName' => 'iOS', - 'platformVersion' => ''7.1', - 'deviceName' => 'iPhone Simulator', - 'app' => APP_PATH, - 'name' => TEST_NAME - } -@driver = Selenium::WebDriver.for(:remote, :desired_capabilities => capabilities, :url => SERVER_URL) - -# I switch to the last window because its always the webview in our case, in other cases you may need to specify a window number -# View the appium logs while running @driver.window_handles to figure out which window is the one you want and find the associated number -# Then switch to it using @driver.switch_to_window("6") - -Given(/^I switch to webview$/) do - webview = @driver.contexts.last - @driver.switch_to.context(webview) -end - -Given(/^I switch out of webview$/) do - @driver.switch_to(@driver.contexts.first) -end - -# Now you can use CSS to select an element inside your webview - -And(/^I click a webview button $/) do - @driver.find_element(:css, ".green_button").click -end -``` - -```python -# python -APP_PATH = "https://dl.dropboxusercontent.com/s/123456789101112/ts_ios.zip" -capabilities = { - 'platformName': 'iOS', - 'platformVersion': '7.1', - 'deviceName': 'iPhone Simulator', - 'app': APP_PATH, - 'name': "Example Hybrid Python Test" -} -driver = webdriver.Remote('http://localhost:4723/wd/hub', capabilities) - -# switch to webview -webview = driver.contexts.last -driver.switch_to.context(webview) - -# do some webby stuff -driver.find_element(:css, ".green_button").click - -# switch back to native view -driver.switch_to(driver.contexts.first) - -# Now you can use CSS to select an element inside your webview -``` - -```php -// php -APP_PATH = 'https://dl.dropboxusercontent.com/s/123456789101112/ts_ios.zip' -class ContextTests extends PHPUnit_Extensions_AppiumTestCase -{ - public static $browsers = array( - array( - 'desiredCapabilities' => array( - 'platformName' => 'iOS', - 'platformVersion' => '7.1', - 'deviceName' => 'iPhone Simulator', - 'app' => $myApp, - 'name' => 'Example Hybrid PHP Test' - ) - ) - ); - - public function testThings() - { - $expected_contexts = array( - 0 => 'NATIVE_APP', - 1 => 'WEBVIEW_1' - ); - - $contexts = $this->contexts(); - $this->assertEquals($expected_contexts, $contexts); - - $this->context('WEBVIEW_1'); - $context = $this->context(); - $this->assertEquals('WEBVIEW_1', $context); - - // do webby stuff - - $this->context('NATIVE_APP'); - - // do mobile stuff - } -} -``` - -### Automating hybrid Android apps - -Appium comes with built-in hybrid support via Chromedriver. Appium also uses -Selendroid under the hood for webview support on devices older than 4.4. (In -that case, you'll want to specify `"automationName": "selendroid"` as a desired -capability). Then follow all the same steps as above for iOS, i.e., -switching contexts, etc... - -Make sure -[setWebContentsDebuggingEnabled](http://developer.android.com/reference/android/webkit/WebView.html#setWebContentsDebuggingEnabled(boolean)) is set to true as described in the [remote debugging docs](https://developer.chrome.com/devtools/docs/remote-debugging#configure-webview). - -```javascript -// javascript -// assuming we have an initialized `driver` object working on a hybrid app -return driver - .context("WEBVIEW") // choose the only available view - .elementsByCss('.some-class').then(function (els) { // get webpage elements by css - els.length.should.be.above(0); // there should be some! - return els[0]; - }).text() // get text of the first element - .should.become("My very own text") // it should be extremely personal and awesome - .context("NATIVE_APP") // leave webview context - // do more native stuff here if we want - .quit() // stop webdrivage - .done(); // end promise chain (may not be needed) -``` - -```java -// java -DesiredCapabilities capabilities = new DesiredCapabilities(); -capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); -capabilities.setCapability("automationName","Selendroid"); -capabilities.setCapability("platformName", "Android"); -capabilities.setCapability("app", myApp); -driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); - -WebElement button = driver.findElement(By.id("buttonStartWebview")); -button.click(); -Thread.sleep(6000); -Set contextNames = driver.getContextHandles(); -for (String contextName : contextNames) { - System.out.println(contextName); - if (contextName.contains("WEBVIEW")){ - driver.context(contextName); - } -} -WebElement inputField = driver.findElement(By.id("name_input")); -inputField.sendKeys("Some name"); -``` - -```python -# python -# assuming we have an initialized `driver` object working on a hybrid app -driver.switch_to.context("WEBVIEW") -elements = driver.find_elements_by_css_selector('.some-class') -assertLess(0, len(elements)) -assertEqual('My very own text', elements[0].text) - -driver.switch_to.context("NATIVE_APP") -driver.quit() -``` +Now you can start an appium test session and follow the generalized instructions above.