Repository navigation
test(e2e): ask Orbit to quit from a task of its own - #16
Conversation
Playwright's app.close() calls app.quit() through the inspector, which can run Orbit's quit handlers part-way through other main-process code, even between Node's check that a due timer is still set and its call to it. A handler that clears that timer there (persistence.flush() clears the state file's next write) makes Node call a cleared timer: "timer._onTimeout is not a function", logged as an exception nothing caught. Seen once in archive's gate run (media.spec.ts:1485). The harness now asks for the quit first through its deferred evaluate, which starts in a task of its own. Electron emits before-quit once, so Playwright's own app.quit() then finds Orbit quitting and does nothing. The new core test keeps main busy in timers for about 3 s and records where the quit handler runs; it fails without the ask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a fixed 3 s burst, Playwright's own work before it asks (flushing the trace) sometimes outlasted the burst, so the test passed without the fix in 1 of 2 runs. Ten 10 ms intervals keep main in timers until the quit: without the ask, 6 of 6 runs fail; with it, 4 of 4 pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: a398f076-d089-475d-a958-a55cdcc511fc) |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: ec06eb5d-1ca9-4287-810a-ad6d940422bf) |
No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: f0cc9473-167b-4d64-af25-d7c5c7efeb13) |
…one; a cancelled quit fails (PR #16 review M1) Review M1: Playwright's close always asks Orbit to quit again, through the inspector. Called as soon as the harness's own ask returned, it finished a quit Orbit had cancelled, so the test passed in 0.2 s where D28 should fail it, and that second ask could again run inside a timer. app.close() now waits for Orbit to exit after its own ask, and calls Playwright's close only then. The 15 s and 35 s budgets still bound the wait and kill Orbit if needed. The ask also waits for a task of its own inside the evaluated function (F3), so it stays deferred where app.evaluate isn't wrapped. New test: a quit Orbit cancels once fails the test (test.fail). Without the wait for exit, 2 of 2 runs report "Expected to fail, but passed". With it, the failure is the quit check's: "still running: it had the quit for 15 s". F1: the timer test now keeps main busy in one 1.5 s timer as the test closes it. Playwright's raw close fails it every time (4 of 4, "quit handler inside a timer: true"); with ten 10 ms intervals it passed 3 of 12 runs. With the fix, at load 46-51: both quit tests passed 3 of 3; core.spec.ts 21 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 2236e6f9-9157-4db7-9dcc-6470d2e6bcbc) |
No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One conflict, in tests/e2e/core.spec.ts: both sides added a test after TB-22's quit-timer test. Kept both: PR #16's cancelled-quit test first, beside the other quit test, then this branch's evaluate test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… task of its own (PR #19 review F7) - The test hook's flush() writes the state file from a task of its own (later), as the other hooks do, so a flush asked through the inspector no longer runs part-way through other main-process code. flushNow() writes in the calling task, for an evaluate that checks the state first and must write in the same turn (downloads' saveStateWhen). - scripts/perf/run.mjs and scripts/capture/record.mjs quit Orbit as the e2e harness does (askQuit, PR #16): app.quit() from a task of its own, then Playwright's close only cleans up. The capture script's setBounds runs from a task of its own too. - run.mjs takes --cold-only, Orbit's cold start alone, for the loading view's cold-start rounds in PERF.md (PR #18 fix check 3, F5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The e2e quit check (D28) caught an exception in archive's gate run that wasn't Orbit's. As Orbit quit, main logged
TypeError: timer._onTimeout is not a function(media.spec.ts:1485, PR #12's gate run). This PR fixes the harness.Cause
app.close()callsapp.quit()through the inspector (electronHandle.evaluate(({ app }) => app.quit())).evaluateBetweenTasksfound the same for test evaluates. It doesn't cover Playwright's own call.persistence.flush()clears the state file's next write, and history'sflushSync()does the same.Fix (
tests/e2e/helpers.ts)app.close()first asks Orbit to quit from a task of its own. The ask goes through the harness's deferred evaluate, and sincef49d66athe evaluated function also waits for a task of its own (review F3).app.close()for the quit to reach main, then 15 s for it to finish.Tests (
core.spec.ts)before-quitlistener logs whether it ran inside a timer.test.fail(), review M1). Orbit cancels the first quit, as a "quit anyway?" question would.core.spec.tspassed 21 of 21. Lint and typecheck: exit 0.Merge gate (D42)
Run locally on the Linux VPS on this PR's head,
e4486e4(mainat32b8894, with PR #14, merged in):test.fixmeframe tests (MD-01, MD-09).mainmoved to60da889(settings, PR Settings window, appearance, onboarding and updates #15). It was merged into this branch with no conflicts (new head2019597), then up the stack into PR test(commandbar): judge CB-22's budgets on the best of up to three rounds #17 and PR test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19. Under D50 the gate reruns once, on PR test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19's head (42c9819), which contains this head; its result goes in PR test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19. Then test(e2e): ask Orbit to quit from a task of its own #16, test(commandbar): judge CB-22's budgets on the best of up to three rounds #17 and test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19 merge in that order.f49d66a) went up the stack, and the gate reran on PR test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19's new head4345e72, which contains them: build, typecheck and lint exit 0; unit 435 of 435; e2e 306 passed, 0 failed, 2 skipped, exit 0, in 16.2 min (10:25–10:41 UTC, load average 9–34). Both TB-22 tests ran in it, and main logged no uncaught exception. Details are in PR test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19.Review
reviews/lead-quit-task.md): approve with 1 must-fix.f49d66a, with F1 and F3.4345e72, which contains this branch.🤖 Generated with Claude Code
Note
Low Risk
Changes are limited to e2e harness teardown and new tests; no production app code.
Overview
Fixes the e2e D28 teardown so Orbit is asked to quit from the main process between Node tasks, instead of relying on Playwright’s inspector-driven
app.quit()first—which could runbefore-quithandlers in the middle of timer dispatch and trigger spurioustimer._onTimeout is not a functionfailures.In
tests/e2e/helpers.ts, wrappedapp.close()now calls newaskQuit()(deferredsetImmediate+app.quit(), up to 10s) and only then Playwright’s native close, after the app has actually exited; apps that already quit skip the ask. Existing quit timeouts (35s / 15s) are unchanged.tests/e2e/core.spec.tsadds [TB-22] coverage: one test asserts quit handlers never run inside a busy timer during close; another (test.fail) documents that a cancelled quit must not be “completed” by Playwright’s second quit attempt.Reviewed by Cursor Bugbot for commit f49d66a. Bugbot is set up for automated code reviews on this repo. Configure here.