diff --git a/tests/e2e/onboarding.spec.ts b/tests/e2e/onboarding.spec.ts index a4fcaa8dc1d..4836e08265c 100644 --- a/tests/e2e/onboarding.spec.ts +++ b/tests/e2e/onboarding.spec.ts @@ -452,17 +452,32 @@ test.describe('Onboarding flow', () => { // has a live, protocol-compatible status; without one it reads // 'disconnected' and the Add Project dialog falls back to Local Mac. // runtimeProtocolVersion 3 clears MIN_COMPATIBLE_RUNTIME_SERVER_VERSION. + const seededStatus = { + runtimeId: `${environment.id}-runtime`, + rendererGraphEpoch: 0, + graphStatus: 'ready' as const, + authoritativeWindowId: null, + liveTabCount: 0, + liveLeafCount: 0, + runtimeProtocolVersion: 3, + minCompatibleRuntimeClientVersion: 1 + } + // Why a snapshot and not a bare status: since #20003 the published snapshot owns host health, + // and main's status owner publishes `checking` for this unreachable host as soon as any + // runtime RPC touches it — which flips the Add Project host to Local mid-test. Pinning the + // seed at the top sequence makes applyRuntimeHostStatusSnapshot's monotonic guard drop those + // publications. A snapshot-less write would also no-op once any snapshot exists. store.getState().setRuntimeEnvironmentStatus(environment.id, { - status: { - runtimeId: `${environment.id}-runtime`, - rendererGraphEpoch: 0, - graphStatus: 'ready', - authoritativeWindowId: null, - liveTabCount: 0, - liveLeafCount: 0, - runtimeProtocolVersion: 3, - minCompatibleRuntimeClientVersion: 1 + snapshot: { + environmentId: environment.id, + pairingRevision: environment.pairingRevision ?? environment.createdAt, + sequence: Number.MAX_SAFE_INTEGER, + checkedAt: Date.now(), + status: seededStatus, + verification: 'verified', + transport: 'ready' }, + status: seededStatus, checkedAt: Date.now() }) // Why: the store's switchRuntimeEnvironment probes reachability, which a diff --git a/tests/e2e/paired-web-add-project-unavailable-host.spec.ts b/tests/e2e/paired-web-add-project-unavailable-host.spec.ts index a4f7ecc8d05..a2448c8620a 100644 --- a/tests/e2e/paired-web-add-project-unavailable-host.spec.ts +++ b/tests/e2e/paired-web-add-project-unavailable-host.spec.ts @@ -35,20 +35,43 @@ async function setOnlyRuntimeHostHealth(page: Page, health: HostHealth): Promise if (nextHealth === 'blocked' && !current?.status) { throw new Error('Paired web runtime status unavailable for compatibility fault') } + // Why rewrite the snapshot and not just the status: since #20003 the published snapshot owns + // host health. A bare `status: null` leaves the paired-web client's verified/ready snapshot in + // place, addRuntimeHost reads transport 'ready' before it ever looks at status, and the host + // still renders Connected. Pinning at the top sequence also stops the live status owner + // restoring the host mid-assertion. + const verified = current?.status ?? current?.snapshot?.status ?? null + const snapshot = { + environmentId: environment.id, + pairingRevision: environment.pairingRevision ?? environment.createdAt, + sequence: Number.MAX_SAFE_INTEGER, + checkedAt: Date.now(), + ...(nextHealth === 'blocked' + ? { + status: { ...verified!, protocolVersion: 0, runtimeProtocolVersion: 0 }, + verification: 'verified' as const, + transport: 'ready' as const + } + : { + status: null, + verification: 'unavailable' as const, + // Why 'unknown' and not 'disconnected': an unavailable/unknown snapshot falls + // through to disconnected health; explicit disconnected transport is treated as + // reconnecting and renders Connecting. + transport: 'unknown' as const + }) + } store.setState({ runtimeStatusByEnvironmentId: new Map(state.runtimeStatusByEnvironmentId).set( environment.id, - nextHealth === 'blocked' - ? { - ...current, - checkedAt: Date.now(), - status: { - ...current!.status!, - protocolVersion: 0, - runtimeProtocolVersion: 0 - } - } - : { ...current, checkedAt: Date.now(), status: null } + { + ...current, + snapshot, + checkedAt: snapshot.checkedAt, + status: snapshot.verification === 'verified' ? snapshot.status : null, + // Why: runtimeHealth answers 'available' on a ready remoteControl even with a null status. + remoteControl: null + } ) }) return environment.name