@variance-authority/storybook-collector 0.13.0 → 0.15.0
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/CHANGELOG.md +341 -0
- package/README.md +27 -21
- package/dist/contract.d.ts +9 -6
- package/dist/contract.js.map +1 -1
- package/dist/execution.d.ts +1 -20
- package/dist/execution.js +0 -7
- package/dist/execution.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +18 -2
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +20 -0
- package/dist/options.js.map +1 -1
- package/dist/page-agent-entry.js +2 -0
- package/dist/page-agent-entry.js.map +1 -1
- package/dist/page-agent.bundle.js +1547 -1095
- package/dist/page-agent.d.ts +11 -2
- package/dist/page-agent.js +7 -3
- package/dist/page-agent.js.map +1 -1
- package/dist/read.d.ts +2 -1
- package/dist/read.js +6 -1
- package/dist/read.js.map +1 -1
- package/dist/source.d.ts +17 -6
- package/dist/source.js +3 -2
- package/dist/source.js.map +1 -1
- package/dist/world.d.ts +13 -3
- package/dist/world.js +4 -12
- package/dist/world.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,346 @@
|
|
|
1
1
|
# @variance-authority/storybook-collector
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1a0b845: A subject is read with nothing hovered, nothing focused, motion reduced and `Date` fixed
|
|
8
|
+
|
|
9
|
+
Every layout and raster baseline made before this release is compared under a
|
|
10
|
+
moved recipe once; a semantic baseline, read in jsdom, does not move. The
|
|
11
|
+
default recipes gained tricks, so the stabilization digest every reading records
|
|
12
|
+
has changed, and each subject names that recipe as the cause of whatever it
|
|
13
|
+
reports. Review the changes and adopt them with `variance accept --all`, as for
|
|
14
|
+
any recipe change.
|
|
15
|
+
|
|
16
|
+
The standard collection recipe now also holds:
|
|
17
|
+
|
|
18
|
+
- `no-hover`: the pointer is moved to `(-1, -1)`, off the page, once per page
|
|
19
|
+
before it loads. On the `variance` fixture's page it is moved again before
|
|
20
|
+
every observation, a frame each, and not put back: a menu a script opened on
|
|
21
|
+
hover is closed for your test's next step.
|
|
22
|
+
- `no-focus`: whatever holds focus is blurred before the subject is read,
|
|
23
|
+
including an element inside a shadow root. On a page your test or agent goes
|
|
24
|
+
on using, focus goes back to that element once the reading and any screenshot
|
|
25
|
+
of it are taken, so a test types where it left off. The blur still ran the
|
|
26
|
+
page's blur handlers, and what they did, such as a validate-on-blur error,
|
|
27
|
+
stays.
|
|
28
|
+
- `reduced-motion`: the browser reports `prefers-reduced-motion: reduce`, which
|
|
29
|
+
stops the script animations of a page that honours it, and the reading
|
|
30
|
+
declares the same preference among the conditions its `@media` rules are
|
|
31
|
+
resolved against. A `prefers-reduced-motion` condition you declare through
|
|
32
|
+
`createHarness`'s `features` wins, and the browser is asked for that value;
|
|
33
|
+
a declared `no-preference` is not recorded as `reduced-motion`. Rules under
|
|
34
|
+
`@media (prefers-reduced-motion: no-preference)` are not read by default;
|
|
35
|
+
leave `reduced-motion` out to read them.
|
|
36
|
+
- `freeze-clock`: `Date` reads one instant. A script added before the page's
|
|
37
|
+
own replaces `new Date()`, `Date()` and `Date.now()`, and gives the same
|
|
38
|
+
instant to `Intl.DateTimeFormat`'s `format()` and `formatToParts()` called
|
|
39
|
+
without a date. Timers, animation frames and `performance.now()` are the
|
|
40
|
+
browser's own, so a page that settles on a timer still settles. No Playwright
|
|
41
|
+
clock is installed, so `page.clock` stays yours, and a clock your test
|
|
42
|
+
installs replaces the fixed `Date`; the reading still records `freeze-clock`,
|
|
43
|
+
so leave it out of the list when your test owns the clock. `Temporal.Now` and
|
|
44
|
+
`Date` inside a worker are not fixed. The default instant is in the past, and
|
|
45
|
+
the browser's cookie store keeps real time: a cookie your page's script sets
|
|
46
|
+
with an expiry counted from `Date.now()` is already expired and is not
|
|
47
|
+
stored, so a dismissed consent banner can come back after the next
|
|
48
|
+
navigation. Set `varianceNow` or `now` near today, or leave `freeze-clock`
|
|
49
|
+
out.
|
|
50
|
+
|
|
51
|
+
The render and layout recipes gain `no-focus` alone, because a painted document
|
|
52
|
+
still honours a collected `autofocus`. The other three have nothing to act on
|
|
53
|
+
there: the date the page printed is already text in the document, and its
|
|
54
|
+
`@media` rules were resolved when it was read. A `<script>` inside a subject
|
|
55
|
+
root is carried into the render document and runs there under the real clock.
|
|
56
|
+
Every document is painted under the render recipe, so a reading taken without
|
|
57
|
+
`no-focus` is still painted blurred.
|
|
58
|
+
|
|
59
|
+
The instant defaults to `DEFAULT_NOW`, `2024-06-15T12:00:00.000Z`, exported
|
|
60
|
+
from `@variance-authority/core/format`. `routeCollector` and
|
|
61
|
+
`storybookCollector` take a `now` option to choose another. It must be an ISO
|
|
62
|
+
8601 instant with its zone; anything else is refused before a browser opens.
|
|
63
|
+
An instant other than the default is spelled in the clock's id,
|
|
64
|
+
`freeze-clock@<instant>`, so it is part of the environment key, and a baseline taken at one instant is compared
|
|
65
|
+
with a run at another with the moved instant named as the cause.
|
|
66
|
+
|
|
67
|
+
To read a subject hovered, focused, animated or at the real time, pass your own
|
|
68
|
+
`stabilize` list without that trick: `DEFAULT_STABILIZE`, the ids of the
|
|
69
|
+
default recipe, filtered, as in
|
|
70
|
+
`DEFAULT_STABILIZE.filter((id) => id !== 'freeze-clock')`. It is exported from
|
|
71
|
+
`@variance-authority/core/format`, `@variance-authority/route-collector`,
|
|
72
|
+
`@variance-authority/storybook-collector` and
|
|
73
|
+
`@variance-authority/playwright-test`. `storybookCollector` and `observe` in
|
|
74
|
+
`@variance-authority/playwright-test` now take `stabilize`, as `routeCollector`
|
|
75
|
+
already did.
|
|
76
|
+
|
|
77
|
+
A trick needs a browser driver to apply it, and is recorded only where one did.
|
|
78
|
+
The collectors apply all four. So does the `variance` fixture in
|
|
79
|
+
`@variance-authority/playwright-test`: it sets `reduced-motion` and
|
|
80
|
+
`freeze-clock` on the `page` it hands your test, before the test body runs, and
|
|
81
|
+
takes the pointer off the page and releases focus before every observation,
|
|
82
|
+
giving focus back after it. Two new fixture options, `varianceStabilize` and
|
|
83
|
+
`varianceNow`, choose what it sets and the instant, and are set with
|
|
84
|
+
`test.use`; `varianceDriven` holds the ids it set. A fixed `Date` stops code
|
|
85
|
+
that times itself with `Date.now()`: a debounce or a throttle, such as lodash's
|
|
86
|
+
`debounce`, sees no time pass and never fires, so a test that types into a
|
|
87
|
+
debounced search box can stop getting results after this upgrade. A loop that
|
|
88
|
+
waits for `Date.now()` to pass a mark hangs the page, an animation timed by
|
|
89
|
+
`Date`, such as jQuery's `animate`, never calls its `complete`, and a client
|
|
90
|
+
that checks a token's `nbf` or `iat` against `Date.now()` can refuse a token
|
|
91
|
+
issued today. None of these failures names the clock. Leave
|
|
92
|
+
`freeze-clock` out for such a test, and for one that sets `page.clock` itself:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { DEFAULT_STABILIZE } from '@variance-authority/playwright-test';
|
|
96
|
+
|
|
97
|
+
test.use({ varianceStabilize: DEFAULT_STABILIZE.filter((id) => id !== 'freeze-clock') });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
On a page the fixture never handed your test,
|
|
101
|
+
such as one opened from your own browser context, call the new
|
|
102
|
+
`driveBeforeTest(page)` before `page.goto()` to set the same two; `observe` and
|
|
103
|
+
`createVariance` there apply `no-hover` and `no-focus`.
|
|
104
|
+
`@variance-authority/vitest-browser` reads from inside your page and has no
|
|
105
|
+
driver; `@variance-authority/presentation` reads through the Playwright `Page`
|
|
106
|
+
you hand it, a test's or an agent's live browser, and does not drive it. Both
|
|
107
|
+
apply `no-focus` only, giving focus back once the reading is taken. A trick left out is absent from the
|
|
108
|
+
reading's ids and digest, as a trick a tier cannot reach already is.
|
|
109
|
+
`stabilizeForObservation` in `@variance-authority/dom` takes `driven`, the ids
|
|
110
|
+
your driver applied, and returns `refocus()`, which gives focus back to the
|
|
111
|
+
element the reading released. `stabilizeAsRequested` takes the recipe by id, as
|
|
112
|
+
a page agent receives it. `createHarness` in `@variance-authority/playwright`
|
|
113
|
+
takes a `stabilize` recipe to drive its page before the first navigation, and
|
|
114
|
+
reports the ids it applied as `driven`. `createObservedHarness` opens one with
|
|
115
|
+
`observeNetwork` started before its first navigation, as both collectors do.
|
|
116
|
+
|
|
117
|
+
`createPlaywrightRenderer` no longer couples `no-focus` to `waitForFonts`:
|
|
118
|
+
`waitForFonts: false` turns off the waits alone.
|
|
119
|
+
|
|
120
|
+
`@variance-authority/core/format` exports `DEFAULT_STABILIZE`, `DEFAULT_NOW`,
|
|
121
|
+
`driveBeforeLoad` and `driveBeforeRead`, which apply the driver tricks to a
|
|
122
|
+
Playwright `Page`, `forDriver`, which drops the ones a host did not apply,
|
|
123
|
+
`featuresOf`, which declares the motion preference a recipe drove, and
|
|
124
|
+
`recipeToDrive`, which resolves a `stabilize` list and checks `now` before a
|
|
125
|
+
browser opens.
|
|
126
|
+
- d432339: A page reads its clock in UTC and formats for en-US, not for the host
|
|
127
|
+
|
|
128
|
+
The harness the route and Storybook collectors open, and the pages the renderer
|
|
129
|
+
paints, took their timezone and locale from the machine. A date the page
|
|
130
|
+
formatted read one way on a laptop in Sydney and another on a runner in
|
|
131
|
+
Virginia, from one build, and the two compared against one baseline as
|
|
132
|
+
`changed`.
|
|
133
|
+
|
|
134
|
+
Both are pinned: `timezoneId` to `UTC` and `locale` to `en-US`. Declare either
|
|
135
|
+
in `variance.config.json` (`"timezoneId": "Europe/Berlin"`, `"locale":
|
|
136
|
+
"de-DE"`) and the collectors and the renderer pin that instead; an unknown
|
|
137
|
+
timezone or a malformed language tag is refused by its field.
|
|
138
|
+
`createHarness` and `createPlaywrightRenderer` take the same two options.
|
|
139
|
+
`@variance-authority/core/format` exports their type, `PageRegion`, and
|
|
140
|
+
`@variance-authority/playwright` exports `pinnedRegion`, which fills in `UTC`
|
|
141
|
+
and `en-US` for a field left out.
|
|
142
|
+
|
|
143
|
+
Both are part of the environment key, and the renderer's pair is part of
|
|
144
|
+
`RenderIdentity.rasterization`. A capture read in another timezone or language
|
|
145
|
+
is another environment, and an image painted in one is compared with a
|
|
146
|
+
baseline painted in another, with `rasterization` named as the cause of
|
|
147
|
+
whatever moved.
|
|
148
|
+
|
|
149
|
+
`@variance-authority/playwright-test` paints each test in the timezone and
|
|
150
|
+
locale its page runs in: whatever the Playwright project or `test.use` sets.
|
|
151
|
+
When neither sets a `timezoneId`, `varianceFixtures` opens the page in `UTC`
|
|
152
|
+
rather than the host's, so a laptop and a runner in UTC read it under one key;
|
|
153
|
+
Playwright Test already opens it in `en-US` when nothing sets a `locale`. That
|
|
154
|
+
reaches every test of a suite extended with `varianceFixtures`, including tests
|
|
155
|
+
that never call `variance`, so a test that asserts a date formatted in your
|
|
156
|
+
machine's timezone reads it in `UTC` after this upgrade; set `timezoneId` in
|
|
157
|
+
`use` to keep your own. A page
|
|
158
|
+
you hand to `createVariance` is read and painted in whatever its own context
|
|
159
|
+
opened it in, which is the host's timezone when that context set none: open the
|
|
160
|
+
context with `timezoneId: 'UTC'` to match. `varianceRenderer` is now a test fixture, chosen
|
|
161
|
+
by the page, and `varianceRenderers` is the worker's set of renderers, one per
|
|
162
|
+
timezone and locale; a project that overrode `varianceRenderer` at worker scope
|
|
163
|
+
overrides it at test scope instead.
|
|
164
|
+
|
|
165
|
+
**Every image painted by the Playwright renderer is compared under a moved
|
|
166
|
+
recipe once after this upgrade**, because its recipe now carries the timezone
|
|
167
|
+
and locale. Each subject names `rasterization` as the cause of what it reports,
|
|
168
|
+
and one whose pixels moved is `changed`:
|
|
169
|
+
|
|
170
|
+
- With `variance run`: the run exits `1` for each `changed` subject. Review
|
|
171
|
+
them in `variance report --format html` and adopt them with
|
|
172
|
+
`variance accept --all`. A rerun reports `unchanged`.
|
|
173
|
+
- With `@variance-authority/playwright-test`: the suite fails each `changed`
|
|
174
|
+
test, and `--update-snapshots=changed` adopts them.
|
|
175
|
+
- With `@variance-authority/vitest-browser`: `vitest --update` adopts them, as
|
|
176
|
+
`variance accept --all` does.
|
|
177
|
+
|
|
178
|
+
`@variance-authority/vitest-browser` reads no `variance.config.json` and takes no
|
|
179
|
+
timezone or locale, so it paints in `UTC` and `en-US` whatever its tab ran in,
|
|
180
|
+
and neither it nor `@variance-authority/unit-test` writes a timezone or locale
|
|
181
|
+
to the environment key.
|
|
182
|
+
- 3e6a370: A run's cases travel in its record
|
|
183
|
+
|
|
184
|
+
The case index, the cases a run replaced and the run that replaced them are
|
|
185
|
+
sections of `coverage.bin`, no longer a `coverage.bin.cases.bin` beside it. The
|
|
186
|
+
one file is landed, layered, seeded, repinned, sharded and shared by the same
|
|
187
|
+
rules and under the same lock as the coverage it was recorded with, so the two
|
|
188
|
+
always answer for the same runs. A share, a seed or a fetch carries the index
|
|
189
|
+
and drops the replaced cases and the run that names them, which belong to the
|
|
190
|
+
machine that ran.
|
|
191
|
+
|
|
192
|
+
A record that carries cases is format 10, and a reader that knows only coverage
|
|
193
|
+
refuses it rather than misreading it. A record without cases keeps format 9. A
|
|
194
|
+
`coverage.bin.cases.bin` left from an earlier run is not read; the next run
|
|
195
|
+
writes its cases into the record.
|
|
196
|
+
|
|
197
|
+
The `executionFile` option is removed from `withTestSelection`, the Jest and
|
|
198
|
+
rstest seams, `startRecording`, the Playwright reporter and the Storybook
|
|
199
|
+
collector, along with the JSON it could write. `decodeExecutionIndex`,
|
|
200
|
+
`readExecutionIndex`, `variance covering --against`, `variance review` and
|
|
201
|
+
`distill --execution` read the index out of a record; JSON stays readable as
|
|
202
|
+
the spelling a foreign tool supplies. `landCaseIndexes` is replaced by
|
|
203
|
+
`landCases`, which returns the sections for the record you write, and
|
|
204
|
+
`lastCaseRunOf` reads the run they name. `caseLayerFiles` and
|
|
205
|
+
`executionIndexBytes`, which named and read the file beside the record, are
|
|
206
|
+
removed. `CaseSections`, `caseSectionsAt`, `caseSectionsOf`, `caseIndexOf`,
|
|
207
|
+
`recordedCases`, `withCaseSections`, `keepsCases` and `sharedRecord` read and
|
|
208
|
+
write the sections.
|
|
209
|
+
- 754d04c: The port a build is served on is no part of a render
|
|
210
|
+
|
|
211
|
+
The route and Storybook collectors serve a build on a free port, and that port
|
|
212
|
+
reached three identities. An asset the page loaded was keyed by its absolute
|
|
213
|
+
URL, a `url()` in a computed style was the absolute URL the engine resolved,
|
|
214
|
+
and the document digest read both. One build served twice therefore read as two
|
|
215
|
+
environment keys and two documents: each run missed the render cache, painted
|
|
216
|
+
again, and read its document as changed against its own baseline.
|
|
217
|
+
|
|
218
|
+
An asset the page's own origin serves is keyed by its path, and the page's
|
|
219
|
+
origin is dropped from a `url()` before it is hashed. An asset another origin
|
|
220
|
+
serves keeps its absolute URL. A capture carries the page's `baseUrl` so core
|
|
221
|
+
knows which origin is the page's, and the renderer resolves a path-keyed asset
|
|
222
|
+
against the document's `baseUrl`. `documentDigest` reads the page's own base
|
|
223
|
+
and resources as paths, so one build served on two ports addresses one render
|
|
224
|
+
and the render cache hits; the document keeps them absolute for the renderer,
|
|
225
|
+
and a resource another origin serves stays absolute in the digest too.
|
|
226
|
+
|
|
227
|
+
A `@variance-authority/unit-test` capture keys its assets the same way, so a
|
|
228
|
+
`@variance-authority/vitest-browser` observation, which captures through it on
|
|
229
|
+
the port the runner had free, keeps the port out of its environment key.
|
|
230
|
+
`readCapture` reads back a capture whose asset is keyed by path, resolving the
|
|
231
|
+
key against the document's `baseUrl` as the renderer does; `assetUrl` in
|
|
232
|
+
`@variance-authority/core/format` is that one resolution.
|
|
233
|
+
|
|
234
|
+
**The document of every page served over `http` moves once after this
|
|
235
|
+
upgrade.** `documentDigest` now reads a document's base and resources without
|
|
236
|
+
the page's origin, so the digest of every document served over `http` or
|
|
237
|
+
`https` changes: every route, every story, every Vitest browser page, and every
|
|
238
|
+
Playwright page opened by URL. A moved document misses the render cache once,
|
|
239
|
+
and its image is still compared with the baseline: a page whose pixels did not
|
|
240
|
+
move reads `unchanged`, and one whose pixels did reads `changed`, which
|
|
241
|
+
`variance accept --all` and Playwright's `--update-snapshots=changed` adopt.
|
|
242
|
+
`RULESET_VERSION` moves from `r1` to `r2`. The ruleset is part of a snapshot's
|
|
243
|
+
environment key, not of the render recipe, so no image names it as a moved
|
|
244
|
+
identity; a snapshot diffed against one recorded under `r1` names the ruleset as its
|
|
245
|
+
environment root.
|
|
246
|
+
|
|
247
|
+
### Patch Changes
|
|
248
|
+
|
|
249
|
+
- 8dbc834: The components a module declares are read from its code. A declaration written
|
|
250
|
+
in a block comment, a JSDoc example or a template literal no longer names a
|
|
251
|
+
component, so a commented-out `function Retired()` no longer makes its file
|
|
252
|
+
the one that declares `Retired`. A name is declared by a statement of the
|
|
253
|
+
module itself: a function, class or binding inside a function body belongs to
|
|
254
|
+
that function and no longer counts. An `export async function Page()`, an
|
|
255
|
+
abstract class, a generator, a second name in `const A = 1, B = 2`, and a name
|
|
256
|
+
that starts with a non-ASCII capital or carries a `$` are now declared too.
|
|
257
|
+
|
|
258
|
+
`variance run`, `variance collect`, the Storybook collector and the route
|
|
259
|
+
collector resolve a component to its `file:line` from the same reading, through
|
|
260
|
+
`indexDeclarations`, which `sense` now exports. What that changes in the index
|
|
261
|
+
they build from `source.dirs`:
|
|
262
|
+
|
|
263
|
+
- The line is the first line of the declaring statement. A decorated class
|
|
264
|
+
sits at its first decorator, or at its `export` when the decorator is written
|
|
265
|
+
above the `export`, and a second name in a multi-line `const` at the line of
|
|
266
|
+
the `const`.
|
|
267
|
+
- `.d.ts` files, and test, spec and story files your own `exclude` lets
|
|
268
|
+
through, declare nothing.
|
|
269
|
+
- A file the parser cannot read declares nothing, where the text scan found
|
|
270
|
+
the components in it. That covers a Flow-annotated `.js` file, and a `.vue`,
|
|
271
|
+
`.svelte` or `.mdx` file you add to a collector's `extensions`, which is
|
|
272
|
+
parsed as TSX and fails. The index built from `source.dirs` or a collector's
|
|
273
|
+
`source` does not say so: the file's components are missing, and nothing
|
|
274
|
+
names the file. Only `sense`'s source index, for a file it records, names the
|
|
275
|
+
parse error in that file's record.
|
|
276
|
+
- Building the index needs `sense`'s native addon, which ships for macOS on
|
|
277
|
+
arm64, Linux on x64 and arm64 with glibc, and Windows on x64. On any other
|
|
278
|
+
machine, including an Intel Mac, an Alpine image and Windows on arm64, a
|
|
279
|
+
configured `source.dirs` fails at its first file and names why the addon did
|
|
280
|
+
not load, where the text scan ran anywhere. The route collector depends on
|
|
281
|
+
`sense` for it.
|
|
282
|
+
|
|
283
|
+
`indexSource` is removed from `@variance-authority/core/attribute`. Build a
|
|
284
|
+
`SourceIndex` with `indexDeclarations` from `@variance-authority/sense`, or
|
|
285
|
+
write one as plain data: a map from component name to `{ file, line, via }`.
|
|
286
|
+
`SourceRef['via']` loses `'declared'`, which nothing produced, and
|
|
287
|
+
`readCapture` in `@variance-authority/unit-test` refuses a capture whose source
|
|
288
|
+
index carries it.
|
|
289
|
+
|
|
290
|
+
The source index format moves to version 18, so an index written before this
|
|
291
|
+
release is rebuilt once instead of keeping the names it read from comments.
|
|
292
|
+
- 1eed290: A form filled in after mount is painted and compared filled in
|
|
293
|
+
|
|
294
|
+
Text a play function or a test typed into an `<input>` or `<textarea>`, the
|
|
295
|
+
option it chose in a `<select>` and the box it ticked now reach the render
|
|
296
|
+
document and the snapshot. Before, the document painted the form as the server
|
|
297
|
+
sent it and the snapshot did not move for typed text or a chosen option. A
|
|
298
|
+
password field's value is carried as one `•` per character, and a file input's
|
|
299
|
+
value is not carried. A form nobody filled in reads exactly as before.
|
|
300
|
+
|
|
301
|
+
This is part of `RULESET_VERSION` `r2`. A subject whose form was filled in
|
|
302
|
+
after the server sent it has a new render document, so after this upgrade its
|
|
303
|
+
image shows the form filled in and reads `changed` against a baseline painted
|
|
304
|
+
empty; `variance accept --all` adopts it. A Playwright test painted its form
|
|
305
|
+
filled in before this change, so it does not move there.
|
|
306
|
+
- ef496ad: READMEs name what the program does
|
|
307
|
+
|
|
308
|
+
The package READMEs, and the `@variance-authority/vantage` and
|
|
309
|
+
`@variance-authority/playwright-test` descriptions, no longer write a test, a
|
|
310
|
+
record or a run as something that says, asks or knows. Each sentence names what
|
|
311
|
+
the program does: a command prints, a record holds a field, a test runs or
|
|
312
|
+
covers. Where a value is filled in from configuration or a default rather than
|
|
313
|
+
recorded, the README says so. Four renamed headings change their anchors:
|
|
314
|
+
`help`'s "Where the declaration is undocumented", `playwright`'s "Where a
|
|
315
|
+
component is declared, read from the engine", `storybook`'s "What a pass sends
|
|
316
|
+
the preview" and `sense`'s "Correct what a file's text declares it imports".
|
|
317
|
+
- 4799688: The checkout says what the probes meant: no build writes module records
|
|
318
|
+
|
|
319
|
+
A probe names the file it was placed on and the digest of that text,
|
|
320
|
+
`path@digest`. When a run is folded, each module the journals name is cut again
|
|
321
|
+
from the file in the checkout. A file whose text still matches is read on its
|
|
322
|
+
own regions; a file that has moved on since the build reads as one whose reach
|
|
323
|
+
is not known, which selects every test that entered it; a file that is gone
|
|
324
|
+
counts the same way. Nothing is written beside the build, and nothing under the
|
|
325
|
+
cache's `test-selection/<key>/<label>/` is read.
|
|
326
|
+
|
|
327
|
+
Breaking:
|
|
328
|
+
|
|
329
|
+
- `testSelectionProbes()` takes no `cacheRoot`. Its `label` names the journey
|
|
330
|
+
head and nothing else.
|
|
331
|
+
- `recordExecution()` takes no `label` and no `heads`.
|
|
332
|
+
- The Jest journeys options (`withJourneyCoverage`) take no `heads`.
|
|
333
|
+
- `jestStore` is removed from `@variance-authority/sense/jest`.
|
|
334
|
+
- `instrumentModule(code, file)` takes no options: call it first, on the file as
|
|
335
|
+
it is on disk.
|
|
336
|
+
- A journal names a module by `path@digest`. A driver that writes its own
|
|
337
|
+
journal builds the id with `moduleId(path, code)` from
|
|
338
|
+
`@variance-authority/sense/journal`.
|
|
339
|
+
|
|
340
|
+
## 0.14.0
|
|
341
|
+
|
|
342
|
+
Lockstep release — nothing in this package changed. Every `@variance-authority/*` package shares one version.
|
|
343
|
+
|
|
3
344
|
## 0.13.0
|
|
4
345
|
|
|
5
346
|
Lockstep release — nothing in this package changed. Every `@variance-authority/*` package shares one version.
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ This is the Storybook adapter, not the `variance` binary. The binary lives in
|
|
|
34
34
|
| Module format | ESM only. Every `@variance-authority/*` package sets `"type": "module"`; `require()` will not load them. |
|
|
35
35
|
| Browser | Chromium, installed through Playwright. The binaries do not arrive with an `npm install`. |
|
|
36
36
|
| Storybook | Exercised against Storybook 10 with `@storybook/react-vite`. Index versions `3`, `4` and `5` are read, and the story root is looked for at `#storybook-root` (Storybook 7 and later) and then `#root` (before it). Storybook 8.3 and later emit `storyFinished`, which this package waits for; it falls back to `storyRendered` when the event is absent. |
|
|
37
|
-
| Builder | Collection reads `index.json` and drives the preview channel, so it
|
|
37
|
+
| Builder | Collection reads `index.json` and drives the preview channel, so it works the same whether Vite or Webpack 5 built the Storybook. Two optional precision features are Vite plugins with no Webpack equivalent, and are named as such where they appear below: `@variance-authority/jsx-source` and the `tests` option. |
|
|
38
38
|
| React | Required for component attribution, the `wiring` band and the Suspense wait. The reading is of the expando `react-dom` writes on host nodes — `__reactFiber$` on React 17 and later, `__reactInternalInstance$` on React 16 — so there is no React version to match and none is imported. The suite runs against React 19. |
|
|
39
39
|
|
|
40
40
|
A Storybook whose renderer is not React still collects: documents, pixels,
|
|
@@ -64,7 +64,7 @@ the build.
|
|
|
64
64
|
|
|
65
65
|
### 2. Write the collector module
|
|
66
66
|
|
|
67
|
-
The collector is the module that
|
|
67
|
+
The collector is the module that defines how the run finds and reads your
|
|
68
68
|
stories. Create it anywhere in your project and default-export the factory.
|
|
69
69
|
|
|
70
70
|
```js
|
|
@@ -146,8 +146,9 @@ full form is:
|
|
|
146
146
|
npx variance accept [--config <path>] <subject>... | --all | --shape <fingerprint>[,...] [--message-file <path> [--message <text>]]
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
Keep `--all` for the first run and for deliberate re-baselines; it
|
|
150
|
-
story nobody looked at from one that changed. After setup, name
|
|
149
|
+
Keep `--all` for the first run and for deliberate re-baselines; it does not
|
|
150
|
+
separate a story nobody looked at from one that changed. After setup, name
|
|
151
|
+
subject ids:
|
|
151
152
|
|
|
152
153
|
```bash
|
|
153
154
|
npx variance accept --config variance.config.json story:checkout--empty story:checkout--one-item
|
|
@@ -207,6 +208,8 @@ Everything below is passed to `storybookCollector(...)` in the module from step
|
|
|
207
208
|
| `source` | Reports should resolve component names to `file:line`. | Omitted; component names remain available. An empty or mistyped scan is refused. |
|
|
208
209
|
| `baseUrl` | Storybook is already running, e.g. `http://localhost:6006`. | Omitted; the directory containing `subjects.index` is served on loopback for the run. |
|
|
209
210
|
| `headless` | You need to watch collection while debugging. | `true`. |
|
|
211
|
+
| `stabilize` | A story is about its hover, focus, motion or the current date, or your preview has its own determinism strategy. | The standard collection recipe, which also takes the pointer off the page, releases focus, asks for reduced motion and fixes `Date`; `[]` records an untouched story. A list replaces the recipe: start from the exported `DEFAULT_STABILIZE` and filter out what you want read, as in `DEFAULT_STABILIZE.filter((id) => id !== 'no-hover')`. |
|
|
212
|
+
| `now` | Your stories print a date and you want one other than the default. | `2024-06-15T12:00:00.000Z`. An ISO 8601 instant with its zone; anything else is refused before a browser opens. |
|
|
210
213
|
| `network` | Asset bytes at stable URLs must participate in render identity. | `true`; set `false` only when asset URLs are already content-addressed. |
|
|
211
214
|
| `hashAssets` | Your asset URLs already contain their own content hash. | `true`. Read only while `network` is on, so GIF freezing and blanking survive it — this, not `network: false`, is the setting for a content-addressed build. |
|
|
212
215
|
| `wiring` | Your preview's renderer is not React. | `true`. Reads props, context, hook cells and keys into a band of its own; turning it off changes no stored digest. |
|
|
@@ -356,7 +359,7 @@ export default {
|
|
|
356
359
|
framework: { name: '@storybook/react-vite', options: {} },
|
|
357
360
|
viteFinal: async (config) => ({
|
|
358
361
|
...config,
|
|
359
|
-
plugins: [...(config.plugins ?? []), testSelectionProbes(
|
|
362
|
+
plugins: [...(config.plugins ?? []), testSelectionProbes()],
|
|
360
363
|
}),
|
|
361
364
|
};
|
|
362
365
|
```
|
|
@@ -367,19 +370,21 @@ import { storybookCollector } from '@variance-authority/storybook-collector';
|
|
|
367
370
|
|
|
368
371
|
export default storybookCollector({
|
|
369
372
|
source: { dirs: ['src'] },
|
|
370
|
-
tests:
|
|
373
|
+
tests: true,
|
|
371
374
|
});
|
|
372
375
|
```
|
|
373
376
|
|
|
374
|
-
`tests` accepts `true` or an options object: `
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
377
|
+
`tests` accepts `true` or an options object: `cacheRoot` and `coverageFile`
|
|
378
|
+
override the repository-keyed cache paths for the coverage layers and the
|
|
379
|
+
coverage index. The preview's probes report each module's file and a digest of
|
|
380
|
+
the text they were placed on, and the run cuts that file again from your
|
|
381
|
+
checkout, so the preview's build writes nothing the run has to find. `suite` names the suite this run is, when the root `variance.config.json`
|
|
378
382
|
declares its suites under `suites`; it is required once any suite is declared,
|
|
379
|
-
and cannot be combined with `coverageFile`. `root` is the directory Storybook
|
|
380
|
-
and a relative `coverageFile`
|
|
381
|
-
path the run records is relative to the repository that
|
|
382
|
-
Without a collector in the page, the run
|
|
383
|
+
and cannot be combined with `coverageFile`. `root` is the directory Storybook
|
|
384
|
+
ran in, the cwd by default: story paths and a relative `coverageFile` are read
|
|
385
|
+
from it, and every path the run records is relative to the repository that
|
|
386
|
+
contains it. Without a collector in the page, the run prints that on stderr and
|
|
387
|
+
records nothing.
|
|
383
388
|
|
|
384
389
|
`mode` is the probe recipe, and it has to be the one the preview was built
|
|
385
390
|
under: a snapshot names the recipe its ordinals were cut by, and a merge
|
|
@@ -387,17 +392,17 @@ discards a layer cut by another one, so a preview probing under `entries` and a
|
|
|
387
392
|
run folding under `presence` would each wipe the other. `preconditions` names
|
|
388
393
|
files whose contents are a precondition of every story's observation — a
|
|
389
394
|
`preview.js` that decides what renders belongs there, because a change to it
|
|
390
|
-
invalidates every reading and no crossing will ever
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
395
|
+
invalidates every reading and no crossing will ever record it. `commit`
|
|
396
|
+
stamps the snapshot with the commit it was taken at, the checkout's `HEAD` by
|
|
397
|
+
default, which is what lets a later run separate a stale layer from a current
|
|
398
|
+
one.
|
|
394
399
|
|
|
395
400
|
A story is its own owner in the recorded index — Storybook is an execution
|
|
396
401
|
surface this tool drives one subject at a time — and a story that did not render
|
|
397
402
|
still contributes its crossings while never justifying a later skip.
|
|
398
403
|
|
|
399
404
|
Every run also writes the execution index: one row per story per region,
|
|
400
|
-
|
|
405
|
+
inside the coverage index. That is the file
|
|
401
406
|
`variance covering` and `@variance-authority/distill` read to answer *which
|
|
402
407
|
stories walk this branch*.
|
|
403
408
|
|
|
@@ -431,8 +436,9 @@ a diff touches.
|
|
|
431
436
|
URL already identifies the bytes.
|
|
432
437
|
- **The browser is missing:** run `npx playwright install chromium`, then
|
|
433
438
|
`npx variance doctor` again.
|
|
434
|
-
- **A rerun reports
|
|
435
|
-
|
|
439
|
+
- **A rerun reports every story `changed`, a moved identity first:** the
|
|
440
|
+
approved image was stored under a different identity than this machine
|
|
441
|
+
produces. `npx variance doctor` lists the
|
|
436
442
|
identities the baseline root stores.
|
|
437
443
|
|
|
438
444
|
## Boundaries
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CallSiteResolver, SourceIndex } from '@variance-authority/core/attribute';
|
|
2
|
-
import type { Diagnostic, RenderDocument, SemanticSnapshot, SubjectRef, Viewport } from '@variance-authority/core/format';
|
|
2
|
+
import type { Diagnostic, PageRegion, RenderDocument, SemanticSnapshot, SubjectRef, Viewport } from '@variance-authority/core/format';
|
|
3
3
|
/**
|
|
4
4
|
* The contract a collector satisfies, restated here rather than imported.
|
|
5
5
|
*
|
|
@@ -10,8 +10,11 @@ import type { Diagnostic, RenderDocument, SemanticSnapshot, SubjectRef, Viewport
|
|
|
10
10
|
* collector in its own tests, so a drift between them fails a build rather than
|
|
11
11
|
* a run.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The subset of a run's configuration this collector reads. A region field it
|
|
15
|
+
* leaves out is pinned to `UTC` or `en-US`, never the host's.
|
|
16
|
+
*/
|
|
17
|
+
export interface CollectorConfig extends PageRegion {
|
|
15
18
|
readonly viewport: Viewport;
|
|
16
19
|
readonly fonts?: readonly string[];
|
|
17
20
|
/**
|
|
@@ -102,9 +105,9 @@ export type Collected = {
|
|
|
102
105
|
* Stabilization tricks applied to the page before this subject was read.
|
|
103
106
|
*
|
|
104
107
|
* Reported so a run can say what it did to somebody else's page. The
|
|
105
|
-
* digest of the same list is in the environment key, which is what
|
|
106
|
-
* differently-stabilized baseline
|
|
107
|
-
* person reads.
|
|
108
|
+
* digest of the same list is in the environment key, which is what names a
|
|
109
|
+
* differently-stabilized baseline as the cause of a change; this is the
|
|
110
|
+
* half a person reads.
|
|
108
111
|
*/
|
|
109
112
|
readonly stabilization?: readonly string[];
|
|
110
113
|
/**
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"","sourcesContent":["import type { CallSiteResolver, SourceIndex } from '@variance-authority/core/attribute';\nimport type {\n Diagnostic,\n RenderDocument,\n SemanticSnapshot,\n SubjectRef,\n Viewport,\n} from '@variance-authority/core/format';\n\n/**\n * The contract a collector satisfies, restated here rather than imported.\n *\n * The CLI declares the same shapes and this package does not depend on it — a\n * surface package pulling in the binary would put a config parser, a docket and\n * six commands into an adopter's dependency tree to describe four values\n * (ADR-0024). What keeps the two copies honest is that the CLI imports *this*\n * collector in its own tests, so a drift between them fails a build rather than\n * a run.\n */\n\n
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"","sourcesContent":["import type { CallSiteResolver, SourceIndex } from '@variance-authority/core/attribute';\nimport type {\n Diagnostic,\n PageRegion,\n RenderDocument,\n SemanticSnapshot,\n SubjectRef,\n Viewport,\n} from '@variance-authority/core/format';\n\n/**\n * The contract a collector satisfies, restated here rather than imported.\n *\n * The CLI declares the same shapes and this package does not depend on it — a\n * surface package pulling in the binary would put a config parser, a docket and\n * six commands into an adopter's dependency tree to describe four values\n * (ADR-0024). What keeps the two copies honest is that the CLI imports *this*\n * collector in its own tests, so a drift between them fails a build rather than\n * a run.\n */\n\n/**\n * The subset of a run's configuration this collector reads. A region field it\n * leaves out is pinned to `UTC` or `en-US`, never the host's.\n */\nexport interface CollectorConfig extends PageRegion {\n readonly viewport: Viewport;\n readonly fonts?: readonly string[];\n /**\n * Subtrees this project excludes, from `config.ignore` (spec 0024).\n *\n * Only the two fields a page needs: a rule id to record the mark under, and a\n * selector to find it with. Everything else about a rule — its reason, its\n * expiry, the bands it narrows to — is decided after collection, where a clock\n * and the whole run's diffs are available and a browser is not.\n */\n readonly ignore?: readonly {\n readonly id: string;\n readonly select?: string;\n /**\n * Subjects the rule names. Read here to decide what to send, and never sent.\n *\n * A page can resolve a selector and cannot know which subject it is one of,\n * so scoping has to happen on this side of the boundary. Without it a rule\n * written for one route marks its element in every subject that renders the\n * same shared layout, and a real regression inside that element is absorbed\n * everywhere — an ignore silencing more than it says, which is the one thing\n * the mechanism must not do.\n */\n readonly subjects?: readonly string[];\n\n /** Tags the subject must carry. Read here to decide what to send, never sent. */\n readonly tags?: readonly string[];\n }[];\n\n /**\n * Images to serve as nothing, from `config.blank`.\n *\n * Handed to the driver rather than to the page, because it is enforced on the\n * wire — before a byte is decoded, and therefore before the layout it would\n * have participated in exists. Not scoped by subject, and it cannot be: a\n * request carries no idea which subject will end up using it, which is the\n * same reason the asset map is per page rather than per subject.\n */\n readonly blank?: readonly {\n readonly id: string;\n readonly url?: string;\n readonly minPixels?: number;\n readonly maxPixels?: number;\n }[];\n\n /**\n * The subjects section of the config, as the operator wrote it.\n *\n * `index` is optional because it belongs to `subjects.kind: \"storybook\"`, and\n * a config naming any other kind still reaches this collector — the kind is\n * chosen in the config and the collector is chosen in the config, and nothing\n * makes the operator pick a matching pair. Typed as always present, the first\n * thing to read it hands `undefined` to `node:path` and the adopter gets a\n * TypeError about an argument instead of a sentence naming the wrong word.\n */\n readonly subjects: { readonly index?: string };\n}\n\nexport interface PlannedSubject {\n readonly subject: SubjectRef;\n readonly viewport?: Viewport;\n\n /**\n * What the subject declares itself to be, from the artifact that produced it.\n *\n * Storybook's built index carries `tags` and does not carry a story's\n * `parameters`, so a tag is the one per-story declaration the index can plan\n * from — and it is the right one anyway: what a subject *is* belongs in its\n * own name, next to it, rather than in a central file repeating every id.\n * How a story is *read* — excluded, resized, read at several widths — comes\n * from its parameters, which `withParameters` lays over this plan from the\n * running preview.\n *\n * Selection lives here; definition lives in the config. A tag is a word a\n * story wears, and what that word *means* is the operator's to write down\n * once, where a typo can be refused by name.\n */\n readonly tags?: readonly string[];\n}\n\nexport interface Plan {\n readonly subjects: readonly PlannedSubject[];\n readonly notObserved: readonly unknown[];\n readonly warnings: readonly string[];\n}\n\nexport type Collected =\n | {\n readonly ok: true;\n readonly document: RenderDocument;\n readonly snapshot?: SemanticSnapshot;\n readonly source?: SourceIndex;\n\n /**\n * Stabilization tricks applied to the page before this subject was read.\n *\n * Reported so a run can say what it did to somebody else's page. The\n * digest of the same list is in the environment key, which is what names a\n * differently-stabilized baseline as the cause of a change; this is the\n * half a person reads.\n */\n readonly stabilization?: readonly string[];\n\n /**\n * What the adapter noticed about the *way* this subject was produced.\n *\n * Not about the picture — the document and the snapshot carry that — but\n * about the reading: a story Storybook finished by reporting it failed, a\n * preview that had to be reloaded to show it, a render that never\n * finished. Each of those leaves a real image that a run can compare, and\n * each of them changes what a person should conclude from a green verdict,\n * so they travel with the subject rather than being dropped at the seam.\n */\n readonly diagnostics?: readonly Diagnostic[];\n }\n | { readonly ok: false; readonly because: string };\n\nexport interface CollectorContext {\n readonly config: CollectorConfig;\n /** Computed by the run from the story index. Returned unchanged. */\n readonly plan?: Plan;\n}\n\nexport interface Collector {\n plan(): Promise<Plan>;\n collect(subject: PlannedSubject): Promise<Collected>;\n\n /**\n * The frames a snapshot carries, spent on demand.\n *\n * Present when the collector drives a browser, because resolving a frame means\n * fetching the module it names and the page is the only place that request is\n * already correct. Absent otherwise, and absence is not a degradation: it means\n * either that nothing captured frames, or that the run has no live page to\n * fetch through — and both are a report without call sites rather than a\n * report that is wrong.\n *\n * Handed to `locateSites` with the few nodes a region or a finding names. The\n * resolver is per collector rather than per subject: the cache is what makes\n * this bounded, and a suite's subjects share their modules.\n */\n readonly callSites?: CallSiteResolver;\n\n /**\n * Collect this subject again, in a world nothing else has touched.\n *\n * The saving this whole tool is built on is that the world is *not* rebuilt\n * between subjects (ADR-0009): one browser, one page, one Storybook, for the\n * length of a run. What it buys is the possibility that story B renders\n * differently because story A ran first — and a comparison cannot tell that\n * apart from a regression, because both arrive as \"the pixels moved\".\n *\n * One clean collection settles it. If the difference is gone with nothing else\n * in the world, the baseline was right and the session moved this subject, and\n * the run reports `order-dependent` rather than asking anybody to accept it.\n *\n * Optional in the contract the CLI declares, because a collector holding a\n * single page open has no clean world to offer and the run says so rather than\n * reading silence as \"nothing leaked\". This collector implements it: a\n * Storybook preview can be opened twice, and the second one costs a browser\n * launch — which is why the run only spends it on subjects it already called\n * `changed`, up to `alone.limit`.\n */\n collectAlone?(subject: PlannedSubject): Promise<Collected>;\n\n /**\n * Another standing world from the same recipe and the same server, for a\n * second worker. Optional in the contract the CLI declares; absent, a run\n * configured with `workers` collects in one world and says so.\n */\n openWorker?(): Promise<Collector>;\n\n /**\n * `costs` is what the run timed for each story, first collection to\n * decision, by subject id; the execution record carries it as each story's\n * duration.\n */\n close(costs?: ReadonlyMap<string, number>): Promise<void>;\n}\n"]}
|
package/dist/execution.d.ts
CHANGED
|
@@ -21,23 +21,12 @@ export interface StoryExecutionOptions {
|
|
|
21
21
|
* read from here, because it is relative to the directory Storybook ran in.
|
|
22
22
|
*/
|
|
23
23
|
readonly root?: string;
|
|
24
|
-
/**
|
|
25
|
-
readonly label?: string;
|
|
26
|
-
/** Where the build wrote its block records. Defaults to the repository's cache. */
|
|
24
|
+
/** Where the coverage layers live. Defaults to the repository's cache. */
|
|
27
25
|
readonly cacheRoot?: string;
|
|
28
26
|
/** Coverage index. Defaults to the repository-keyed cache the runner seams share. */
|
|
29
27
|
readonly coverageFile?: string;
|
|
30
28
|
/** The suite this run is, as the root `variance.config.json` declares it under `suites`. */
|
|
31
29
|
readonly suite?: string;
|
|
32
|
-
/**
|
|
33
|
-
* Where the execution index — which individual story entered which region —
|
|
34
|
-
* goes. Defaults beside the snapshot, as the Vitest seam's does.
|
|
35
|
-
*
|
|
36
|
-
* A story is the case this costs nothing to name: the driver shows one at a
|
|
37
|
-
* time, so the per-case grain a unit runner needs a custom runner for is
|
|
38
|
-
* already here.
|
|
39
|
-
*/
|
|
40
|
-
readonly executionFile?: string;
|
|
41
30
|
/**
|
|
42
31
|
* The probe recipe the preview was built with, matching
|
|
43
32
|
* `testSelectionProbes()`'s `mode`. `presence` when absent, as it is there.
|
|
@@ -55,14 +44,6 @@ export interface StoryExecutionOptions {
|
|
|
55
44
|
* the ones no story declares and nothing enters.
|
|
56
45
|
*/
|
|
57
46
|
readonly preconditions?: readonly string[];
|
|
58
|
-
/**
|
|
59
|
-
* Other builds this same run drove, by the label each instrumented under.
|
|
60
|
-
*
|
|
61
|
-
* A preview and the application behind it are two builds of overlapping
|
|
62
|
-
* source, and a story that reaches both is one observation. Their stores
|
|
63
|
-
* join this recording rather than getting one of their own.
|
|
64
|
-
*/
|
|
65
|
-
readonly heads?: readonly string[];
|
|
66
47
|
/**
|
|
67
48
|
* Where this recording stands. Defaults to the checkout's `HEAD`, which is
|
|
68
49
|
* the answer in every case except a caller that already knows better.
|
package/dist/execution.js
CHANGED
|
@@ -94,7 +94,6 @@ export async function createStoryRecorder(index, options) {
|
|
|
94
94
|
record = await recordExecution({
|
|
95
95
|
root,
|
|
96
96
|
subjects: zip(observed, timedOnce(observed.map((one) => one.owner), costs)),
|
|
97
|
-
...(options.label === undefined ? {} : { label: options.label }),
|
|
98
97
|
...(options.cacheRoot === undefined ? {} : { cacheRoot: options.cacheRoot }),
|
|
99
98
|
// Against `root`, as the runner seams resolve them, never the checkout.
|
|
100
99
|
...(options.coverageFile === undefined
|
|
@@ -105,16 +104,10 @@ export async function createStoryRecorder(index, options) {
|
|
|
105
104
|
...(options.preconditions === undefined
|
|
106
105
|
? {}
|
|
107
106
|
: { preconditions: options.preconditions }),
|
|
108
|
-
...(options.heads === undefined || options.heads.length === 0
|
|
109
|
-
? {}
|
|
110
|
-
: { heads: options.heads }),
|
|
111
107
|
...(options.commit === undefined ? {} : { commit: options.commit }),
|
|
112
108
|
...(cases.length === 0
|
|
113
109
|
? {}
|
|
114
110
|
: { cases: zip(cases.map((one) => one.observed), timedOnce(cases.map((one) => one.subject), costs)) }),
|
|
115
|
-
...(options.executionFile === undefined
|
|
116
|
-
? {}
|
|
117
|
-
: { executionFile: resolve(ran, options.executionFile) }),
|
|
118
111
|
});
|
|
119
112
|
}
|
|
120
113
|
catch (error) {
|