Skip to content

Commit b2a77c2

Browse files
committed
test: allow happy-dom suite to pass
1 parent e24d2fa commit b2a77c2

4 files changed

Lines changed: 42 additions & 15 deletions

File tree

.github/workflows/test.yml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,25 @@ jobs:
106106
- name: Run tests
107107
run: npm run test:bun
108108

109+
testHappyDom:
110+
name: 'Test: happy-dom'
111+
timeout-minutes: 15
112+
runs-on: ubuntu-latest
113+
steps:
114+
- uses: actions/checkout@v7
115+
- uses: actions/setup-node@v7
116+
with:
117+
node-version: 24
118+
- name: Install dependencies
119+
run: npm ci
120+
- name: Run tests
121+
run: npm run test:happy-dom
122+
109123
# Known-failing environments, reported but never gating.
110124
#
111-
# happy-dom reports the test server's requests as cross-origin and blocks them. workerd's
112-
# remaining failures all trace to cloudflare/workerd#6022, where its `EventTarget` dispatches
113-
# `on<type>` handler properties itself and so fires our `on*` handlers twice.
125+
# Seven of workerd's eight failures trace to cloudflare/workerd#6022, where its `EventTarget`
126+
# dispatches `on<type>` handler properties itself and so fires our `on*` handlers twice. The
127+
# eighth is a message-wording difference only, see CONTRIBUTING.md.
114128
#
115129
# `continue-on-error` is deliberately on the *step* rather than the job. At job level it keeps
116130
# the overall run green but the job still reports a check run with a `failure` conclusion, which
@@ -124,7 +138,7 @@ jobs:
124138
strategy:
125139
fail-fast: false
126140
matrix:
127-
suite: ['happy-dom', 'workerd']
141+
suite: ['workerd']
128142
steps:
129143
- uses: actions/checkout@v7
130144
- uses: actions/setup-node@v7

CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ The suite in `test/client.test.ts` runs against a real HTTP server in every supp
3535

3636
The browser tests need Playwright's browsers installed once, with `npx playwright install chromium firefox webkit`.
3737

38-
The happy-dom and workerd suites are expected to fail today and do not gate CI. happy-dom reports the test server's requests as cross-origin and blocks them. workerd's remaining failures all come from [cloudflare/workerd#6022](https://github.com/cloudflare/workerd/issues/6022): its `EventTarget` dispatches `on<type>` handler properties itself, on top of the `addEventListener` call our `on*` setters make, so those handlers fire twice, assigning `null` only removes one registration, and they fire ahead of listeners registered before them.
38+
The workerd suite is expected to fail today and does not gate CI. Seven of its eight failures come from [cloudflare/workerd#6022](https://github.com/cloudflare/workerd/issues/6022): its `EventTarget` dispatches `on<type>` handler properties itself, on top of the `addEventListener` call our `on*` setters make, so those handlers fire twice, assigning `null` only removes one registration, and they fire ahead of listeners registered before them. The eighth is only a wording difference - workerd reports a refused connection as `Network connection lost.`, which the per-runtime message pattern in the extended-properties test does not cover.
39+
40+
happy-dom gates CI, with one exception: it does not strip `Authorization` when a request is redirected to a different origin, as [the fetch spec requires](https://fetch.spec.whatwg.org/#http-redirect-fetch), so the four cross-origin redirect tests are asserted as known failures there rather than skipped. They turn red - prompting removal of the workaround - as soon as happy-dom fixes it.
3941

4042
The browser suite is the one place where the endpoints are not served by a standalone server. Vitest serves the test page from its own Vite server, so the endpoints are mounted onto that same server (`test/helpers/ssePlugin.ts`) to keep the page and the endpoints same-origin. Serving them separately would make every request cross-origin and silently change what the CORS, cookie and redirect tests actually assert.
4143

test/client.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ import {
1515
request,
1616
serverOrigin,
1717
serverUrl,
18+
suite,
1819
} from './helpers/env.ts'
1920
import {unicodeLines} from './helpers/fixtures.ts'
2021

2122
/** Only meaningful where CORS, cookies and `withCredentials` are actually enforced. */
2223
const browserTest = test.runIf(hasBrowserSemantics)
2324

25+
/**
26+
* happy-dom copies the full header list onto a redirected request and only ever deletes the
27+
* cookie headers, so `Authorization` survives a redirect to a different origin - which the fetch
28+
* spec requires it not to (https://fetch.spec.whatwg.org/#http-redirect-fetch, step 13). Asserted
29+
* as a known failure rather than skipped, so that this turns red - and the workaround gets
30+
* removed - the moment happy-dom starts stripping the header.
31+
*/
32+
const xOriginRedirectTest = suite === 'happy-dom' ? test.fails : test
33+
2434
test('can connect, receive message, manually disconnect', async () => {
2535
const onMessage = getCallCounter({name: 'onMessage'})
2636
const es = new OurEventSource(new URL(`${serverUrl}/`))
@@ -532,7 +542,7 @@ test('can request cross-origin', async () => {
532542

533543
// Cross-origin redirect tests
534544
;[301, 302, 307, 308].forEach((status) => {
535-
test(`redirects: handles ${status} to different origin`, async () => {
545+
xOriginRedirectTest(`redirects: handles ${status} to different origin`, async () => {
536546
const id = Math.random().toString(36).slice(2)
537547
const onMessage = getCallCounter({name: 'onMessage'})
538548
const onOpen = getCallCounter<Event>({name: 'onOpen'})
@@ -730,7 +740,7 @@ test('[NON-SPEC] message event contains extended properties (failed connection)'
730740
timeStamp: expect.any(Number),
731741
// Node, Deno, Bun, Chromium, Webkit, Firefox _ALL_ have different messages 😅
732742
message: expect.stringMatching(
733-
/fetch failed|failed to fetch|load failed|attempting to fetch|connection refused|unable to connect/i,
743+
/fetch failed|failed to fetch|load failed|attempting to fetch|connection refused|ECONNREFUSED|unable to connect/i,
734744
),
735745
code: undefined,
736746
})

vitest.happy-dom.config.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ import {sharedConfig, standaloneServer} from './vitest.config.ts'
88
* browser-semantics tests here (CORS, cookies, `withCredentials`) while still talking to the
99
* standalone server.
1010
*
11-
* This suite is expected to fail for now: happy-dom's CORS handling does not yet line up with a
12-
* real browser's, which is being worked through separately. It runs non-blocking in CI so the
13-
* failures stay visible without gating the other environments.
11+
* The one behaviour it does not match a real browser on is stripping `Authorization` from a
12+
* cross-origin redirect; `test/client.test.ts` marks those four tests as known failures.
1413
*/
1514
export default defineConfig({
1615
test: {
1716
...sharedConfig,
1817
environment: 'happy-dom',
19-
// happy-dom otherwise defaults `location` to `http://localhost:3000`, so give it the server's
20-
// origin - that is the split the browser suite is written against. Note that happy-dom still
21-
// reports these requests as cross-origin and blocks them, which is part of what is being
22-
// worked through separately.
23-
environmentOptions: {happyDom: {url: `http://127.0.0.1:${TEST_PORT}`}},
18+
// Point `location` at the server, so its requests are same-origin - the split the browser
19+
// suite is written against. happy-dom otherwise defaults to `http://localhost:3000`, and
20+
// every request to the server then counts as cross-origin.
21+
//
22+
// The key is `happyDOM`, not `happyDom`: `EnvironmentOptions` carries an index signature, so
23+
// a misspelling type-checks and is then silently ignored, leaving the default URL in place.
24+
environmentOptions: {happyDOM: {url: `http://127.0.0.1:${TEST_PORT}`}},
2425
globalSetup: standaloneServer,
2526
provide: {port: TEST_PORT, suite: 'happy-dom'},
2627
},

0 commit comments

Comments
 (0)