Working with multiple webviews on Android hybrid appsWorking with multiple webviews on Android hybrid apps

Working with Multiple Webviews in Android Hybrid Apps

June 12, 2019
 by 
 Jonathan Lipps Jonathan Lipps
Jonathan Lipps

It's not very common, but it can happen that an Android hybrid app has not just one but two or more webviews (for example, when advertisements are in one webview and app logic is in another).

Multiple Webviews in Android
Check out: Managing Chromedriver for Android Chrome and Webview Testing

If you've ever found yourself in this situation, you may have been frustrated when using Appium's context API, specifically with code like this:


Set handles = driver.getContextHandles();

If you have multiple webviews, you might have expected the Set to contain three items: NATIVE_APP, and two webview-looking strings. But instead you probably found only one webview context. If you were lucky, it was the one you wanted, but there was an equal chance you got the unimportant advertising webview (or whatever). So, how can we be sure to get inside the particular webview we want, even when there are multiple?

Also check: How to Accurately Select Webviews Using the `fullContextList` Capability

The answer lies in how Chromedriver works. Appium uses Chromedriver for Chrome automation, and Chromedriver handles multiple webviews in a single Android app by treating them as separate windows. What this means is that we have access to them using the built-in Webdriver window commands, like so:


Set windowHandles = driver.getWindowHandles();

Then, we can switch into any window we want by picking a certain handle:


driver.switchTo().window(handle);

How do we know from the handle which window to choose? We don't, because it will be a random-looking string like CDwindow-A26869B5EDAEAA9D83947BB274F1D0C7, and it might change from test to test. So our best bet is to switch into each one and perform some validation on the window, to prove that we are in the correct one (for example, looking at the URL or title as a way of ensuring we're in the right webview, and short-circuiting our search when we find it).

Run multiple tests (manual or automated) on real Android devices without experiencing any slowdown. Know more.

Of course, to do any of this we actually have to be in the webview context first, otherwise the getWindowHandles command won't do anything. So to conclude, here's a full example that utilizes a new feature of The App, namely a view with two webviews. The idea behind this test is simply to make an assertion based on the contents of the page displayed in each webview, proving that we can indeed enter and automate both of them. As with this edition, we use a helper function getWebContext to get ourselves into the initial web context so that Chromedriver is operative. Without further ado, here's the full sample:


import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileBy;
import io.appium.java_client.android.AndroidDriver;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import javax.annotation.Nullable;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Edition073_Multiple_Webviews {

    private String APP_ANDROID = "https://github.com/cloudgrey-io/the-app/releases/download/v1.10.0/TheApp-v1.10.0.apk";

    private AndroidDriver driver;
    private static By hybridScreen = MobileBy.AccessibilityId("Dual Webview Demo");
    private static By webview = By.className("android.webkit.WebView");

    @Before
    public void setUp() throws MalformedURLException {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability("platformName", "Android");
        capabilities.setCapability("deviceName", "Android Emulator");
        capabilities.setCapability("automationName", "UiAutomator2");
        capabilities.setCapability("app", APP_ANDROID);

        driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), capabilities);
    }

    @After
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    public void testDualWebviews_Android() {
        WebDriverWait wait = new WebDriverWait(driver, 10);

        // get to dual webview screen and make sure it's loaded
        wait.until(ExpectedConditions.presenceOfElementLocated(hybridScreen)).click();
        wait.until(ExpectedConditions.presenceOfElementLocated(webview));

        // navigate to the webview context
        String webContext = getWebContext(driver);
        driver.context(webContext);

        // go into each available window and get the text from the html page
        ArrayList webviewTexts = new ArrayList<>();
        for (String handle : driver.getWindowHandles()) {
            System.out.println(handle);
            driver.switchTo().window(handle);
            webviewTexts.add(driver.findElement(By.tagName("body")).getText());
        }

        // assert that we got the correct text from each android webview
        Assert.assertThat(webviewTexts,
            Matchers.containsInAnyOrder("This is webview '1'", "This is webview '2'"));
    }

    @Nullable
    private String getWebContext(AppiumDriver driver) {
        ArrayList contexts = new ArrayList(driver.getContextHandles());
        for (String context : contexts) {
            if (!context.equals("NATIVE_APP")) {
                return context;
            }
        }
        return null;
    }
}

That's it! With judicious use of both context and window handles commands, automating multiple webviews is totally doable. Don't forget to check out the full code on GitHub.

Share this

Working with Multiple Webviews in Android Hybrid Apps

4 Parts