Intermediate Chapter 7 · 7 min read

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.

Tip: If a test fails with a 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:

timeouts.test.ts
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 });
});
timeouts.test.js
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 });
});
mobilewright.config.ts
import { defineConfig } from 'mobilewright';

export default defineConfig({
  // Raise the default timeout for every action and assertion
  timeout: 10_000,
});
mobilewright.config.js
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':

waitfor.test.ts
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' });
});
waitfor.test.js
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:

autowait.test.ts
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');
});
autowait.test.js
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');
});

Mobilewright Intermediate Auto-wait Timeouts

Written by PV

© 2026 All Rights Reserved