staysfixed 0.1.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 +61 -0
- package/LICENSE +21 -0
- package/README.md +529 -0
- package/bin/staysfixed.js +18 -0
- package/examples/guards/the-sidebar-still-collapses.js +91 -0
- package/examples/staysfixed.config.electron.js +172 -0
- package/examples/staysfixed.config.web.js +277 -0
- package/package.json +61 -0
- package/src/cli/approve.js +126 -0
- package/src/cli/check.js +73 -0
- package/src/cli/doctor.js +379 -0
- package/src/cli/flake.js +61 -0
- package/src/cli/index.js +519 -0
- package/src/cli/init.js +564 -0
- package/src/cli/mark.js +69 -0
- package/src/cli/status.js +19 -0
- package/src/cli/trace.js +73 -0
- package/src/cli/walk.js +57 -0
- package/src/core/config.js +226 -0
- package/src/core/errors.js +48 -0
- package/src/core/git.js +90 -0
- package/src/core/hash.js +32 -0
- package/src/core/history.js +173 -0
- package/src/core/log.js +144 -0
- package/src/core/paths.js +135 -0
- package/src/drive/browser.js +540 -0
- package/src/drive/cdp.js +382 -0
- package/src/drive/electron.js +326 -0
- package/src/drive/find.js +331 -0
- package/src/drive/launch.js +263 -0
- package/src/drive/page.js +1042 -0
- package/src/freeze/clock.js +213 -0
- package/src/freeze/fonts.js +243 -0
- package/src/freeze/index.js +234 -0
- package/src/freeze/mask.js +187 -0
- package/src/freeze/motion.js +206 -0
- package/src/freeze/network.js +455 -0
- package/src/freeze/random.js +87 -0
- package/src/freeze/settle.js +178 -0
- package/src/guard/api.js +197 -0
- package/src/guard/load.js +324 -0
- package/src/guard/name.js +327 -0
- package/src/guard/run.js +224 -0
- package/src/index.js +61 -0
- package/src/marker/mark.js +260 -0
- package/src/marker/trace.js +293 -0
- package/src/mcp/server.js +377 -0
- package/src/mcp/tools.js +978 -0
- package/src/picture/capture.js +276 -0
- package/src/picture/compare.js +103 -0
- package/src/picture/run.js +284 -0
- package/src/picture/store.js +208 -0
- package/src/report/console.js +540 -0
- package/src/report/html.js +579 -0
- package/src/run.js +614 -0
- package/src/types.js +471 -0
- package/src/walk/run.js +541 -0
package/src/types.js
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stays Fixed — the shared contract.
|
|
3
|
+
*
|
|
4
|
+
* This file holds only JSDoc typedefs. Every module in `src/` types itself against
|
|
5
|
+
* these shapes, so the pieces fit without importing each other's internals.
|
|
6
|
+
*
|
|
7
|
+
* Nothing here runs. It exists so `npm run typecheck` can prove the seams line up.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Config — what a project writes in staysfixed.config.js / .json
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} AppConfig
|
|
16
|
+
* @property {'web'|'electron'} kind What we are opening.
|
|
17
|
+
* @property {string} [url] web: the address to open (also the base for relative screen urls).
|
|
18
|
+
* @property {string} [start] web: shell command that starts the app (optional; we wait for `url`).
|
|
19
|
+
* @property {string} [cwd] Working directory for `start` / `binary`.
|
|
20
|
+
* @property {string} [binary] electron: path to the executable (or a .app bundle).
|
|
21
|
+
* @property {string[]} [args] electron: extra argv.
|
|
22
|
+
* @property {Record<string,string>} [env] Extra environment for the launched process.
|
|
23
|
+
* @property {number} [startTimeoutMs] How long to wait for the app to answer. Default 60000.
|
|
24
|
+
* @property {number} [debugPort] CDP port to use. Default: a free one we pick.
|
|
25
|
+
* @property {string} [attach] Attach to an already-running CDP endpoint instead of launching, e.g. "http://127.0.0.1:9333".
|
|
26
|
+
* @property {string} [browser] web: path to a Chrome/Chromium/Edge binary. Default: found on the system.
|
|
27
|
+
* @property {boolean} [headless] web: run the browser headless. Default true.
|
|
28
|
+
* @property {string} [windowMatch] electron: only drive the window whose title/url contains this.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {object} ViewportConfig
|
|
33
|
+
* @property {number} width CSS pixels. Default 1440.
|
|
34
|
+
* @property {number} height CSS pixels. Default 900.
|
|
35
|
+
* @property {number} [deviceScaleFactor] Default 2 (retina-sharp, still deterministic).
|
|
36
|
+
* @property {boolean} [mobile] Emulate a touch device. Default false.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {object} SettleConfig
|
|
41
|
+
* @property {number} [frames] Consecutive identical frames required. Default 2.
|
|
42
|
+
* @property {number} [intervalMs] Gap between frames. Default 250.
|
|
43
|
+
* @property {number} [timeoutMs] Give up after this. Default 10000.
|
|
44
|
+
* @property {number} [maxDriftPixels] Pixels allowed to differ and still count as "identical". Default 0.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {object} FreezeConfig
|
|
49
|
+
* @property {string|false} [clock] ISO time the app always believes it is. Default '2026-01-01T12:00:00.000Z'. false = leave the clock alone.
|
|
50
|
+
* @property {string} [timezone] IANA zone forced on the page. Default 'UTC'.
|
|
51
|
+
* @property {string} [locale] BCP-47 locale forced on the page. Default 'en-US'.
|
|
52
|
+
* @property {boolean} [motion] Kill animations, transitions, carets, smooth scroll. Default true.
|
|
53
|
+
* @property {'seeded'|'off'} [random] Seed Math.random and crypto randomness. Default 'seeded'.
|
|
54
|
+
* @property {number} [seed] The seed. Default 20260101.
|
|
55
|
+
* @property {boolean} [fonts] Wait for document.fonts.ready and pin text rendering. Default true.
|
|
56
|
+
* @property {'replay'|'block-external'|'live'} [network] How to handle requests. Default 'block-external'.
|
|
57
|
+
* @property {string[]} [networkAllow] Globs always allowed through, even in 'block-external'.
|
|
58
|
+
* @property {SettleConfig} [settle]
|
|
59
|
+
* @property {boolean} [hideScrollbars] Default true.
|
|
60
|
+
* @property {boolean} [hideCaret] Default true.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @typedef {object} ToleranceConfig
|
|
65
|
+
* @property {number} [pixels] Share of pixels allowed to differ, 0..1. Default 0.0005.
|
|
66
|
+
* @property {number} [threshold] Per-pixel colour sensitivity, 0..1 (lower = stricter). Default 0.12.
|
|
67
|
+
* @property {boolean} [antialiasing] Ignore anti-aliasing noise. Default true.
|
|
68
|
+
* @property {number} [maxPixels] Hard cap on differing pixels, overrides `pixels` when set.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A rectangle painted over before comparing, in CSS pixels of the captured page.
|
|
73
|
+
* @typedef {object} MaskRect
|
|
74
|
+
* @property {number} x
|
|
75
|
+
* @property {number} y
|
|
76
|
+
* @property {number} width
|
|
77
|
+
* @property {number} height
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/** @typedef {string|MaskRect} Mask A CSS selector (every match is painted over) or an explicit rectangle. */
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* One declarative step. Exactly one action key should be set.
|
|
84
|
+
* @typedef {object} Step
|
|
85
|
+
* @property {string} [goto] Navigate. Relative paths resolve against app.url.
|
|
86
|
+
* @property {string} [click] CSS selector to click.
|
|
87
|
+
* @property {string} [type] CSS selector to type into (pair with `text`).
|
|
88
|
+
* @property {string} [text] Text for `type`.
|
|
89
|
+
* @property {string} [press] Key to press, e.g. 'Enter', 'Escape'.
|
|
90
|
+
* @property {string} [hover] CSS selector to hover.
|
|
91
|
+
* @property {string} [waitFor] CSS selector to wait for.
|
|
92
|
+
* @property {string} [waitForGone] CSS selector to wait to disappear.
|
|
93
|
+
* @property {number} [wait] Milliseconds to wait (last resort; settle usually beats this).
|
|
94
|
+
* @property {string} [scrollTo] CSS selector to scroll into view.
|
|
95
|
+
* @property {string} [evaluate] JavaScript to run in the page.
|
|
96
|
+
* @property {string} [note] Human note, shown in reports.
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @typedef {object} ScreenConfig
|
|
101
|
+
* @property {string} name File-safe id, e.g. 'sessions-empty'.
|
|
102
|
+
* @property {string} [describe] Plain-language description shown to humans.
|
|
103
|
+
* @property {string} [url] Shorthand for a single `goto` step.
|
|
104
|
+
* @property {Step[]} [steps] Declarative steps run before the shutter.
|
|
105
|
+
* @property {Step[]} [after] Steps run AFTER the picture, to put the app back.
|
|
106
|
+
* Needed when a screen changes something the app SAVES —
|
|
107
|
+
* a reload gives back the screen, not the app's memory.
|
|
108
|
+
* @property {(page: PageApi) => Promise<void>} [do] Or code, when the config is JS.
|
|
109
|
+
* @property {Mask[]} [masks] Extra masks for this screen only.
|
|
110
|
+
* @property {ToleranceConfig} [tolerance] Override tolerance for this screen only.
|
|
111
|
+
* @property {ViewportConfig} [viewport] Override viewport for this screen only.
|
|
112
|
+
* @property {string} [clip] Capture only this element instead of the whole page.
|
|
113
|
+
* @property {boolean} [fullPage] Capture the full scrollable page. Default false.
|
|
114
|
+
* @property {boolean} [skip] Temporarily leave this screen out.
|
|
115
|
+
* @property {FreezeConfig} [freeze] Per-screen freeze overrides.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @typedef {object} WalkConfig
|
|
120
|
+
* @property {string} [describe]
|
|
121
|
+
* @property {ScreenConfig[]} [steps] Screens walked in order before a release. Defaults to `screens`.
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @typedef {object} McpConfig
|
|
126
|
+
* @property {boolean} [allowApprove] Let an agent approve pictures. Default FALSE, on purpose.
|
|
127
|
+
* @property {boolean} [allowMark] Let an agent write known-good markers. Default false.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @typedef {object} StaysFixedConfig
|
|
132
|
+
* @property {AppConfig} app
|
|
133
|
+
* @property {ViewportConfig} [viewport]
|
|
134
|
+
* @property {FreezeConfig} [freeze]
|
|
135
|
+
* @property {ToleranceConfig} [tolerance]
|
|
136
|
+
* @property {Mask[]} [masks] Masks applied to every screen.
|
|
137
|
+
* @property {ScreenConfig[]} [screens]
|
|
138
|
+
* @property {string} [guards] Folder holding guard files. Default '.staysfixed/guards'.
|
|
139
|
+
* @property {WalkConfig} [walk]
|
|
140
|
+
* @property {McpConfig} [mcp]
|
|
141
|
+
* @property {string} [dir] Where state lives. Default '.staysfixed'.
|
|
142
|
+
* @property {number} [flakeLimit] Flakes before a check is condemned. Default 2.
|
|
143
|
+
* @property {number} [retries] Re-captures on a failing screen before calling it a real change. Default 1.
|
|
144
|
+
* @property {number} [concurrency] Screens captured at once. Default 1 (determinism first).
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/** @typedef {StaysFixedConfig & Required<Pick<StaysFixedConfig,'viewport'|'freeze'|'tolerance'|'masks'|'screens'|'guards'|'dir'|'flakeLimit'|'retries'|'concurrency'|'mcp'>>} ResolvedConfig */
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Project — resolved paths and loaded config
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @typedef {object} ProjectPaths
|
|
155
|
+
* @property {string} root Project root (folder holding the config).
|
|
156
|
+
* @property {string} dir Absolute .staysfixed folder.
|
|
157
|
+
* @property {string} approved Approved pictures (committed).
|
|
158
|
+
* @property {string} results Latest run output (ignored by git).
|
|
159
|
+
* @property {string} diffs Diff images for the latest run.
|
|
160
|
+
* @property {string} markers Known-good markers.
|
|
161
|
+
* @property {string} guards Guard files.
|
|
162
|
+
* @property {string} fixtures Recorded network replies.
|
|
163
|
+
* @property {string} historyFile Flake register.
|
|
164
|
+
* @property {string} reportFile Self-contained HTML report.
|
|
165
|
+
* @property {string} configFile The config that was loaded.
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @typedef {object} Project
|
|
170
|
+
* @property {ResolvedConfig} config
|
|
171
|
+
* @property {ProjectPaths} paths
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Driving — CDP, browser, page
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @typedef {object} CdpSession
|
|
180
|
+
* @property {(method: string, params?: Record<string, unknown>, sessionId?: string) => Promise<any>} send
|
|
181
|
+
* @property {(event: string, handler: (params: any, sessionId?: string) => void) => () => void} on
|
|
182
|
+
* @property {() => Promise<void>} close
|
|
183
|
+
* @property {() => boolean} isOpen
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @typedef {object} LaunchedApp
|
|
188
|
+
* @property {CdpSession} cdp
|
|
189
|
+
* @property {PageHandle} page
|
|
190
|
+
* @property {() => Promise<void>} close Stop everything we started. Never touches anything we attached to.
|
|
191
|
+
* @property {string} endpoint The CDP endpoint in use.
|
|
192
|
+
* @property {number|null} pid The process we spawned, if any.
|
|
193
|
+
* @property {'web'|'electron'} kind
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The surface a screen recipe or a guard is handed.
|
|
198
|
+
* @typedef {object} PageApi
|
|
199
|
+
* @property {(url: string) => Promise<void>} goto
|
|
200
|
+
* @property {(selector: string, opts?: {timeoutMs?: number}) => Promise<void>} click
|
|
201
|
+
* @property {(selector: string, text: string) => Promise<void>} type
|
|
202
|
+
* @property {(key: string) => Promise<void>} press
|
|
203
|
+
* @property {(selector: string) => Promise<void>} hover
|
|
204
|
+
* @property {() => Promise<void>} moveMouseAway Park the pointer in the far corner so nothing is left hovered.
|
|
205
|
+
* @property {(selector: string, opts?: {timeoutMs?: number}) => Promise<void>} waitFor
|
|
206
|
+
* @property {(selector: string, opts?: {timeoutMs?: number}) => Promise<void>} waitForGone
|
|
207
|
+
* @property {(selector: string) => Promise<void>} scrollTo
|
|
208
|
+
* @property {(ms: number) => Promise<void>} wait
|
|
209
|
+
* @property {(js: string) => Promise<any>} evaluate
|
|
210
|
+
* @property {(selector: string) => Promise<boolean>} visible
|
|
211
|
+
* @property {(selector: string) => Promise<boolean>} exists
|
|
212
|
+
* @property {(selector: string) => Promise<string>} textOf
|
|
213
|
+
* @property {(selector: string) => Promise<number>} count
|
|
214
|
+
* @property {(selector: string) => Promise<MaskRect|null>} boxOf
|
|
215
|
+
* @property {() => Promise<string>} url
|
|
216
|
+
* @property {() => Promise<string>} title
|
|
217
|
+
* @property {(opts?: CaptureOptions) => Promise<Buffer>} shoot Raw screenshot, no settle, no compare.
|
|
218
|
+
* @property {(v: ViewportConfig) => Promise<void>} setViewport
|
|
219
|
+
* @property {() => string[]} consoleErrors
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @typedef {object} CaptureOptions
|
|
224
|
+
* @property {boolean} [fullPage]
|
|
225
|
+
* @property {string} [clip] Selector to clip to.
|
|
226
|
+
* @property {MaskRect} [rect] Explicit rectangle to clip to.
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Pictures
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @typedef {object} PictureMeta
|
|
235
|
+
* @property {string} name
|
|
236
|
+
* @property {string} [describe]
|
|
237
|
+
* @property {number} width
|
|
238
|
+
* @property {number} height
|
|
239
|
+
* @property {number} deviceScaleFactor
|
|
240
|
+
* @property {string} sha256
|
|
241
|
+
* @property {string} approvedAt ISO timestamp.
|
|
242
|
+
* @property {string} approvedBy git user.name <email>, or 'unknown'.
|
|
243
|
+
* @property {string} [gitSha] Commit the approval was made against.
|
|
244
|
+
* @property {string} tool Stays Fixed version that took it.
|
|
245
|
+
* @property {string} [platform] e.g. 'darwin-arm64'. Pictures are platform-tagged; comparing across platforms warns.
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @typedef {'passed'|'changed'|'new'|'missing'|'failed'|'skipped'|'flaky'} CheckStatus
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @typedef {object} PictureResult
|
|
254
|
+
* @property {string} name
|
|
255
|
+
* @property {string} [describe]
|
|
256
|
+
* @property {CheckStatus} status
|
|
257
|
+
* @property {number} [diffPixels]
|
|
258
|
+
* @property {number} [diffRatio]
|
|
259
|
+
* @property {string} [approvedPath]
|
|
260
|
+
* @property {string} [actualPath]
|
|
261
|
+
* @property {string} [diffPath]
|
|
262
|
+
* @property {string} [message] Plain-language explanation.
|
|
263
|
+
* @property {number} durationMs
|
|
264
|
+
* @property {number} [attempts]
|
|
265
|
+
* @property {string[]} [consoleErrors]
|
|
266
|
+
* @property {{width:number,height:number}} [size]
|
|
267
|
+
* @property {{width:number,height:number}} [approvedSize]
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
// Guards
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @typedef {object} GuardApi
|
|
276
|
+
* @property {PageApi} page Full page control.
|
|
277
|
+
* @property {(path: string) => Promise<void>} open Shorthand for page.goto.
|
|
278
|
+
* @property {(selector: string) => Promise<void>} click
|
|
279
|
+
* @property {(what: string, check: () => unknown | Promise<unknown>) => Promise<void>} expect
|
|
280
|
+
* Assert in plain language: expect('the sidebar is hidden', () => ...).
|
|
281
|
+
* @property {(cmd: string, opts?: {cwd?: string, timeoutMs?: number}) => Promise<{code: number, stdout: string, stderr: string}>} run
|
|
282
|
+
* Run a shell command, for guards that are not about the screen.
|
|
283
|
+
* @property {(file: string) => Promise<string>} read Read a project file.
|
|
284
|
+
* @property {Project} project
|
|
285
|
+
*/
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @typedef {object} Guard
|
|
289
|
+
* @property {string} name Plain language. "the sidebar still collapses".
|
|
290
|
+
* @property {string} [fixed] When the bug was fixed (free text or a date).
|
|
291
|
+
* @property {string} [because] Why this guard exists — the story of the bug.
|
|
292
|
+
* @property {string} [link] Issue / commit / session note.
|
|
293
|
+
* @property {boolean} [skip]
|
|
294
|
+
* @property {number} [timeoutMs] Default 30000.
|
|
295
|
+
* @property {(app: GuardApi) => Promise<void>} run
|
|
296
|
+
* @property {string} [file] Filled in by the loader.
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @typedef {object} GuardResult
|
|
301
|
+
* @property {string} name
|
|
302
|
+
* @property {CheckStatus} status
|
|
303
|
+
* @property {string} [message]
|
|
304
|
+
* @property {string} [failedAt] The plain-language expectation that failed.
|
|
305
|
+
* @property {string} [file]
|
|
306
|
+
* @property {string} [because]
|
|
307
|
+
* @property {number} durationMs
|
|
308
|
+
* @property {number} [attempts]
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Runs, markers, history
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @typedef {object} RunSummary
|
|
317
|
+
* @property {string} id Run id, sortable: '20260829-013245'.
|
|
318
|
+
* @property {string} startedAt
|
|
319
|
+
* @property {number} durationMs
|
|
320
|
+
* @property {PictureResult[]} pictures
|
|
321
|
+
* @property {GuardResult[]} guards
|
|
322
|
+
* @property {{passed:number,changed:number,new:number,failed:number,missing:number,skipped:number}} totals
|
|
323
|
+
* @property {boolean} ok
|
|
324
|
+
* @property {GitInfo} git
|
|
325
|
+
* @property {string} tool
|
|
326
|
+
* @property {string} platform
|
|
327
|
+
* @property {string[]} [condemned] Names of checks that have flaked past the limit.
|
|
328
|
+
*/
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @typedef {object} GitInfo
|
|
332
|
+
* @property {string|null} sha
|
|
333
|
+
* @property {string|null} shortSha
|
|
334
|
+
* @property {string|null} branch
|
|
335
|
+
* @property {boolean} dirty
|
|
336
|
+
* @property {string|null} user
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @typedef {object} Marker
|
|
341
|
+
* @property {string} label 'v0.15.0', 'before-the-store-work', anything.
|
|
342
|
+
* @property {string} at ISO timestamp.
|
|
343
|
+
* @property {string} [note]
|
|
344
|
+
* @property {GitInfo} git
|
|
345
|
+
* @property {Record<string,string>} pictures screen name -> sha256 of the approved picture.
|
|
346
|
+
* @property {Record<string,CheckStatus>} guards
|
|
347
|
+
* @property {string} tool
|
|
348
|
+
* @property {string} platform
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* @typedef {object} HistoryEntry
|
|
353
|
+
* @property {string} name
|
|
354
|
+
* @property {'picture'|'guard'} kind
|
|
355
|
+
* @property {number} runs
|
|
356
|
+
* @property {number} flakes Times it changed its mind without the code changing.
|
|
357
|
+
* @property {CheckStatus[]} recent Last N statuses, newest last.
|
|
358
|
+
* @property {string} [lastFlakeAt]
|
|
359
|
+
* @property {string} [lastFlakeGitSha]
|
|
360
|
+
* @property {boolean} [condemned] Past the flake limit — fix it or delete it.
|
|
361
|
+
*/
|
|
362
|
+
|
|
363
|
+
/** @typedef {Record<string, HistoryEntry>} History */
|
|
364
|
+
|
|
365
|
+
export {};
|
|
366
|
+
|
|
367
|
+
// ---------------------------------------------------------------------------
|
|
368
|
+
// Extra shapes the modules pass between themselves
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* What `createPage` actually returns: the public PageApi plus the plumbing the
|
|
373
|
+
* freeze layer and the capture loop need. Screens and guards only ever see PageApi.
|
|
374
|
+
* @typedef {PageApi & {
|
|
375
|
+
* send: (method: string, params?: Record<string, unknown>) => Promise<any>,
|
|
376
|
+
* on: (event: string, handler: (params: any) => void) => () => void,
|
|
377
|
+
* sessionId: string,
|
|
378
|
+
* targetId: string,
|
|
379
|
+
* addInitScript: (source: string) => Promise<string>,
|
|
380
|
+
* removeInitScript: (id: string) => Promise<void>,
|
|
381
|
+
* insertCss: (css: string) => Promise<string>,
|
|
382
|
+
* removeCss: (id: string) => Promise<void>,
|
|
383
|
+
* baseUrl: string|null,
|
|
384
|
+
* clearConsole: () => void,
|
|
385
|
+
* }} PageHandle
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @typedef {object} FreezeHandle
|
|
390
|
+
* @property {() => Promise<void>} release Undo everything that can be undone.
|
|
391
|
+
* @property {() => FreezeStats} stats
|
|
392
|
+
*/
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* @typedef {object} FreezeStats
|
|
396
|
+
* @property {number} requestsAllowed
|
|
397
|
+
* @property {number} requestsBlocked
|
|
398
|
+
* @property {number} requestsReplayed
|
|
399
|
+
* @property {number} requestsRecorded
|
|
400
|
+
* @property {string[]} blockedUrls
|
|
401
|
+
*/
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @typedef {object} SettleReport
|
|
405
|
+
* @property {boolean} settled
|
|
406
|
+
* @property {number} attempts
|
|
407
|
+
* @property {number} lastDriftPixels
|
|
408
|
+
* @property {number} waitedMs
|
|
409
|
+
*/
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @typedef {object} CaptureReport
|
|
413
|
+
* @property {Buffer} png
|
|
414
|
+
* @property {number} width
|
|
415
|
+
* @property {number} height
|
|
416
|
+
* @property {SettleReport} settle
|
|
417
|
+
* @property {string[]} consoleErrors
|
|
418
|
+
* @property {FreezeStats} [freeze]
|
|
419
|
+
*/
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* @typedef {object} CompareReport
|
|
423
|
+
* @property {boolean} equal
|
|
424
|
+
* @property {number} diffPixels
|
|
425
|
+
* @property {number} diffRatio
|
|
426
|
+
* @property {Buffer|null} diffPng
|
|
427
|
+
* @property {boolean} sizeMismatch
|
|
428
|
+
* @property {{width:number,height:number}} size
|
|
429
|
+
* @property {{width:number,height:number}} approvedSize
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* @typedef {object} WalkStep
|
|
434
|
+
* @property {number} index
|
|
435
|
+
* @property {string} name
|
|
436
|
+
* @property {string} [describe]
|
|
437
|
+
* @property {string} file Absolute path to the photo of this step.
|
|
438
|
+
* @property {string} [url]
|
|
439
|
+
* @property {string} [title]
|
|
440
|
+
* @property {number} durationMs
|
|
441
|
+
* @property {string[]} [consoleErrors]
|
|
442
|
+
* @property {string} [error]
|
|
443
|
+
*/
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* @typedef {object} WalkReport
|
|
447
|
+
* @property {string} id
|
|
448
|
+
* @property {string} dir
|
|
449
|
+
* @property {WalkStep[]} steps
|
|
450
|
+
* @property {boolean} ok
|
|
451
|
+
* @property {GitInfo} git
|
|
452
|
+
* @property {string} [reportFile]
|
|
453
|
+
*/
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* @typedef {object} TraceFinding
|
|
457
|
+
* @property {string} name
|
|
458
|
+
* @property {'unchanged'|'changed'|'unknown'} verdict
|
|
459
|
+
* @property {Marker} [lastGood] Newest marker where this screen looked like it does now.
|
|
460
|
+
* @property {Marker} [firstBad] Oldest marker after that where it did not.
|
|
461
|
+
* @property {{sha: string, shortSha: string, subject: string, author: string, date: string}[]} [commits]
|
|
462
|
+
* @property {string[]} [files]
|
|
463
|
+
* @property {string} [message]
|
|
464
|
+
*/
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* @typedef {object} TraceReport
|
|
468
|
+
* @property {TraceFinding[]} findings
|
|
469
|
+
* @property {number} markersSearched
|
|
470
|
+
* @property {string} [message]
|
|
471
|
+
*/
|