| title | Measure accessibility for native mobile |
|---|---|
| description | Use accessibility IDs to verify that key screens are reachable and usable by screen readers on iOS and Android. |
| sidebarTitle | Native mobile |
Use accessibility IDs to verify that key screens are reachable and usable by screen readers on iOS and Android. The ~ selector prefix is WebdriverIO shorthand for locating an element by its accessibility ID — this proves the element is correctly exposed to the accessibility layer and keeps your tests stable across visual refactors.
Verify a screen's key elements are reachable via accessibility semantics
const email = await driver.$("~Email");
const password = await driver.$("~Password");
const loginBtn = await driver.$("~Log in");
await expect(email).toBeDisplayed();
await expect(password).toBeDisplayed();
await expect(loginBtn).toBeEnabled();Assert that icon buttons have meaningful accessible names
const menuIcon = await driver.$("~Main menu");
await expect(menuIcon).toBeDisplayed();
const label = await menuIcon.getAttribute("content-desc");
if (!label || label.trim().length < 3) {
throw new Error("A11y: menu icon accessible label is missing or too short");
}- Your app has key interactive elements that must be exposed to VoiceOver (iOS) or TalkBack (Android).
- Your team wants to catch regressions where a developer removed or renamed an accessibility label.
- Your team wants a lightweight proxy for accessibility hygiene without a full WCAG audit.
- Your app has icon-only buttons that must have meaningful accessible names.
| Platform | Attribute | WebdriverIO selector |
|---|---|---|
| Android | content-desc |
driver.$('~your-label') |
| iOS | accessibilityIdentifier / accessible name |
driver.$('~your-id') |
| Goal | How to do it |
|---|---|
| Prove an element is on the accessibility layer | Locate it with driver.$('~accessibility-id') |
| Assert a screen is navigable via accessibility semantics | Use ~ selectors throughout the full flow |
| Assert an icon button has a meaningful label | getAttribute('content-desc') and check length |
| Assert an error or toast is readable | await expect($('~error-id')).toHaveText('...') |
import { flow } from "@qawolf/flows/android";
export default flow(
"Accessibility — login screen",
{ target: "Android - Any Phone (Latest | Emulator)", launch: true },
async ({ driver, test }) => {
await test("verify login screen elements are accessible", async () => {
// Arrange
await driver.$("~Email").waitForDisplayed({ timeout: 10_000 });
// Act
const email = driver.$("~Email");
const password = driver.$("~Password");
const loginBtn = driver.$("~Log in");
await email.setValue(process.env.USER_EMAIL);
await password.setValue(process.env.USER_PASSWORD);
await loginBtn.click();
// Assert
await expect(driver.$("~Home")).toBeDisplayed();
const menuIcon = driver.$("~Main menu");
await expect(menuIcon).toBeDisplayed();
const label = await menuIcon.getAttribute("content-desc");
if (!label || label.trim().length < 3) {
throw new Error("A11y: menu icon accessible label is missing or too short");
}
});
},
);