executable-stories-formatters 0.17.0 → 1.0.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.
- package/README.md +6 -4
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +183 -1
- package/dist/adapters.d.ts +183 -1
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +2525 -16783
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2336 -15957
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -1259
- package/dist/index.d.ts +117 -1259
- package/dist/index.js +2335 -15950
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- package/schemas/README.md +0 -1
- package/templates/astro-thin/astro.config.mjs +62 -0
- package/templates/astro-thin/executable-stories.config.mjs +47 -0
- package/templates/astro-thin/gitignore +3 -0
- package/templates/astro-thin/package.json +20 -0
- package/templates/astro-thin/reports/sample-run.json +133 -0
- package/templates/astro-thin/src/content/docs/404.md +20 -0
- package/templates/astro-thin/src/content/docs/guides/writing-docs.mdx +7 -0
- package/templates/astro-thin/src/content/docs/index.mdx +44 -0
- package/templates/astro-thin/src/content.config.ts +26 -0
- package/templates/astro-thin/src/styles/stories.css +32 -0
- package/templates/astro-thin/tsconfig.json +5 -0
- package/dist/index-CXrzCk9p.d.cts +0 -628
- package/dist/index-CXrzCk9p.d.ts +0 -628
- package/schemas/story-report-v1.json +0 -456
- package/templates/astro-starlight/astro.config.mjs +0 -57
- package/templates/astro-starlight/gitignore +0 -14
- package/templates/astro-starlight/package.json +0 -20
- package/templates/astro-starlight/public/stories/assets/.gitkeep +0 -0
- package/templates/astro-starlight/public/stories/notes-index.json +0 -4
- package/templates/astro-starlight/public/stories/story-report.json +0 -17
- package/templates/astro-starlight/src/components/ApiOperations.astro +0 -366
- package/templates/astro-starlight/src/components/Checklist.astro +0 -15
- package/templates/astro-starlight/src/components/HealthDashboard.astro +0 -171
- package/templates/astro-starlight/src/components/PageTitle.astro +0 -53
- package/templates/astro-starlight/src/components/VerifiedBy.astro +0 -281
- package/templates/astro-starlight/src/components/VerifiedStep.astro +0 -91
- package/templates/astro-starlight/src/content/docs/examples/example-adr.mdx +0 -45
- package/templates/astro-starlight/src/content/docs/guides/behavior-portal.mdx +0 -41
- package/templates/astro-starlight/src/content/docs/guides/writing-docs.mdx +0 -49
- package/templates/astro-starlight/src/content/docs/index.mdx +0 -49
- package/templates/astro-starlight/src/content/docs/stories/.gitkeep +0 -0
- package/templates/astro-starlight/src/content.config.ts +0 -18
- package/templates/astro-starlight/src/lib/config.ts +0 -50
- package/templates/astro-starlight/src/lib/render-doc-entry.ts +0 -154
- package/templates/astro-starlight/src/lib/report-health.ts +0 -61
- package/templates/astro-starlight/src/lib/verification.ts +0 -247
- package/templates/astro-starlight/src/pages/explorer/explorer.css +0 -729
- package/templates/astro-starlight/src/pages/explorer/index.astro +0 -404
- package/templates/astro-starlight/src/styles/global.css +0 -293
- package/templates/astro-starlight/src/styles/themes/corporate.css +0 -83
- package/templates/astro-starlight/src/styles/themes/dashboard.css +0 -76
- package/templates/astro-starlight/src/styles/themes/default.css +0 -86
- package/templates/astro-starlight/src/styles/themes/minimal.css +0 -87
- package/templates/astro-starlight/src/styles/themes/playful.css +0 -77
- package/templates/astro-starlight/src/styles/themes/terminal.css +0 -77
- package/templates/astro-starlight/tsconfig.json +0 -13
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* <ApiOperations tag="Transfers" hasRun endpoints={[...]} summary={{...}} />
|
|
4
|
-
*
|
|
5
|
-
* Renders the API coverage table for one OpenAPI tag. Generated by
|
|
6
|
-
* `executable-stories import-openapi`, which writes an MDX page that imports
|
|
7
|
-
* this component and passes the endpoints + their test coverage.
|
|
8
|
-
*
|
|
9
|
-
* Each endpoint shows an HTTP-method pill, its path, a coverage badge, and the
|
|
10
|
-
* stories that exercise it — linked straight into the Scenario Explorer, so the
|
|
11
|
-
* API docs can never drift from the tests that prove them.
|
|
12
|
-
*/
|
|
13
|
-
import { explorerUrl } from "../lib/config";
|
|
14
|
-
|
|
15
|
-
type CoverageStatus = "covered" | "failing" | "uncovered";
|
|
16
|
-
|
|
17
|
-
export interface ApiStoryRef {
|
|
18
|
-
id: string;
|
|
19
|
-
title: string;
|
|
20
|
-
status: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ApiEndpoint {
|
|
24
|
-
method: string;
|
|
25
|
-
path: string;
|
|
26
|
-
summary: string;
|
|
27
|
-
status: CoverageStatus;
|
|
28
|
-
stories: ApiStoryRef[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface Props {
|
|
32
|
-
tag: string;
|
|
33
|
-
hasRun: boolean;
|
|
34
|
-
endpoints: ApiEndpoint[];
|
|
35
|
-
summary: { total: number; covered: number; failing: number; uncovered: number };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const { hasRun, endpoints, summary } = Astro.props;
|
|
39
|
-
|
|
40
|
-
const coverageLabel: Record<CoverageStatus, string> = {
|
|
41
|
-
covered: "Covered",
|
|
42
|
-
failing: "Failing",
|
|
43
|
-
uncovered: "Uncovered",
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const methodClass = (method: string) => `m-${method.toLowerCase()}`;
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
<section class="api-ops">
|
|
50
|
-
<header class="api-ops__bar">
|
|
51
|
-
<p class="api-ops__lede">
|
|
52
|
-
Generated from the OpenAPI spec.{" "}
|
|
53
|
-
{hasRun
|
|
54
|
-
? "Coverage reflects the last test run."
|
|
55
|
-
: "Re-run with --run <story-report.json> to show per-endpoint coverage."}
|
|
56
|
-
</p>
|
|
57
|
-
{
|
|
58
|
-
hasRun && (
|
|
59
|
-
<div class="api-ops__stats" role="group" aria-label="Coverage summary">
|
|
60
|
-
<span class="stat stat--covered">
|
|
61
|
-
<strong>{summary.covered}</strong> covered
|
|
62
|
-
</span>
|
|
63
|
-
{summary.failing > 0 && (
|
|
64
|
-
<span class="stat stat--failing">
|
|
65
|
-
<strong>{summary.failing}</strong> failing
|
|
66
|
-
</span>
|
|
67
|
-
)}
|
|
68
|
-
<span class="stat stat--uncovered">
|
|
69
|
-
<strong>{summary.uncovered}</strong> no test
|
|
70
|
-
</span>
|
|
71
|
-
<span class="stat stat--total">of {summary.total}</span>
|
|
72
|
-
</div>
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
</header>
|
|
76
|
-
|
|
77
|
-
<div class="api-ops__rows" role="table" aria-label="API endpoints">
|
|
78
|
-
<div class="api-ops__row api-ops__row--head" role="row">
|
|
79
|
-
<span role="columnheader">Endpoint</span>
|
|
80
|
-
<span role="columnheader">Summary</span>
|
|
81
|
-
{hasRun && <span role="columnheader">Coverage</span>}
|
|
82
|
-
{hasRun && <span role="columnheader">Verified by</span>}
|
|
83
|
-
</div>
|
|
84
|
-
{
|
|
85
|
-
endpoints.map((ep) => (
|
|
86
|
-
<div class="api-ops__row" role="row">
|
|
87
|
-
<span class="cell-endpoint" role="cell">
|
|
88
|
-
<span class:list={["method", methodClass(ep.method)]}>{ep.method}</span>
|
|
89
|
-
<code class="path">{ep.path}</code>
|
|
90
|
-
</span>
|
|
91
|
-
<span class="cell-summary" role="cell">{ep.summary || "—"}</span>
|
|
92
|
-
{hasRun && (
|
|
93
|
-
<span role="cell">
|
|
94
|
-
<span class:list={["coverage", `coverage--${ep.status}`]}>
|
|
95
|
-
<span class="coverage__dot" aria-hidden="true" />
|
|
96
|
-
{coverageLabel[ep.status]}
|
|
97
|
-
</span>
|
|
98
|
-
</span>
|
|
99
|
-
)}
|
|
100
|
-
{hasRun && (
|
|
101
|
-
<span class="cell-stories" role="cell">
|
|
102
|
-
{ep.stories.length === 0 ? (
|
|
103
|
-
<span class="no-stories">—</span>
|
|
104
|
-
) : (
|
|
105
|
-
ep.stories.map((s) => (
|
|
106
|
-
<a class:list={["story-chip", `story-chip--${s.status}`]} href={explorerUrl(s.id)}>
|
|
107
|
-
<span class="story-chip__dot" aria-hidden="true" />
|
|
108
|
-
{s.title}
|
|
109
|
-
</a>
|
|
110
|
-
))
|
|
111
|
-
)}
|
|
112
|
-
</span>
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
))
|
|
116
|
-
}
|
|
117
|
-
</div>
|
|
118
|
-
</section>
|
|
119
|
-
|
|
120
|
-
<style>
|
|
121
|
-
.api-ops {
|
|
122
|
-
margin: 1.25rem 0 2rem;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.api-ops__bar {
|
|
126
|
-
display: flex;
|
|
127
|
-
flex-wrap: wrap;
|
|
128
|
-
align-items: center;
|
|
129
|
-
justify-content: space-between;
|
|
130
|
-
gap: 0.75rem 1.25rem;
|
|
131
|
-
margin-bottom: 1rem;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.api-ops__lede {
|
|
135
|
-
margin: 0;
|
|
136
|
-
color: var(--es-text-muted);
|
|
137
|
-
font-size: var(--sl-text-sm);
|
|
138
|
-
max-width: 52ch;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.api-ops__stats {
|
|
142
|
-
display: flex;
|
|
143
|
-
align-items: center;
|
|
144
|
-
gap: 0.4rem;
|
|
145
|
-
flex-wrap: wrap;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.stat {
|
|
149
|
-
display: inline-flex;
|
|
150
|
-
align-items: baseline;
|
|
151
|
-
gap: 0.3rem;
|
|
152
|
-
font-size: var(--sl-text-xs);
|
|
153
|
-
color: var(--es-text-muted);
|
|
154
|
-
padding: 0.25rem 0.6rem;
|
|
155
|
-
border: 1px solid var(--es-line);
|
|
156
|
-
border-radius: var(--es-radius-pill);
|
|
157
|
-
background: var(--es-surface);
|
|
158
|
-
}
|
|
159
|
-
.stat strong {
|
|
160
|
-
font-size: var(--sl-text-sm);
|
|
161
|
-
color: var(--es-text);
|
|
162
|
-
}
|
|
163
|
-
.stat--covered {
|
|
164
|
-
color: var(--es-passed);
|
|
165
|
-
border-color: color-mix(in oklab, var(--es-passed) 40%, var(--es-line));
|
|
166
|
-
background: var(--es-passed-soft);
|
|
167
|
-
}
|
|
168
|
-
.stat--covered strong {
|
|
169
|
-
color: var(--es-passed);
|
|
170
|
-
}
|
|
171
|
-
.stat--failing {
|
|
172
|
-
color: var(--es-failed);
|
|
173
|
-
border-color: color-mix(in oklab, var(--es-failed) 40%, var(--es-line));
|
|
174
|
-
background: var(--es-failed-soft);
|
|
175
|
-
}
|
|
176
|
-
.stat--failing strong {
|
|
177
|
-
color: var(--es-failed);
|
|
178
|
-
}
|
|
179
|
-
.stat--uncovered {
|
|
180
|
-
color: var(--es-skipped);
|
|
181
|
-
border-color: color-mix(in oklab, var(--es-skipped) 40%, var(--es-line));
|
|
182
|
-
background: var(--es-skipped-soft);
|
|
183
|
-
}
|
|
184
|
-
.stat--uncovered strong {
|
|
185
|
-
color: var(--es-skipped);
|
|
186
|
-
}
|
|
187
|
-
.stat--total {
|
|
188
|
-
border: none;
|
|
189
|
-
background: none;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.api-ops__rows {
|
|
193
|
-
border: 1px solid var(--es-line);
|
|
194
|
-
border-radius: var(--es-radius);
|
|
195
|
-
overflow: hidden;
|
|
196
|
-
background: var(--es-bg);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.api-ops__row {
|
|
200
|
-
display: grid;
|
|
201
|
-
grid-template-columns: minmax(220px, 1.2fr) minmax(140px, 1.4fr) auto minmax(160px, 1.3fr);
|
|
202
|
-
gap: 1rem;
|
|
203
|
-
align-items: center;
|
|
204
|
-
padding: 0.7rem 1rem;
|
|
205
|
-
border-top: 1px solid var(--es-line);
|
|
206
|
-
}
|
|
207
|
-
.api-ops__row:first-child {
|
|
208
|
-
border-top: none;
|
|
209
|
-
}
|
|
210
|
-
.api-ops__row:not(.api-ops__row--head):hover {
|
|
211
|
-
background: var(--es-surface);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.api-ops__row--head {
|
|
215
|
-
background: var(--es-surface-2);
|
|
216
|
-
font-size: var(--sl-text-xs);
|
|
217
|
-
font-weight: 600;
|
|
218
|
-
text-transform: uppercase;
|
|
219
|
-
letter-spacing: 0.06em;
|
|
220
|
-
color: var(--es-text-muted);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.cell-endpoint {
|
|
224
|
-
display: inline-flex;
|
|
225
|
-
align-items: center;
|
|
226
|
-
gap: 0.6rem;
|
|
227
|
-
min-width: 0;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.method {
|
|
231
|
-
flex: none;
|
|
232
|
-
font-family: var(--es-mono);
|
|
233
|
-
font-size: 0.7rem;
|
|
234
|
-
font-weight: 700;
|
|
235
|
-
letter-spacing: 0.04em;
|
|
236
|
-
padding: 0.2rem 0.45rem;
|
|
237
|
-
border-radius: var(--es-radius-sm);
|
|
238
|
-
color: #fff;
|
|
239
|
-
background: var(--es-method-default);
|
|
240
|
-
}
|
|
241
|
-
.m-get {
|
|
242
|
-
background: var(--es-method-get);
|
|
243
|
-
}
|
|
244
|
-
.m-post {
|
|
245
|
-
background: var(--es-method-post);
|
|
246
|
-
}
|
|
247
|
-
.m-put {
|
|
248
|
-
background: var(--es-method-put);
|
|
249
|
-
}
|
|
250
|
-
.m-patch {
|
|
251
|
-
background: var(--es-method-patch);
|
|
252
|
-
}
|
|
253
|
-
.m-delete {
|
|
254
|
-
background: var(--es-method-delete);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.path {
|
|
258
|
-
font-family: var(--es-mono);
|
|
259
|
-
font-size: var(--sl-text-sm);
|
|
260
|
-
color: var(--es-text);
|
|
261
|
-
background: none;
|
|
262
|
-
border: none;
|
|
263
|
-
padding: 0;
|
|
264
|
-
overflow: hidden;
|
|
265
|
-
text-overflow: ellipsis;
|
|
266
|
-
white-space: nowrap;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.cell-summary {
|
|
270
|
-
color: var(--es-text-soft);
|
|
271
|
-
font-size: var(--sl-text-sm);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
.coverage {
|
|
275
|
-
display: inline-flex;
|
|
276
|
-
align-items: center;
|
|
277
|
-
gap: 0.4rem;
|
|
278
|
-
font-size: var(--sl-text-xs);
|
|
279
|
-
font-weight: 600;
|
|
280
|
-
padding: 0.2rem 0.6rem;
|
|
281
|
-
border-radius: var(--es-radius-pill);
|
|
282
|
-
border: 1px solid currentcolor;
|
|
283
|
-
white-space: nowrap;
|
|
284
|
-
}
|
|
285
|
-
.coverage__dot {
|
|
286
|
-
width: 0.5rem;
|
|
287
|
-
height: 0.5rem;
|
|
288
|
-
border-radius: 50%;
|
|
289
|
-
background: currentcolor;
|
|
290
|
-
}
|
|
291
|
-
.coverage--covered {
|
|
292
|
-
color: var(--es-passed);
|
|
293
|
-
background: var(--es-passed-soft);
|
|
294
|
-
}
|
|
295
|
-
.coverage--failing {
|
|
296
|
-
color: var(--es-failed);
|
|
297
|
-
background: var(--es-failed-soft);
|
|
298
|
-
}
|
|
299
|
-
.coverage--uncovered {
|
|
300
|
-
color: var(--es-skipped);
|
|
301
|
-
background: var(--es-skipped-soft);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
.cell-stories {
|
|
305
|
-
display: flex;
|
|
306
|
-
flex-wrap: wrap;
|
|
307
|
-
gap: 0.35rem;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.story-chip {
|
|
311
|
-
display: inline-flex;
|
|
312
|
-
align-items: center;
|
|
313
|
-
gap: 0.35rem;
|
|
314
|
-
font-size: var(--sl-text-xs);
|
|
315
|
-
text-decoration: none;
|
|
316
|
-
color: var(--es-text-soft);
|
|
317
|
-
padding: 0.2rem 0.5rem;
|
|
318
|
-
border: 1px solid var(--es-line);
|
|
319
|
-
border-radius: var(--es-radius-pill);
|
|
320
|
-
background: var(--es-surface);
|
|
321
|
-
transition: border-color 120ms ease, color 120ms ease;
|
|
322
|
-
}
|
|
323
|
-
.story-chip:hover {
|
|
324
|
-
border-color: var(--es-accent);
|
|
325
|
-
color: var(--es-text);
|
|
326
|
-
}
|
|
327
|
-
.story-chip:focus-visible {
|
|
328
|
-
outline: 2px solid var(--es-accent);
|
|
329
|
-
outline-offset: 2px;
|
|
330
|
-
}
|
|
331
|
-
.story-chip__dot {
|
|
332
|
-
width: 0.45rem;
|
|
333
|
-
height: 0.45rem;
|
|
334
|
-
border-radius: 50%;
|
|
335
|
-
background: var(--chip-dot, var(--es-text-muted));
|
|
336
|
-
}
|
|
337
|
-
.story-chip--passed {
|
|
338
|
-
--chip-dot: var(--es-passed);
|
|
339
|
-
}
|
|
340
|
-
.story-chip--failed {
|
|
341
|
-
--chip-dot: var(--es-failed);
|
|
342
|
-
}
|
|
343
|
-
.story-chip--skipped {
|
|
344
|
-
--chip-dot: var(--es-skipped);
|
|
345
|
-
}
|
|
346
|
-
.story-chip--pending {
|
|
347
|
-
--chip-dot: var(--es-pending);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.no-stories {
|
|
351
|
-
color: var(--es-text-muted);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
@media (max-width: 720px) {
|
|
355
|
-
.api-ops__row {
|
|
356
|
-
grid-template-columns: 1fr;
|
|
357
|
-
gap: 0.5rem;
|
|
358
|
-
}
|
|
359
|
-
.api-ops__row--head {
|
|
360
|
-
display: none;
|
|
361
|
-
}
|
|
362
|
-
.cell-summary {
|
|
363
|
-
color: var(--es-text-muted);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
</style>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* <Checklist> wraps a set of <VerifiedStep> items into a clean runbook list.
|
|
4
|
-
*/
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
<ul class="checklist"><slot /></ul>
|
|
8
|
-
|
|
9
|
-
<style>
|
|
10
|
-
.checklist {
|
|
11
|
-
margin: 1rem 0;
|
|
12
|
-
padding: 0;
|
|
13
|
-
border-top: 1px solid var(--sl-color-gray-6);
|
|
14
|
-
}
|
|
15
|
-
</style>
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* <HealthDashboard /> — a living landing widget.
|
|
4
|
-
*
|
|
5
|
-
* Reads the latest run at build time and shows pass rate, counts, freshness,
|
|
6
|
-
* and any failing stories. Drop it at the top of the home page so the first
|
|
7
|
-
* thing a stakeholder sees is whether the documented system is green.
|
|
8
|
-
*/
|
|
9
|
-
import { report } from "../lib/config";
|
|
10
|
-
import { summarizeHealth, type FailingScenario } from "../lib/report-health";
|
|
11
|
-
|
|
12
|
-
const health = summarizeHealth(report);
|
|
13
|
-
const passPct = Math.round(health.passRate * 100);
|
|
14
|
-
const lastRun = health.lastRunMs
|
|
15
|
-
? new Date(health.lastRunMs).toLocaleString(undefined, {
|
|
16
|
-
dateStyle: "medium",
|
|
17
|
-
timeStyle: "short",
|
|
18
|
-
})
|
|
19
|
-
: null;
|
|
20
|
-
|
|
21
|
-
const overall = health.empty
|
|
22
|
-
? "empty"
|
|
23
|
-
: health.failed > 0
|
|
24
|
-
? "failing"
|
|
25
|
-
: "healthy";
|
|
26
|
-
|
|
27
|
-
const metrics: Array<{ label: string; value: string | number }> = [
|
|
28
|
-
{ label: "Pass rate", value: health.empty ? "—" : `${passPct}%` },
|
|
29
|
-
{ label: "Scenarios", value: health.total },
|
|
30
|
-
{ label: "Passed", value: health.passed },
|
|
31
|
-
{ label: "Failed", value: health.failed },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
const failing: FailingScenario[] = health.failing;
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
<section class:list={["health", `health--${overall}`]} aria-label="Test health dashboard">
|
|
38
|
-
<header class="health__head">
|
|
39
|
-
<span class="health__dot" aria-hidden="true"></span>
|
|
40
|
-
<strong class="health__title">
|
|
41
|
-
{health.empty ? "No test run yet" : health.failed > 0 ? "Attention needed" : "All systems documented & passing"}
|
|
42
|
-
</strong>
|
|
43
|
-
{lastRun && <span class="health__meta">last run {lastRun}</span>}
|
|
44
|
-
</header>
|
|
45
|
-
|
|
46
|
-
<div class="health__metrics">
|
|
47
|
-
{
|
|
48
|
-
metrics.map((m) => (
|
|
49
|
-
<div class="health__metric">
|
|
50
|
-
<span class="health__metric-value">{m.value}</span>
|
|
51
|
-
<span class="health__metric-label">{m.label}</span>
|
|
52
|
-
</div>
|
|
53
|
-
))
|
|
54
|
-
}
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
health.empty && (
|
|
59
|
-
<p class="health__hint">
|
|
60
|
-
Generate a run with <code>executable-stories build-docs reports/raw-run.json --site-dir .</code> to light up this dashboard.
|
|
61
|
-
</p>
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
failing.length > 0 && (
|
|
67
|
-
<details class="health__failing" open>
|
|
68
|
-
<summary>{failing.length} failing {failing.length === 1 ? "story" : "stories"}</summary>
|
|
69
|
-
<ul>
|
|
70
|
-
{failing.map((f) => (
|
|
71
|
-
<li>
|
|
72
|
-
<span class="health__failing-title">{f.title}</span>
|
|
73
|
-
{f.feature && <span class="health__failing-feature"> · {f.feature}</span>}
|
|
74
|
-
</li>
|
|
75
|
-
))}
|
|
76
|
-
</ul>
|
|
77
|
-
</details>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
</section>
|
|
81
|
-
|
|
82
|
-
<style>
|
|
83
|
-
.health {
|
|
84
|
-
--health-accent: var(--sl-color-gray-3);
|
|
85
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
86
|
-
border-radius: 0.75rem;
|
|
87
|
-
padding: 1.25rem 1.5rem;
|
|
88
|
-
margin: 0 0 2rem;
|
|
89
|
-
background: var(--sl-color-gray-7, var(--sl-color-gray-6));
|
|
90
|
-
}
|
|
91
|
-
.health--healthy {
|
|
92
|
-
--health-accent: #16a34a;
|
|
93
|
-
}
|
|
94
|
-
.health--failing {
|
|
95
|
-
--health-accent: #dc2626;
|
|
96
|
-
}
|
|
97
|
-
.health--empty {
|
|
98
|
-
--health-accent: #d97706;
|
|
99
|
-
}
|
|
100
|
-
.health__head {
|
|
101
|
-
display: flex;
|
|
102
|
-
align-items: center;
|
|
103
|
-
gap: 0.6rem;
|
|
104
|
-
margin-bottom: 1rem;
|
|
105
|
-
}
|
|
106
|
-
.health__dot {
|
|
107
|
-
width: 0.7rem;
|
|
108
|
-
height: 0.7rem;
|
|
109
|
-
border-radius: 999px;
|
|
110
|
-
background: var(--health-accent);
|
|
111
|
-
flex: none;
|
|
112
|
-
}
|
|
113
|
-
.health__title {
|
|
114
|
-
font-size: var(--sl-text-lg);
|
|
115
|
-
}
|
|
116
|
-
.health__meta {
|
|
117
|
-
margin-left: auto;
|
|
118
|
-
color: var(--sl-color-gray-3);
|
|
119
|
-
font-size: var(--sl-text-xs);
|
|
120
|
-
}
|
|
121
|
-
.health__metrics {
|
|
122
|
-
display: grid;
|
|
123
|
-
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
124
|
-
gap: 0.75rem;
|
|
125
|
-
}
|
|
126
|
-
.health__metric {
|
|
127
|
-
border: 1px solid var(--sl-color-gray-5);
|
|
128
|
-
border-radius: 0.5rem;
|
|
129
|
-
padding: 0.75rem;
|
|
130
|
-
text-align: center;
|
|
131
|
-
background: var(--sl-color-black, transparent);
|
|
132
|
-
}
|
|
133
|
-
.health__metric-value {
|
|
134
|
-
display: block;
|
|
135
|
-
font-size: var(--sl-text-2xl);
|
|
136
|
-
font-weight: 700;
|
|
137
|
-
line-height: 1;
|
|
138
|
-
}
|
|
139
|
-
.health__metric-label {
|
|
140
|
-
display: block;
|
|
141
|
-
margin-top: 0.35rem;
|
|
142
|
-
font-size: var(--sl-text-xs);
|
|
143
|
-
color: var(--sl-color-gray-3);
|
|
144
|
-
}
|
|
145
|
-
.health__hint {
|
|
146
|
-
margin: 1rem 0 0;
|
|
147
|
-
font-size: var(--sl-text-sm);
|
|
148
|
-
color: var(--sl-color-gray-2);
|
|
149
|
-
}
|
|
150
|
-
.health__failing {
|
|
151
|
-
margin-top: 1rem;
|
|
152
|
-
}
|
|
153
|
-
.health__failing > summary {
|
|
154
|
-
cursor: pointer;
|
|
155
|
-
color: #dc2626;
|
|
156
|
-
font-weight: 600;
|
|
157
|
-
}
|
|
158
|
-
.health__failing ul {
|
|
159
|
-
margin: 0.5rem 0 0;
|
|
160
|
-
padding-left: 1.1rem;
|
|
161
|
-
}
|
|
162
|
-
.health__failing-feature {
|
|
163
|
-
color: var(--sl-color-gray-3);
|
|
164
|
-
font-size: var(--sl-text-sm);
|
|
165
|
-
}
|
|
166
|
-
@media (max-width: 640px) {
|
|
167
|
-
.health__metrics {
|
|
168
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
</style>
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* Starlight PageTitle override.
|
|
4
|
-
*
|
|
5
|
-
* Renders the default page title, then — if the page declares `verifiedBy` in
|
|
6
|
-
* its frontmatter — a live verification badge underneath it. Any page (ADR,
|
|
7
|
-
* runbook, hand-written prose) gets the badge for free, no component import.
|
|
8
|
-
*/
|
|
9
|
-
import Default from '@astrojs/starlight/components/PageTitle.astro';
|
|
10
|
-
import { report } from '../lib/config';
|
|
11
|
-
import { hasScenarioId } from '../lib/verification';
|
|
12
|
-
import VerifiedBy from './VerifiedBy.astro';
|
|
13
|
-
|
|
14
|
-
const verifiedBy = Astro.locals.starlightRoute?.entry?.data?.verifiedBy;
|
|
15
|
-
const scenarioId = Astro.locals.starlightRoute?.entry?.data?.scenarioId;
|
|
16
|
-
const hasVerifiedBy = Object.prototype.hasOwnProperty.call(
|
|
17
|
-
Astro.locals.starlightRoute?.entry?.data ?? {},
|
|
18
|
-
"verifiedBy",
|
|
19
|
-
);
|
|
20
|
-
const staleScenarioNote =
|
|
21
|
-
typeof scenarioId === "string" && scenarioId.length > 0 && !hasScenarioId(report, scenarioId);
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
<Default><slot /></Default>
|
|
25
|
-
{hasVerifiedBy && <VerifiedBy refs={verifiedBy ?? []} />}
|
|
26
|
-
{
|
|
27
|
-
staleScenarioNote && (
|
|
28
|
-
<p class="scenario-note-stale">
|
|
29
|
-
Stale note: scenario <code>{scenarioId}</code> is not present in the latest test run.
|
|
30
|
-
</p>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
<style>
|
|
35
|
-
.scenario-note-stale {
|
|
36
|
-
margin: -0.75rem 0 1.75rem;
|
|
37
|
-
padding: 0.65rem 0.9rem;
|
|
38
|
-
border: 1px solid var(--es-line);
|
|
39
|
-
border-left: 3px solid var(--es-unverified);
|
|
40
|
-
border-radius: var(--es-radius);
|
|
41
|
-
background: var(--es-skipped-soft);
|
|
42
|
-
color: var(--es-text);
|
|
43
|
-
font-size: var(--sl-text-xs);
|
|
44
|
-
font-weight: 600;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.scenario-note-stale code {
|
|
48
|
-
background: var(--es-bg);
|
|
49
|
-
border: 1px solid var(--es-line);
|
|
50
|
-
border-radius: 0.25rem;
|
|
51
|
-
padding: 0.05rem 0.35rem;
|
|
52
|
-
}
|
|
53
|
-
</style>
|