rspec-tracer 1.2.2 → 2.0.0.pre.1

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -45
  3. data/README.md +439 -429
  4. data/bin/rspec-tracer +15 -0
  5. data/lib/rspec_tracer/cache/Rakefile +43 -0
  6. data/lib/rspec_tracer/cli/cache_clear.rb +98 -0
  7. data/lib/rspec_tracer/cli/cache_info.rb +103 -0
  8. data/lib/rspec_tracer/cli/doctor.rb +275 -0
  9. data/lib/rspec_tracer/cli/explain.rb +148 -0
  10. data/lib/rspec_tracer/cli/report_open.rb +82 -0
  11. data/lib/rspec_tracer/cli.rb +116 -0
  12. data/lib/rspec_tracer/configuration.rb +1100 -3
  13. data/lib/rspec_tracer/engine.rb +1076 -0
  14. data/lib/rspec_tracer/example.rb +21 -6
  15. data/lib/rspec_tracer/filter.rb +35 -0
  16. data/lib/rspec_tracer/line_stub.rb +61 -0
  17. data/lib/rspec_tracer/load_config.rb +2 -2
  18. data/lib/rspec_tracer/logger.rb +15 -0
  19. data/lib/rspec_tracer/rails/README.md +78 -0
  20. data/lib/rspec_tracer/rails/i18n_tracking.rb +137 -0
  21. data/lib/rspec_tracer/rails/notifications.rb +263 -0
  22. data/lib/rspec_tracer/rails/preset.rb +94 -0
  23. data/lib/rspec_tracer/rails/railtie.rb +22 -0
  24. data/lib/rspec_tracer/rails.rb +15 -0
  25. data/lib/rspec_tracer/remote_cache/README.md +140 -0
  26. data/lib/rspec_tracer/remote_cache/Rakefile +35 -11
  27. data/lib/rspec_tracer/remote_cache/archive.rb +137 -0
  28. data/lib/rspec_tracer/remote_cache/backend.rb +73 -0
  29. data/lib/rspec_tracer/remote_cache/git_ancestry.rb +241 -0
  30. data/lib/rspec_tracer/remote_cache/local_fs_backend.rb +439 -0
  31. data/lib/rspec_tracer/remote_cache/redis_backend.rb +554 -0
  32. data/lib/rspec_tracer/remote_cache/s3_backend.rb +712 -0
  33. data/lib/rspec_tracer/remote_cache/user_tasks.rb +397 -0
  34. data/lib/rspec_tracer/remote_cache/validator.rb +40 -62
  35. data/lib/rspec_tracer/remote_cache.rb +22 -0
  36. data/lib/rspec_tracer/reporters/README.md +103 -0
  37. data/lib/rspec_tracer/reporters/base.rb +87 -0
  38. data/lib/rspec_tracer/reporters/coverage_json_reporter.rb +338 -0
  39. data/lib/rspec_tracer/reporters/html/.gitignore +19 -0
  40. data/lib/rspec_tracer/reporters/html/.prettierignore +4 -0
  41. data/lib/rspec_tracer/reporters/html/.prettierrc.json +9 -0
  42. data/lib/rspec_tracer/reporters/html/README.md +80 -0
  43. data/lib/rspec_tracer/reporters/html/dist/assets/index.css +2 -0
  44. data/lib/rspec_tracer/reporters/html/dist/assets/index.js +1 -0
  45. data/lib/rspec_tracer/reporters/html/dist/index.html +24 -0
  46. data/lib/rspec_tracer/reporters/html/eslint.config.js +62 -0
  47. data/lib/rspec_tracer/reporters/html/package-lock.json +4941 -0
  48. data/lib/rspec_tracer/reporters/html/package.json +29 -0
  49. data/lib/rspec_tracer/reporters/html/src/app.jsx +130 -0
  50. data/lib/rspec_tracer/reporters/html/src/components/AllExamples.jsx +86 -0
  51. data/lib/rspec_tracer/reporters/html/src/components/DuplicateExamples.jsx +68 -0
  52. data/lib/rspec_tracer/reporters/html/src/components/ExamplesDependency.jsx +78 -0
  53. data/lib/rspec_tracer/reporters/html/src/components/FilesDependency.jsx +72 -0
  54. data/lib/rspec_tracer/reporters/html/src/components/FlakyExamples.jsx +42 -0
  55. data/lib/rspec_tracer/reporters/html/src/components/ReportTable.jsx +131 -0
  56. data/lib/rspec_tracer/reporters/html/src/components/SearchBar.jsx +19 -0
  57. data/lib/rspec_tracer/reporters/html/src/index.html +23 -0
  58. data/lib/rspec_tracer/reporters/html/src/main.jsx +37 -0
  59. data/lib/rspec_tracer/reporters/html/src/styles.css +434 -0
  60. data/lib/rspec_tracer/reporters/html/vite.config.js +42 -0
  61. data/lib/rspec_tracer/reporters/html_reporter.rb +266 -0
  62. data/lib/rspec_tracer/reporters/json_reporter.rb +88 -0
  63. data/lib/rspec_tracer/reporters/payload_builder.rb +235 -0
  64. data/lib/rspec_tracer/reporters/registry.rb +120 -0
  65. data/lib/rspec_tracer/reporters/terminal_reporter.rb +264 -0
  66. data/lib/rspec_tracer/rspec/README.md +73 -0
  67. data/lib/rspec_tracer/rspec/installation.rb +97 -0
  68. data/lib/rspec_tracer/rspec/metadata.rb +96 -0
  69. data/lib/rspec_tracer/rspec/parallel_tests.rb +459 -0
  70. data/lib/rspec_tracer/rspec/reporter_hook.rb +84 -0
  71. data/lib/rspec_tracer/rspec/runner_hook.rb +178 -0
  72. data/lib/rspec_tracer/source_file.rb +24 -7
  73. data/lib/rspec_tracer/storage/README.md +35 -0
  74. data/lib/rspec_tracer/storage/backend.rb +68 -0
  75. data/lib/rspec_tracer/storage/json_backend.rb +866 -0
  76. data/lib/rspec_tracer/storage/lazy_snapshot.rb +65 -0
  77. data/lib/rspec_tracer/storage/schema.rb +43 -0
  78. data/lib/rspec_tracer/storage/serializer/json.rb +41 -0
  79. data/lib/rspec_tracer/storage/serializer/msgpack.rb +90 -0
  80. data/lib/rspec_tracer/storage/snapshot.rb +127 -0
  81. data/lib/rspec_tracer/storage/sqlite_backend.rb +686 -0
  82. data/lib/rspec_tracer/time_formatter.rb +37 -18
  83. data/lib/rspec_tracer/tracker/README.md +36 -0
  84. data/lib/rspec_tracer/tracker/coverage_adapter.rb +174 -0
  85. data/lib/rspec_tracer/tracker/declared_globs.rb +100 -0
  86. data/lib/rspec_tracer/tracker/dependency_graph.rb +134 -0
  87. data/lib/rspec_tracer/tracker/env_matcher.rb +127 -0
  88. data/lib/rspec_tracer/tracker/env_snapshot.rb +77 -0
  89. data/lib/rspec_tracer/tracker/example_registry.rb +153 -0
  90. data/lib/rspec_tracer/tracker/file_digest.rb +61 -0
  91. data/lib/rspec_tracer/tracker/filter.rb +127 -0
  92. data/lib/rspec_tracer/tracker/input.rb +99 -0
  93. data/lib/rspec_tracer/tracker/io_hooks/file.rb +55 -0
  94. data/lib/rspec_tracer/tracker/io_hooks/io.rb +24 -0
  95. data/lib/rspec_tracer/tracker/io_hooks/json.rb +23 -0
  96. data/lib/rspec_tracer/tracker/io_hooks/kernel.rb +26 -0
  97. data/lib/rspec_tracer/tracker/io_hooks/yaml.rb +38 -0
  98. data/lib/rspec_tracer/tracker/io_hooks.rb +195 -0
  99. data/lib/rspec_tracer/tracker/loaded_files_tracker.rb +295 -0
  100. data/lib/rspec_tracer/tracker/new_file_detector.rb +62 -0
  101. data/lib/rspec_tracer/tracker/whole_suite_invalidators.rb +96 -0
  102. data/lib/rspec_tracer/version.rb +4 -1
  103. data/lib/rspec_tracer.rb +232 -381
  104. metadata +93 -43
  105. data/lib/rspec_tracer/cache.rb +0 -207
  106. data/lib/rspec_tracer/coverage_merger.rb +0 -42
  107. data/lib/rspec_tracer/coverage_reporter.rb +0 -187
  108. data/lib/rspec_tracer/coverage_writer.rb +0 -58
  109. data/lib/rspec_tracer/html_reporter/Rakefile +0 -18
  110. data/lib/rspec_tracer/html_reporter/assets/javascripts/application.js +0 -56
  111. data/lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js +0 -10881
  112. data/lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js +0 -15381
  113. data/lib/rspec_tracer/html_reporter/assets/stylesheets/application.css +0 -196
  114. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/datatables.css +0 -459
  115. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/jquery-ui.css +0 -436
  116. data/lib/rspec_tracer/html_reporter/assets/stylesheets/print.css +0 -92
  117. data/lib/rspec_tracer/html_reporter/assets/stylesheets/reset.css +0 -265
  118. data/lib/rspec_tracer/html_reporter/public/application.css +0 -5
  119. data/lib/rspec_tracer/html_reporter/public/application.js +0 -6
  120. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc.png +0 -0
  121. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc_disabled.png +0 -0
  122. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_both.png +0 -0
  123. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc.png +0 -0
  124. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc_disabled.png +0 -0
  125. data/lib/rspec_tracer/html_reporter/public/favicon.png +0 -0
  126. data/lib/rspec_tracer/html_reporter/public/loading.gif +0 -0
  127. data/lib/rspec_tracer/html_reporter/reporter.rb +0 -242
  128. data/lib/rspec_tracer/html_reporter/views/duplicate_examples.erb +0 -34
  129. data/lib/rspec_tracer/html_reporter/views/examples.erb +0 -58
  130. data/lib/rspec_tracer/html_reporter/views/examples_dependency.erb +0 -36
  131. data/lib/rspec_tracer/html_reporter/views/files_dependency.erb +0 -36
  132. data/lib/rspec_tracer/html_reporter/views/flaky_examples.erb +0 -38
  133. data/lib/rspec_tracer/html_reporter/views/layout.erb +0 -38
  134. data/lib/rspec_tracer/remote_cache/aws.rb +0 -176
  135. data/lib/rspec_tracer/remote_cache/cache.rb +0 -75
  136. data/lib/rspec_tracer/remote_cache/repo.rb +0 -210
  137. data/lib/rspec_tracer/report_generator.rb +0 -158
  138. data/lib/rspec_tracer/report_merger.rb +0 -68
  139. data/lib/rspec_tracer/report_writer.rb +0 -141
  140. data/lib/rspec_tracer/reporter.rb +0 -204
  141. data/lib/rspec_tracer/rspec_reporter.rb +0 -41
  142. data/lib/rspec_tracer/rspec_runner.rb +0 -56
  143. data/lib/rspec_tracer/ruby_coverage.rb +0 -9
  144. data/lib/rspec_tracer/runner.rb +0 -278
@@ -0,0 +1,37 @@
1
+ import { render } from 'preact';
2
+ import { App } from './app.jsx';
3
+ import './styles.css';
4
+
5
+ // Hydration entry point. Parse the JSON payload Ruby injected into the
6
+ // `<script id="report-data">` tag, render the interactive Preact view
7
+ // into `#app`, and strip the server-side fallback tables (rendered by
8
+ // HtmlReporter for the no-JS case). If parsing fails for any reason,
9
+ // leave the fallback tables in place - graceful degradation matches
10
+ // the AC "report works with JavaScript disabled."
11
+ function boot() {
12
+ const mount = document.getElementById('app');
13
+ if (!mount) return;
14
+
15
+ const dataNode = document.getElementById('report-data');
16
+ if (!dataNode) return;
17
+
18
+ let payload;
19
+ try {
20
+ payload = JSON.parse(dataNode.textContent || '{}');
21
+ } catch (err) {
22
+ console.error('rspec-tracer: failed to parse embedded report data', err);
23
+ return;
24
+ }
25
+
26
+ render(<App payload={payload} />, mount);
27
+ mount.setAttribute('data-hydrate', 'ready');
28
+
29
+ const fallback = document.getElementById('fallback');
30
+ if (fallback) fallback.remove();
31
+ }
32
+
33
+ if (document.readyState === 'loading') {
34
+ document.addEventListener('DOMContentLoaded', boot);
35
+ } else {
36
+ boot();
37
+ }
@@ -0,0 +1,434 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --bg: #f7f8fa;
4
+ --surface: #ffffff;
5
+ --surface-alt: #f0f3f7;
6
+ --border: #d8dde4;
7
+ --border-strong: #b0b8c2;
8
+ --text: #1b1f26;
9
+ --text-muted: #515b66;
10
+ --accent: #1a5fb4;
11
+ --accent-contrast: #ffffff;
12
+ --danger: #a6160f;
13
+ --warning: #8a5a00;
14
+ --success: #146c2e;
15
+ --neutral: #4a5460;
16
+ --skipped: #5f6774;
17
+ --flaky: #8a3fa6;
18
+ --focus-ring: #1a5fb4;
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ html,
26
+ body {
27
+ margin: 0;
28
+ padding: 0;
29
+ background: var(--bg);
30
+ color: var(--text);
31
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
32
+ font-size: 14px;
33
+ line-height: 1.45;
34
+ }
35
+
36
+ code {
37
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
38
+ font-size: 0.92em;
39
+ }
40
+
41
+ a {
42
+ color: var(--accent);
43
+ }
44
+
45
+ .noscript-banner {
46
+ background: #fff4d6;
47
+ color: #6a4a00;
48
+ border-bottom: 1px solid #e0c98a;
49
+ padding: 12px 20px;
50
+ font-size: 13px;
51
+ }
52
+
53
+ .report-root {
54
+ max-width: 1400px;
55
+ margin: 0 auto;
56
+ padding: 24px;
57
+ }
58
+
59
+ .report-header h1 {
60
+ margin: 0 0 12px;
61
+ font-size: 22px;
62
+ }
63
+
64
+ .summary {
65
+ display: flex;
66
+ flex-wrap: wrap;
67
+ gap: 8px;
68
+ margin: 0 0 12px;
69
+ padding: 0;
70
+ }
71
+
72
+ .summary-item {
73
+ background: var(--surface);
74
+ border: 1px solid var(--border);
75
+ border-radius: 6px;
76
+ padding: 10px 14px;
77
+ min-width: 90px;
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 2px;
81
+ }
82
+
83
+ .summary-item dt {
84
+ margin: 0;
85
+ font-size: 11px;
86
+ text-transform: uppercase;
87
+ letter-spacing: 0.04em;
88
+ color: var(--text-muted);
89
+ }
90
+
91
+ .summary-item dd {
92
+ margin: 0;
93
+ font-size: 20px;
94
+ font-weight: 600;
95
+ }
96
+
97
+ .summary-passed dd {
98
+ color: var(--success);
99
+ }
100
+ .summary-failed dd {
101
+ color: var(--danger);
102
+ }
103
+ .summary-pending dd {
104
+ color: var(--warning);
105
+ }
106
+ .summary-skipped dd {
107
+ color: var(--skipped);
108
+ }
109
+
110
+ .report-meta {
111
+ margin: 0 0 20px;
112
+ color: var(--text-muted);
113
+ font-size: 12px;
114
+ display: flex;
115
+ gap: 16px;
116
+ flex-wrap: wrap;
117
+ }
118
+
119
+ .tab-bar {
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ gap: 4px;
123
+ border-bottom: 1px solid var(--border);
124
+ margin-bottom: 16px;
125
+ }
126
+
127
+ .tab-button {
128
+ appearance: none;
129
+ background: transparent;
130
+ border: 1px solid transparent;
131
+ border-bottom: none;
132
+ border-top-left-radius: 6px;
133
+ border-top-right-radius: 6px;
134
+ color: var(--text);
135
+ cursor: pointer;
136
+ font: inherit;
137
+ padding: 8px 14px;
138
+ display: inline-flex;
139
+ align-items: center;
140
+ gap: 8px;
141
+ }
142
+
143
+ .tab-button:hover {
144
+ background: var(--surface);
145
+ border-color: var(--border);
146
+ }
147
+
148
+ .tab-button.is-active {
149
+ background: var(--surface);
150
+ border-color: var(--border);
151
+ border-bottom-color: var(--surface);
152
+ margin-bottom: -1px;
153
+ font-weight: 600;
154
+ }
155
+
156
+ .tab-button:focus-visible {
157
+ outline: 2px solid var(--focus-ring);
158
+ outline-offset: 2px;
159
+ }
160
+
161
+ .tab-count {
162
+ background: var(--surface-alt);
163
+ border-radius: 10px;
164
+ padding: 1px 8px;
165
+ font-size: 11px;
166
+ color: var(--text-muted);
167
+ }
168
+
169
+ .tab-button.is-active .tab-count {
170
+ background: var(--border);
171
+ color: var(--text);
172
+ }
173
+
174
+ .report-panel {
175
+ background: var(--surface);
176
+ border: 1px solid var(--border);
177
+ border-radius: 6px;
178
+ padding: 20px;
179
+ }
180
+
181
+ .report-table {
182
+ display: flex;
183
+ flex-direction: column;
184
+ gap: 12px;
185
+ }
186
+
187
+ .search-bar {
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 4px;
191
+ max-width: 360px;
192
+ }
193
+
194
+ .search-label {
195
+ font-size: 11px;
196
+ text-transform: uppercase;
197
+ letter-spacing: 0.04em;
198
+ color: var(--text-muted);
199
+ }
200
+
201
+ .search-input {
202
+ appearance: none;
203
+ border: 1px solid var(--border);
204
+ border-radius: 4px;
205
+ font: inherit;
206
+ padding: 6px 10px;
207
+ background: var(--surface);
208
+ color: var(--text);
209
+ }
210
+
211
+ .search-input:focus-visible {
212
+ outline: 2px solid var(--focus-ring);
213
+ outline-offset: 1px;
214
+ border-color: var(--focus-ring);
215
+ }
216
+
217
+ .report-table__counts {
218
+ margin: 0;
219
+ color: var(--text-muted);
220
+ font-size: 12px;
221
+ }
222
+
223
+ .data-table {
224
+ width: 100%;
225
+ border-collapse: collapse;
226
+ border: 1px solid var(--border);
227
+ }
228
+
229
+ .data-table th,
230
+ .data-table td {
231
+ border-bottom: 1px solid var(--border);
232
+ padding: 8px 12px;
233
+ text-align: left;
234
+ vertical-align: top;
235
+ }
236
+
237
+ .data-table thead th {
238
+ background: var(--surface-alt);
239
+ color: var(--text-muted);
240
+ font-size: 11px;
241
+ text-transform: uppercase;
242
+ letter-spacing: 0.04em;
243
+ font-weight: 600;
244
+ border-bottom: 1px solid var(--border-strong);
245
+ position: sticky;
246
+ top: 0;
247
+ }
248
+
249
+ .data-table tbody tr:nth-child(even) {
250
+ background: var(--surface-alt);
251
+ }
252
+
253
+ .data-table .is-numeric {
254
+ text-align: right;
255
+ font-variant-numeric: tabular-nums;
256
+ }
257
+
258
+ .sort-button {
259
+ appearance: none;
260
+ background: transparent;
261
+ border: none;
262
+ color: inherit;
263
+ font: inherit;
264
+ padding: 0;
265
+ cursor: pointer;
266
+ display: inline-flex;
267
+ align-items: center;
268
+ gap: 4px;
269
+ text-transform: inherit;
270
+ letter-spacing: inherit;
271
+ }
272
+
273
+ .sort-button:focus-visible {
274
+ outline: 2px solid var(--focus-ring);
275
+ outline-offset: 2px;
276
+ }
277
+
278
+ .sort-indicator {
279
+ font-size: 10px;
280
+ color: var(--text-muted);
281
+ }
282
+
283
+ .sort-button[data-sort='asc'] .sort-indicator,
284
+ .sort-button[data-sort='desc'] .sort-indicator {
285
+ color: var(--text);
286
+ }
287
+
288
+ .report-table__empty {
289
+ padding: 20px;
290
+ text-align: center;
291
+ color: var(--text-muted);
292
+ font-style: italic;
293
+ }
294
+
295
+ .cell-description {
296
+ max-width: 520px;
297
+ }
298
+
299
+ .cell-location,
300
+ .cell-file,
301
+ .cell-id {
302
+ color: var(--text-muted);
303
+ }
304
+
305
+ .cell-deps {
306
+ max-width: 520px;
307
+ }
308
+
309
+ .badge {
310
+ display: inline-block;
311
+ padding: 2px 8px;
312
+ border-radius: 10px;
313
+ font-size: 11px;
314
+ font-weight: 600;
315
+ text-transform: uppercase;
316
+ letter-spacing: 0.04em;
317
+ color: var(--accent-contrast);
318
+ }
319
+
320
+ .status-passed {
321
+ background: var(--success);
322
+ }
323
+ .status-failed {
324
+ background: var(--danger);
325
+ }
326
+ .status-pending {
327
+ background: var(--warning);
328
+ }
329
+ .status-skipped {
330
+ background: var(--skipped);
331
+ }
332
+ .status-flaky {
333
+ background: var(--flaky);
334
+ }
335
+ .status-unknown {
336
+ background: var(--neutral);
337
+ }
338
+
339
+ .deps-details {
340
+ margin: 0;
341
+ }
342
+
343
+ .deps-details + .deps-details {
344
+ margin-top: 6px;
345
+ }
346
+
347
+ .deps-details summary {
348
+ cursor: pointer;
349
+ color: var(--accent);
350
+ }
351
+
352
+ .deps-details summary::marker {
353
+ color: var(--text-muted);
354
+ }
355
+
356
+ .deps-list {
357
+ margin: 6px 0 0;
358
+ padding-left: 18px;
359
+ }
360
+
361
+ .deps-list li {
362
+ padding: 2px 0;
363
+ display: flex;
364
+ gap: 8px;
365
+ align-items: baseline;
366
+ }
367
+
368
+ .dep-count {
369
+ color: var(--text-muted);
370
+ font-size: 12px;
371
+ }
372
+
373
+ .visually-hidden {
374
+ position: absolute !important;
375
+ width: 1px !important;
376
+ height: 1px !important;
377
+ margin: -1px !important;
378
+ padding: 0 !important;
379
+ overflow: hidden !important;
380
+ clip: rect(0, 0, 0, 0) !important;
381
+ white-space: nowrap !important;
382
+ border: 0 !important;
383
+ }
384
+
385
+ /* Fallback (server-rendered) tables: styled like the interactive
386
+ ones so no-JS readers see a consistent view. Preact replaces
387
+ them on hydrate. */
388
+ .fallback-root {
389
+ display: flex;
390
+ flex-direction: column;
391
+ gap: 24px;
392
+ padding: 0 0 24px;
393
+ }
394
+
395
+ .fallback-section h2 {
396
+ margin: 0 0 8px;
397
+ font-size: 16px;
398
+ }
399
+
400
+ .fallback-table {
401
+ width: 100%;
402
+ border-collapse: collapse;
403
+ border: 1px solid var(--border);
404
+ }
405
+
406
+ .fallback-table caption {
407
+ text-align: left;
408
+ padding: 6px 0;
409
+ color: var(--text-muted);
410
+ font-size: 12px;
411
+ }
412
+
413
+ .fallback-table th,
414
+ .fallback-table td {
415
+ border-bottom: 1px solid var(--border);
416
+ padding: 6px 10px;
417
+ text-align: left;
418
+ vertical-align: top;
419
+ }
420
+
421
+ .fallback-table thead th {
422
+ background: var(--surface-alt);
423
+ font-size: 11px;
424
+ text-transform: uppercase;
425
+ letter-spacing: 0.04em;
426
+ color: var(--text-muted);
427
+ }
428
+
429
+ @media (prefers-reduced-motion: reduce) {
430
+ * {
431
+ animation-duration: 0.01ms !important;
432
+ transition-duration: 0.01ms !important;
433
+ }
434
+ }
@@ -0,0 +1,42 @@
1
+ import { defineConfig } from 'vite';
2
+ import preact from '@preact/preset-vite';
3
+
4
+ // Produce a deterministic, committable dist/ tree.
5
+ //
6
+ // - No asset hashing: filenames stay stable so `git diff dist/` shows
7
+ // real code changes instead of hash churn, and the drift check in
8
+ // CI (`npm ci && npm run build && git diff --exit-code dist/`) is
9
+ // meaningful.
10
+ // - Single-file entry points for JS + CSS so the Ruby-side reporter
11
+ // can reference them by predictable paths.
12
+ // - Relative base so the emitted HTML opens correctly when users
13
+ // double-click rspec_tracer_report/index.html from their filesystem
14
+ // (no CDN, no baked-in origin).
15
+ export default defineConfig({
16
+ plugins: [preact()],
17
+ // Source entry lives in src/index.html; dist/ sits beside src/ at
18
+ // the package root so the Ruby reporter can find it at a stable
19
+ // relative path (lib/rspec_tracer/reporters/html/dist/).
20
+ root: 'src',
21
+ base: './',
22
+ build: {
23
+ outDir: '../dist',
24
+ emptyOutDir: true,
25
+ assetsDir: 'assets',
26
+ cssCodeSplit: false,
27
+ reportCompressedSize: false,
28
+ rollupOptions: {
29
+ output: {
30
+ entryFileNames: 'assets/index.js',
31
+ chunkFileNames: 'assets/[name].js',
32
+ assetFileNames: (assetInfo) => {
33
+ // Vite names the CSS bundle after the entry module; force
34
+ // `assets/index.css` for a stable, review-friendly diff.
35
+ const name = assetInfo.name || '';
36
+ if (name.endsWith('.css')) return 'assets/index.css';
37
+ return 'assets/[name][extname]';
38
+ },
39
+ },
40
+ },
41
+ },
42
+ });