From e38d1648013527594830f2a1e0b54ac112957631 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Thu, 3 Sep 2026 12:32:54 -0700 Subject: [PATCH 1/2] Stop exitTimelineMode's test leaking a load that gates the poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The suite calls exitTimelineMode(), which fires a live-HEAD reload it never awaits. loadSource sets SCAN_PROGRESS before its first await and clears it only in `finally`, and the stub EventSource never emits, so that load parks forever and leaves SCAN_PROGRESS set for the rest of the file. tick() yields to a foreground load on exactly that signal, so any later test asserting the poll fires depends on having nulled SCAN_PROGRESS itself. The config shuffles test order on purpose to surface couplings like this, and the live-poll resume test has failed twice on main this way (runs 31991836275 and 33795322008). Cancelling settles the load through the abort path streamManifest already honours, so its `finally` runs. The added assertion is the guard: drop the cancelLoad() and it fails with `expected { kind: 'local', … } to be null`. Closes #212 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011Qzxns3a3Q12RfayHZRrGZ --- app/tests/hooks/useTimelineMode.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/tests/hooks/useTimelineMode.test.ts b/app/tests/hooks/useTimelineMode.test.ts index 9855fd8b..b581a3d2 100644 --- a/app/tests/hooks/useTimelineMode.test.ts +++ b/app/tests/hooks/useTimelineMode.test.ts @@ -18,7 +18,7 @@ import { import { LoadingStep, TIMELINE_LOADING_STEPS, BuildStage } from '@/constants/progress'; import { LIVE_UPDATES } from '@/state/settings/fields/updates'; import { EMPTY_MANIFEST } from '../_helpers/manifestFixtures'; -import { setupLiveUpdates } from '@/hooks/useManifestSource'; +import { setupLiveUpdates, cancelLoad } from '@/hooks/useManifestSource'; import { TimelineStage } from '@/types'; import type { PickTarget, TimelineBundle, TimelineProgress } from '@/types'; import { StubEventSource, installEventSource } from '../_helpers/eventSource'; @@ -286,7 +286,13 @@ describe('exitTimelineMode', () => { setScrubPos(2); TIMELINE_BUNDLE.value = BUNDLE; }); - afterEach(() => { + afterEach(async () => { + // The live-HEAD reload this suite starts parks on a stub stream that never + // emits, so without a cancel it outlives the test holding SCAN_PROGRESS set + // — which silently gates the poll in whichever test shuffle runs next. + cancelLoad(); + await flush(); + expect(SCAN_PROGRESS.value).toBeNull(); restoreEventSource(); TIMELINE_MODE.value = false; SCENE_HANDLE.value = null; From 1dd57930c038645f09cabbfe958d72a9ce0d0a7e Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Thu, 3 Sep 2026 12:39:28 -0700 Subject: [PATCH 2/2] Trim the cancel comment to the 2-line cap Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011Qzxns3a3Q12RfayHZRrGZ --- app/tests/hooks/useTimelineMode.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/tests/hooks/useTimelineMode.test.ts b/app/tests/hooks/useTimelineMode.test.ts index b581a3d2..c83453b6 100644 --- a/app/tests/hooks/useTimelineMode.test.ts +++ b/app/tests/hooks/useTimelineMode.test.ts @@ -288,8 +288,7 @@ describe('exitTimelineMode', () => { }); afterEach(async () => { // The live-HEAD reload this suite starts parks on a stub stream that never - // emits, so without a cancel it outlives the test holding SCAN_PROGRESS set - // — which silently gates the poll in whichever test shuffle runs next. + // emits; uncancelled it outlives the test, holding SCAN_PROGRESS non-null. cancelLoad(); await flush(); expect(SCAN_PROGRESS.value).toBeNull();