Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Function waitForNavigationComplete

    • Wait for nD navigation to complete: poll until getState().isLoading is false.

      There is no "wait for the navigation to START" phase — the helper only ever waits for isLoading to be false, so it resolves on the first poll if the loader is already idle. That is the intended behaviour on both sides of the race: a same-task trigger (a keyboard nav) sets the flag synchronously — SceneLoader.updateView takes the lock before its first await — so it is already true before this helper gets to poll, while a fully cache-served pass may never be OBSERVED true at all: polling is discrete (waitForFunction on rAF below, then a 100 ms loop), so such a pass can start and finish between two polls. That is why the callers in spatial-index-accuracy.spec.ts prefer this silent variant. Waiting for true first would hang in the second case.

      The only preliminary wait is for the flag to EXIST (typeof isLoading === 'boolean'), i.e. for the debug interface to be installed. That wait failing no longer returns on its own (#1726): it FALLS THROUGH into the poll loop below, so the one end-of-budget decision governs. Its old comment claimed the failure meant "navigation completed before we start polling", which does not actually explain a missing isLoading — the flag exists whenever the debug interface does (getState() always returns a real boolean, see core/app/debug/debug-state.ts) — so returning there returned on nothing. Falling through gives the state probe a chance to answer, or to produce a diagnostic of its own. The preliminary failure is still worth REPORTING — "the flag was unreadable for the first N ms" is exactly what explains a 15 s budget that never saw isLoading clear — so its duration (deterministically the clamped preliminary budget) is interpolated into the answered-but-unsettled warn. It is NOT a probe failure: it stays out of the probe counts, and out of the throw, where the carried probe rejection is both fresher and the better headline.

      Silent only on the tolerated case. The give-up is split in two:

      • A probe ANSWERED but isLoading never cleared within timeout: returns normally, after one console.warn naming the helper, how many probes went out and how many yielded no usable state, and the preliminary failure if there was one. This is what the nine call sites rely on — several deliberately pick this variant because their action never toggles isLoading at all, and they succeed on the first probe.
      • Every probe FAILED, so none ever answered: THROWS, carrying the last probe failure and the same counts. The helper read no viewer state, so returning would let the caller assert on state nothing has confirmed.

      The gain is ATTRIBUTION, not a rescued pass: at all nine call sites the statement following the wait is itself a page probe (getLuxarState, a bare page.evaluate, or getDimensionValue, which answers -1 and then fails its own expect), so the old silence already surfaced as a CONFUSING FAILURE one statement later — never as a pass. What changes is the name on it, and only when the decision is reached at all: the fall-through can take a starved page ~55 s past the start of the wait, which at the three sites whose next statement is an unbounded page.evaluate usually lands beyond the 60 s per-test timeout in playwright.config.ts — and beyond it at the six bounded spatial-index-accuracy.spec.ts sites too, since the spec has already spent seconds reaching the wait. At those six the THROW is additionally near-unreachable: they sit right after a waitForSpatialQueryOrThrow, which cannot itself have succeeded unless the page answered within the last 8 s, so what they get from this is the attributed warn. Closing the vacuous pass is therefore about the CONTRACT, for a future caller that does not happen to re-probe straight afterwards. What is closed is a swallowed probe FAILURE, not a swallowed timeout; see waitForPointsLoaded for why the probes here are not clamped to the loop's budget.

      Use waitForNavigationCompleteOrThrow for tests that depend on navigation actually finishing.

      Unit-tested in src/tests/unit/tests/e2e-helpers-silent-waits.test.ts.

      Parameters

      • page: Page

        Playwright page

      • timeout: number = 15000

        Poll budget in ms, not a hard ceiling on the call. The preliminary wait above consumes at most max(1, min(timeout, 10000)) of it — floored at 1 ms because Playwright reads timeout: 0 as NO timeout, so a non-positive budget would otherwise wait there for the whole test — and the do/while below then always runs at least one probe, bounded by its own 45 s deadline rather than by this budget, so a call can overrun timeout by a whole probe (both end-of-budget reports state the measured elapsed time alongside this budget for exactly that reason). Probing at least once matters because the preliminary wait can spend the whole budget on its own, and reporting "no probe answered" without having probed would be a false diagnostic.

      Returns Promise<void>