@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,286 @@
1
+ /**
2
+ * Renders a Report as ONE self-contained HTML file: CSS inline, screenshots
3
+ * as `data:` URIs, no fonts, no CDN, no network access of any kind at view
4
+ * time. That is not a stylistic preference — it is what lets monomind's
5
+ * document pipeline ingest the file as an ordinary document, so test history
6
+ * becomes searchable, and what lets the report be mailed or archived and
7
+ * still render years later.
8
+ *
9
+ * Reading order matches how you triage: verdict, errors, failed requests,
10
+ * vitals, accessibility, screenshots.
11
+ */
12
+ import { budgetLabel } from './budget.js';
13
+ import { EVIDENCE_STYLES, renderEvidence, renderFlake } from './render-evidence.js';
14
+ import { renderDiff, renderTrend, TREND_STYLES } from './render-trend.js';
15
+ import { escapeHtml } from './util.js';
16
+ export { escapeHtml };
17
+ function ms(value) {
18
+ return value === undefined || !Number.isFinite(value) ? 'not measured' : `${Math.round(value)}ms`;
19
+ }
20
+ function vitalRating(metric, v) {
21
+ if (v === undefined || !Number.isFinite(v))
22
+ return 'none';
23
+ const scale = {
24
+ lcp: [2500, 4000],
25
+ fcp: [1800, 3000],
26
+ cls: [0.1, 0.25],
27
+ inp: [200, 500],
28
+ ttfb: [800, 1800],
29
+ };
30
+ const bounds = scale[metric];
31
+ if (!bounds)
32
+ return 'none';
33
+ return v <= bounds[0] ? 'good' : v <= bounds[1] ? 'mixed' : 'poor';
34
+ }
35
+ const STYLES = `
36
+ :root{
37
+ --bg:#fbfbfd; --panel:#fff; --ink:#15171c; --muted:#5d6470; --line:#e3e6ec;
38
+ --pass:#0f7b4f; --pass-bg:#e7f6ee; --fail:#b3261e; --fail-bg:#fdeceb;
39
+ --warn:#8a5a00; --warn-bg:#fff5e0; --code:#f3f4f7;
40
+ }
41
+ @media (prefers-color-scheme: dark){
42
+ :root{
43
+ --bg:#101317; --panel:#171b21; --ink:#e7eaf0; --muted:#98a1b0; --line:#262c35;
44
+ --pass:#57d69c; --pass-bg:#11291f; --fail:#ff9a92; --fail-bg:#2c1614;
45
+ --warn:#f0c168; --warn-bg:#2b2113; --code:#1d222a;
46
+ }
47
+ }
48
+ *{box-sizing:border-box}
49
+ body{margin:0;padding:32px 16px 72px;background:var(--bg);color:var(--ink);
50
+ font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}
51
+ main{max-width:940px;margin:0 auto}
52
+ h1{font-size:22px;margin:0 0 4px;letter-spacing:-.01em}
53
+ h2{font-size:15px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);
54
+ margin:36px 0 12px;font-weight:600}
55
+ a{color:inherit}
56
+ .sub{color:var(--muted);font-size:13px;margin:0 0 24px;word-break:break-all}
57
+ .verdict{border-radius:12px;padding:18px 20px;margin-bottom:8px;border:1px solid transparent}
58
+ .verdict.pass{background:var(--pass-bg);border-color:var(--pass);color:var(--pass)}
59
+ .verdict.fail{background:var(--fail-bg);border-color:var(--fail);color:var(--fail)}
60
+ .verdict .tag{font-size:26px;font-weight:700;letter-spacing:.02em}
61
+ .verdict .line{color:var(--ink);margin-top:6px;font-size:14px}
62
+ .stats{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 0;padding:0;list-style:none}
63
+ .stats li{background:var(--panel);border:1px solid var(--line);border-radius:8px;
64
+ padding:8px 12px;font-size:13px;color:var(--muted)}
65
+ .stats li b{display:block;font-size:18px;color:var(--ink)}
66
+ .stats li.bad b{color:var(--fail)}
67
+ table{width:100%;border-collapse:collapse;background:var(--panel);
68
+ border:1px solid var(--line);border-radius:10px;overflow:hidden;font-size:13px}
69
+ th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--line);vertical-align:top}
70
+ th{color:var(--muted);font-weight:600;white-space:nowrap}
71
+ tr:last-child td{border-bottom:none}
72
+ td.num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
73
+ .wrap{word-break:break-all}
74
+ code,.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}
75
+ code{background:var(--code);padding:1px 5px;border-radius:4px}
76
+ .pill{display:inline-block;padding:1px 7px;border-radius:999px;font-size:11px;
77
+ font-weight:600;text-transform:uppercase;letter-spacing:.04em}
78
+ .pill.error{background:var(--fail-bg);color:var(--fail)}
79
+ .pill.warning{background:var(--warn-bg);color:var(--warn)}
80
+ .pill.good{background:var(--pass-bg);color:var(--pass)}
81
+ .pill.mixed{background:var(--warn-bg);color:var(--warn)}
82
+ .pill.poor{background:var(--fail-bg);color:var(--fail)}
83
+ .pill.none{background:var(--code);color:var(--muted)}
84
+ .empty{color:var(--muted);font-size:13px;background:var(--panel);border:1px solid var(--line);
85
+ border-radius:10px;padding:12px}
86
+ .note{color:var(--muted);font-size:12px;margin-top:8px}
87
+ figure{margin:0 0 20px}
88
+ figcaption{color:var(--muted);font-size:12px;margin-bottom:6px}
89
+ img{max-width:100%;height:auto;display:block;border:1px solid var(--line);border-radius:10px}
90
+ footer{margin-top:40px;color:var(--muted);font-size:12px;border-top:1px solid var(--line);
91
+ padding-top:12px}
92
+ @media (max-width:640px){body{padding:20px 12px 48px}th,td{padding:8px}}
93
+ `.trim();
94
+ function emptyBlock(text) {
95
+ return `<p class="empty">${escapeHtml(text)}</p>`;
96
+ }
97
+ function renderVerdict(report) {
98
+ const pass = report.verdict === 'pass';
99
+ const headline = pass
100
+ ? 'All budgets met.'
101
+ : `${report.failures.length} budget ${report.failures.length === 1 ? 'failure' : 'failures'}.`;
102
+ const rows = report.failures
103
+ .map((f) => `<tr>
104
+ <td>${escapeHtml(budgetLabel(f.budget))}</td>
105
+ <td class="num mono">${escapeHtml(f.expected)}</td>
106
+ <td class="num mono">${escapeHtml(f.actual)}</td>
107
+ <td class="wrap">${escapeHtml(f.detail ?? '')}</td>
108
+ </tr>`)
109
+ .join('');
110
+ const table = report.failures.length
111
+ ? `<table><thead><tr><th>Budget</th><th>Expected</th><th>Actual</th><th>Evidence</th></tr></thead>
112
+ <tbody>${rows}</tbody></table>`
113
+ : '';
114
+ const unmeasured = report.unmeasured.length
115
+ ? `<p class="note">Not measured on this run: ${escapeHtml(report.unmeasured.join(', '))} — the page never reported the metric, so its budget was neither met nor breached.</p>`
116
+ : '';
117
+ return `<section class="verdict ${pass ? 'pass' : 'fail'}">
118
+ <div class="tag">${pass ? 'PASS' : 'FAIL'}</div>
119
+ <div class="line">${escapeHtml(headline)}</div>
120
+ </section>
121
+ ${table}
122
+ ${unmeasured}`;
123
+ }
124
+ function renderStats(report) {
125
+ const c = report.counts;
126
+ const item = (label, value, bad) => `<li class="${bad ? 'bad' : ''}"><b>${value}</b>${escapeHtml(label)}</li>`;
127
+ return `<ul class="stats">
128
+ ${item('console errors', c.consoleErrors, c.consoleErrors > 0)}
129
+ ${item('page errors', c.pageErrors, c.pageErrors > 0)}
130
+ ${item('failed requests', c.failedRequests, c.failedRequests > 0)}
131
+ ${item('requests', c.requests, false)}
132
+ ${item('a11y errors', c.a11yErrors, c.a11yErrors > 0)}
133
+ ${item('a11y warnings', c.a11yWarnings, false)}
134
+ </ul>`;
135
+ }
136
+ function renderErrors(report) {
137
+ const rows = [];
138
+ for (const e of report.pageErrors) {
139
+ const where = e.url ? `${e.url}${e.lineNumber !== undefined ? `:${e.lineNumber}` : ''}` : '';
140
+ rows.push(`<tr>
141
+ <td><span class="pill error">uncaught</span></td>
142
+ <td class="wrap">${escapeHtml(e.text)}</td>
143
+ <td class="wrap mono">${escapeHtml(where)}</td>
144
+ </tr>`);
145
+ }
146
+ for (const m of report.console) {
147
+ if (m.type !== 'error' && m.type !== 'warn' && m.type !== 'warning')
148
+ continue;
149
+ const pill = m.type === 'error' ? 'error' : 'warning';
150
+ const where = m.url ? `${m.url}${m.lineNumber !== undefined ? `:${m.lineNumber}` : ''}` : '';
151
+ rows.push(`<tr>
152
+ <td><span class="pill ${pill}">console.${escapeHtml(m.type)}</span></td>
153
+ <td class="wrap">${escapeHtml(m.text)}</td>
154
+ <td class="wrap mono">${escapeHtml(where)}</td>
155
+ </tr>`);
156
+ }
157
+ if (!rows.length)
158
+ return emptyBlock('No console errors, warnings or uncaught exceptions.');
159
+ return `<table><thead><tr><th>Kind</th><th>Message</th><th>Source</th></tr></thead>
160
+ <tbody>${rows.join('')}</tbody></table>`;
161
+ }
162
+ function renderRequests(requests) {
163
+ const failed = requests.filter((r) => r.failed);
164
+ if (!failed.length)
165
+ return emptyBlock('Every request returned a successful response.');
166
+ const rows = failed
167
+ .map((r) => `<tr>
168
+ <td class="mono">${escapeHtml(r.status ?? '—')}</td>
169
+ <td class="mono">${escapeHtml(r.method)}</td>
170
+ <td class="wrap mono">${escapeHtml(r.url)}</td>
171
+ <td class="wrap">${escapeHtml(r.errorText ?? '')}</td>
172
+ </tr>`)
173
+ .join('');
174
+ return `<table><thead><tr><th>Status</th><th>Method</th><th>URL</th><th>Error</th></tr></thead>
175
+ <tbody>${rows}</tbody></table>`;
176
+ }
177
+ function renderVitals(report) {
178
+ const v = report.vitals;
179
+ const defs = [
180
+ ['lcp', 'LCP', 'Largest Contentful Paint'],
181
+ ['fcp', 'FCP', 'First Contentful Paint'],
182
+ ['cls', 'CLS', 'Cumulative Layout Shift'],
183
+ ['inp', 'INP', 'Interaction to Next Paint'],
184
+ ['ttfb', 'TTFB', 'Time to First Byte'],
185
+ ];
186
+ const rows = defs
187
+ .map(([key, short, long]) => {
188
+ const raw = v[key];
189
+ const rating = vitalRating(key, raw);
190
+ const shown = key === 'cls' ? (raw === undefined ? 'not measured' : raw.toFixed(3)) : ms(raw);
191
+ return `<tr>
192
+ <td><b>${short}</b> <span style="color:var(--muted)">${escapeHtml(long)}</span></td>
193
+ <td class="num mono">${escapeHtml(shown)}</td>
194
+ <td><span class="pill ${rating}">${rating === 'none' ? 'n/a' : rating}</span></td>
195
+ </tr>`;
196
+ })
197
+ .join('');
198
+ const load = `<tr><td>Load event</td><td class="num mono">${escapeHtml(ms(v.loadTime))}</td><td></td></tr>
199
+ <tr><td>DOM interactive</td><td class="num mono">${escapeHtml(ms(v.domInteractive))}</td><td></td></tr>
200
+ <tr><td>Resources</td><td class="num mono">${escapeHtml(v.resources ?? '—')}</td><td></td></tr>`;
201
+ return `<table><thead><tr><th>Metric</th><th>Value</th><th>Rating</th></tr></thead>
202
+ <tbody>${rows}${load}</tbody></table>`;
203
+ }
204
+ function renderA11y(findings) {
205
+ const disclaimer = '<p class="note">Colour contrast is not evaluated: it cannot be derived from the accessibility tree, and guessing it would be worse than omitting it.</p>';
206
+ if (!findings.length) {
207
+ return `${emptyBlock('No accessibility findings from the AX tree.')}${disclaimer}`;
208
+ }
209
+ const order = { error: 0, warning: 1 };
210
+ const rows = [...findings]
211
+ .sort((a, b) => order[a.impact] - order[b.impact])
212
+ .map((f) => `<tr>
213
+ <td><span class="pill ${f.impact}">${escapeHtml(f.impact)}</span></td>
214
+ <td class="mono">${escapeHtml(f.rule)}</td>
215
+ <td>${escapeHtml(f.role)}</td>
216
+ <td>${f.name ? escapeHtml(f.name) : '<span style="color:var(--muted)">(no accessible name)</span>'}</td>
217
+ <td class="wrap mono">${escapeHtml(f.locator)}</td>
218
+ <td class="wrap">${escapeHtml(f.detail)}</td>
219
+ </tr>`)
220
+ .join('');
221
+ return `<table><thead><tr><th>Impact</th><th>Rule</th><th>Role</th><th>Name</th><th>Locator</th><th>Detail</th></tr></thead>
222
+ <tbody>${rows}</tbody></table>${disclaimer}`;
223
+ }
224
+ function renderScreenshots(shots) {
225
+ if (!shots.length)
226
+ return emptyBlock('No screenshot was captured.');
227
+ return shots
228
+ .map((s) => `<figure>
229
+ <figcaption>${escapeHtml(s.label)} — ${s.width}×${s.height}</figcaption>
230
+ <img alt="${escapeHtml(`${s.label} screenshot`)}" src="${escapeHtml(s.dataUrl)}">
231
+ </figure>`)
232
+ .join('');
233
+ }
234
+ export function renderHtml(report, options = {}) {
235
+ const title = report.title || report.finalUrl || report.url;
236
+ const notes = report.notes.length
237
+ ? `<h2>Run notes</h2><ul class="empty">${report.notes.map((n) => `<li>${escapeHtml(n)}</li>`).join('')}</ul>`
238
+ : '';
239
+ return `<!doctype html>
240
+ <html lang="en">
241
+ <head>
242
+ <meta charset="utf-8">
243
+ <meta name="viewport" content="width=device-width, initial-scale=1">
244
+ <meta name="color-scheme" content="light dark">
245
+ <title>${escapeHtml(`Page report — ${title}`)}</title>
246
+ <style>${STYLES}
247
+ ${TREND_STYLES}
248
+ ${EVIDENCE_STYLES}</style>
249
+ </head>
250
+ <body>
251
+ <main>
252
+ <h1>${escapeHtml(title)}</h1>
253
+ <p class="sub">${escapeHtml(report.finalUrl || report.url)} · captured ${escapeHtml(report.capturedAt)} · ${Math.round(report.durationMs)}ms</p>
254
+
255
+ ${renderVerdict(report)}
256
+ ${renderFlake(options.flake)}
257
+ ${renderStats(report)}
258
+
259
+ ${renderTrend(report.trend)}
260
+ ${renderDiff(report.diff)}
261
+
262
+ <h2>Errors</h2>
263
+ ${renderErrors(report)}
264
+
265
+ ${renderEvidence(report.evidence)}
266
+
267
+ <h2>Failed requests</h2>
268
+ ${renderRequests(report.requests)}
269
+
270
+ <h2>Web vitals</h2>
271
+ ${renderVitals(report)}
272
+
273
+ <h2>Accessibility</h2>
274
+ ${renderA11y(report.a11y)}
275
+
276
+ <h2>Screenshots</h2>
277
+ ${renderScreenshots(report.screenshots)}
278
+
279
+ ${notes}
280
+ <footer>Generated by monobrowse report — self-contained, no external resources.</footer>
281
+ </main>
282
+ </body>
283
+ </html>
284
+ `;
285
+ }
286
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/report/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Assembles the run-to-run diff (RIG-10) from what the previous run left in
3
+ * the history directory: its structural signature (in the JSON record) and
4
+ * its screenshot (the sibling PNG).
5
+ *
6
+ * Every missing piece degrades into a note rather than an absent section, so
7
+ * a reader can tell "nothing changed" apart from "we could not check".
8
+ */
9
+ import { type HistoryRun } from './history.js';
10
+ import { type PixelDiffOptions } from './pixel-diff.js';
11
+ import type { Report, RunDiff } from './types.js';
12
+ export interface BuildRunDiffOptions {
13
+ pixels?: PixelDiffOptions;
14
+ /** Set false to skip the pixel half entirely. */
15
+ comparePixels?: boolean;
16
+ }
17
+ export declare function buildRunDiff(dir: string, previous: HistoryRun | undefined, current: Report, options?: BuildRunDiffOptions): Promise<RunDiff | undefined>;
18
+ //# sourceMappingURL=run-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-diff.d.ts","sourceRoot":"","sources":["../../../src/report/run-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,KAAK,gBAAgB,EAAa,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,iDAAiD;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAoC9B"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Assembles the run-to-run diff (RIG-10) from what the previous run left in
3
+ * the history directory: its structural signature (in the JSON record) and
4
+ * its screenshot (the sibling PNG).
5
+ *
6
+ * Every missing piece degrades into a note rather than an absent section, so
7
+ * a reader can tell "nothing changed" apart from "we could not check".
8
+ */
9
+ import { loadRunScreenshot } from './history.js';
10
+ import { pixelDiff } from './pixel-diff.js';
11
+ import { pngFromDataUrl } from './png.js';
12
+ import { diffStructure } from './structure.js';
13
+ export async function buildRunDiff(dir, previous, current, options = {}) {
14
+ if (!previous)
15
+ return undefined;
16
+ const notes = [];
17
+ const diff = {
18
+ previousRunId: previous.id,
19
+ previousCapturedAt: previous.capturedAt,
20
+ notes,
21
+ };
22
+ if (previous.structure?.length && current.structure?.length) {
23
+ diff.structure = diffStructure(previous.structure, current.structure);
24
+ }
25
+ else if (!current.structure?.length) {
26
+ notes.push('No accessibility tree this run — structural diff skipped.');
27
+ }
28
+ else {
29
+ notes.push('The previous run stored no structural signature — nothing to diff against.');
30
+ }
31
+ if (options.comparePixels !== false) {
32
+ const currentShot = current.screenshots.find((s) => s.label === 'page') ?? current.screenshots[0];
33
+ const previousPng = await loadRunScreenshot(dir, previous);
34
+ if (!previousPng) {
35
+ notes.push('The previous run archived no screenshot — pixel diff skipped.');
36
+ }
37
+ else if (!currentShot?.dataUrl) {
38
+ notes.push('No screenshot this run — pixel diff skipped.');
39
+ }
40
+ else {
41
+ try {
42
+ diff.pixels = pixelDiff(previousPng, pngFromDataUrl(currentShot.dataUrl), options.pixels);
43
+ }
44
+ catch (err) {
45
+ notes.push(`Pixel diff failed: ${err.message}`);
46
+ }
47
+ }
48
+ }
49
+ return diff;
50
+ }
51
+ //# sourceMappingURL=run-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-diff.js","sourceRoot":"","sources":["../../../src/report/run-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAmB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAyB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAS/C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,QAAgC,EAChC,OAAe,EACf,OAAO,GAAwB,EAAE;IAEjC,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAEhC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAY;QACpB,aAAa,EAAE,QAAQ,CAAC,EAAE;QAC1B,kBAAkB,EAAE,QAAQ,CAAC,UAAU;QACvC,KAAK;KACN,CAAC;IAEF,IAAI,QAAQ,CAAC,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAC5D,IAAI,CAAC,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACxE,CAAC;SAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC1E,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;QACpC,MAAM,WAAW,GACf,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QAC9E,CAAC;aAAM,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5F,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,CAAC,sBAAuB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Structural signature of a page, and the diff between two of them (RIG-10).
3
+ *
4
+ * The signature is derived from the accessibility tree we already fetch for
5
+ * the a11y rules, so this costs no extra CDP round-trip. It is deliberately
6
+ * the AX tree and not the DOM: the AX tree is what the page *means* — a
7
+ * className churned by a CSS-in-JS build is invisible to it, while a button
8
+ * losing its label, a heading changing text, or a form field disappearing
9
+ * all show up. That is the signal a "did this deploy change anything real?"
10
+ * diff wants.
11
+ *
12
+ * Everything here is pure, so the tests feed hand-written trees.
13
+ */
14
+ import type { AxNode, StructureDiff, StructureNode } from './types.js';
15
+ /** Bound on signature size: a 40k-node AX tree must not become a 40k-row diff. */
16
+ export declare const MAX_STRUCTURE_NODES = 1500;
17
+ /**
18
+ * Walk the AX tree into a flat, path-addressed signature.
19
+ *
20
+ * Path segments are `role[n]`, n counting siblings of the same role after
21
+ * transparent wrappers are spliced away. Two runs of an unchanged page
22
+ * therefore produce identical paths; an inserted element shifts only its own
23
+ * role's numbering, which the diff's second pass then recovers from.
24
+ */
25
+ export declare function buildStructure(nodes: AxNode[]): StructureNode[];
26
+ /**
27
+ * Diff two signatures.
28
+ *
29
+ * Two passes, because path alone is too brittle to use on its own: inserting
30
+ * one list item renumbers every later sibling, which a naive path diff would
31
+ * report as "everything below here was replaced".
32
+ *
33
+ * 1. Exact path match — same slot. Same name means unchanged, a different
34
+ * name means renamed (the useful case: a label that silently changed).
35
+ * 2. Whatever is left is matched on role+name, which recovers elements that
36
+ * merely moved. Only what survives both passes is genuinely gained/lost.
37
+ */
38
+ export declare function diffStructure(previous: StructureNode[], current: StructureNode[]): StructureDiff;
39
+ /** One line for a CI log: "3 gained, 1 lost, 2 renamed". */
40
+ export declare function summarizeStructureDiff(diff: StructureDiff): string;
41
+ //# sourceMappingURL=structure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../../src/report/structure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWvE,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAexC;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAkE/D;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,aAAa,CA4DhG;AAED,4DAA4D;AAC5D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAOlE"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Structural signature of a page, and the diff between two of them (RIG-10).
3
+ *
4
+ * The signature is derived from the accessibility tree we already fetch for
5
+ * the a11y rules, so this costs no extra CDP round-trip. It is deliberately
6
+ * the AX tree and not the DOM: the AX tree is what the page *means* — a
7
+ * className churned by a CSS-in-JS build is invisible to it, while a button
8
+ * losing its label, a heading changing text, or a form field disappearing
9
+ * all show up. That is the signal a "did this deploy change anything real?"
10
+ * diff wants.
11
+ *
12
+ * Everything here is pure, so the tests feed hand-written trees.
13
+ */
14
+ /** Roles that exist only to hold other nodes — spliced out, children kept. */
15
+ const TRANSPARENT_ROLES = new Set([
16
+ 'none',
17
+ 'generic',
18
+ 'presentation',
19
+ 'inlinetextbox',
20
+ 'genericcontainer',
21
+ ]);
22
+ /** Bound on signature size: a 40k-node AX tree must not become a 40k-row diff. */
23
+ export const MAX_STRUCTURE_NODES = 1500;
24
+ const MAX_DEPTH = 25;
25
+ /** Accessible names are compared verbatim, so trim the essays. */
26
+ const MAX_NAME_LENGTH = 160;
27
+ function strValue(v) {
28
+ return typeof v === 'string' ? v : v == null ? '' : String(v);
29
+ }
30
+ function normalizeName(raw) {
31
+ const flat = raw.replace(/\s+/g, ' ').trim();
32
+ if (!flat)
33
+ return null;
34
+ return flat.length > MAX_NAME_LENGTH ? `${flat.slice(0, MAX_NAME_LENGTH - 1)}…` : flat;
35
+ }
36
+ /**
37
+ * Walk the AX tree into a flat, path-addressed signature.
38
+ *
39
+ * Path segments are `role[n]`, n counting siblings of the same role after
40
+ * transparent wrappers are spliced away. Two runs of an unchanged page
41
+ * therefore produce identical paths; an inserted element shifts only its own
42
+ * role's numbering, which the diff's second pass then recovers from.
43
+ */
44
+ export function buildStructure(nodes) {
45
+ const byId = new Map();
46
+ for (const node of nodes)
47
+ byId.set(String(node.nodeId), node);
48
+ const childIds = (node) => (node.childIds ?? []).map(String);
49
+ /** Visible children, with transparent wrappers replaced by their children. */
50
+ const effectiveChildren = (node, depth) => {
51
+ const out = [];
52
+ const queue = [...childIds(node)];
53
+ let guard = 0;
54
+ while (queue.length && guard++ < 4000) {
55
+ const child = byId.get(queue.shift());
56
+ if (!child)
57
+ continue;
58
+ const role = strValue(child.role?.value).toLowerCase();
59
+ if (child.ignored || TRANSPARENT_ROLES.has(role)) {
60
+ if (depth < MAX_DEPTH)
61
+ queue.unshift(...childIds(child));
62
+ continue;
63
+ }
64
+ out.push(child);
65
+ }
66
+ return out;
67
+ };
68
+ const referenced = new Set();
69
+ for (const node of nodes)
70
+ for (const id of childIds(node))
71
+ referenced.add(id);
72
+ const roots = nodes.filter((n) => !referenced.has(String(n.nodeId)));
73
+ // In a cycle every node has a parent, so there is no unreferenced root and
74
+ // the walk below would visit nothing. Start somewhere rather than silently
75
+ // returning an empty signature that the diff would read as "page is gone".
76
+ if (!roots.length && nodes.length)
77
+ roots.push(nodes[0]);
78
+ const out = [];
79
+ const visited = new Set();
80
+ const counters = new Map();
81
+ const walk = (node, parentPath, depth) => {
82
+ if (out.length >= MAX_STRUCTURE_NODES || depth > MAX_DEPTH)
83
+ return;
84
+ const id = String(node.nodeId);
85
+ if (visited.has(id))
86
+ return; // a malformed tree must not loop us forever
87
+ visited.add(id);
88
+ const role = strValue(node.role?.value).toLowerCase() || 'unknown';
89
+ let siblingsSeen = counters.get(parentPath);
90
+ if (!siblingsSeen) {
91
+ siblingsSeen = new Map();
92
+ counters.set(parentPath, siblingsSeen);
93
+ }
94
+ const n = (siblingsSeen.get(role) ?? 0) + 1;
95
+ siblingsSeen.set(role, n);
96
+ const path = `${parentPath}/${role}[${n}]`;
97
+ out.push({ path, role, name: normalizeName(strValue(node.name?.value)), depth });
98
+ for (const child of effectiveChildren(node, depth))
99
+ walk(child, path, depth + 1);
100
+ };
101
+ for (const root of roots) {
102
+ const role = strValue(root.role?.value).toLowerCase();
103
+ if (root.ignored || TRANSPARENT_ROLES.has(role)) {
104
+ for (const child of effectiveChildren(root, 0))
105
+ walk(child, '', 0);
106
+ }
107
+ else {
108
+ walk(root, '', 0);
109
+ }
110
+ }
111
+ return out;
112
+ }
113
+ // ---------------------------------------------------------------------------
114
+ // Diff
115
+ // ---------------------------------------------------------------------------
116
+ /**
117
+ * Identity for the second matching pass. Names are whitespace-collapsed to a
118
+ * single line by normalizeName, so a newline is a separator no value carries.
119
+ */
120
+ function identityKey(node) {
121
+ return `${node.role}\n${node.name ?? ''}`;
122
+ }
123
+ /**
124
+ * Diff two signatures.
125
+ *
126
+ * Two passes, because path alone is too brittle to use on its own: inserting
127
+ * one list item renumbers every later sibling, which a naive path diff would
128
+ * report as "everything below here was replaced".
129
+ *
130
+ * 1. Exact path match — same slot. Same name means unchanged, a different
131
+ * name means renamed (the useful case: a label that silently changed).
132
+ * 2. Whatever is left is matched on role+name, which recovers elements that
133
+ * merely moved. Only what survives both passes is genuinely gained/lost.
134
+ */
135
+ export function diffStructure(previous, current) {
136
+ const prevByPath = new Map(previous.map((n) => [n.path, n]));
137
+ const currByPath = new Map(current.map((n) => [n.path, n]));
138
+ const renamed = [];
139
+ const unmatchedPrev = [];
140
+ const unmatchedCurr = [];
141
+ let unchanged = 0;
142
+ for (const node of current) {
143
+ const before = prevByPath.get(node.path);
144
+ if (!before) {
145
+ unmatchedCurr.push(node);
146
+ continue;
147
+ }
148
+ if (before.role === node.role && before.name === node.name) {
149
+ unchanged++;
150
+ }
151
+ else if (before.role === node.role) {
152
+ renamed.push({ path: node.path, role: node.role, from: before.name, to: node.name });
153
+ }
154
+ else {
155
+ // Same slot, different role — that is a replacement, not a rename.
156
+ unmatchedCurr.push(node);
157
+ unmatchedPrev.push(before);
158
+ }
159
+ }
160
+ for (const node of previous) {
161
+ if (!currByPath.has(node.path))
162
+ unmatchedPrev.push(node);
163
+ }
164
+ // Pass two: role+name identity, which is what "the same element, elsewhere"
165
+ // actually means to a reader.
166
+ const pool = new Map();
167
+ for (const node of unmatchedPrev) {
168
+ const key = identityKey(node);
169
+ const bucket = pool.get(key);
170
+ if (bucket)
171
+ bucket.push(node);
172
+ else
173
+ pool.set(key, [node]);
174
+ }
175
+ const moved = [];
176
+ const gained = [];
177
+ for (const node of unmatchedCurr) {
178
+ const bucket = pool.get(identityKey(node));
179
+ const match = bucket?.shift();
180
+ if (match) {
181
+ moved.push({ role: node.role, name: node.name, from: match.path, to: node.path });
182
+ }
183
+ else {
184
+ gained.push(node);
185
+ }
186
+ }
187
+ const lost = [...pool.values()].flat();
188
+ return {
189
+ gained,
190
+ lost,
191
+ renamed,
192
+ moved,
193
+ unchanged,
194
+ changed: gained.length + lost.length + renamed.length,
195
+ };
196
+ }
197
+ /** One line for a CI log: "3 gained, 1 lost, 2 renamed". */
198
+ export function summarizeStructureDiff(diff) {
199
+ const parts = [];
200
+ if (diff.gained.length)
201
+ parts.push(`${diff.gained.length} gained`);
202
+ if (diff.lost.length)
203
+ parts.push(`${diff.lost.length} lost`);
204
+ if (diff.renamed.length)
205
+ parts.push(`${diff.renamed.length} renamed`);
206
+ if (diff.moved.length)
207
+ parts.push(`${diff.moved.length} moved`);
208
+ return parts.length ? parts.join(', ') : 'structurally identical';
209
+ }
210
+ //# sourceMappingURL=structure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure.js","sourceRoot":"","sources":["../../../src/report/structure.ts"],"names":[],"mappings":"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"}