Skip to content

Expose a public app idle/busy status that works with synchronization disabled #4945

Description

@HerrNiklasRaab

Describe your idea

Problem

Detox already tracks app idle/busy state internally and exposes it through the
undocumented device.deviceDriver.client.currentStatus() (iOS returns a
structured { app_status: "idle" | "busy", ... } payload). This is useful for
building idle-aware polling on top of waitFor(...).withTimeout(...): if the
poll times out and the app is genuinely idle, fail fast; if the app is still
busy, keep polling.

The catch: once await device.disableSynchronization() is called,
currentStatus() reports app_status: "idle" unconditionally — even while the
app is mid-navigation, fetching, laying out, etc. So the idle signal becomes
unusable in exactly the scenarios where you'd want to disable sync (slow/flaky
sync sources, long-polling sockets, animations you'd rather poll past).

In other words, today users have to choose between:

  • sync on → reliable idle signal, but tests can hang indefinitely on
    background tasks Detox tracks as "busy" forever, and
  • sync off → tests progress, but no trustworthy idle/busy signal at all.

Proposed enhancement

Expose a public, supported API that reports the app's current idle/busy
state and that continues to function when synchronization is disabled.
Something like:

const { idle, busyResources } = await device.appStatus();
// idle: boolean
// busyResources: string[]   // e.g. ["RN bridge", "JS timers", "layout"]

Key requirements:

  1. Independent of disableSynchronization(). Disabling sync should stop
    Detox from blocking on busy resources, not stop it from observing them.
  2. Cross-platform. Today the underlying currentStatus() returns a
    structured object on iOS but a free-form string ("The app seems to be idle")
    on Android, which isn't programmatically usable. A unified shape would help.
  3. Stable / documented. A public method on device so consumers don't
    reach into device.deviceDriver.client (which can break across versions).

Use case

Layered idle-aware polling for tests that run with sync disabled:

// pseudo-code
while (Date.now() < deadline) {
  try {
    await waitFor(el).toBeVisible().withTimeout(200);
    return;
  } catch {
    const { idle } = await device.appStatus();
    if (idle) throw new Error(\"element not visible and app is idle\");
    // app is still busy → keep polling
  }
}

This pattern only works if appStatus() reflects real busy/idle state
regardless of whether synchronization is enabled.

Alternatives considered

  • Re-enabling sync selectively — not viable for us; some background work
    legitimately never settles from Detox's perspective.
  • Reading deviceDriver.client.currentStatus() directly — what we do today;
    fragile (private API) and broken-by-design on Android + when sync is off.
  • Polling app-side test hooks — pollutes production code with test-only
    paths, which we want to avoid.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions