Test Example in Java

This page is not available in the language you requested. You have been redirected to the English version of the page.
Link to this page copied to clipboard
Not for use with personal data

Be sure to check out the full Appium setup guide with Axe DevTools Mobile if you're just getting started, or more examples of Axe DevTools Mobile for Appium in other languages.

Full Example with UIAutomator2

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AndroidDebugTest {
    private static AndroidDriver driver;

    private static final String[] TAGS = {"appium", "qa"};
    // ignoreRules is passed directly into each 'mobile: axeScan' call
    private static Map<String, Object> scanSettings;
    private static final String API_KEY = "<DEQUE_APIKEY>";
    private static final String PROJECT_ID = "<DEQUE_PROJECT_ID>";
    private static final String APP_PACKAGE = "<YOUR_APP_PACKAGE_NAME>";

    // It is very important to make a call to 'mobile: axeStartSession' inside @BeforeAll annotated
    // method to set up your session for posting to Axe Developer Hub
    @BeforeAll
    public static void setUp() throws MalformedURLException {
        UiAutomator2Options options = new UiAutomator2Options()
                .setPlatformName("Android")
                .setDeviceName("Android")
                .setAppPackage(APP_PACKAGE)
                .setAppActivity(".MainActivity")
                .setAutomationName("AxeUiAutomator2")
                .setUiautomator2ServerLaunchTimeout(Duration.ofMillis(60000))
                .setUiautomator2ServerInstallTimeout(Duration.ofMillis(60000))
                .setAdbExecTimeout(Duration.ofMillis(60000))
                .setIgnoreHiddenApiPolicyError(true)
                .setDisableWindowAnimation(true)
                .setCapability("waitForIdle", true)
                .setNewCommandTimeout(Duration.ofSeconds(300))
                .setNoReset(false)
                .setFullReset(false);

        driver = new AndroidDriver(new URL("http://localhost:4723/"), options);

        scanSettings = new HashMap<>();
        // tags and ignoreRules are deprecated and will be removed once we fully transition to Axe Developer Hub
        scanSettings.put("ignoreRules", new String[]{"ScreenOrientation"});
        scanSettings.put("tags", TAGS);

        // Make a one-time call to set up your session for posting to Axe Developer Hub
        // This also accepts `axeAccountURL` in case of a private instance
        // The `axeSettings` object still accepts `apiKey` with `axeAccountURL`, but is not required
        // if you are going to make one time @BeforeAll annotated call to 'mobile: axeStartSession'
        Map<String, Object> axeSettings = new HashMap<>();
        axeSettings.put("apiKey", API_KEY);
        axeSettings.put("projectId", PROJECT_ID);
        axeSettings.put("axeUploadResults", true); // default - set to false to keep results local only
        driver.executeScript("mobile: axeStartSession", axeSettings);
    }

    @AfterAll
    public static void tearDown() {
        if (driver != null) {
            // Generate an HTML report from all accumulated scans before quitting
            // The report aggregates every 'mobile: axeScan' call made during this session.
            try {
                Map<String, Object> reportSettings = new HashMap<>();
                reportSettings.put("scanName", "My Accessibility Report");
                reportSettings.put("axeHtmlReportPath", "build/AxeDevToolsMobileResults");

                @SuppressWarnings("unchecked")
                Map<String, Object> reportResult = (Map<String, Object>) driver.executeScript(
                        "mobile: axeGenerateHtmlReportAndSummary", reportSettings);

                if (reportResult.containsKey("axeError")) {
                    System.out.println("Report generation failed: " + reportResult.get("axeError"));
                } else {
                    System.out.println("HTML report saved to: " + reportResult.get("localDirectory"));
                }
            } catch (Exception e) {
                System.out.println("Failed to generate HTML report: " + e.getMessage());
            }

            driver.quit();
        }
    }

    private void dismissSystemUIError() {
        try {
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
            WebElement systemPopup = wait.until(ExpectedConditions.presenceOfElementLocated(
                    By.xpath("//*[contains(@text, 'System UI')]")));

            if (systemPopup.isDisplayed()) {
                WebElement waitButton = wait.until(ExpectedConditions.presenceOfElementLocated(
                        By.xpath("//*[contains(@text, 'Wait')]")));
                waitButton.click();
                System.out.println("Dismissed the System UI error popup by clicking 'Wait'.");
            }
        } catch (Exception e) {
            System.out.println("No System UI error popup appeared.");
        }
    }

    private void launchApp() {
        try {
            driver.terminateApp(APP_PACKAGE);
            Thread.sleep(1000); // Give the app time to fully terminate
        } catch (Exception e) {
            System.out.println("App was not running or failed to terminate: " + e.getMessage());
        }

        driver.activateApp(APP_PACKAGE);

        try {
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
            wait.until(ExpectedConditions.presenceOfElementLocated(
                    By.xpath("//*[contains(@text, 'Screen Name')]")));
        } catch (Exception e) {
            dismissSystemUIError();
            // Try again after dismissing the error (if not, then fail)
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
            wait.until(ExpectedConditions.presenceOfElementLocated(
                    By.xpath("//*[contains(@text, 'Screen Name')]")));
        }
    }

    @BeforeEach
    public void beforeEach() {
        launchApp();
    }

    // Now since your session is authenticated you can keep making 'mobile: axeScan' call
    // like below in test1 and test2. The scans will be uploaded to the dashboard and also grouped in Developer Hub.
    @Test
    @Order(1)
    public void test1() {
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(
                By.xpath("//*[contains(@text, 'Screen Name')]")));
        System.out.println(element.getText());

        Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", scanSettings);
        List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
        System.out.println("debug: Total results: " + results.size());
    }

    @Test
    @Order(2)
    public void test2() {
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(
                By.xpath("//*[contains(@text, 'Screen Name')]")));
        element.click();

        driver.findElement(By.xpath("//*[contains(@text, 'announced by a screen')]"));

        Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", scanSettings);
        List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
        System.out.println("debug: Total results: " + results.size());
    }
}

Full Example with XCUITest

import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.junit.jupiter.api.*;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class IOSMapsDebugTest {
    private static IOSDriver driver;

    private static final String[] TAGS = {"appium", "qa", "ios"};
    // ignoreRules is passed directly into each 'mobile: axeScan' call
    private static Map<String, Object> scanSettings;
    private static final String API_KEY = "<DEQUE_APIKEY>";
    private static final String PROJECT_ID = "<DEQUE_PROJECT_ID>";
    private static final String BUNDLE_ID = "com.apple.Maps";

    // It is very important to make a call to 'mobile: axeStartSession' inside @BeforeAll annotated
    // method to set up your session for posting to Axe Developer Hub
    @BeforeAll
    public static void setUp() throws MalformedURLException {
        // Matching the working sample - minimal capabilities
        XCUITestOptions options = new XCUITestOptions()
                .setPlatformName("iOS")
                .setAutomationName("AxeXCUITest")
                .setUdid("<YOUR_DEVICE_OR_SIMULATOR_UDID>")
                .setBundleId(BUNDLE_ID)
                .setWdaLaunchTimeout(Duration.ofMillis(960000)); // 16 minutes like the working sample
                // NOT specifying platformVersion - let it auto-detect

        driver = new IOSDriver(new URL("http://127.0.0.1:4723/"), options);

        scanSettings = new HashMap<>();
        // tags and ignoreRules are deprecated and will be removed once we fully transition to Axe Developer Hub
        scanSettings.put("ignoreRules", new String[]{"ScreenOrientation"});
        scanSettings.put("tags", TAGS);

        // Make a one-time call to set up your session for posting to Axe Developer Hub
        // This also accepts `axeAccountURL` in case of a private instance
        // The `axeSettings` object still accepts `apiKey` with `axeAccountURL`, but is not required
        // if you are going to make one time @BeforeAll annotated call to 'mobile: axeStartSession'
        Map<String, Object> axeSettings = new HashMap<>();
        axeSettings.put("apiKey", API_KEY);
        axeSettings.put("projectId", PROJECT_ID);
        axeSettings.put("axeAccountURL", "https://mobile-qa.dequelabs.com");
        axeSettings.put("axeUploadResults", true); // default - set to false to keep results local only
        driver.executeScript("mobile: axeStartSession", axeSettings);
    }

    @AfterAll
    public static void tearDown() {
        if (driver != null) {
            // Generate HTML reports before quitting the driver
            driver.executeScript("mobile: axeGenerateHtmlReportAndSummary", new HashMap<>());

            // Alternative: Generate HTML reports to a custom path
            // Map<String, Object> reportSettings = new HashMap<>();
            // reportSettings.put("outputPath", "/Users/me/MyReports");
            // driver.executeScript("mobile: axeGenerateHtmlReportAndSummary", reportSettings);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            driver.quit();
        }
    }

    private void launchApp() {
        driver.activateApp(BUNDLE_ID);
    }

    @BeforeEach
    public void beforeEach() {
        launchApp();
    }

    // Now since your session is authenticated you can keep making 'mobile: axeScan' call
    // like below in test1 and test2. The scans will be uploaded to the dashboard and also grouped in Developer Hub.
    @Test
    @Order(1)
    @Timeout(10)
    public void test1_scanMainScreen() {
        Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", scanSettings);
        List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
        System.out.println("debug: Total results: " + results.size());
    }

    @Test
    @Order(2)
    @Timeout(10)
    public void test2_clickSearchAndScan() {
        Map<String, Object> appiumScanResult = (Map<String, Object>) driver.executeScript("mobile: axeScan", scanSettings);
        List<?> results = (List<?>) appiumScanResult.get("axeRuleResults");
        System.out.println("debug: Total results: " + results.size());
    }
}
note
Using TestNG? No problem. Our Appium drivers are compatible with this testing framework.