Skip to content

feat(core): add waitForReady option to launchApp to avoid first-render flakiness#75

Open
krismuhi wants to merge 1 commit intomainfrom
feat/launch-app-wait-for-ready
Open

feat(core): add waitForReady option to launchApp to avoid first-render flakiness#75
krismuhi wants to merge 1 commit intomainfrom
feat/launch-app-wait-for-ready

Conversation

@krismuhi
Copy link
Copy Markdown
Member

@krismuhi krismuhi commented May 2, 2026

Problem

launchApp() resolves as soon as the OS acknowledges the launch. On cold start, the app hasn't rendered its first screen yet, causing the immediately-following assertion to fail. This is especially visible on first run after boot.

Observed in practice: a test checking for a welcome screen text timed out at 27 s on first run, then passed reliably (5–8 s) on subsequent warm runs. The beforeEach did await device.launchApp(bundleId) followed immediately by await expect(screen.getByText('Welcome to Milliways')).toBeVisible().

Fix

Opt-in waitForReady on LaunchOptions — polls getViewHierarchy() until ≥ 2 visible elements appear, up to a configurable timeout (default 5 s). Timeout is soft: if the app hasn't rendered by then, it resolves anyway and the first assertion produces the real error message.

Usage

```typescript
await device.launchApp('com.example.app', { waitForReady: true });
// or with a custom timeout:
await device.launchApp('com.example.app', { waitForReady: 10_000 });
```

Changes

  • packages/protocol/src/types.ts: Added waitForReady?: boolean | number to LaunchOptions
  • packages/mobilewright-core/src/device.ts: Implemented pollUntilReady private method; launchApp now calls it when waitForReady is set
  • packages/mobilewright-core/src/device.test.ts: New test file covering the polling behaviour, soft timeout, error resilience, and the no-op path when waitForReady is not set

Test plan

  • waitForReady: true polls getViewHierarchy() at least once
  • Resolves when ≥ 2 visible elements appear in hierarchy
  • Resolves immediately when hierarchy is already ready
  • Accepts a custom millisecond timeout (waitForReady: 10_000)
  • Soft-timeout: resolves without throwing when app never becomes ready
  • Continues polling (doesn't crash) when getViewHierarchy throws during early startup
  • Does not poll at all when waitForReady is not set

@gmegidish
Copy link
Copy Markdown
Member

I think the bug report is correct, launchApp should wait for getForegroundApp to be the same bundleId as what we were launching, and if it fails within the time limit, then we need to throw an exception.

The code commit seems to be related to node modules. But I'll fix the launchApp. The default should be to auto wait, as with tap and all the rest.

…"module"

Catch ERR_REQUIRE_ESM thrown by loadConfigFromFile and print a clear,
human-readable message telling the user exactly what to add to their
package.json, instead of dumping a raw Node.js internal stack trace.
@krismuhi krismuhi force-pushed the feat/launch-app-wait-for-ready branch from e0440a8 to 694b41c Compare May 3, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants