Auto-waiting & Timeouts
Understand the actionability checks Mobilewright runs before every action, and how to control timeouts and waitFor states.
Why You Never Write sleep()
Flaky mobile tests are almost always timing problems: the test taps a button that is still animating, or reads text that has not rendered yet. Mobilewright solves this with actionability checks. Before every action, it waits until the element:
1. Exists in the view hierarchy → 2. Is visible on screen → 3. Is enabled for interaction → 4. Has stable bounds (not animating or moving).
If any check fails, Mobilewright retries every 100ms (stability is checked every 50ms to detect quickly when an element stops moving). If the checks still fail after the timeout — 5 seconds by default — the action throws a LocatorError with a clear message.
LocatorError, the element never became actionable in time. Check the failure screenshot in the report — often the app is showing an unexpected dialog or the locator matches nothing.Controlling Timeouts
You can override the timeout for a single action or assertion, or change the default globally in your config:
import { test, expect } from '@mobilewright/test';
test('per-action and per-assertion timeouts', async ({ screen }) => {
// Give this one tap up to 10 seconds
await screen.getByText('Submit').tap({ timeout: 10_000 });
// And this one assertion too
await expect(screen.getByText('Done')).toBeVisible({ timeout: 10_000 });
});
const { test, expect } = require('@mobilewright/test');
test('per-action and per-assertion timeouts', async ({ screen }) => {
// Give this one tap up to 10 seconds
await screen.getByText('Submit').tap({ timeout: 10_000 });
// And this one assertion too
await expect(screen.getByText('Done')).toBeVisible({ timeout: 10_000 });
});
import { defineConfig } from 'mobilewright';
export default defineConfig({
// Raise the default timeout for every action and assertion
timeout: 10_000,
});
const { defineConfig } = require('mobilewright');
module.exports = defineConfig({
// Raise the default timeout for every action and assertion
timeout: 10_000,
});
Waiting for a Specific State
Sometimes you want to wait for something without acting on it — for example, wait for a spinner to disappear before continuing. That is what waitFor() is for. Supported states are 'visible', 'hidden', 'enabled', and 'disabled':
import { test } from '@mobilewright/test';
test('waiting without acting', async ({ screen }) => {
// Wait for an element to appear
await screen.getByText('Loading complete').waitFor({ state: 'visible' });
// Wait for a spinner to disappear
await screen.getByText('Spinner').waitFor({ state: 'hidden' });
// Wait for a button to become interactive
await screen.getByRole('button', { name: 'Submit' }).waitFor({ state: 'enabled' });
});
const { test } = require('@mobilewright/test');
test('waiting without acting', async ({ screen }) => {
// Wait for an element to appear
await screen.getByText('Loading complete').waitFor({ state: 'visible' });
// Wait for a spinner to disappear
await screen.getByText('Spinner').waitFor({ state: 'hidden' });
// Wait for a button to become interactive
await screen.getByRole('button', { name: 'Submit' }).waitFor({ state: 'enabled' });
});
Assertions Auto-Wait Too
Remember that expect() assertions also retry until they pass. In practice this means you rarely need waitFor() at all — an assertion or action usually handles the waiting for you:
import { test, expect } from '@mobilewright/test';
test('assertions handle the waiting', async ({ screen }) => {
// Keeps checking until the text is visible (up to the timeout)
await expect(screen.getByText('Success')).toBeVisible();
// Waits for the text content to match
await expect(screen.getByRole('text')).toHaveText('Done');
});
const { test, expect } = require('@mobilewright/test');
test('assertions handle the waiting', async ({ screen }) => {
// Keeps checking until the text is visible (up to the timeout)
await expect(screen.getByText('Success')).toBeVisible();
// Waits for the text content to match
await expect(screen.getByRole('text')).toHaveText('Done');
});
Written by PV
© 2026 All Rights Reserved