@fusion.dev/helix-cli 0.215.3 → 0.217.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/package.json +7 -7
- package/visual/README.md +29 -0
- package/visual/driver.mjs +50 -8
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-cli",
|
|
3
3
|
"description": "The Helix command line, for a workspace that reaches it through npm, and the visual tests it runs.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.217.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@fusion.dev/helix-templates": "0.
|
|
33
|
+
"@fusion.dev/helix-templates": "0.217.0"
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://fusion.dev",
|
|
36
36
|
"keywords": [
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"visual-regression"
|
|
45
45
|
],
|
|
46
46
|
"optionalDependencies": {
|
|
47
|
-
"@fusion.dev/helix-cli-darwin-arm64": "0.
|
|
48
|
-
"@fusion.dev/helix-cli-darwin-x64": "0.
|
|
49
|
-
"@fusion.dev/helix-cli-linux-arm64": "0.
|
|
50
|
-
"@fusion.dev/helix-cli-linux-x64": "0.
|
|
51
|
-
"@fusion.dev/helix-cli-win32-x64": "0.
|
|
47
|
+
"@fusion.dev/helix-cli-darwin-arm64": "0.217.0",
|
|
48
|
+
"@fusion.dev/helix-cli-darwin-x64": "0.217.0",
|
|
49
|
+
"@fusion.dev/helix-cli-linux-arm64": "0.217.0",
|
|
50
|
+
"@fusion.dev/helix-cli-linux-x64": "0.217.0",
|
|
51
|
+
"@fusion.dev/helix-cli-win32-x64": "0.217.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@anthropic-ai/sdk": ">=0.132",
|
package/visual/README.md
CHANGED
|
@@ -286,6 +286,35 @@ picture is committed.
|
|
|
286
286
|
that differs stops and names what differs, because every picture would
|
|
287
287
|
differ for that reason alone.
|
|
288
288
|
|
|
289
|
+
## Picture a screen in a state
|
|
290
|
+
|
|
291
|
+
A page pictured as it loads shows every menu closed and every field empty. To
|
|
292
|
+
picture it with a menu open, a field filled, or a list scrolled, save the screen
|
|
293
|
+
and keep the file beside the entry's baselines as `<stem>.state.json`, where
|
|
294
|
+
`<stem>` is what the entry's baseline files start with. The run puts it back
|
|
295
|
+
once the page has upgraded and before it waits for the page to hold still.
|
|
296
|
+
|
|
297
|
+
Save a screen from the page itself, in the browser's console or from a test.
|
|
298
|
+
Helix puts its capture on the page only when asked, so a page in production
|
|
299
|
+
hands no script the text in its fields. Ask with `exposeState` from
|
|
300
|
+
`@fusion.dev/helix-core`, or, from a tool that drives a page it did not write,
|
|
301
|
+
set the slot to `true` before the page loads, as the run does:
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
import { exposeState } from '@fusion.dev/helix-core'
|
|
305
|
+
|
|
306
|
+
exposeState()
|
|
307
|
+
const { captureState } = globalThis[Symbol.for('fusion.dev/helix/state')]
|
|
308
|
+
copy(JSON.stringify(captureState(), null, 2))
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The state is put back through each element's own requests, so a page that
|
|
312
|
+
controls a component hears the same events it hears from a reader. An entry
|
|
313
|
+
whose state names an element the page no longer draws, or one the page refuses,
|
|
314
|
+
fails rather than picturing a different screen. A password field, a field whose
|
|
315
|
+
`autocomplete` names a password, a one-time code or a payment card, and anything
|
|
316
|
+
under `snapshot="off"` are never saved.
|
|
317
|
+
|
|
289
318
|
## The report
|
|
290
319
|
|
|
291
320
|
Every `helix visual test` and `helix visual update` writes a report into the config's
|
package/visual/driver.mjs
CHANGED
|
@@ -56,14 +56,15 @@ const STYLE_ID = 'helix-visual-harness'
|
|
|
56
56
|
const DIAGNOSTICS = '__helix-diagnostics'
|
|
57
57
|
|
|
58
58
|
/* **Read and written synchronously, on the descriptors themselves.** Bun
|
|
59
|
-
1.1.43, the runtime Fusion's CI
|
|
60
|
-
standard input until the pipe closes, so a
|
|
61
|
-
request the binary is waiting to have
|
|
62
|
-
returns each line as it arrives, in
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
behind it would never
|
|
59
|
+
1.1.43, the runtime Fusion's CI pinned when this was written, hands a
|
|
60
|
+
script nothing from a piped standard input until the pipe closes, so a
|
|
61
|
+
stream reader would wait for a request the binary is waiting to have
|
|
62
|
+
answered. `readSync` on descriptor 0 returns each line as it arrives, in
|
|
63
|
+
Bun and in Node. A request is read only when the last one is answered,
|
|
64
|
+
which is the one time this has nothing else to do. Answers are written
|
|
65
|
+
with `writeSync` for the same reason: the next thing after an answer is
|
|
66
|
+
that blocking read, and an answer still queued behind it would never
|
|
67
|
+
reach the binary. */
|
|
67
68
|
for (const name of ['debug', 'info', 'log']) {
|
|
68
69
|
console[name] = console.error
|
|
69
70
|
}
|
|
@@ -405,6 +406,43 @@ const codeBlocks = (page, { minimum, selector }) =>
|
|
|
405
406
|
{ minimum, selector },
|
|
406
407
|
)
|
|
407
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Asks every page in `context` for its state tools before the page loads,
|
|
411
|
+
* since Helix puts them on `globalThis` only where something asked for them.
|
|
412
|
+
*/
|
|
413
|
+
const askForState = (context) =>
|
|
414
|
+
context.addInitScript(() => {
|
|
415
|
+
globalThis[Symbol.for('fusion.dev/helix/state')] ??= true
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Puts back the screen an entry's `<stem>.state.json` saved, through the
|
|
420
|
+
* page's own Helix: `restoreState` from `@fusion.dev/helix-core`, which the
|
|
421
|
+
* first copy of Helix to load puts on `globalThis`, since `askForState`
|
|
422
|
+
* asked for it. An entry whose state names
|
|
423
|
+
* an element the page no longer draws, or one the page refused, fails, since
|
|
424
|
+
* its picture would not be of the screen the state describes.
|
|
425
|
+
*/
|
|
426
|
+
const restore = async (page, state) => {
|
|
427
|
+
const report = await page.evaluate(async (saved) => {
|
|
428
|
+
const tools = globalThis[Symbol.for('fusion.dev/helix/state')]
|
|
429
|
+
if (!tools) {
|
|
430
|
+
return { error: 'the page has no Helix to restore its state with' }
|
|
431
|
+
}
|
|
432
|
+
return tools.restoreState(saved)
|
|
433
|
+
}, state)
|
|
434
|
+
if (report.error) {
|
|
435
|
+
throw new Error(report.error)
|
|
436
|
+
}
|
|
437
|
+
const wrong = [
|
|
438
|
+
...report.missed.map((path) => `${path} is not on the page`),
|
|
439
|
+
...report.refused.map((path) => `${path} refused its state`),
|
|
440
|
+
]
|
|
441
|
+
if (wrong.length > 0) {
|
|
442
|
+
throw new Error(`the saved state did not take: ${wrong.join('; ')}`)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
408
446
|
/** One entry, every shot of it from one navigation. */
|
|
409
447
|
const take = async (lane, item, { elements, readyProperty }, progress) => {
|
|
410
448
|
const { page } = lane
|
|
@@ -442,6 +480,9 @@ const take = async (lane, item, { elements, readyProperty }, progress) => {
|
|
|
442
480
|
`nothing defines ${missing.map((tag) => `<${tag}>`).join(', ')}, after ${UPGRADES} seconds of waiting`,
|
|
443
481
|
)
|
|
444
482
|
}
|
|
483
|
+
if (item.state) {
|
|
484
|
+
await restore(page, item.state)
|
|
485
|
+
}
|
|
445
486
|
} else if (shot.harness !== null) {
|
|
446
487
|
await page.evaluate(
|
|
447
488
|
([id, rules]) => {
|
|
@@ -622,6 +663,7 @@ const lanes = async (count) => {
|
|
|
622
663
|
reducedMotion: 'reduce',
|
|
623
664
|
viewport: { height: 800, width: 1280 },
|
|
624
665
|
})
|
|
666
|
+
await askForState(context)
|
|
625
667
|
if (state.network === 'origin') {
|
|
626
668
|
await context.route('**/*', (route) => {
|
|
627
669
|
let origin
|