Skip to content

Match the e2e startup tripwire against the full page-error description - #167

Open
alex-rawlings-yyc wants to merge 7 commits into
mainfrom
e2e-page-error-diagnostics
Open

Match the e2e startup tripwire against the full page-error description#167
alex-rawlings-yyc wants to merge 7 commits into
mainfrom
e2e-page-error-diagnostics

Conversation

@alex-rawlings-yyc

@alex-rawlings-yyc alex-rawlings-yyc commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

withFatalStartupTripwire exists to fast-fail a doomed cold start: when the
renderer emits the theme-settle timeout that leaves dock tabs stuck at
"Unknown", waiting out the full readiness budget is pointless because only a
fresh launch recovers it.

But the tripwire matched FATAL_STARTUP_PAGE_ERROR against err.message
alone, and that is not reliably where the text is. When the page throws a
non-Error value, Playwright's pageerror event wraps it in a bare Error
whose .message collapses to something useless like "Object" — the real
text survives only in the stack. A fatal error arriving that way slipped
straight past the tripwire, and the launch burned its entire readiness budget
before failing with a misleading timeout instead of the actual cause.

The same flattening made the CI log unhelpful: Page error: Object.

Changes

  • describePageError() — combines an error's message, its stack (which
    often carries the true text for a wrapped throwable), and a JSON dump of
    own-enumerable properties, skipping the empty cases a real Error produces.
  • Tripwire matches the full description rather than .message, so a fatal
    error whose text survives only in the stack now trips it.
  • The smoke fixture's pageerror logging uses it too, so the CI log shows
    the real error instead of the "Object" husk.
  • JSDoc on withFatalStartupTripwire recording why the boot-time
    PlatformError rejections (e.g. the platform.getOSPlatform -32601 race)
    must stay log-only: promoting them to fatal signals was tried and reverted
    after CI showed launches that emit them usually recover, so tripping on a
    per-launch-recurring signal exhausted the retry budget and turned
    flaky-but-green runs into hard failures.

Notes

Stacked on #169 — please merge that first; this rebases onto main as a
single commit afterward.

An earlier revision of this branch also added an in-page installPageErrorCapture()
hook (~100 lines) to log the pre-flattening constructor name and fields. It has
been dropped: it was only ever armed on the smoke path, while the startup
failure that motivated it lives on the CDP path — which #169 now addresses at
the root with real readiness gates (extension activation, project
registration), the first-run overlay bypass, and forced Power interface mode.

This does not change pass/fail behavior on a healthy run. It makes a doomed
start fail fast and legibly instead of slowly and misleadingly.


This change is Reviewable

Summary by CodeRabbit

  • Tests
    • Improved automated test reliability through more deterministic application startup and readiness checks.
    • Enhanced diagnostics for startup and page errors, making failures easier to investigate.
    • Improved cleanup after failed or interrupted test runs to prevent stale processes and temporary data from affecting subsequent runs.
    • Added safeguards to isolate and restore test settings between runs.
    • Strengthened navigation and project-loading checks across supported layouts.

@alex-rawlings-yyc alex-rawlings-yyc added 🟩Low Low-priority PR up next Auto adds an issue to the PT Lexical Extensions project labels Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

The E2E harness now isolates settings around Electron launches, improves page-error diagnostics and readiness checks, and coordinates renderer, Electron, and user-data cleanup through shared process utilities.

Changes

E2E harness lifecycle

Layer / File(s) Summary
Harness primitives and settings isolation
.gitignore, e2e-tests/process-utils.ts, e2e-tests/fixtures/helpers.ts
Adds settings backup/restore, richer page-error descriptions, project metadata polling, process-exit waiting, PID polling, and retryable directory removal.
Fixture lifecycle and readiness flow
e2e-tests/fixtures/app.fixture.ts, e2e-tests/fixtures/helpers.ts
Applies and restores E2E settings around fixtures, improves startup diagnostics, waits for project metadata, and supports Home-based layouts.
CDP startup and readiness gates
e2e-tests/global-setup-cdp.ts
Seeds settings before launch, waits for interlinearizer activation and project metadata, and cleans up failed or warm-instance setup state.
PID tracking and teardown integration
e2e-tests/global-setup.ts, e2e-tests/global-teardown.ts, e2e-tests/global-teardown-cdp.ts
Centralizes the renderer PID path and uses structured kill results, exit polling, retryable removal, and settings restoration during teardown.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TestHarness
  participant Electron
  participant ProjectLookupService
  participant Renderer
  participant Cleanup
  TestHarness->>Electron: seed settings and launch
  Electron->>Renderer: activate extension and settle layout
  TestHarness->>ProjectLookupService: poll project metadata
  ProjectLookupService-->>TestHarness: return installed projects
  TestHarness->>Cleanup: restore settings and remove artifacts
  Cleanup->>Renderer: wait for process exit
  Cleanup->>Electron: remove isolated user-data directory
Loading

Possibly related PRs

Suggested reviewers: imnasnainaec

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: matching the e2e startup tripwire against the full page-error description.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch e2e-page-error-diagnostics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@alex-rawlings-yyc alex-rawlings-yyc self-assigned this Jul 27, 2026
@alex-rawlings-yyc alex-rawlings-yyc changed the title Log real e2e renderer page errors instead of the flattened "Object" husk Match the e2e startup tripwire against the full page-error description Jul 27, 2026
@alex-rawlings-yyc
alex-rawlings-yyc force-pushed the e2e-page-error-diagnostics branch from 7f22b73 to 7ed5e0b Compare July 27, 2026 16:35
coderabbitai[bot]

This comment was marked as outdated.

@alex-rawlings-yyc
alex-rawlings-yyc marked this pull request as ready for review July 27, 2026 17:27
@alex-rawlings-yyc

This comment was marked as outdated.

@alex-rawlings-yyc alex-rawlings-yyc left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-rawlings-yyc resolved 2 discussions.
Reviewable status: 0 of 8 files reviewed, all discussions resolved (waiting on alex-rawlings-yyc).

@coderabbitai

This comment was marked as outdated.

@alex-rawlings-yyc
alex-rawlings-yyc force-pushed the e2e-page-error-diagnostics branch 3 times, most recently from 48d9e06 to c917ddb Compare July 27, 2026 19:57
alex-rawlings-yyc and others added 5 commits July 27, 2026 16:00
Playwright wraps a non-Error value thrown in the renderer into a bare
`Error` whose `.message` collapses to something useless like "Object",
keeping the real text only in the stack. `withFatalStartupTripwire`
matched FATAL_STARTUP_PAGE_ERROR against `.message` alone, so a
theme-settle failure arriving that way slipped past the tripwire and cost
the launch its whole readiness budget instead of fast-failing.

Add `describePageError()`, which combines the message, the stack, and a
JSON dump of own-enumerable properties, and match the tripwire against
that. The smoke fixture's `pageerror` logging uses it too, so the CI log
shows the real error rather than "Page error: Object".

Also record why the boot-time PlatformError rejections must stay
log-only: promoting them to fatal signals was tried and reverted after CI
showed launches that emit them usually recover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seeding and launch now run inside the CDP setup's guarded path so a
failure can't leak settings or the user-data dir, PID-marker removal no
longer masks a successful kill, and the CDP teardown's tail always runs.
Also drop stale simple-mode wording now that the suite forces Power
mode.
Playwright skips globalTeardown when globalSetup throws, so a dev server
spawned here but stuck before readiness survived the failed run, holding
port 1212 and serving a stale bundle to later runs.
A throw from the CDP_PID_FILE write left `exited` undefined, so
cleanUpFailedLaunch skipped its post-SIGKILL wait and removed the
user-data dir while the app still held it.
@alex-rawlings-yyc
alex-rawlings-yyc force-pushed the e2e-page-error-diagnostics branch from d7ccbe3 to c5d7f45 Compare July 27, 2026 22:00
Power mode renders a per-tab BookChapterControl with the same aria-label
and no disabled prop, so a page-wide .first() relied on render order and
would have made the enabled-wait vacuous.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
e2e-tests/global-setup.ts (1)

219-266: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

PID-file write happens before the failure-cleanup try, leaking the dev server on write failure.

fs.writeFileSync(DEV_SERVER_PID_FILE, ...) (Lines 231-233) runs before the try block that calls killSpawnedDevServer on failure. writeFileSync throws synchronously on error (e.g. EACCES/ENOENT), so if it fails, the spawn already succeeded but the function exits without ever reaching the cleanup path — leaking exactly the orphaned dev-server process this PR is meant to prevent.

The sibling globalSetupCdp handles this same class of failure explicitly by wrapping its analogous write in its own try/catch with cleanup, so there's established precedent for guarding it here too.

🐛 Proposed fix: move the PID-file write inside the try block
-    if (devServer.pid) {
-      fs.writeFileSync(DEV_SERVER_PID_FILE, String(devServer.pid));
-    }
-
     // From here on this run owns a spawned, detached dev server. Playwright skips globalTeardown
     // when globalSetup throws, so a readiness failure below must kill it on the spot or it survives
     // the failed run, holds port 1212, and silently serves a stale bundle to every later run.
     // Scoped to this branch only: the already-running branch above didn't start the server, so this
     // run must leave it alone.
     try {
+      if (devServer.pid) {
+        fs.writeFileSync(DEV_SERVER_PID_FILE, String(devServer.pid));
+      }
       console.log(`Waiting for renderer dev server on port ${RENDERER_PORT}...`);
       await waitForPort(RENDERER_PORT, 60_000);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e-tests/global-setup.ts` around lines 219 - 266, Move the
DEV_SERVER_PID_FILE write and related renderer readiness operations into the try
block that owns cleanup, so synchronous fs.writeFileSync failures also invoke
killSpawnedDevServer(devServer.pid) before rethrowing. Keep the existing
detached-server setup and readiness behavior unchanged.
e2e-tests/global-setup-cdp.ts (1)

358-377: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Unconditional settings restore + marker cleanup on port reuse risks clobbering a concurrently-running owner.

clearStaleOwnershipMarkers() now also calls restoreSeededSettings() (new in this PR) whenever globalSetupCdp decides to reuse an already-running instance on CDP_PORT (Line 91-102). The code assumes that instance is always a stale leftover from a crashed prior launched run, but the same code path fires just as readily when the port is held by a developer's active npm run start:cdp session or by another concurrently-executing test run. In either case, restoring settings.json out from under a live owner (or deleting CDP_PID_FILE/CDP_USER_DATA_FILE it still needs) can corrupt its settings state or break its own teardown.

Consider gating this cleanup on actual ownership evidence — e.g. a per-run token recorded alongside the PID marker, or at minimum verifying the PID in CDP_PID_FILE is no longer alive — before restoring settings or removing markers, rather than acting unconditionally whenever the port happens to be occupied.

Based on learnings, "assume cleanup can be triggered by local concurrent runs or stale-marker recovery... implement a setup-established per-run ownership token and require it in teardown to guard: process termination, user-data cleanup, marker deletion, and settings backup restoration—so teardown only performs cleanup when the current run owns the resources/markers."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e-tests/global-setup-cdp.ts` around lines 358 - 377, Make ownership cleanup
conditional in clearStaleOwnershipMarkers and globalTeardownCdp by recording a
setup-established per-run ownership token and validating it before restoring
seeded settings, terminating processes, deleting user data, or removing marker
files. Preserve warm-instance reuse without modifying resources belonging to a
developer session or another concurrent test run, and only perform teardown
cleanup when the current run’s token matches the recorded ownership metadata.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@e2e-tests/global-setup-cdp.ts`:
- Around line 358-377: Make ownership cleanup conditional in
clearStaleOwnershipMarkers and globalTeardownCdp by recording a
setup-established per-run ownership token and validating it before restoring
seeded settings, terminating processes, deleting user data, or removing marker
files. Preserve warm-instance reuse without modifying resources belonging to a
developer session or another concurrent test run, and only perform teardown
cleanup when the current run’s token matches the recorded ownership metadata.

In `@e2e-tests/global-setup.ts`:
- Around line 219-266: Move the DEV_SERVER_PID_FILE write and related renderer
readiness operations into the try block that owns cleanup, so synchronous
fs.writeFileSync failures also invoke killSpawnedDevServer(devServer.pid) before
rethrowing. Keep the existing detached-server setup and readiness behavior
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e973eff-d82d-45d6-bd5d-06ba69e48d6c

📥 Commits

Reviewing files that changed from the base of the PR and between 7ed5e0b and 26ec4b9.

📒 Files selected for processing (5)
  • e2e-tests/fixtures/app.fixture.ts
  • e2e-tests/fixtures/helpers.ts
  • e2e-tests/global-setup-cdp.ts
  • e2e-tests/global-setup.ts
  • e2e-tests/global-teardown.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • e2e-tests/fixtures/app.fixture.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🟩Low Low-priority PR up next Auto adds an issue to the PT Lexical Extensions project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant