Skip to content

Demote non-browser e2e assertions to a fetch-based integration layer #777

Description

@vivek7405

Problem

Split off from #774 (which landed the config-level testing wins: parallel Node/Bun e2e jobs, the WebKit + Firefox browser matrix, and opt-in coverage). This issue covers the one heavy, separable item deliberately deferred from that PR because it is a large, careful refactor rather than a minimal one.

test/e2e/e2e.test.mjs (~144KB, ~119 test() blocks) drives everything through Puppeteer against the running blog, but a large share of those blocks assert only on the HTTP response, the SSR HTML string, response headers, or the importmap, none of which need a real browser. Each Puppeteer navigation is 10-50x slower than a fetch() against the same server, and the whole suite now runs twice (Node + Bun), so every demotable block is paid for four times over.

Design / approach

Add a fast fetch()-based node:test integration layer that spawns ONE blog server (reusing the e2e harness's existing spawn / freePort helpers) and asserts over plain fetch(), then move the non-browser blocks out of the Puppeteer file into it and delete them from the Puppeteer suite. Keep in Puppeteer only blocks that genuinely need a DOM: page.evaluate, navigation/hydration, client-router, slots, view transitions, streaming-into-DOM.

Clear demotion candidates already spotted in test/e2e/e2e.test.mjs (audit each, do not assume): import map includes all framework entries (~L195), modulepreload links are deduplicated (~L207), every modulepreload resolves (~L216), health endpoint responds (~L672), /api/posts GET returns an array (~L1286), POST /api/posts without auth is rejected (~L1293), 404 for unknown pathname serves text/html (~L1306), no CSRF cookie on a GET response (~L1337), dynamic route renders post title in <head> (~L1025), unknown slug hits notFound 404 (~L1043), dashboard middleware redirects (~L1055), metadata <title>/<meta> per route (~L1240).

Also fold in the cheap cleanup deferred from #774: extract the per-file hand-rolled const assert = { ok, equal, deepEqual, ... } block (duplicated across 45 browser .test.js files) into one shared test/browser-assert.js module the tests import. Superset of methods actually used: ok, equal, deepEqual, strictEqual, notStrictEqual, notEqual, match, isTrue, isFalse, isOk, isArray, doesNotThrow, throws. Match the existing inline semantics exactly (strict equality, JSON-stringify deepEqual) so it is a pure drop-in.

Implementation notes (for the implementing agent)

Acceptance criteria

  • A fetch-based integration test file covers the demoted assertions and runs under the fast node driver (no Puppeteer)
  • The migrated blocks are deleted from test/e2e/e2e.test.mjs; the Puppeteer suite retains only genuinely DOM-needing tests
  • One shared browser-assert module replaces the 45 inline const assert = {} blocks; browser suite stays green on Chromium/Firefox/WebKit
  • e2e wall-clock measurably drops vs the post-Improve testing architecture and cut e2e wall-clock time #774 baseline
  • agent-docs/testing.md updated

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions