Skip to content

Commit cc60962

Browse files
authored
feat: extract shared DOM/testing core to @pokujs/dom + benchmark suite (#14)
* feat: extract shared DOM/testing core to @pokujs/dom, add benchmark suite with teardown-skew fix - Move shared infrastructure (screen proxy, metrics emitter, getNow, DOM env\n setup, plugin internals) to @pokujs/dom dependency\n- All src/ modules now re-export from @pokujs/dom; react-specific logic retained\n- Add @pokujs/dom ^0.1.0 as a runtime dependency\n- Add full benchmark suite (Poku vs Jest vs Vitest, happy-dom & jsdom)\n- Fix benchmark happy-dom timing: parse Poku reported Duration from stdout\n to exclude Happy DOM async-teardown overhead from wall-clock measurements\n- benchmark/results.json and benchmark/REPORT.md included with latest results * chore: update @pokujs/dom to ^1.0.1 * fix: use registry-resolved package-lock.json for @pokujs/dom * chore: update benchmark report and results * chore: bump @pokujs/dom to ^1.1.0 * fix: add deno.json import map for @pokujs/dom Deno with isolation:none loads all test files in the same npm:poku process. In that context bare specifiers like @pokujs/dom are not resolved from node_modules automatically. Adding an explicit import map entry pointing to npm:@pokujs/dom fixes resolution for all test files that transitively import @pokujs/dom. * chore: bump @pokujs/dom to ^1.1.1 and add multi-suite runner * chore: bump @pokujs/dom to ^1.1.2 * chore: update package-lock.json for @pokujs/dom ^1.1.2
1 parent 070fbab commit cc60962

18 files changed

Lines changed: 1649 additions & 1059 deletions

benchmark/REPORT.md

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,97 @@
11
# React Testing Framework Benchmark Report
22

3-
> Generated: Wed, 01 Apr 2026 12:41:50 GMT
3+
> Generated: Sun, 05 Apr 2026 19:06:36 GMT
44
55
## Environment
66

7-
| Property | Value |
8-
| ------------- | ------------- |
9-
| Node.js | v22.5.1 |
10-
| Platform | darwin 25.4.0 |
11-
| CPU | Apple M3 Pro |
12-
| CPU Cores | 12 |
13-
| Total RAM | 18.0 GB |
14-
| Runs/scenario | 7 (trim ±1) |
7+
| Property | Value |
8+
|---|---|
9+
| Node.js | v22.5.1 |
10+
| Platform | darwin 25.4.0 |
11+
| CPU | Apple M3 Pro |
12+
| CPU Cores | 12 |
13+
| Total RAM | 18.0 GB |
14+
| Runs/scenario | 7 (trim ±1) |
1515

1616
## Scenarios
1717

1818
Each scenario runs the **same 9 React tests** across 5 test files:
1919

20-
| Test File | Tests |
21-
| ---------------------- | ------------------------------------------ |
22-
| 'counter.test.jsx' | 1 — stateful counter, event interaction |
23-
| 'hooks.test.jsx' | 2 — custom hook harness + `renderHook` |
24-
| 'lifecycle.test.jsx' | 2 — `rerender`, `unmount` + effect cleanup |
25-
| 'context.test.jsx' | 1 — `createContext` + wrapper injection |
26-
| 'concurrency.test.jsx' | 2 — React 19 `use()` + `useTransition` |
20+
| Test File | Tests |
21+
|---|---|
22+
| 'counter.test.jsx' | 1 — stateful counter, event interaction |
23+
| 'hooks.test.jsx' | 2 — custom hook harness + `renderHook` |
24+
| 'lifecycle.test.jsx' | 2 — `rerender`, `unmount` + effect cleanup |
25+
| 'context.test.jsx' | 1 — `createContext` + wrapper injection |
26+
| 'concurrency.test.jsx' | 2 — React 19 `use()` + `useTransition` |
2727

2828
### Frameworks under test
2929

30-
| Combination | DOM layer | Assertion style |
31-
| --------------------------------- | ------------------------------ | -------------------- |
32-
| poku + @pokujs/react | happy-dom | `assert.strictEqual` |
33-
| poku + @pokujs/react | jsdom | `assert.strictEqual` |
34-
| jest 29 + @testing-library/react | jsdom (jest-environment-jsdom) | `expect().toBe()` |
35-
| vitest 3 + @testing-library/react | jsdom | `expect().toBe()` |
36-
| vitest 3 + @testing-library/react | happy-dom | `expect().toBe()` |
30+
| Combination | DOM layer | Assertion style |
31+
|---|---|---|
32+
| poku + @pokujs/react | happy-dom | `assert.strictEqual` |
33+
| poku + @pokujs/react | jsdom | `assert.strictEqual` |
34+
| jest 29 + @testing-library/react | jsdom (jest-environment-jsdom) | `expect().toBe()` |
35+
| vitest 3 + @testing-library/react | jsdom | `expect().toBe()` |
36+
| vitest 3 + @testing-library/react | happy-dom | `expect().toBe()` |
3737

3838
## Results
3939

4040
| Scenario | Mean | Min | Max | Stdev | Peak RSS | vs poku+happy-dom |
41-
| ------------------ | ------ | ------ | ------ | ------ | -------- | ----------------- |
42-
| poku + happy-dom | 0.560s | 0.515s | 0.600s | 0.033s | 154.3 MB | _(baseline)_ |
43-
| poku + jsdom | 0.444s | 0.429s | 0.451s | 0.008s | 157.1 MB | -21% |
44-
| jest + jsdom | 1.040s | 0.975s | 1.135s | 0.056s | 203.4 MB | +86% |
45-
| vitest + jsdom | 1.193s | 1.129s | 1.269s | 0.057s | 152.3 MB | +113% |
46-
| vitest + happy-dom | 1.041s | 0.990s | 1.126s | 0.047s | 117.1 MB | +86% |
47-
48-
> **Wall-clock time** is measured with `performance.now()` around the child-process spawn.
41+
|--------------------|--------|--------|--------|--------|----------|-------------------|
42+
| poku + happy-dom | 0.115s | 0.112s | 0.109s | 0.004s | 127.4 MB | *(baseline)* |
43+
| poku + jsdom | 0.294s | 0.280s | 0.299s | 0.007s | 172.1 MB | +157% |
44+
| jest + jsdom | 0.843s | 0.768s | 0.879s | 0.041s | 198.8 MB | +636% |
45+
| vitest + jsdom | 0.968s | 0.951s | 0.986s | 0.011s | 147.8 MB | +745% |
46+
| vitest + happy-dom | 0.845s | 0.825s | 0.856s | 0.013s | 115.2 MB | +637% |
47+
48+
> **Poku elapsed time** uses Poku's reported suite `Duration` (ANSI-stripped parse) to avoid teardown-skew artifacts.
49+
> **Jest/Vitest elapsed time** is measured with `performance.now()` around the child-process spawn.
4950
> **Peak RSS** is captured via `/usr/bin/time -l` on macOS (bytes → MB).
5051
> The baseline for relative comparisons is **poku + happy-dom**.
5152
5253
## Analysis
5354

5455
### Overall ranking (mean wall-clock time)
5556

56-
1. **poku + jsdom** — 0.444s
57-
2. **poku + happy-dom** — 0.560s
58-
3. **jest + jsdom**1.040s
59-
4. **vitest + happy-dom**1.041s
60-
5. **vitest + jsdom**1.193s
57+
1. **poku + happy-dom** — 0.115s
58+
2. **poku + jsdom** — 0.294s
59+
3. **jest + jsdom**0.843s
60+
4. **vitest + happy-dom**0.845s
61+
5. **vitest + jsdom**0.968s
6162

6263
### Speed comparison
6364

64-
- poku+happy-dom vs jest+jsdom: jest is **86% slower**
65-
- poku+happy-dom vs vitest+jsdom: vitest is **113% slower**
65+
- poku+happy-dom vs jest+jsdom: jest is **636% slower**
66+
- poku+happy-dom vs vitest+jsdom: vitest is **745% slower**
6667
- jest+jsdom vs vitest+jsdom: vitest is **15% slower** than jest
6768

6869
### DOM adapter impact
6970

70-
- **poku**: happy-dom vs jsdom — jsdom is **-21% faster**
71+
- **poku**: happy-dom vs jsdom — jsdom is **157% slower**
7172
- **vitest**: happy-dom vs jsdom — jsdom is **15% slower**
7273

7374
### Memory footprint
7475

75-
- **vitest + happy-dom**: 117.1 MB peak RSS
76-
- **vitest + jsdom**: 152.3 MB peak RSS
77-
- **poku + happy-dom**: 154.3 MB peak RSS
78-
- **poku + jsdom**: 157.1 MB peak RSS
79-
- **jest + jsdom**: 203.4 MB peak RSS
76+
- **vitest + happy-dom**: 115.2 MB peak RSS
77+
- **poku + happy-dom**: 127.4 MB peak RSS
78+
- **vitest + jsdom**: 147.8 MB peak RSS
79+
- **poku + jsdom**: 172.1 MB peak RSS
80+
- **jest + jsdom**: 198.8 MB peak RSS
8081

8182
### Consistency (lower stdev = more predictable)
8283

83-
- **poku + jsdom**: σ = 0.008s
84-
- **poku + happy-dom**: σ = 0.033s
85-
- **vitest + happy-dom**: σ = 0.047s
86-
- **jest + jsdom**: σ = 0.056s
87-
- **vitest + jsdom**: σ = 0.057s
84+
- **poku + happy-dom**: σ = 0.004s
85+
- **poku + jsdom**: σ = 0.007s
86+
- **vitest + jsdom**: σ = 0.011s
87+
- **vitest + happy-dom**: σ = 0.013s
88+
- **jest + jsdom**: σ = 0.041s
8889

8990
## Key findings
9091

91-
- **Fastest**: poku + jsdom — 0.444s mean
92-
- **Slowest**: vitest + jsdom — 1.193s mean
93-
- **Speed spread**: 169% difference between fastest and slowest
92+
- **Fastest**: poku + happy-dom — 0.115s mean
93+
- **Slowest**: vitest + jsdom — 0.968s mean
94+
- **Speed spread**: 745% difference between fastest and slowest
9495

9596
### Interpretation
9697

@@ -100,7 +101,6 @@ processes with minimal bootstrap — means cold-start overhead is proportional t
100101
files, not to the framework's own initialization.
101102

102103
**jest** carries the heaviest startup cost due to:
103-
104104
1. Babel transformation of every TSX file on first run (no persistent cache in this benchmark)
105105
2. 'jest-worker' process pool initialisation
106106
3. JSDOM environment setup per test file

benchmark/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmark/results.json

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"timestamp": "2026-04-01T12:41:50.774Z",
2+
"timestamp": "2026-04-05T19:06:36.388Z",
33
"system": {
44
"nodeVersion": "v22.5.1",
55
"platform": "darwin 25.4.0",
@@ -14,68 +14,68 @@
1414
"results": [
1515
{
1616
"label": "poku + happy-dom",
17-
"mean": 559.9537916000002,
18-
"min": 515.060708,
19-
"max": 599.9880840000001,
20-
"stddev": 33.06234198220535,
21-
"meanRss": 161808384,
22-
"meanUserCpu": 672,
23-
"meanSysCpu": 202,
17+
"mean": 114.5411498,
18+
"min": 111.865375,
19+
"max": 108.566625,
20+
"stddev": 4.164501573153617,
21+
"meanRss": 133624627.2,
22+
"meanUserCpu": 358,
23+
"meanSysCpu": 80,
2424
"runs": 7,
2525
"validRuns": 7,
2626
"failures": 0
2727
},
2828
{
2929
"label": "poku + jsdom",
30-
"mean": 443.6319584000001,
31-
"min": 428.5047089999998,
32-
"max": 450.59829100000024,
33-
"stddev": 7.9857833050707905,
34-
"meanRss": 164767334.4,
35-
"meanUserCpu": 432,
36-
"meanSysCpu": 92,
30+
"mean": 294.0646082,
31+
"min": 280.499125,
32+
"max": 298.546375,
33+
"stddev": 6.855649968732701,
34+
"meanRss": 180456652.8,
35+
"meanUserCpu": 532,
36+
"meanSysCpu": 102,
3737
"runs": 7,
3838
"validRuns": 7,
3939
"failures": 0
4040
},
4141
{
4242
"label": "jest + jsdom",
43-
"mean": 1040.1169332000002,
44-
"min": 975.1540420000001,
45-
"max": 1134.9154159999998,
46-
"stddev": 56.06532298235639,
47-
"meanRss": 213300019.2,
48-
"meanUserCpu": 936,
49-
"meanSysCpu": 228,
43+
"mean": 842.5360584000006,
44+
"min": 768.3345829999998,
45+
"max": 879.3446250000015,
46+
"stddev": 40.57889525259689,
47+
"meanRss": 208506060.8,
48+
"meanUserCpu": 784,
49+
"meanSysCpu": 152,
5050
"runs": 7,
5151
"validRuns": 7,
5252
"failures": 0
5353
},
5454
{
5555
"label": "vitest + jsdom",
56-
"mean": 1192.7835997999996,
57-
"min": 1129.2755830000006,
58-
"max": 1268.7202919999982,
59-
"stddev": 56.83080807294834,
60-
"meanRss": 159652249.6,
61-
"meanUserCpu": 3798,
62-
"meanSysCpu": 1380,
56+
"mean": 967.6274329999997,
57+
"min": 950.946915999999,
58+
"max": 985.529625000001,
59+
"stddev": 11.190019303221952,
60+
"meanRss": 155002470.4,
61+
"meanUserCpu": 3524,
62+
"meanSysCpu": 954,
6363
"runs": 7,
6464
"validRuns": 7,
6565
"failures": 0
6666
},
6767
{
6868
"label": "vitest + happy-dom",
69-
"mean": 1041.3667920000007,
70-
"min": 990.0950420000008,
71-
"max": 1126.3293750000012,
72-
"stddev": 47.26983715919258,
73-
"meanRss": 122814464,
74-
"meanUserCpu": 3346,
75-
"meanSysCpu": 1156,
69+
"mean": 844.6526001999998,
70+
"min": 824.9157500000001,
71+
"max": 856.1071249999986,
72+
"stddev": 13.21280400724632,
73+
"meanRss": 120832000,
74+
"meanUserCpu": 3056,
75+
"meanSysCpu": 900,
7676
"runs": 7,
7777
"validRuns": 7,
7878
"failures": 0
7979
}
8080
]
81-
}
81+
}

0 commit comments

Comments
 (0)