@monoes/monobrowse 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/src/__tests__/profiler-heap.test.d.ts +2 -0
  2. package/dist/src/__tests__/profiler-heap.test.d.ts.map +1 -0
  3. package/dist/src/__tests__/profiler-heap.test.js +154 -0
  4. package/dist/src/__tests__/profiler-heap.test.js.map +1 -0
  5. package/dist/src/__tests__/report-a11y.test.d.ts +2 -0
  6. package/dist/src/__tests__/report-a11y.test.d.ts.map +1 -0
  7. package/dist/src/__tests__/report-a11y.test.js +147 -0
  8. package/dist/src/__tests__/report-a11y.test.js.map +1 -0
  9. package/dist/src/__tests__/report-analyze.test.d.ts +2 -0
  10. package/dist/src/__tests__/report-analyze.test.d.ts.map +1 -0
  11. package/dist/src/__tests__/report-analyze.test.js +157 -0
  12. package/dist/src/__tests__/report-analyze.test.js.map +1 -0
  13. package/dist/src/__tests__/report-budget.test.d.ts +2 -0
  14. package/dist/src/__tests__/report-budget.test.d.ts.map +1 -0
  15. package/dist/src/__tests__/report-budget.test.js +80 -0
  16. package/dist/src/__tests__/report-budget.test.js.map +1 -0
  17. package/dist/src/__tests__/report-diff.test.d.ts +2 -0
  18. package/dist/src/__tests__/report-diff.test.d.ts.map +1 -0
  19. package/dist/src/__tests__/report-diff.test.js +231 -0
  20. package/dist/src/__tests__/report-diff.test.js.map +1 -0
  21. package/dist/src/__tests__/report-evidence.test.d.ts +2 -0
  22. package/dist/src/__tests__/report-evidence.test.d.ts.map +1 -0
  23. package/dist/src/__tests__/report-evidence.test.js +214 -0
  24. package/dist/src/__tests__/report-evidence.test.js.map +1 -0
  25. package/dist/src/__tests__/report-flake.test.d.ts +2 -0
  26. package/dist/src/__tests__/report-flake.test.d.ts.map +1 -0
  27. package/dist/src/__tests__/report-flake.test.js +182 -0
  28. package/dist/src/__tests__/report-flake.test.js.map +1 -0
  29. package/dist/src/__tests__/report-history.test.d.ts +2 -0
  30. package/dist/src/__tests__/report-history.test.d.ts.map +1 -0
  31. package/dist/src/__tests__/report-history.test.js +229 -0
  32. package/dist/src/__tests__/report-history.test.js.map +1 -0
  33. package/dist/src/__tests__/report-output.test.d.ts +2 -0
  34. package/dist/src/__tests__/report-output.test.d.ts.map +1 -0
  35. package/dist/src/__tests__/report-output.test.js +169 -0
  36. package/dist/src/__tests__/report-output.test.js.map +1 -0
  37. package/dist/src/__tests__/report-png.test.d.ts +2 -0
  38. package/dist/src/__tests__/report-png.test.d.ts.map +1 -0
  39. package/dist/src/__tests__/report-png.test.js +84 -0
  40. package/dist/src/__tests__/report-png.test.js.map +1 -0
  41. package/dist/src/__tests__/report-render-extras.test.d.ts +2 -0
  42. package/dist/src/__tests__/report-render-extras.test.d.ts.map +1 -0
  43. package/dist/src/__tests__/report-render-extras.test.js +285 -0
  44. package/dist/src/__tests__/report-render-extras.test.js.map +1 -0
  45. package/dist/src/__tests__/report-render.test.d.ts +2 -0
  46. package/dist/src/__tests__/report-render.test.d.ts.map +1 -0
  47. package/dist/src/__tests__/report-render.test.js +143 -0
  48. package/dist/src/__tests__/report-render.test.js.map +1 -0
  49. package/dist/src/__tests__/report-run.test.d.ts +2 -0
  50. package/dist/src/__tests__/report-run.test.d.ts.map +1 -0
  51. package/dist/src/__tests__/report-run.test.js +234 -0
  52. package/dist/src/__tests__/report-run.test.js.map +1 -0
  53. package/dist/src/browser/index.d.ts +1 -0
  54. package/dist/src/browser/index.d.ts.map +1 -1
  55. package/dist/src/browser/index.js +1 -0
  56. package/dist/src/browser/index.js.map +1 -1
  57. package/dist/src/browser/profiler.d.ts.map +1 -1
  58. package/dist/src/browser/profiler.js +19 -22
  59. package/dist/src/browser/profiler.js.map +1 -1
  60. package/dist/src/cli/commands.d.ts.map +1 -1
  61. package/dist/src/cli/commands.js +254 -0
  62. package/dist/src/cli/commands.js.map +1 -1
  63. package/dist/src/report/a11y.d.ts +52 -0
  64. package/dist/src/report/a11y.d.ts.map +1 -0
  65. package/dist/src/report/a11y.js +231 -0
  66. package/dist/src/report/a11y.js.map +1 -0
  67. package/dist/src/report/analyze.d.ts +20 -0
  68. package/dist/src/report/analyze.d.ts.map +1 -0
  69. package/dist/src/report/analyze.js +136 -0
  70. package/dist/src/report/analyze.js.map +1 -0
  71. package/dist/src/report/budget.d.ts +27 -0
  72. package/dist/src/report/budget.d.ts.map +1 -0
  73. package/dist/src/report/budget.js +143 -0
  74. package/dist/src/report/budget.js.map +1 -0
  75. package/dist/src/report/collect-a11y.d.ts +25 -0
  76. package/dist/src/report/collect-a11y.d.ts.map +1 -0
  77. package/dist/src/report/collect-a11y.js +154 -0
  78. package/dist/src/report/collect-a11y.js.map +1 -0
  79. package/dist/src/report/collect.d.ts +45 -0
  80. package/dist/src/report/collect.d.ts.map +1 -0
  81. package/dist/src/report/collect.js +275 -0
  82. package/dist/src/report/collect.js.map +1 -0
  83. package/dist/src/report/evidence.d.ts +80 -0
  84. package/dist/src/report/evidence.d.ts.map +1 -0
  85. package/dist/src/report/evidence.js +279 -0
  86. package/dist/src/report/evidence.js.map +1 -0
  87. package/dist/src/report/flake.d.ts +88 -0
  88. package/dist/src/report/flake.d.ts.map +1 -0
  89. package/dist/src/report/flake.js +232 -0
  90. package/dist/src/report/flake.js.map +1 -0
  91. package/dist/src/report/history.d.ts +85 -0
  92. package/dist/src/report/history.d.ts.map +1 -0
  93. package/dist/src/report/history.js +176 -0
  94. package/dist/src/report/history.js.map +1 -0
  95. package/dist/src/report/index.d.ts +102 -0
  96. package/dist/src/report/index.d.ts.map +1 -0
  97. package/dist/src/report/index.js +216 -0
  98. package/dist/src/report/index.js.map +1 -0
  99. package/dist/src/report/pixel-diff.d.ts +30 -0
  100. package/dist/src/report/pixel-diff.d.ts.map +1 -0
  101. package/dist/src/report/pixel-diff.js +175 -0
  102. package/dist/src/report/pixel-diff.js.map +1 -0
  103. package/dist/src/report/png.d.ts +28 -0
  104. package/dist/src/report/png.d.ts.map +1 -0
  105. package/dist/src/report/png.js +259 -0
  106. package/dist/src/report/png.js.map +1 -0
  107. package/dist/src/report/render-evidence.d.ts +16 -0
  108. package/dist/src/report/render-evidence.d.ts.map +1 -0
  109. package/dist/src/report/render-evidence.js +174 -0
  110. package/dist/src/report/render-evidence.js.map +1 -0
  111. package/dist/src/report/render-trend.d.ts +16 -0
  112. package/dist/src/report/render-trend.d.ts.map +1 -0
  113. package/dist/src/report/render-trend.js +194 -0
  114. package/dist/src/report/render-trend.js.map +1 -0
  115. package/dist/src/report/render.d.ts +21 -0
  116. package/dist/src/report/render.d.ts.map +1 -0
  117. package/dist/src/report/render.js +286 -0
  118. package/dist/src/report/render.js.map +1 -0
  119. package/dist/src/report/run-diff.d.ts +18 -0
  120. package/dist/src/report/run-diff.d.ts.map +1 -0
  121. package/dist/src/report/run-diff.js +51 -0
  122. package/dist/src/report/run-diff.js.map +1 -0
  123. package/dist/src/report/structure.d.ts +41 -0
  124. package/dist/src/report/structure.d.ts.map +1 -0
  125. package/dist/src/report/structure.js +210 -0
  126. package/dist/src/report/structure.js.map +1 -0
  127. package/dist/src/report/trend.d.ts +26 -0
  128. package/dist/src/report/trend.d.ts.map +1 -0
  129. package/dist/src/report/trend.js +164 -0
  130. package/dist/src/report/trend.js.map +1 -0
  131. package/dist/src/report/types.d.ts +320 -0
  132. package/dist/src/report/types.d.ts.map +1 -0
  133. package/dist/src/report/types.js +11 -0
  134. package/dist/src/report/types.js.map +1 -0
  135. package/dist/src/report/util.d.ts +17 -0
  136. package/dist/src/report/util.d.ts.map +1 -0
  137. package/dist/src/report/util.js +45 -0
  138. package/dist/src/report/util.js.map +1 -0
  139. package/dist/tsconfig.tsbuildinfo +1 -1
  140. package/package.json +1 -1
  141. package/src/__tests__/profiler-heap.test.ts +191 -0
  142. package/src/__tests__/report-a11y.test.ts +180 -0
  143. package/src/__tests__/report-analyze.test.ts +202 -0
  144. package/src/__tests__/report-budget.test.ts +92 -0
  145. package/src/__tests__/report-diff.test.ts +272 -0
  146. package/src/__tests__/report-evidence.test.ts +276 -0
  147. package/src/__tests__/report-flake.test.ts +214 -0
  148. package/src/__tests__/report-history.test.ts +293 -0
  149. package/src/__tests__/report-output.test.ts +210 -0
  150. package/src/__tests__/report-png.test.ts +95 -0
  151. package/src/__tests__/report-render-extras.test.ts +315 -0
  152. package/src/__tests__/report-render.test.ts +175 -0
  153. package/src/__tests__/report-run.test.ts +264 -0
  154. package/src/browser/index.ts +1 -0
  155. package/src/browser/profiler.ts +28 -21
  156. package/src/cli/commands.ts +272 -0
  157. package/src/report/a11y.ts +242 -0
  158. package/src/report/analyze.ts +181 -0
  159. package/src/report/budget.ts +155 -0
  160. package/src/report/collect-a11y.ts +200 -0
  161. package/src/report/collect.ts +408 -0
  162. package/src/report/evidence.ts +379 -0
  163. package/src/report/flake.ts +303 -0
  164. package/src/report/history.ts +219 -0
  165. package/src/report/index.ts +307 -0
  166. package/src/report/pixel-diff.ts +197 -0
  167. package/src/report/png.ts +290 -0
  168. package/src/report/render-evidence.ts +194 -0
  169. package/src/report/render-trend.ts +225 -0
  170. package/src/report/render.ts +314 -0
  171. package/src/report/run-diff.ts +63 -0
  172. package/src/report/structure.ts +212 -0
  173. package/src/report/trend.ts +189 -0
  174. package/src/report/types.ts +356 -0
  175. package/src/report/util.ts +48 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profiler-heap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler-heap.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/profiler-heap.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Regression cover for the 0-byte heap snapshot.
3
+ *
4
+ * Chrome emits `HeapProfiler.reportHeapSnapshotProgress` with finished:true
5
+ * BEFORE it streams a single `addHeapSnapshotChunk` (verified against Chrome
6
+ * 153). startHeapSnapshot used to resolve on that progress event, detach its
7
+ * chunk listener and write an empty file every time. The stub below replays
8
+ * that exact ordering, so a regression fails here rather than in a profiling
9
+ * session nobody notices until DevTools refuses to load the snapshot.
10
+ */
11
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
12
+ import { tmpdir } from 'node:os';
13
+ import { join } from 'node:path';
14
+ import { afterEach, describe, expect, it, vi } from 'vitest';
15
+ import { startHeapSnapshot } from '../browser/profiler.js';
16
+ /**
17
+ * A CdpClient that replays Chrome's real takeHeapSnapshot sequence: progress
18
+ * events first, then the chunks, and only then the command response.
19
+ */
20
+ function heapClient(sessionId, options = {}) {
21
+ const { chunks = ['{"snapshot":', '{"node_count":2}', '}'], emitProgressFirst = true } = options;
22
+ const listeners = new Map();
23
+ const calls = [];
24
+ const emit = (event, params, sid) => {
25
+ for (const fn of listeners.get(event) ?? [])
26
+ fn(params, sid);
27
+ };
28
+ // Chrome delivers each of these in its own WebSocket frame, so the stub
29
+ // yields between them. Emitting synchronously would paper over the very
30
+ // bug this file exists to catch: the buggy implementation resolved on the
31
+ // progress event, and only lost the chunks because they arrived a turn
32
+ // later, after its `finally` had detached the listener.
33
+ const tick = () => new Promise((r) => setTimeout(r, 0));
34
+ const client = {
35
+ on: vi.fn((event, fn) => {
36
+ if (!listeners.has(event))
37
+ listeners.set(event, new Set());
38
+ listeners.get(event).add(fn);
39
+ return () => listeners.get(event)?.delete(fn);
40
+ }),
41
+ send: vi.fn(async (method, _params, _sid) => {
42
+ calls.push(method);
43
+ if (method === 'HeapProfiler.takeHeapSnapshot') {
44
+ if (emitProgressFirst) {
45
+ emit('HeapProfiler.reportHeapSnapshotProgress', { done: 1, total: 1, finished: true }, sessionId);
46
+ await tick();
47
+ }
48
+ for (const chunk of chunks) {
49
+ emit('HeapProfiler.addHeapSnapshotChunk', { chunk }, options.chunkSessionId ?? sessionId);
50
+ await tick();
51
+ }
52
+ }
53
+ return {};
54
+ }),
55
+ };
56
+ return { client, calls, listeners };
57
+ }
58
+ const dirs = [];
59
+ async function outPath(name = 'heap.heapsnapshot') {
60
+ const dir = await mkdtemp(join(tmpdir(), 'monobrowse-heap-'));
61
+ dirs.push(dir);
62
+ return join(dir, name);
63
+ }
64
+ afterEach(async () => {
65
+ await Promise.all(dirs.splice(0).map((d) => rm(d, { recursive: true, force: true })));
66
+ });
67
+ let seq = 0;
68
+ const nextSession = () => `sid-${++seq}`;
69
+ describe('startHeapSnapshot', () => {
70
+ it('writes every streamed chunk, not an empty file', async () => {
71
+ const path = await outPath();
72
+ const sid = nextSession();
73
+ const { client } = heapClient(sid);
74
+ const written = await startHeapSnapshot(client, sid, path);
75
+ expect(written).toBe(path);
76
+ const contents = await readFile(path, 'utf8');
77
+ expect(contents).toBe('{"snapshot":{"node_count":2}}');
78
+ expect(contents.length).toBeGreaterThan(0);
79
+ });
80
+ it('does not resolve on the finished progress event that precedes the chunks', async () => {
81
+ // The regression: with emitProgressFirst the old implementation resolved
82
+ // before any chunk arrived. Ordering is the whole point of this case.
83
+ const path = await outPath();
84
+ const sid = nextSession();
85
+ const { client } = heapClient(sid, {
86
+ emitProgressFirst: true,
87
+ chunks: ['a'.repeat(64), 'b'.repeat(64)],
88
+ });
89
+ await startHeapSnapshot(client, sid, path);
90
+ expect((await readFile(path, 'utf8')).length).toBe(128);
91
+ });
92
+ it('works when Chrome sends no progress events at all', async () => {
93
+ const path = await outPath();
94
+ const sid = nextSession();
95
+ const { client } = heapClient(sid, { emitProgressFirst: false, chunks: ['{}'] });
96
+ await expect(startHeapSnapshot(client, sid, path)).resolves.toBe(path);
97
+ await expect(readFile(path, 'utf8')).resolves.toBe('{}');
98
+ });
99
+ it('asks Chrome not to report progress, since nothing consumes it', async () => {
100
+ const path = await outPath();
101
+ const sid = nextSession();
102
+ const { client } = heapClient(sid);
103
+ await startHeapSnapshot(client, sid, path);
104
+ const take = client.send.mock.calls.find((c) => c[0] === 'HeapProfiler.takeHeapSnapshot');
105
+ expect(take?.[1]).toEqual({ reportProgress: false });
106
+ });
107
+ it('enables and disables the heap profiler around the capture', async () => {
108
+ const path = await outPath();
109
+ const sid = nextSession();
110
+ const { client, calls } = heapClient(sid);
111
+ await startHeapSnapshot(client, sid, path);
112
+ expect(calls).toEqual([
113
+ 'HeapProfiler.enable',
114
+ 'HeapProfiler.takeHeapSnapshot',
115
+ 'HeapProfiler.disable',
116
+ ]);
117
+ });
118
+ it('ignores chunks belonging to another session', async () => {
119
+ const path = await outPath();
120
+ const sid = nextSession();
121
+ const { client } = heapClient(sid, { chunkSessionId: 'other-session' });
122
+ await expect(startHeapSnapshot(client, sid, path)).rejects.toThrow(/no data/i);
123
+ });
124
+ it('fails loudly instead of writing a 0-byte snapshot', async () => {
125
+ const path = await outPath();
126
+ const sid = nextSession();
127
+ const { client } = heapClient(sid, { chunks: [] });
128
+ await expect(startHeapSnapshot(client, sid, path)).rejects.toThrow(/produced no data/);
129
+ await expect(readFile(path, 'utf8')).rejects.toThrow();
130
+ });
131
+ it('detaches its chunk listener once the snapshot is done', async () => {
132
+ const path = await outPath();
133
+ const sid = nextSession();
134
+ const { client, listeners } = heapClient(sid);
135
+ await startHeapSnapshot(client, sid, path);
136
+ expect(listeners.get('HeapProfiler.addHeapSnapshotChunk')?.size ?? 0).toBe(0);
137
+ });
138
+ it('releases the in-progress guard after a failure, so a retry is possible', async () => {
139
+ const failing = {
140
+ on: vi.fn(() => () => { }),
141
+ send: vi.fn(async (method) => {
142
+ if (method === 'HeapProfiler.takeHeapSnapshot')
143
+ throw new Error('target crashed');
144
+ return {};
145
+ }),
146
+ };
147
+ const sid = nextSession();
148
+ await expect(startHeapSnapshot(failing, sid)).rejects.toThrow('target crashed');
149
+ const path = await outPath();
150
+ const { client } = heapClient(sid);
151
+ await expect(startHeapSnapshot(client, sid, path)).resolves.toBe(path);
152
+ });
153
+ });
154
+ //# sourceMappingURL=profiler-heap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler-heap.test.js","sourceRoot":"","sources":["../../../src/__tests__/profiler-heap.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=report-a11y.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-a11y.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/report-a11y.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The a11y rules are pure functions over a CDP accessibility tree, so the
3
+ * fixtures here are hand-written AX node arrays — no Chrome involved.
4
+ */
5
+ import { describe, expect, it } from 'vitest';
6
+ import { countA11y, findFocusOrderIssues, findHeadingIssues, findNamingIssues, orderNodes, runA11yRules, } from '../report/a11y.js';
7
+ function node(partial) {
8
+ return partial;
9
+ }
10
+ function ax(nodeId, role, name = '', extra = {}) {
11
+ return node({ nodeId, role: { value: role }, name: { value: name }, ...extra });
12
+ }
13
+ function heading(nodeId, level, name = `h${level}`) {
14
+ return node({
15
+ nodeId,
16
+ role: { value: 'heading' },
17
+ name: { value: name },
18
+ properties: [{ name: 'level', value: { value: level } }],
19
+ });
20
+ }
21
+ describe('findNamingIssues', () => {
22
+ it('flags a button with no accessible name', () => {
23
+ const findings = findNamingIssues([ax(1, 'button', '')]);
24
+ expect(findings).toHaveLength(1);
25
+ expect(findings[0]).toMatchObject({
26
+ rule: 'unlabelled-control',
27
+ impact: 'error',
28
+ role: 'button',
29
+ name: null,
30
+ locator: 'ax-node:1',
31
+ });
32
+ });
33
+ it('leaves named controls alone, including whitespace-trimmed names', () => {
34
+ expect(findNamingIssues([ax(1, 'button', 'Save'), ax(2, 'link', 'Home')])).toEqual([]);
35
+ });
36
+ it('treats a whitespace-only name as no name', () => {
37
+ expect(findNamingIssues([ax(1, 'link', ' ')])).toHaveLength(1);
38
+ });
39
+ it('reports an unlabelled form field under its own rule, not unlabelled-control', () => {
40
+ const findings = findNamingIssues([ax(1, 'textbox', '')]);
41
+ expect(findings[0].rule).toBe('form-field-no-label');
42
+ expect(findings[0].impact).toBe('error');
43
+ });
44
+ it('flags an image with no alt text', () => {
45
+ const findings = findNamingIssues([ax(1, 'image', '')]);
46
+ expect(findings[0].rule).toBe('image-missing-alt');
47
+ });
48
+ it('does not flag a decorative image, which the AX tree marks ignored', () => {
49
+ expect(findNamingIssues([ax(1, 'image', '', { ignored: true })])).toEqual([]);
50
+ });
51
+ it('uses a resolved CSS selector as the locator when one is supplied', () => {
52
+ const locators = new Map([[42, '#submit']]);
53
+ const findings = findNamingIssues([ax(1, 'button', '', { backendDOMNodeId: 42 })], locators);
54
+ expect(findings[0].locator).toBe('#submit');
55
+ });
56
+ it('ignores roles that carry no naming requirement', () => {
57
+ expect(findNamingIssues([ax(1, 'paragraph', ''), ax(2, 'generic', '')])).toEqual([]);
58
+ });
59
+ });
60
+ describe('findHeadingIssues', () => {
61
+ it('flags a skipped heading level', () => {
62
+ const findings = findHeadingIssues([heading(1, 1), heading(2, 3)]);
63
+ expect(findings).toHaveLength(1);
64
+ expect(findings[0].rule).toBe('heading-order-jump');
65
+ expect(findings[0].impact).toBe('warning');
66
+ expect(findings[0].detail).toContain('h1 -> h3');
67
+ });
68
+ it('accepts a well-formed outline, including going back up a level', () => {
69
+ expect(findHeadingIssues([heading(1, 1), heading(2, 2), heading(3, 3), heading(4, 2)])).toEqual([]);
70
+ });
71
+ it('flags a document whose first heading is not h1', () => {
72
+ const findings = findHeadingIssues([heading(1, 2)]);
73
+ expect(findings[0].detail).toContain('starts at h2');
74
+ });
75
+ it('reads levels in document order, not array order', () => {
76
+ // Array order says h1, h3, h2 (a jump); tree order says h1, h2, h3 (fine).
77
+ const nodes = [
78
+ node({ nodeId: 'root', role: { value: 'main' }, childIds: ['a', 'c', 'b'] }),
79
+ { ...heading('b', 3), parentId: 'root' },
80
+ { ...heading('a', 1), parentId: 'root' },
81
+ { ...heading('c', 2), parentId: 'root' },
82
+ ];
83
+ expect(findHeadingIssues(nodes)).toEqual([]);
84
+ });
85
+ it('skips headings with no usable level property', () => {
86
+ expect(findHeadingIssues([ax(1, 'heading', 'Untitled')])).toEqual([]);
87
+ });
88
+ });
89
+ describe('orderNodes', () => {
90
+ it('walks childIds depth-first and keeps unreachable nodes rather than dropping them', () => {
91
+ const nodes = [
92
+ node({ nodeId: 'r', childIds: ['x'] }),
93
+ node({ nodeId: 'x', parentId: 'r', childIds: ['y'] }),
94
+ node({ nodeId: 'y', parentId: 'x' }),
95
+ node({ nodeId: 'orphan', parentId: 'gone' }),
96
+ ];
97
+ expect(orderNodes(nodes).map((n) => n.nodeId)).toEqual(['r', 'x', 'y', 'orphan']);
98
+ });
99
+ it('does not loop forever on a cyclic tree', () => {
100
+ const nodes = [
101
+ node({ nodeId: 'a', childIds: ['b'] }),
102
+ node({ nodeId: 'b', parentId: 'a', childIds: ['a'] }),
103
+ ];
104
+ expect(orderNodes(nodes)).toHaveLength(2);
105
+ });
106
+ });
107
+ describe('findFocusOrderIssues', () => {
108
+ const candidate = (partial) => ({
109
+ tag: 'button',
110
+ role: '',
111
+ name: 'Next',
112
+ tabindex: -1,
113
+ locator: '#next',
114
+ ...partial,
115
+ });
116
+ it('flags a natively focusable element taken out of the tab order', () => {
117
+ const findings = findFocusOrderIssues([candidate({})]);
118
+ expect(findings).toHaveLength(1);
119
+ expect(findings[0]).toMatchObject({ rule: 'negative-tabindex', impact: 'warning' });
120
+ expect(findings[0].detail).toContain('tabindex="-1"');
121
+ });
122
+ it('ignores non-negative tabindex', () => {
123
+ expect(findFocusOrderIssues([candidate({ tabindex: 0 })])).toEqual([]);
124
+ });
125
+ it('ignores a plain container that is merely script-focusable', () => {
126
+ expect(findFocusOrderIssues([candidate({ tag: 'div', role: '' })])).toEqual([]);
127
+ });
128
+ it('flags a div that claims an interactive role', () => {
129
+ expect(findFocusOrderIssues([candidate({ tag: 'div', role: 'button' })])).toHaveLength(1);
130
+ });
131
+ });
132
+ describe('runA11yRules', () => {
133
+ it('combines every rule and counts errors separately from warnings', () => {
134
+ const findings = runA11yRules({
135
+ nodes: [ax(1, 'button', ''), ax(2, 'image', ''), heading(3, 1), heading(4, 4)],
136
+ focusCandidates: [{ tag: 'a', role: '', name: 'Skip', tabindex: -1, locator: '#skip' }],
137
+ });
138
+ expect(findings.map((f) => f.rule).sort()).toEqual([
139
+ 'heading-order-jump',
140
+ 'image-missing-alt',
141
+ 'negative-tabindex',
142
+ 'unlabelled-control',
143
+ ]);
144
+ expect(countA11y(findings)).toEqual({ errors: 2, warnings: 2 });
145
+ });
146
+ });
147
+ //# sourceMappingURL=report-a11y.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-a11y.test.js","sourceRoot":"","sources":["../../../src/__tests__/report-a11y.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=report-analyze.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-analyze.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/report-analyze.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Budget evaluation decides the exit code, so these cover the boundaries
3
+ * (at the limit passes, one over fails) and the cases where a metric simply
4
+ * was not measured — which must not silently grade as a pass.
5
+ */
6
+ import { describe, expect, it } from 'vitest';
7
+ import { buildReport, evaluateBudget, failedRequests, summarizeVerdict, } from '../report/analyze.js';
8
+ import { DEFAULT_BUDGET, parseBudget } from '../report/budget.js';
9
+ function capture(partial = {}) {
10
+ return {
11
+ url: 'https://example.test/',
12
+ finalUrl: 'https://example.test/',
13
+ title: 'Example',
14
+ capturedAt: '2026-09-21T10:00:00.000Z',
15
+ durationMs: 1234,
16
+ console: [],
17
+ pageErrors: [],
18
+ requests: [],
19
+ vitals: {},
20
+ a11y: [],
21
+ screenshots: [],
22
+ notes: [],
23
+ ...partial,
24
+ };
25
+ }
26
+ const consoleEntry = (type, text) => ({
27
+ type,
28
+ text,
29
+ timestamp: 0,
30
+ });
31
+ const request = (partial = {}) => ({
32
+ url: 'https://example.test/api',
33
+ method: 'GET',
34
+ failed: false,
35
+ ...partial,
36
+ });
37
+ const a11yError = (rule) => ({
38
+ rule,
39
+ impact: 'error',
40
+ role: 'button',
41
+ name: null,
42
+ locator: '#x',
43
+ detail: 'no name',
44
+ });
45
+ describe('evaluateBudget', () => {
46
+ it('passes a clean page against the defaults', () => {
47
+ const result = evaluateBudget(capture({ vitals: { lcp: 900, cls: 0.02, inp: 80 } }), DEFAULT_BUDGET);
48
+ expect(result.verdict).toBe('pass');
49
+ expect(result.failures).toEqual([]);
50
+ expect(result.unmeasured).toEqual([]);
51
+ });
52
+ it('fails on a console error and names expected vs actual', () => {
53
+ const result = evaluateBudget(capture({ console: [consoleEntry('error', 'Uncaught TypeError: x is not a function')] }), parseBudget({ lcp: null, cls: null, inp: null }));
54
+ expect(result.verdict).toBe('fail');
55
+ expect(result.failures).toHaveLength(1);
56
+ expect(result.failures[0]).toMatchObject({
57
+ budget: 'maxConsoleErrors',
58
+ expected: '<= 0',
59
+ actual: '1',
60
+ });
61
+ expect(result.failures[0].detail).toContain('Uncaught TypeError');
62
+ });
63
+ it('does not count console warnings as errors', () => {
64
+ const result = evaluateBudget(capture({ console: [consoleEntry('warn', 'deprecated')] }), parseBudget({ lcp: null, cls: null, inp: null }));
65
+ expect(result.verdict).toBe('pass');
66
+ });
67
+ it('fails on failed requests and quotes the first few', () => {
68
+ const requests = [
69
+ request({ failed: true, status: 500, url: 'https://example.test/a' }),
70
+ request({ failed: true, errorText: 'net::ERR_NAME_NOT_RESOLVED', url: 'https://x.test/b' }),
71
+ request({ status: 200 }),
72
+ ];
73
+ const result = evaluateBudget(capture({ requests }), DEFAULT_BUDGET);
74
+ const failure = result.failures.find((f) => f.budget === 'maxFailedRequests');
75
+ expect(failure).toBeDefined();
76
+ expect(failure?.actual).toBe('2');
77
+ expect(failure?.detail).toContain('500 GET https://example.test/a');
78
+ expect(failure?.detail).toContain('net::ERR_NAME_NOT_RESOLVED');
79
+ });
80
+ it('treats a metric exactly at the limit as passing, and one over as failing', () => {
81
+ const budget = parseBudget({ lcp: 2500, cls: null, inp: null });
82
+ expect(evaluateBudget(capture({ vitals: { lcp: 2500 } }), budget).verdict).toBe('pass');
83
+ const over = evaluateBudget(capture({ vitals: { lcp: 2501 } }), budget);
84
+ expect(over.verdict).toBe('fail');
85
+ expect(over.failures[0]).toMatchObject({
86
+ budget: 'lcpMs',
87
+ expected: '<= 2500ms',
88
+ actual: '2501ms',
89
+ });
90
+ });
91
+ it('records an unmeasured metric instead of passing it', () => {
92
+ const result = evaluateBudget(capture({ vitals: {} }), parseBudget({ lcp: 2500, cls: null, inp: null }));
93
+ expect(result.verdict).toBe('pass');
94
+ expect(result.unmeasured).toEqual(['LCP (Largest Contentful Paint)']);
95
+ });
96
+ it('skips a budget set to null entirely', () => {
97
+ const result = evaluateBudget(capture({ vitals: { lcp: 99_000 } }), parseBudget({ lcp: null, cls: null, inp: null }));
98
+ expect(result.verdict).toBe('pass');
99
+ expect(result.unmeasured).toEqual([]);
100
+ });
101
+ it('counts a11y errors but not a11y warnings against the a11y budget', () => {
102
+ const a11y = [
103
+ a11yError('unlabelled-control'),
104
+ { ...a11yError('heading-order-jump'), impact: 'warning' },
105
+ ];
106
+ const result = evaluateBudget(capture({ a11y }), DEFAULT_BUDGET);
107
+ const failure = result.failures.find((f) => f.budget === 'maxA11yErrors');
108
+ expect(failure?.actual).toBe('1');
109
+ expect(failure?.detail).toContain('unlabelled-control at #x');
110
+ });
111
+ it('reports a CLS breach as a score, not milliseconds', () => {
112
+ const result = evaluateBudget(capture({ vitals: { cls: 0.42 } }), parseBudget({ lcp: null, inp: null }));
113
+ expect(result.failures[0]).toMatchObject({
114
+ budget: 'clsScore',
115
+ expected: '<= 0.1',
116
+ actual: '0.42',
117
+ });
118
+ });
119
+ });
120
+ describe('failedRequests', () => {
121
+ it('selects only entries marked failed', () => {
122
+ expect(failedRequests([request({ failed: true }), request()])).toHaveLength(1);
123
+ });
124
+ });
125
+ describe('buildReport', () => {
126
+ it('carries the verdict, the failure list and the headline counts', () => {
127
+ const report = buildReport(capture({
128
+ console: [consoleEntry('error', 'boom'), consoleEntry('warn', 'meh')],
129
+ requests: [request({ failed: true, status: 404 }), request({ status: 200 })],
130
+ a11y: [a11yError('image-missing-alt')],
131
+ vitals: { lcp: 1000, cls: 0, inp: 10 },
132
+ }), DEFAULT_BUDGET);
133
+ expect(report.verdict).toBe('fail');
134
+ expect(report.counts).toEqual({
135
+ consoleErrors: 1,
136
+ consoleWarnings: 1,
137
+ pageErrors: 0,
138
+ requests: 2,
139
+ failedRequests: 1,
140
+ a11yErrors: 1,
141
+ a11yWarnings: 0,
142
+ });
143
+ expect(report.failures.map((f) => f.budget).sort()).toEqual([
144
+ 'maxA11yErrors',
145
+ 'maxConsoleErrors',
146
+ 'maxFailedRequests',
147
+ ]);
148
+ });
149
+ it('summarizes a pass and a fail in one line each', () => {
150
+ const pass = buildReport(capture({ vitals: { lcp: 10, cls: 0, inp: 1 } }), DEFAULT_BUDGET);
151
+ expect(summarizeVerdict(pass)).toContain('PASS');
152
+ const fail = buildReport(capture({ pageErrors: [{ text: 'boom', timestamp: 0 }] }), DEFAULT_BUDGET);
153
+ expect(summarizeVerdict(fail)).toContain('FAIL');
154
+ expect(summarizeVerdict(fail)).toContain('Uncaught page errors');
155
+ });
156
+ });
157
+ //# sourceMappingURL=report-analyze.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-analyze.test.js","sourceRoot":"","sources":["../../../src/__tests__/report-analyze.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=report-budget.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-budget.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/report-budget.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Budgets are the CI gate, so the parser's job is to be strict: a typo'd key
3
+ * must fail loudly rather than quietly disabling a check.
4
+ */
5
+ import { mkdtemp, writeFile } from 'node:fs/promises';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ import { describe, expect, it } from 'vitest';
9
+ import { DEFAULT_BUDGET, formatThreshold, loadBudget, parseBudget } from '../report/budget.js';
10
+ describe('DEFAULT_BUDGET', () => {
11
+ it('gates on zero errors and the Core Web Vitals "good" boundaries', () => {
12
+ expect(DEFAULT_BUDGET).toMatchObject({
13
+ maxConsoleErrors: 0,
14
+ maxPageErrors: 0,
15
+ maxFailedRequests: 0,
16
+ maxA11yErrors: 0,
17
+ lcpMs: 2500,
18
+ clsScore: 0.1,
19
+ inpMs: 200,
20
+ });
21
+ });
22
+ });
23
+ describe('parseBudget', () => {
24
+ it('merges over the defaults rather than replacing them', () => {
25
+ const budget = parseBudget({ lcpMs: 4000 });
26
+ expect(budget.lcpMs).toBe(4000);
27
+ expect(budget.maxConsoleErrors).toBe(0);
28
+ });
29
+ it('accepts short and snake_case spellings', () => {
30
+ const budget = parseBudget({ lcp: 3000, cls: 0.25, max_console_errors: 5, 'a11y-errors': 2 });
31
+ expect(budget).toMatchObject({
32
+ lcpMs: 3000,
33
+ clsScore: 0.25,
34
+ maxConsoleErrors: 5,
35
+ maxA11yErrors: 2,
36
+ });
37
+ });
38
+ it('treats null as "do not enforce"', () => {
39
+ expect(parseBudget({ lcp: null }).lcpMs).toBeNull();
40
+ });
41
+ it('rejects an unknown key instead of silently ignoring it', () => {
42
+ expect(() => parseBudget({ maxConsoleError: 0 })).toThrow(/Unknown budget key/);
43
+ });
44
+ it('rejects non-numeric and negative values', () => {
45
+ expect(() => parseBudget({ lcp: 'fast' })).toThrow(/must be a number/);
46
+ expect(() => parseBudget({ lcp: -1 })).toThrow(/negative/);
47
+ });
48
+ it('rejects a non-object budget', () => {
49
+ expect(() => parseBudget([1, 2])).toThrow(/expected a JSON object/);
50
+ expect(() => parseBudget(null)).toThrow(/expected a JSON object/);
51
+ });
52
+ });
53
+ describe('loadBudget', () => {
54
+ it('returns the defaults when nothing is passed', async () => {
55
+ await expect(loadBudget()).resolves.toEqual(DEFAULT_BUDGET);
56
+ });
57
+ it('parses inline JSON', async () => {
58
+ await expect(loadBudget('{"lcp": 1000}')).resolves.toMatchObject({ lcpMs: 1000 });
59
+ });
60
+ it('reads a budget file', async () => {
61
+ const dir = await mkdtemp(join(tmpdir(), 'monobrowse-budget-'));
62
+ const file = join(dir, 'budget.json');
63
+ await writeFile(file, JSON.stringify({ maxFailedRequests: 3 }), 'utf8');
64
+ await expect(loadBudget(file)).resolves.toMatchObject({ maxFailedRequests: 3 });
65
+ });
66
+ it('names the file it could not read', async () => {
67
+ await expect(loadBudget('/nope/does-not-exist.json')).rejects.toThrow(/Budget file not found/);
68
+ });
69
+ it('reports malformed inline JSON as such', async () => {
70
+ await expect(loadBudget('{oops}')).rejects.toThrow(/not valid JSON/);
71
+ });
72
+ });
73
+ describe('formatThreshold', () => {
74
+ it('units millisecond budgets and leaves counts and scores bare', () => {
75
+ expect(formatThreshold('lcpMs', 2500)).toBe('<= 2500ms');
76
+ expect(formatThreshold('clsScore', 0.1)).toBe('<= 0.1');
77
+ expect(formatThreshold('maxConsoleErrors', 0)).toBe('<= 0');
78
+ });
79
+ });
80
+ //# sourceMappingURL=report-budget.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-budget.test.js","sourceRoot":"","sources":["../../../src/__tests__/report-budget.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=report-diff.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-diff.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/report-diff.test.ts"],"names":[],"mappings":""}