Skip to content

test: rewrite to e2e#2370

Draft
bjohansebas wants to merge 24 commits into
hot-middlewarefrom
hot-e2e-tests
Draft

test: rewrite to e2e#2370
bjohansebas wants to merge 24 commits into
hot-middlewarefrom
hot-e2e-tests

Conversation

@bjohansebas

Copy link
Copy Markdown
Member

Summary

What kind of change does this PR introduce?

Did you add tests for your changes?

Does this PR introduce a breaking change?

If relevant, what needs to be documented once your changes are merged or what have you already documented?

Use of AI

@changeset-bot

changeset-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: eaff5d2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

bjohansebas and others added 8 commits July 25, 2026 17:53
… the console

The hot client, overlay, and building indicator are now exercised end
to end in headless Chrome (puppeteer 22, the last CJS release jest can
require) against a real webpack watcher: updates applied without
reload, the full-reload fallback, reconnect with catch-up sync after a
server restart, disconnect() semantics, overlay lifecycle (Escape on
the host page, HTML-in-error-message escaping, warnings, pagination),
the building badge, multi-compiler ?name= filtering with per-bundle
overlay slots, and the custom publish/subscribe API.

Console behavior is snapshotted from the real browser console: the full
info-level update cycle, logging=none/warn/error gates, and the
per-bundle dedup where a sibling's clean rebuild does not re-log
another bundle's unchanged warning while its own rebuild does. Each
multi-compiler compilation gets its own context dir so editing one
entry cannot invalidate the sibling's watcher and shuffle event order.

The jsdom suites stay, slimmed to what a browser cannot exercise
deterministically: process-update internals, shared state between
bundled copies, option parsing, and protocol edges. The review of the
new tests also surfaced a real client bug, now fixed: an EventSource
error event queued behind disconnect() re-armed the reconnect timer on
the orphaned wrapper — close() is now final.

e2e runs serially through the new test:e2e script (four concurrent
Chrome instances plus watchers starved the rest of the suite), and CI
shards every test job 4x with jest --shard, uploading per-shard
coverage that a final job aggregates for codecov, mirroring
webpack-dev-server's setup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d unit tests

A logging=log e2e run now snapshots the collapsed "Updated modules:"
group and its per-module entries straight from the browser console
(puppeteer surfaces group frames as console events). The jsdom
equivalents — the process-update logging describe and the
building-message log test, whose output already appears verbatim in the
info-level e2e snapshot — are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eir console

The inactivity watchdog is now exercised against a genuinely silent SSE
connection (a server heartbeat far beyond the client timeout): the
second connect proves it fires on pure silence and the third that it
re-arms after a reconnect. Manual recovery gets its own test —
disconnect() followed by setOptionsAndConnect() opens a fresh
connection that still delivers updates. Both, plus the server-restart
catch-up test, snapshot their console sequence; browser network-error
noise is filtered out of the snapshots since its volume depends on
reconnect timing.

The fake-timer equivalents in the jsdom suite are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d copies

The multi-compiler helper already produces what the jsdom suite could
only simulate: two genuinely separate bundled copies of the indicator
module on one page. Each bundle now exposes its copy as a global and
the tests drive both from the browser — a second copy adopts the same
badge instead of stacking one, per-source counting spans copies, hiding
an unknown source is ignored, and a leaner state shape left by an older
package version gets its missing fields filled (planted via
evaluateOnNewDocument before the bundles load).

The jsdom indicator suite is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e jsdom suite

The subscribe/subscribeAll contract, the ?name= filter, per-source badge
counting, the shared SSE connection between bundled clients (asserted
explicitly in the multi-compiler console snapshot: one "connected" for
two clients), the dev-server-shaped overlay option, and the warnings
filter function — parsed from the entry query and applied against two
real warning-producing modules — now all run in Chrome. The warnings
overlay test also covers clearing on a recovered build, and the
pagination test snapshots the two-error console output that the deleted
jsdom snapshot used to document.

client.test.js keeps only what the browser cannot pin deterministically:
problem-type transitions, the multi-bundle problem union, option
forwarding to the overlay factory, dynamicPublicPath URL building, and
protocol edges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Breaking both bundles shows the union of their problems in the shared
overlay — paginated one per page, in whichever order the compilers
finished. The two jsdom equivalents are removed: the union test, and
no-apply-on-errored-builds, which the multi-compiler console snapshot
already documents (no "Checking for updates" follows a bundle's error
report).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
webpack's invalid hook sometimes reports the watched directory instead
of the changed file under the polling watcher, so the "bundle
rebuilding (…)" line alternated between the file and the fixture dir
and flaked the snapshots. The normalizer now collapses both forms to
"(<fixture> changed)".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bundles' real hot clients call indicator.hide("a")/hide("b") when
their connect-time syncs arrive — the same source names these tests
register — so a late sync could wipe a source mid-test. progress=false
keeps the client away from the indicator entirely.
bjohansebas and others added 16 commits July 25, 2026 17:55
test/fixtures/js3 was compiled output (a misdirected copy of the
dev-server-false test's dist, whose real destination under test/outputs
is gitignored) swept up by a bulk git add.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
--testPathIgnorePatterns is variadic, so a positional file after
`npm run test:only --` was consumed as another ignore pattern — silently
excluding the very file being targeted (the = form appends all the
same). The exclusion now lives in jest.config.js and lifts itself when a
test/e2e path is requested explicitly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…copies

Same pattern as the indicator: each compilation exposes its own bundled
copy of the overlay module and the tests drive both from the browser —
a second copy adopts the shared iframe instead of stacking one, both
sources paginate together in the union, either copy can dismiss what
the other rendered, errors from one copy outrank another's warnings
until the erroring source recovers, clearing an unreported source does
not rebuild the card another copy is showing, and a leaner state shape
left by an older package version gets its missing fields filled.

The jsdom equivalents are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backdrop click, close (×) button, and click-inside-stays-open now run
against real mouse events in the overlay iframe — the backdrop case
clicks the frame's top-left corner away from the centered card, and the
close-button pass rides the catch-up sync that re-shows the overlay
after a reload. The jsdom dismiss describe is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The one-error-one-warning-one-* fixture configs build into
test/fixtures/js3 by design, so every logging run recreates it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pack 5.109

Runtime-error accumulation (two errors paging "1 / 2" ↔ "2 / 2") and
unhandled promise rejections now run against real page errors — the
rejection fixture rejects from the page's own script so the event
carries the real reason. Their jsdom equivalents are removed.

The branch also settles on webpack 5.109: the lockfile catches up with
the already-bumped range, and the assertions and console snapshots
adopt 5.109's parse-error format (numbered code-frame gutter, caret
line, and the "File was parsed as module type" note) instead of
normalizing it away.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…logger

webpack/lib/logging/runtime.js pulls tapable into the client bundle,
and tapable compiles its hooks with new Function — an EvalError under a
require-trusted-types-for 'script' CSP that killed the client's logging
and surfaced spurious runtime errors in the overlay. The replacement is
a level-gated console logger with byte-identical output (same prefix
merging, same level gates, groups from the "log" level up), verified
against the existing console snapshots. It also drops tapable and
webpack's logging machinery from the bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The page is served with require-trusted-types-for 'script' and a
trusted-types allowlist holding only the configured policy name — under
real Chrome enforcement, which jsdom cannot do, the overlay only
renders if every HTML write went through that policy (including inside
the about:blank iframe, which inherits the page's CSP). The jsdom
trusted-types test is removed; the helper gained a pageHeaders option.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…time

Under a require-trusted-types-for 'script' CSP, tapable — bundled
through webpack's logging runtime — cannot compile its hooks: the first
log call throws an EvalError from new Function, killing whatever
listener happened to log (and with it the update flow). The logger
methods are now guarded, so webpack's logging runtime stays the engine
and enforcement just turns logging off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…blish

Two browser pages connected to the same server both apply a single
edit — broadcast proven end to end. The unit variant also stops passing
vacuously: it now publishes while both SSE clients are attached instead
of only observing the per-client connect-time sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
waitForFunction's default requestAnimationFrame polling never fires on
a hidden document, so the backgrounded first tab of the broadcast test
hung on a wait for text it had already rendered. Also settle the
connections before editing so both pages receive the broadcast rather
than a catch-up sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… browser

invalidate() with unchanged sources rebuilds and reaches the page as a
building event followed by a no-op sync — the client neither re-renders
nor reloads (a built here would 404 on the never-emitted manifest).
close() ends the SSE stream under a connected page: the client falls
into its reconnect loop against an endpoint that no longer speaks SSE
and the page keeps running untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ntime filter

Arrow keys page through problems with real keyboard events (the frame
holds focus after the pager click), paginate=false shows the full
problem list with no counter, and a runtime filter configured by a
later bundled copy is honored by the window listeners the first copy
attached — proven non-vacuously by first catching an error through
copy A, then watching copy B's rejecting filter keep the overlay away.
The three jsdom equivalents are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The accent-bar colors are asserted (both hues, including the
error-to-warning flip) by the shared-state e2e, and the default
pagination counter by the pagination e2e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…om describe

The pagination e2e now clamps at the last page under real clicks and
key presses, and a shared-state test drives a bundled copy through the
page-index semantics: a re-publish of the same problems keeps the page
the user navigated to, a different set starts back at page one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem replacement is asserted by the page-retention e2e (a new set
swaps the rendered content), and the overlay API shape is exercised by
every shared-state test driving real bundled copies.

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