Skip to content

Popup follow-ups: teardown safety, entry pacing, sleep lint - #37

Open
mmkal wants to merge 6 commits into
mainfrom
popup-child-dispose-failures
Open

Popup follow-ups: teardown safety, entry pacing, sleep lint#37
mmkal wants to merge 6 commits into
mainfrom
popup-child-dispose-failures

Conversation

@mmkal

@mmkal mmkal commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Follow-ups to the popup overlay feature (#36), bundled per review:

1. Popup teardown can't drop the main page's video

A throwing popup dispose (e.g. a screencast copy failure) aborted the parent's finalize, silently losing the main page's artifacts (cursor bugbot's catch on #36 — the fix landed on that branch minutes after the squash-merge snapshot, so it missed main).

2. Consumers never need waitForTimeout for popup videos

Deleting the demo's waitForTimeout calls broke the render — instant flows act before the popup screencast's first captured frame (annotations floating over a not-yet-visible popup), and action holds froze successive mid-slide frames, replaying the enter animation in slow motion. Now videoMode paces itself: the popup's first action holds until the popup has loaded and the enter animation window has real footage (~450ms ceiling; real auth popups load slower than that anyway). Animation windows are also protected from dead-air compression, and a render-time backstop defers any highlight that still lands mid-slide. The demo and overlay specs run with zero waitForTimeout calls.

3. page.waitForTimeout joins the timeout lint

A sleep waits whether or not the app is ready, so it now needs the same nearby justification comment as an explicit timeout:

await page.waitForTimeout(500);
// error middlewright(require-timeout-comment): Avoid waitForTimeout — a sleep waits
// whether or not the app is ready. ...

The video-mode footage specs are exempted via a new allowSleeps option in .oxlintrc.json overrides — there the sleeps are the test input shaping the recorded timeline — while the timeout-option check stays active in them.

Latest main merged in.

Before / after

All three from the same demo spec (spec/popup-overlay-demo.spec.ts).

a) Before — on main, with the spec's waitForTimeout calls papering over the flaw:

a-before-with-timeouts.mp4

b) On main with the timeouts removed — input fields float over a popup that isn't visible yet, and the enter animation plays in slow motion (holds freezing successive mid-slide frames):

b-timeouts-removed.mp4

c) This PR, no timeouts anywhere — videoMode paces the popup entry itself:

c-with-fixes-v2.mp4

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes affect plugin teardown ordering, video render timing/compositing, and lint rules across specs—high user-visible impact but covered by new tests and targeted overrides.

Overview
Follow-ups to popup overlay video: parent teardown no longer depends on popup dispose succeeding, videoMode paces popup entry without spec waitForTimeout, and waitForTimeout is linted like explicit timeouts with footage-spec exemptions.

Teardown: addPlugins async dispose now collects failures from rejected child wraps and from throwing child asyncDispose, still runs the parent’s afterTest / afterTestFinalize, then rethrows the first child failure. A new popup spec asserts the parent finalizer runs when popup teardown throws.

Popup video pacing: The first action on a popup waits for load (bounded timeout) and until ~450ms after open so screencast footage covers the enter animation. Render path adds subtractVideoSpans so dead-air compression cannot eat enter/exit windows, defers highlights that still start mid-slide, and improves overlay fill-reveal compositing when sparse child frames lag state.

Lint & specs: middlewright/require-timeout-comment reports bare waitForTimeout unless a justified comment is nearby; allowSleeps: true in .oxlintrc.json overrides for video-mode footage specs. Demo/popup specs drop manual pacing sleeps; debug-mode adds a comment for an intentional sleep.

Reviewed by Cursor Bugbot for commit 6776d21. Bugbot is set up for automated code reviews on this repo. Configure here.

Cursor bugbot caught that a throwing child asyncDispose (e.g. a popup
screencast copy failure) aborted the parent's afterTest/afterTestFinalize,
dropping the main page's video artifacts. Child wrap and dispose failures
are now collected and rethrown only after the parent's own teardown runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Aug 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@37

commit: 6435822

mmkal and others added 3 commits August 18, 2026 09:42
Removing the demo's waitForTimeout calls (antithetical to this repo's
goal) broke the render: instant flows act before the popup screencast's
first captured frame, so synthetic fill annotations floated over a
popup that wasn't visible yet, and action holds froze successive
mid-slide frames - replaying the enter animation in slow motion.

Three-part fix, all library-side:
- Entry pacing: the child recorder holds the popup's first action until
  the popup fires load and the enter animation window (~450ms) has real
  footage. Video mode already paces deliberately at close (recorder
  settling); this is the entry-side counterpart. Real auth popups load
  slower than this, so it usually costs nothing.
- Dead air recorded during an animation window is carved out before
  segment planning, so compression can't fast-forward a slide.
- Render-time backstop: a projected child highlight starting mid-slide
  defers past the enter window, so holds can't freeze mid-slide frames
  even when pacing was bypassed.

The demo and overlay specs now run with zero waitForTimeout calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
page.waitForTimeout joins the require-timeout-comment rule: a sleep
waits whether or not the app is ready, so it needs the same nearby
justification comment as an explicit timeout. New allowSleeps option
exempts the video-mode footage specs via .oxlintrc overrides - there
the sleeps ARE the test input (they shape the recorded timeline under
test) and annotating every one would be noise. The timeout-option check
stays active in those files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mmkal mmkal changed the title Keep parent finalize alive through popup dispose failures Popup follow-ups: teardown safety, entry pacing, sleep lint Aug 18, 2026
mmkal and others added 2 commits August 18, 2026 09:45
The fixture config pointed at the package's ./lint-plugin export, which
resolves to dist - so rule changes silently no-oped in the spec until a
manual pnpm build. Point it at lint-plugin.local.js through the
node_modules symlink instead (a path bypasses the exports map): the
plugin loads from ./src via tsx exactly like the repo's own config.
Verified by running the spec with dist renamed away.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The popup screencast emits frames only on change, so the fill reveal's
frozen composite base could lag the logical state: the password fill's
base frame usually predated any captured frame containing the username
value, blanking 'mmkal' for the password reveal's whole hold before live
footage brought it back. The pre-action screenshot has the exact right
state (earlier values present, this field empty), so it now covers the
whole overlay box on the frozen base; footage supplies only the backdrop
dim and the page behind. The ring and typed-reveal overlays sit on top
as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant