craftdriver 1.6.0 → 1.8.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 +35 -6
- package/README.md +17 -6
- package/dist/cli/agentSession.d.ts +62 -0
- package/dist/cli/agentSession.d.ts.map +1 -0
- package/dist/cli/agentSession.js +100 -0
- package/dist/cli/agentSession.js.map +1 -0
- package/dist/cli/artifactPaths.d.ts +42 -0
- package/dist/cli/artifactPaths.d.ts.map +1 -0
- package/dist/cli/artifactPaths.js +144 -0
- package/dist/cli/artifactPaths.js.map +1 -0
- package/dist/cli/bounds.d.ts +88 -0
- package/dist/cli/bounds.d.ts.map +1 -0
- package/dist/cli/bounds.js +128 -0
- package/dist/cli/bounds.js.map +1 -0
- package/dist/cli/client.d.ts +3 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +24 -8
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +152 -39
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/defaults.d.ts +23 -3
- package/dist/cli/defaults.d.ts.map +1 -1
- package/dist/cli/defaults.js +50 -5
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/dispatcher.d.ts +54 -11
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +851 -54
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +305 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +14 -14
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +328 -180
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/journal.d.ts +153 -0
- package/dist/cli/journal.d.ts.map +1 -0
- package/dist/cli/journal.js +252 -0
- package/dist/cli/journal.js.map +1 -0
- package/dist/cli/lineReader.d.ts +33 -0
- package/dist/cli/lineReader.d.ts.map +1 -0
- package/dist/cli/lineReader.js +66 -0
- package/dist/cli/lineReader.js.map +1 -0
- package/dist/cli/locatorCandidates.d.ts +44 -0
- package/dist/cli/locatorCandidates.d.ts.map +1 -0
- package/dist/cli/locatorCandidates.js +190 -0
- package/dist/cli/locatorCandidates.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +39 -1
- package/dist/cli/mcp/artifacts.d.ts.map +1 -1
- package/dist/cli/mcp/artifacts.js +104 -9
- package/dist/cli/mcp/artifacts.js.map +1 -1
- package/dist/cli/mcp/bounds.d.ts +65 -0
- package/dist/cli/mcp/bounds.d.ts.map +1 -0
- package/dist/cli/mcp/bounds.js +124 -0
- package/dist/cli/mcp/bounds.js.map +1 -0
- package/dist/cli/mcp/params.d.ts +38 -0
- package/dist/cli/mcp/params.d.ts.map +1 -0
- package/dist/cli/mcp/params.js +174 -0
- package/dist/cli/mcp/params.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +94 -16
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +304 -150
- package/dist/cli/mcp/server.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +62 -17
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +373 -158
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/cli/pageSemantics.d.ts +42 -0
- package/dist/cli/pageSemantics.d.ts.map +1 -0
- package/dist/cli/pageSemantics.js +194 -0
- package/dist/cli/pageSemantics.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +6 -18
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +689 -45
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/cli/protocol.d.ts +8 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/sessionRegistry.d.ts +42 -0
- package/dist/cli/sessionRegistry.d.ts.map +1 -0
- package/dist/cli/sessionRegistry.js +99 -0
- package/dist/cli/sessionRegistry.js.map +1 -0
- package/dist/cli/snapshot.d.ts +100 -18
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +248 -102
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/stateStore.d.ts +50 -0
- package/dist/cli/stateStore.d.ts.map +1 -0
- package/dist/cli/stateStore.js +122 -0
- package/dist/cli/stateStore.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bidi/index.d.ts +1 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +2 -2
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +3 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -0
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +14 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +45 -7
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +30 -6
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +190 -45
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +7 -2
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +38 -3
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +196 -30
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +54 -18
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +295 -92
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +3 -3
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +7 -3
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/driver.d.ts +7 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +12 -0
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/errors.d.ts +15 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +15 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/initScript.d.ts +9 -0
- package/dist/lib/initScript.d.ts.map +1 -0
- package/dist/lib/initScript.js +14 -0
- package/dist/lib/initScript.js.map +1 -0
- package/dist/lib/page.d.ts +22 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +48 -0
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/secureFile.d.ts +8 -0
- package/dist/lib/secureFile.d.ts.map +1 -0
- package/dist/lib/secureFile.js +67 -0
- package/dist/lib/secureFile.js.map +1 -0
- package/dist/lib/sessionStateValidation.d.ts +35 -0
- package/dist/lib/sessionStateValidation.d.ts.map +1 -0
- package/dist/lib/sessionStateValidation.js +223 -0
- package/dist/lib/sessionStateValidation.js.map +1 -0
- package/dist/lib/timing.d.ts +8 -0
- package/dist/lib/timing.d.ts.map +1 -1
- package/dist/lib/timing.js +8 -0
- package/dist/lib/timing.js.map +1 -1
- package/dist/lib/tracing.d.ts +2 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +12 -1
- package/dist/lib/tracing.js.map +1 -1
- package/dist/lib/visual/antialias.d.ts +41 -0
- package/dist/lib/visual/antialias.d.ts.map +1 -0
- package/dist/lib/visual/antialias.js +126 -0
- package/dist/lib/visual/antialias.js.map +1 -0
- package/dist/lib/visual/codec.d.ts +24 -0
- package/dist/lib/visual/codec.d.ts.map +1 -0
- package/dist/lib/visual/codec.js +11 -0
- package/dist/lib/visual/codec.js.map +1 -0
- package/dist/lib/visual/compare.d.ts +38 -0
- package/dist/lib/visual/compare.d.ts.map +1 -0
- package/dist/lib/visual/compare.js +209 -0
- package/dist/lib/visual/compare.js.map +1 -0
- package/dist/lib/visual/diff.d.ts +18 -0
- package/dist/lib/visual/diff.d.ts.map +1 -0
- package/dist/lib/visual/diff.js +23 -0
- package/dist/lib/visual/diff.js.map +1 -0
- package/dist/lib/visual/expectScreenshot.d.ts +65 -0
- package/dist/lib/visual/expectScreenshot.d.ts.map +1 -0
- package/dist/lib/visual/expectScreenshot.js +287 -0
- package/dist/lib/visual/expectScreenshot.js.map +1 -0
- package/dist/lib/visual/index.d.ts +10 -0
- package/dist/lib/visual/index.d.ts.map +1 -0
- package/dist/lib/visual/index.js +9 -0
- package/dist/lib/visual/index.js.map +1 -0
- package/dist/lib/visual/pixelMath.d.ts +32 -0
- package/dist/lib/visual/pixelMath.d.ts.map +1 -0
- package/dist/lib/visual/pixelMath.js +70 -0
- package/dist/lib/visual/pixelMath.js.map +1 -0
- package/dist/lib/visual/pngHeader.d.ts +27 -0
- package/dist/lib/visual/pngHeader.d.ts.map +1 -0
- package/dist/lib/visual/pngHeader.js +63 -0
- package/dist/lib/visual/pngHeader.js.map +1 -0
- package/dist/lib/visual/pngjsCodec.d.ts +22 -0
- package/dist/lib/visual/pngjsCodec.d.ts.map +1 -0
- package/dist/lib/visual/pngjsCodec.js +70 -0
- package/dist/lib/visual/pngjsCodec.js.map +1 -0
- package/dist/lib/visual/types.d.ts +89 -0
- package/dist/lib/visual/types.d.ts.map +1 -0
- package/dist/lib/visual/types.js +20 -0
- package/dist/lib/visual/types.js.map +1 -0
- package/dist/lib/visual/visualError.d.ts +32 -0
- package/dist/lib/visual/visualError.d.ts.map +1 -0
- package/dist/lib/visual/visualError.js +59 -0
- package/dist/lib/visual/visualError.js.map +1 -0
- package/dist/lib/wait.d.ts.map +1 -1
- package/dist/lib/wait.js +7 -1
- package/dist/lib/wait.js.map +1 -1
- package/docs/agents.md +56 -61
- package/docs/api-reference.md +16 -1
- package/docs/browser-api.md +24 -4
- package/docs/browser-context.md +18 -12
- package/docs/cli.md +346 -26
- package/docs/error-codes.md +6 -4
- package/docs/getting-started.md +6 -1
- package/docs/index.md +1 -1
- package/docs/mcp.md +129 -62
- package/docs/public/examples/agent-actions.html +48 -0
- package/docs/public/examples/agent-debug.html +86 -0
- package/docs/public/examples/agent-late-mutation.html +18 -0
- package/docs/public/examples/agent-ref-shift.html +33 -0
- package/docs/recipes/login-once-reuse-session.md +32 -4
- package/docs/screenshots.md +15 -6
- package/docs/session-management.md +64 -3
- package/docs/visual-testing.md +254 -0
- package/docs/why-craftdriver.md +4 -3
- package/package.json +5 -1
- package/skills/craftdriver/SKILL.md +59 -75
- package/skills/craftdriver/agents/openai.yaml +4 -0
- package/skills/craftdriver/cheatsheet.md +18 -4
- package/skills/craftdriver/cli.md +194 -68
- package/skills/craftdriver/patterns.md +14 -8
- package/skills/craftdriver/workflow.md +149 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated pngjs adapter — the single place that touches the CommonJS-shaped
|
|
3
|
+
* `pngjs` API. The rest of craftdriver is ESM and works only with the
|
|
4
|
+
* promise-based {@link PngCodec} and normalized {@link DecodedImage} defined in
|
|
5
|
+
* `./codec`; pngjs types never leak past this file.
|
|
6
|
+
*
|
|
7
|
+
* Decode and encode both use pngjs's asynchronous stream APIs (`parse` /
|
|
8
|
+
* `pack`) rather than the synchronous variants, so a large image cannot block
|
|
9
|
+
* the event loop inside a single call. CRC checking is left at pngjs's default
|
|
10
|
+
* (enabled) so corrupt chunks are rejected rather than silently decoded.
|
|
11
|
+
*
|
|
12
|
+
* Maintenance risk (recorded, not hidden): `pngjs@7.0.0` is pinned exactly. It
|
|
13
|
+
* had no reported npm/OSV advisory as of 2026-07-17, but that is a timestamped
|
|
14
|
+
* check, not proof of safety — its last release was 2023 and it publishes no
|
|
15
|
+
* security policy. It relies on Node's built-in `zlib`, so keeping Node patched
|
|
16
|
+
* is part of the posture. CI runs `npm audit --omit=dev`; this seam is kept
|
|
17
|
+
* narrow (see `./codec`) so the codec can be replaced without touching callers
|
|
18
|
+
* if the project goes unmaintained or an advisory lands.
|
|
19
|
+
*/
|
|
20
|
+
import { PNG } from 'pngjs';
|
|
21
|
+
import { CraftdriverError, ErrorCode } from '../errors.js';
|
|
22
|
+
function wrapDecodeError(cause) {
|
|
23
|
+
return new CraftdriverError(ErrorCode.INVALID_ARGUMENT, 'Failed to decode PNG image.', {
|
|
24
|
+
cause,
|
|
25
|
+
hint: 'The buffer is not a valid PNG, or its chunks failed CRC validation.',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decode a compressed PNG buffer to a fresh, tightly-packed 8-bit RGBA view.
|
|
30
|
+
*
|
|
31
|
+
* pngjs always normalizes palette/grayscale/16-bit/interlaced inputs to 8-bit
|
|
32
|
+
* RGBA in `png.data`, but that buffer may be a view into a larger pool. We copy
|
|
33
|
+
* into a right-sized `Uint8Array` so downstream code can rely on
|
|
34
|
+
* `data.length === width * height * 4` and on a predictable backing buffer.
|
|
35
|
+
*/
|
|
36
|
+
function decode(buffer) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const png = new PNG();
|
|
39
|
+
const input = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
|
|
40
|
+
png.parse(input, (err, decoded) => {
|
|
41
|
+
if (err || !decoded) {
|
|
42
|
+
reject(wrapDecodeError(err ?? new Error('pngjs produced no image')));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const { width, height } = decoded;
|
|
46
|
+
const expected = width * height * 4;
|
|
47
|
+
const data = new Uint8Array(expected);
|
|
48
|
+
data.set(decoded.data.subarray(0, expected));
|
|
49
|
+
resolve({ width, height, data });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Encode normalized 8-bit RGBA back to a compressed PNG buffer. */
|
|
54
|
+
function encode(image) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const png = new PNG({ width: image.width, height: image.height });
|
|
57
|
+
// pngjs owns a Buffer at png.data of the right length; fill it in place.
|
|
58
|
+
png.data = Buffer.from(image.data.buffer, image.data.byteOffset, image.data.byteLength);
|
|
59
|
+
const chunks = [];
|
|
60
|
+
png
|
|
61
|
+
.pack()
|
|
62
|
+
.on('data', (chunk) => chunks.push(chunk))
|
|
63
|
+
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
64
|
+
.on('error', (err) => reject(new CraftdriverError(ErrorCode.DRIVER_ERROR, 'Failed to encode diff PNG image.', {
|
|
65
|
+
cause: err,
|
|
66
|
+
})));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export const pngjsCodec = { decode, encode };
|
|
70
|
+
//# sourceMappingURL=pngjsCodec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pngjsCodec.js","sourceRoot":"","sources":["../../../src/lib/visual/pngjsCodec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG3D,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,IAAI,gBAAgB,CAAC,SAAS,CAAC,gBAAgB,EAAE,6BAA6B,EAAE;QACrF,KAAK;QACL,IAAI,EAAE,qEAAqE;KAC5E,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,MAAM,CAAC,MAAkB;IAChC,OAAO,IAAI,OAAO,CAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;YAChC,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,eAAe,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC;gBACrE,OAAO;YACT,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;YAClC,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC;YACpC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC7C,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,SAAS,MAAM,CAAC,KAAmB;IACjC,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAClE,yEAAyE;QACzE,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxF,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,GAAG;aACA,IAAI,EAAE;aACN,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACjD,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;aAC/C,EAAE,CAAC,OAAO,EAAE,CAAC,GAAY,EAAE,EAAE,CAC5B,MAAM,CACJ,IAAI,gBAAgB,CAAC,SAAS,CAAC,YAAY,EAAE,kCAAkC,EAAE;YAC/E,KAAK,EAAE,GAAG;SACX,CAAC,CACH,CACF,CAAC;IACN,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAa,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public and internal types for visual screenshot assertions.
|
|
3
|
+
*
|
|
4
|
+
* The `(actual, expected)` argument order is used consistently across every
|
|
5
|
+
* public and internal helper (`compareScreenshots`, `compareDecoded`,
|
|
6
|
+
* `createDiff`). Both buffers share a type, so reversing the order would be a
|
|
7
|
+
* silent bug — never deviate from it.
|
|
8
|
+
*/
|
|
9
|
+
import type { By } from '../by.js';
|
|
10
|
+
/**
|
|
11
|
+
* Default upper bound on decoded image size, in pixels, enforced before RGBA
|
|
12
|
+
* allocation. RGBA is ≥4 bytes/pixel, so expected + actual + final raw diff is
|
|
13
|
+
* ~240 MB of raw arrays at this limit before compressed buffers and pngjs
|
|
14
|
+
* working memory — and parallel assertions multiply that. Raise it explicitly
|
|
15
|
+
* for unusually long full-page screenshots; lower it when many workers run
|
|
16
|
+
* concurrently. A 100M default would permit ≥1.2 GB and is not a real guard.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_MAX_IMAGE_PIXELS = 20000000;
|
|
19
|
+
/** Default upper bound on a compressed PNG input buffer, in bytes (50 MiB). */
|
|
20
|
+
export declare const DEFAULT_MAX_INPUT_BYTES: number;
|
|
21
|
+
/**
|
|
22
|
+
* Mutually exclusive capture scope forwarded to `browser.screenshot()`.
|
|
23
|
+
* `fullPage` and `selector` cannot be combined (enforced by the union here and
|
|
24
|
+
* re-checked at runtime for JavaScript callers).
|
|
25
|
+
*/
|
|
26
|
+
export type VisualScreenshotOptions = {
|
|
27
|
+
fullPage: true;
|
|
28
|
+
selector?: never;
|
|
29
|
+
} | {
|
|
30
|
+
selector: string | By;
|
|
31
|
+
fullPage?: false;
|
|
32
|
+
} | {
|
|
33
|
+
fullPage?: false;
|
|
34
|
+
selector?: never;
|
|
35
|
+
};
|
|
36
|
+
/** Policies applied to a single buffer-to-buffer comparison. */
|
|
37
|
+
export interface ScreenshotCompareOptions {
|
|
38
|
+
/** Inclusive maximum difference for each visible RGB channel. Integer 0..255. Default: 0. */
|
|
39
|
+
pixelTolerance?: number;
|
|
40
|
+
/** Maximum counted different pixels. Non-negative integer. */
|
|
41
|
+
maxDiffPixels?: number;
|
|
42
|
+
/** Maximum counted different pixels as a percentage of all pixels. 0..100. */
|
|
43
|
+
maxDiffPercentage?: number;
|
|
44
|
+
/** Exclude pixels classified as anti-aliasing from diff totals. Default: false. */
|
|
45
|
+
ignoreAntialiasing?: boolean;
|
|
46
|
+
/** Reject images above this decoded size before allocation. Default: 20 million pixels. */
|
|
47
|
+
maxImagePixels?: number;
|
|
48
|
+
/** Reject compressed PNG input above this size. Default: 50 MiB. */
|
|
49
|
+
maxInputBytes?: number;
|
|
50
|
+
}
|
|
51
|
+
/** Options for the retrying `browser.expectScreenshot()` assertion. */
|
|
52
|
+
export interface ExpectScreenshotOptions extends ScreenshotCompareOptions {
|
|
53
|
+
/** Overall retry time in ms. Defaults to the browser's default timeout (5000 ms). */
|
|
54
|
+
timeout?: number;
|
|
55
|
+
/** Delay between failed attempts in ms. Integer >= 10. Default: 50. */
|
|
56
|
+
interval?: number;
|
|
57
|
+
/** Mutually exclusive capture scope passed to `browser.screenshot()`. */
|
|
58
|
+
screenshot?: VisualScreenshotOptions;
|
|
59
|
+
}
|
|
60
|
+
/** Result of one buffer-to-buffer comparison. */
|
|
61
|
+
export interface VisualComparisonResult {
|
|
62
|
+
matches: boolean;
|
|
63
|
+
diffPixels: number;
|
|
64
|
+
/** Raw, unrounded value in 0..100. */
|
|
65
|
+
diffPercentage: number;
|
|
66
|
+
ignoredAntialiasPixels: number;
|
|
67
|
+
expectedWidth: number;
|
|
68
|
+
expectedHeight: number;
|
|
69
|
+
actualWidth: number;
|
|
70
|
+
actualHeight: number;
|
|
71
|
+
dimensionMismatch: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Result of a successful `browser.expectScreenshot()` assertion. */
|
|
74
|
+
export interface ScreenshotMatchResult extends VisualComparisonResult {
|
|
75
|
+
attempts: number;
|
|
76
|
+
elapsedMs: number;
|
|
77
|
+
/**
|
|
78
|
+
* How the on-disk baseline was resolved:
|
|
79
|
+
* - `'matched'` — an existing baseline was compared and passed.
|
|
80
|
+
* - `'created'` — no baseline existed; the captured screenshot was written as
|
|
81
|
+
* the new baseline (always on; the diff fields are zero).
|
|
82
|
+
* - `'updated'` — an existing baseline differed and was overwritten with the
|
|
83
|
+
* captured screenshot (only under `CRAFTDRIVER_UPDATE_VISUAL_BASELINES=true`;
|
|
84
|
+
* the diff fields describe state relative to the new baseline, i.e. zero —
|
|
85
|
+
* the magnitude of the accepted change is reported to stderr).
|
|
86
|
+
*/
|
|
87
|
+
baseline: 'matched' | 'created' | 'updated';
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/visual/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEnC;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,WAAa,CAAC;AAEnD,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAC/B;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,GACpC;IAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,GAC3C;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAE3C,gEAAgE;AAChE,MAAM,WAAW,wBAAwB;IACvC,6FAA6F;IAC7F,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,2FAA2F;IAC3F,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,yEAAyE;IACzE,UAAU,CAAC,EAAE,uBAAuB,CAAC;CACtC;AAED,iDAAiD;AACjD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;OASG;IACH,QAAQ,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;CAC7C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public and internal types for visual screenshot assertions.
|
|
3
|
+
*
|
|
4
|
+
* The `(actual, expected)` argument order is used consistently across every
|
|
5
|
+
* public and internal helper (`compareScreenshots`, `compareDecoded`,
|
|
6
|
+
* `createDiff`). Both buffers share a type, so reversing the order would be a
|
|
7
|
+
* silent bug — never deviate from it.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Default upper bound on decoded image size, in pixels, enforced before RGBA
|
|
11
|
+
* allocation. RGBA is ≥4 bytes/pixel, so expected + actual + final raw diff is
|
|
12
|
+
* ~240 MB of raw arrays at this limit before compressed buffers and pngjs
|
|
13
|
+
* working memory — and parallel assertions multiply that. Raise it explicitly
|
|
14
|
+
* for unusually long full-page screenshots; lower it when many workers run
|
|
15
|
+
* concurrently. A 100M default would permit ≥1.2 GB and is not a real guard.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_MAX_IMAGE_PIXELS = 20_000_000;
|
|
18
|
+
/** Default upper bound on a compressed PNG input buffer, in bytes (50 MiB). */
|
|
19
|
+
export const DEFAULT_MAX_INPUT_BYTES = 50 * 1024 * 1024;
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/visual/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;AAEnD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed failure for a visual assertion.
|
|
3
|
+
*
|
|
4
|
+
* JavaScript cannot both throw and return a normal value, so the final actual
|
|
5
|
+
* screenshot and generated diff PNG travel on the error as `Buffer`
|
|
6
|
+
* properties, while a JSON-serializable summary lives in `detail` for agents.
|
|
7
|
+
*/
|
|
8
|
+
import { CraftdriverError } from '../errors.js';
|
|
9
|
+
import type { VisualComparisonResult } from './types.js';
|
|
10
|
+
export interface VisualMismatchInfo {
|
|
11
|
+
expectedPath: string;
|
|
12
|
+
timeout: number;
|
|
13
|
+
attempts: number;
|
|
14
|
+
elapsedMs: number;
|
|
15
|
+
comparison: VisualComparisonResult;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Thrown by `browser.expectScreenshot()` when no screenshot matched the
|
|
19
|
+
* baseline before the timeout. Carries the final actual and diff PNG buffers
|
|
20
|
+
* plus the comparison summary; `code` is `VISUAL_MISMATCH`.
|
|
21
|
+
*/
|
|
22
|
+
export declare class VisualMismatchError extends CraftdriverError {
|
|
23
|
+
/** Final browser screenshot (compressed PNG). */
|
|
24
|
+
readonly actual: Buffer;
|
|
25
|
+
/** Final generated diff (compressed PNG). */
|
|
26
|
+
readonly diff: Buffer;
|
|
27
|
+
readonly comparison: VisualComparisonResult;
|
|
28
|
+
readonly attempts: number;
|
|
29
|
+
readonly elapsedMs: number;
|
|
30
|
+
constructor(actual: Buffer, diff: Buffer, info: VisualMismatchInfo);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=visualError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualError.d.ts","sourceRoot":"","sources":["../../../src/lib/visual/visualError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAa,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,sBAAsB,CAAC;CACpC;AAgBD;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,gBAAgB;IACvD,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;CAyBnE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed failure for a visual assertion.
|
|
3
|
+
*
|
|
4
|
+
* JavaScript cannot both throw and return a normal value, so the final actual
|
|
5
|
+
* screenshot and generated diff PNG travel on the error as `Buffer`
|
|
6
|
+
* properties, while a JSON-serializable summary lives in `detail` for agents.
|
|
7
|
+
*/
|
|
8
|
+
import { CraftdriverError, ErrorCode } from '../errors.js';
|
|
9
|
+
function buildMessage(info) {
|
|
10
|
+
const { comparison, timeout, attempts } = info;
|
|
11
|
+
if (comparison.dimensionMismatch) {
|
|
12
|
+
return (`Screenshot did not match after ${timeout} ms: dimensions differ ` +
|
|
13
|
+
`(expected ${comparison.expectedWidth}x${comparison.expectedHeight}, ` +
|
|
14
|
+
`actual ${comparison.actualWidth}x${comparison.actualHeight}) across ${attempts} attempts`);
|
|
15
|
+
}
|
|
16
|
+
const pixels = comparison.diffPixels.toLocaleString('en-US');
|
|
17
|
+
const pct = comparison.diffPercentage.toFixed(4);
|
|
18
|
+
return `Screenshot did not match after ${timeout} ms: ${pixels} pixels differed (${pct}%) across ${attempts} attempts`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown by `browser.expectScreenshot()` when no screenshot matched the
|
|
22
|
+
* baseline before the timeout. Carries the final actual and diff PNG buffers
|
|
23
|
+
* plus the comparison summary; `code` is `VISUAL_MISMATCH`.
|
|
24
|
+
*/
|
|
25
|
+
export class VisualMismatchError extends CraftdriverError {
|
|
26
|
+
/** Final browser screenshot (compressed PNG). */
|
|
27
|
+
actual;
|
|
28
|
+
/** Final generated diff (compressed PNG). */
|
|
29
|
+
diff;
|
|
30
|
+
comparison;
|
|
31
|
+
attempts;
|
|
32
|
+
elapsedMs;
|
|
33
|
+
constructor(actual, diff, info) {
|
|
34
|
+
super(ErrorCode.VISUAL_MISMATCH, buildMessage(info), {
|
|
35
|
+
detail: {
|
|
36
|
+
expectedPath: info.expectedPath,
|
|
37
|
+
timeout: info.timeout,
|
|
38
|
+
attempts: info.attempts,
|
|
39
|
+
elapsedMs: info.elapsedMs,
|
|
40
|
+
diffPixels: info.comparison.diffPixels,
|
|
41
|
+
diffPercentage: info.comparison.diffPercentage,
|
|
42
|
+
ignoredAntialiasPixels: info.comparison.ignoredAntialiasPixels,
|
|
43
|
+
expectedWidth: info.comparison.expectedWidth,
|
|
44
|
+
expectedHeight: info.comparison.expectedHeight,
|
|
45
|
+
actualWidth: info.comparison.actualWidth,
|
|
46
|
+
actualHeight: info.comparison.actualHeight,
|
|
47
|
+
dimensionMismatch: info.comparison.dimensionMismatch,
|
|
48
|
+
},
|
|
49
|
+
hint: 'Inspect error.actual and error.diff (PNG buffers) to see the regression. If the change is intentional, re-run with CRAFTDRIVER_UPDATE_VISUAL_BASELINES=true to overwrite the baseline.',
|
|
50
|
+
});
|
|
51
|
+
this.name = 'VisualMismatchError';
|
|
52
|
+
this.actual = actual;
|
|
53
|
+
this.diff = diff;
|
|
54
|
+
this.comparison = info.comparison;
|
|
55
|
+
this.attempts = info.attempts;
|
|
56
|
+
this.elapsedMs = info.elapsedMs;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=visualError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visualError.js","sourceRoot":"","sources":["../../../src/lib/visual/visualError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAW3D,SAAS,YAAY,CAAC,IAAwB;IAC5C,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC/C,IAAI,UAAU,CAAC,iBAAiB,EAAE,CAAC;QACjC,OAAO,CACL,kCAAkC,OAAO,yBAAyB;YAClE,aAAa,UAAU,CAAC,aAAa,IAAI,UAAU,CAAC,cAAc,IAAI;YACtE,UAAU,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,YAAY,YAAY,QAAQ,WAAW,CAC3F,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,OAAO,kCAAkC,OAAO,QAAQ,MAAM,qBAAqB,GAAG,aAAa,QAAQ,WAAW,CAAC;AACzH,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,gBAAgB;IACvD,iDAAiD;IACxC,MAAM,CAAS;IACxB,6CAA6C;IACpC,IAAI,CAAS;IACb,UAAU,CAAyB;IACnC,QAAQ,CAAS;IACjB,SAAS,CAAS;IAE3B,YAAY,MAAc,EAAE,IAAY,EAAE,IAAwB;QAChE,KAAK,CAAC,SAAS,CAAC,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE;YACnD,MAAM,EAAE;gBACN,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU;gBACtC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,cAAc;gBAC9C,sBAAsB,EAAE,IAAI,CAAC,UAAU,CAAC,sBAAsB;gBAC9D,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,aAAa;gBAC5C,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,cAAc;gBAC9C,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,WAAW;gBACxC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,YAAY;gBAC1C,iBAAiB,EAAE,IAAI,CAAC,UAAU,CAAC,iBAAiB;aACrD;YACD,IAAI,EAAE,wLAAwL;SAC/L,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAClC,CAAC;CACF"}
|
package/dist/lib/wait.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAQD,qBAAa,aAAa;IAMtB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGlB,MAAM,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,EACvC,UAAU,CAAC,EAAE,MAAM;IAYf,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAQD,qBAAa,aAAa;IAMtB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGlB,MAAM,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,EACvC,UAAU,CAAC,EAAE,MAAM;IAYf,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAmCtE;AAOD,eAAO,MAAM,KAAK;8BACU,EAAE,MAAY,QAAQ,MAAM;6BAI7B,EAAE,MAAY,QAAQ,MAAM;gCASzB,EAAE,MAAY,QAAQ,MAAM;+BAS7B,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;kCAUtC,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;CAaxE,CAAC"}
|
package/dist/lib/wait.js
CHANGED
|
@@ -27,7 +27,11 @@ export class WebDriverWait {
|
|
|
27
27
|
async until(condition, message) {
|
|
28
28
|
const deadline = Date.now() + this.timeoutMs;
|
|
29
29
|
let lastErr;
|
|
30
|
-
|
|
30
|
+
// Check-first: the condition is always evaluated at least once, even with a
|
|
31
|
+
// zero (or already-elapsed) timeout. A `while (Date.now() < deadline)` loop
|
|
32
|
+
// would run zero iterations at timeout 0 and report an already-satisfiable
|
|
33
|
+
// condition as a spurious timeout.
|
|
34
|
+
for (;;) {
|
|
31
35
|
try {
|
|
32
36
|
const result = await condition(this.driver);
|
|
33
37
|
// Resolve on truthy or non-null/undefined result
|
|
@@ -38,6 +42,8 @@ export class WebDriverWait {
|
|
|
38
42
|
catch (e) {
|
|
39
43
|
lastErr = e;
|
|
40
44
|
}
|
|
45
|
+
if (Date.now() >= deadline)
|
|
46
|
+
break;
|
|
41
47
|
await new Promise((r) => setTimeout(r, this.intervalMs));
|
|
42
48
|
}
|
|
43
49
|
const errMsg = message ?? this.timeoutMsg ?? `Wait timed out after ${this.timeoutMs}ms`;
|
package/dist/lib/wait.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAUnF,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAClD,OAAO,cAAc,KAAK,iBAAiB,IAAI,cAAc,KAAK,yBAAyB,CAAC;AAC9F,CAAC;AAED,MAAM,OAAO,aAAa;IAMd;IALF,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,UAAU,CAAU;IAE5B,YACU,MAAc,EACtB,gBAAuC,EACvC,UAAmB;QAFX,WAAM,GAAN,MAAM,CAAQ;QAItB,IAAI,OAAO,gBAAgB,KAAK,QAAQ,EAAE,CAAC;YACzC,IAAI,CAAC,SAAS,GAAG,gBAAgB,EAAE,OAAO,IAAI,0BAA0B,CAAC;YACzE,IAAI,CAAC,UAAU,GAAG,gBAAgB,EAAE,QAAQ,IAAI,wBAAwB,CAAC;YACzE,IAAI,CAAC,UAAU,GAAG,gBAAgB,EAAE,UAAU,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,GAAG,gBAAgB,IAAI,0BAA0B,CAAC;YAChE,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,wBAAwB,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAI,SAAuB,EAAE,OAAgB;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7C,IAAI,OAAgB,CAAC;QACrB,
|
|
1
|
+
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAUnF,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAClD,OAAO,cAAc,KAAK,iBAAiB,IAAI,cAAc,KAAK,yBAAyB,CAAC;AAC9F,CAAC;AAED,MAAM,OAAO,aAAa;IAMd;IALF,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,UAAU,CAAU;IAE5B,YACU,MAAc,EACtB,gBAAuC,EACvC,UAAmB;QAFX,WAAM,GAAN,MAAM,CAAQ;QAItB,IAAI,OAAO,gBAAgB,KAAK,QAAQ,EAAE,CAAC;YACzC,IAAI,CAAC,SAAS,GAAG,gBAAgB,EAAE,OAAO,IAAI,0BAA0B,CAAC;YACzE,IAAI,CAAC,UAAU,GAAG,gBAAgB,EAAE,QAAQ,IAAI,wBAAwB,CAAC;YACzE,IAAI,CAAC,UAAU,GAAG,gBAAgB,EAAE,UAAU,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,GAAG,gBAAgB,IAAI,0BAA0B,CAAC;YAChE,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,wBAAwB,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAI,SAAuB,EAAE,OAAgB;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7C,IAAI,OAAgB,CAAC;QACrB,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,mCAAmC;QACnC,SAAS,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC5C,iDAAiD;gBACjD,IAAI,MAAM,IAAK,MAAc,KAAK,CAAC,IAAK,MAAc,KAAK,KAAK,EAAE,CAAC;oBACjE,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,GAAG,CAAC,CAAC;YACd,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAM;YAClC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,IAAI,IAAI,CAAC,UAAU,IAAI,wBAAwB,IAAI,CAAC,SAAS,IAAI,CAAC;QACxF,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,MAAM,CAAE,OAAe,EAAE,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAC7F,iFAAiF;QACjF,IAAI,gBAAgB,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE;gBAC7C,MAAM,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;gBAC9D,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAC;QACL,CAAC;QACD,MAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE;YAClD,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE;YACnC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IACL,CAAC;CACF;AAED,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,MAAuB;IACnE,IAAI,MAAM,YAAY,UAAU;QAAE,OAAO,MAAM,CAAC;IAChD,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,cAAc,EAAE,CAAC,OAAW,EAAE,EAAE,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QACxD,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,aAAa,EAAE,CAAC,OAAW,EAAE,EAAE,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QACvD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAY,CAAC,CAAC,eAAe;QACtC,CAAC;IACH,CAAC;IAED,gBAAgB,EAAE,CAAC,OAAW,EAAE,EAAE,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QAC1D,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAClC,OAAO,KAAY,CAAC,CAAC,6BAA6B;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,gBAAgB,EAAE,CAAC,MAAuB,EAAE,EAAE,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QACtE,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YACvC,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,SAAiB,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAgB,CAAC,CAAC,qCAAqC;QAChE,CAAC;IACH,CAAC;IAED,mBAAmB,EAAE,CAAC,MAAuB,EAAE,EAAE,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;QACzE,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;gBACvC,OAAO,OAAO,CAAC,CAAC,CAAE,KAAa,CAAC,CAAC,CAAC,IAAI,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,CAAC,wCAAwC;YACvD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,YAAY;QAC3B,CAAC;IACH,CAAC;CACF,CAAC"}
|
package/docs/agents.md
CHANGED
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
# AI Agent Guide
|
|
2
2
|
|
|
3
|
-
CraftDriver
|
|
3
|
+
CraftDriver's recommended agent surface is the CLI plus a project-local skill.
|
|
4
|
+
The agent explores the running application, learns its DOM and accessibility
|
|
5
|
+
structure, validates durable selectors, and then writes and runs ordinary
|
|
6
|
+
CraftDriver tests. MCP is optional.
|
|
4
7
|
|
|
5
|
-
##
|
|
8
|
+
## Install safely
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
| -------------------------- | ------------------ | -------------------------------------------------------------------------------- |
|
|
9
|
-
| Shell-capable coding agent | CLI | Simple commands, persistent daemon, JSON output when piped. |
|
|
10
|
-
| MCP-aware host | MCP server | Typed tools, compact accessibility snapshots, artifact spilling for screenshots. |
|
|
11
|
-
| Code-writing assistant | `craftdriver init` | Project rules teach selector preference, auto-waiting, and error-code handling. |
|
|
12
|
-
| Custom orchestrator | Skill pack | Small always-on `SKILL.md` with deeper references loaded on demand. |
|
|
10
|
+
From a project that already has a `package.json`:
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
```bash
|
|
13
|
+
npm install --save-dev craftdriver
|
|
14
|
+
npx craftdriver init codex
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This installs `.agents/skills/craftdriver/` with an ownership manifest. It does
|
|
18
|
+
not read or change `AGENTS.md`, `CLAUDE.md`, Copilot instructions, Cursor rules,
|
|
19
|
+
`GEMINI.md`, or `.codex/config.toml`. It refuses user-edited, unowned, extra, or
|
|
20
|
+
symlinked destination content instead of overwriting it.
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
Preview without writes:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx craftdriver init codex --dry-run
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Explore, then write the test
|
|
17
29
|
|
|
18
30
|
```bash
|
|
19
31
|
npx craftdriver daemon start
|
|
20
32
|
npx craftdriver go http://127.0.0.1:8080/login.html
|
|
21
33
|
npx craftdriver snapshot
|
|
34
|
+
npx craftdriver find 'label=Username' --all
|
|
22
35
|
npx craftdriver fill 'label=Username' alice
|
|
23
36
|
npx craftdriver fill 'label=Password' hunter2
|
|
24
37
|
npx craftdriver click 'role=button[name=Sign in]'
|
|
@@ -26,67 +39,49 @@ npx craftdriver text '#result'
|
|
|
26
39
|
npx craftdriver daemon stop
|
|
27
40
|
```
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
The daemon keeps the page and cookies between commands. Snapshot refs are
|
|
43
|
+
ephemeral exploration state in the current release: take a fresh snapshot
|
|
44
|
+
immediately before one ref action, and never copy `ref=eN` into a test. Validate
|
|
45
|
+
role/name, label, test ID, text, or stable CSS selectors against the live page,
|
|
46
|
+
then use those durable locators in test source.
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
The daemon uses a Unix socket and is not available on Windows. Windows agents
|
|
49
|
+
should use the optional MCP server for a persistent session, or run the whole
|
|
50
|
+
flow as one `craftdriver --ephemeral` script.
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```jsonc
|
|
42
|
-
{
|
|
43
|
-
"mcpServers": {
|
|
44
|
-
"craftdriver": {
|
|
45
|
-
"command": "npx",
|
|
46
|
-
"args": ["-y", "craftdriver", "mcp"],
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Mutating tools return compact accessibility snapshot diffs, so the agent can see what changed without a separate read. Large artifacts such as screenshots are written to disk instead of inlined into model context.
|
|
52
|
+
The installed `workflow.md` guides the full loop: inspect existing project test
|
|
53
|
+
conventions, explore, validate selectors, write the smallest test, run the
|
|
54
|
+
focused command, and debug from fresh browser evidence.
|
|
53
55
|
|
|
54
|
-
Read the
|
|
56
|
+
Read the [CLI reference](./cli.md) for all current commands.
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## Optional MCP
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
CLI + Skill does not require MCP. For a host that prefers structured tool
|
|
61
|
+
calls, print the local project-pinned configuration:
|
|
59
62
|
|
|
60
63
|
```bash
|
|
61
|
-
npx craftdriver init
|
|
62
|
-
npx craftdriver init copilot
|
|
63
|
-
npx craftdriver init claude
|
|
64
|
-
npx craftdriver init cursor
|
|
65
|
-
npx craftdriver init gemini
|
|
66
|
-
npx craftdriver init all
|
|
64
|
+
npx craftdriver init codex --mcp
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
The
|
|
67
|
+
The command prints:
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
## Skill Pack
|
|
78
|
-
|
|
79
|
-
The npm tarball includes a tiered skill pack under `skills/craftdriver/`:
|
|
69
|
+
```toml
|
|
70
|
+
[mcp_servers.craftdriver]
|
|
71
|
+
command = "npx"
|
|
72
|
+
args = ["--no-install", "craftdriver", "mcp"]
|
|
73
|
+
```
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| `cheatsheet.md` | Fast command and API reference for tests. |
|
|
85
|
-
| `patterns.md` | Worked recipes such as login, uploads, network waits, a11y, tracing, and virtual time. |
|
|
86
|
-
| `cli.md` | Agent-facing CLI reference. |
|
|
75
|
+
Add it manually to the host configuration. CraftDriver never reads or writes
|
|
76
|
+
that configuration. See the [MCP reference](./mcp.md) for the current optional
|
|
77
|
+
tool surface and protocol bounds.
|
|
87
78
|
|
|
88
|
-
|
|
79
|
+
## Installed skill files
|
|
89
80
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
| File | Purpose |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `SKILL.md` | Short browser-to-test rules and routing. |
|
|
84
|
+
| `workflow.md` | Exploration, selector validation, test authoring, and debugging loop. |
|
|
85
|
+
| `cli.md` | Current shell command reference for agent exploration. |
|
|
86
|
+
| `cheatsheet.md` | Public TypeScript API reference for writing tests. |
|
|
87
|
+
| `patterns.md` | Focused library recipes loaded on demand. |
|
package/docs/api-reference.md
CHANGED
|
@@ -124,6 +124,20 @@ Launch packaged Electron applications, drive their renderer, and opt in to main-
|
|
|
124
124
|
| `ElectronMainLogHandler` | type | — | [electron](electron.md) |
|
|
125
125
|
| `ElectronMainLogLevel` | type | Normalized level for a main-process log entry. | [electron](electron.md) |
|
|
126
126
|
|
|
127
|
+
## Visual Testing
|
|
128
|
+
|
|
129
|
+
Assert screenshots against baselines with pixel/percentage tolerances and optional anti-alias handling.
|
|
130
|
+
|
|
131
|
+
| Symbol | Kind | Summary | Docs |
|
|
132
|
+
|---|---|---|---|
|
|
133
|
+
| `compareScreenshots` | function | Compare two compressed PNG buffers without disk access, browser capture, retries, or throwing on a normal mismatch. Applies the same byte/pixel input limits as the retrying assertion. | [visual-testing](visual-testing.md) |
|
|
134
|
+
| `VisualMismatchError` | class | Thrown by `browser.expectScreenshot()` when no screenshot matched the baseline before the timeout. Carries the final actual and diff PNG buffers plus the comparison summary; `code` is `VISUAL_MISMATCH`. | [visual-testing](visual-testing.md) |
|
|
135
|
+
| `ScreenshotCompareOptions` | type | Policies applied to a single buffer-to-buffer comparison. | [visual-testing](visual-testing.md) |
|
|
136
|
+
| `ExpectScreenshotOptions` | type | Options for the retrying `browser.expectScreenshot()` assertion. | [visual-testing](visual-testing.md) |
|
|
137
|
+
| `VisualScreenshotOptions` | type | Mutually exclusive capture scope forwarded to `browser.screenshot()`. `fullPage` and `selector` cannot be combined (enforced by the union here and re-checked at runtime for JavaScript callers). | [visual-testing](visual-testing.md) |
|
|
138
|
+
| `VisualComparisonResult` | type | Result of one buffer-to-buffer comparison. | [visual-testing](visual-testing.md) |
|
|
139
|
+
| `ScreenshotMatchResult` | type | Result of a successful `browser.expectScreenshot()` assertion. | [visual-testing](visual-testing.md) |
|
|
140
|
+
|
|
127
141
|
## Accessibility
|
|
128
142
|
|
|
129
143
|
Run axe-core accessibility checks and inspect violation details.
|
|
@@ -169,5 +183,6 @@ Exports that are public but not yet assigned to a feature group.
|
|
|
169
183
|
| `PageMatcher` | type | Selects a top-level page by `url` and/or `title` for {@link Browser.waitForPage}. A string matches as a substring; a `RegExp` is tested. When both fields are given, both must match. | [browser-api](browser-api.md) |
|
|
170
184
|
| `RemoteAuth` | type | — | — |
|
|
171
185
|
| `RemoteWebDriverOptions` | type | Options for connecting to any W3C-compatible remote WebDriver endpoint — a self-hosted Selenium Grid, BrowserStack, or another cloud provider. Provider-specific detail (BrowserStack's `bstack:options`, etc.) is forwarded through `capabilities` without schema conversion. | — |
|
|
186
|
+
| `SessionStateCookie` | type | — | — |
|
|
172
187
|
|
|
173
|
-
Total exports: **
|
|
188
|
+
Total exports: **94**.
|
package/docs/browser-api.md
CHANGED
|
@@ -29,7 +29,7 @@ const browser = await Browser.launch({
|
|
|
29
29
|
|--------|------|---------|-------------|
|
|
30
30
|
| `browserName` | `'chrome' \| 'chromium' \| 'firefox' \| 'safari'`; any non-empty `string` remotely | `'chrome'` | Local browser to launch or provider-facing remote browser name |
|
|
31
31
|
| `enableBiDi` | `boolean` | browsers: `true`; Electron: `false` | Use WebDriver BiDi. Electron requires an explicit opt-in because negotiation resets its initial renderer to `about:blank`. |
|
|
32
|
-
| `storageState` | `string` | — |
|
|
32
|
+
| `storageState` | `string \| SessionState` | — | Native session-state path/object. Full cookies + multi-origin localStorage on supported BiDi; non-empty state is rejected at Classic launch. |
|
|
33
33
|
| `mobileEmulation` | `MobileEmulation \| DeviceName` | — | Mobile device emulation settings (Chrome/Chromium only) |
|
|
34
34
|
| `downloadsDir` | `string` | temp dir | Directory where downloaded files are saved |
|
|
35
35
|
| `browserPath` | `string` | — | Custom browser binary to launch (Chrome/Chromium/Firefox) — see [Driver Configuration → Browser Binary Configuration](./driver-configuration.md#browser-binary-configuration) |
|
|
@@ -334,9 +334,20 @@ await popup.waitForLoadState('load');
|
|
|
334
334
|
const heading = await popup.find('h1').text();
|
|
335
335
|
console.log(heading);
|
|
336
336
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
// Close it. Prefer this over `popup.evaluate(() => window.close())`, which
|
|
338
|
+
// leaves no window focused — every later command then fails with
|
|
339
|
+
// "no such window". `close()` switches to a surviving page for you.
|
|
340
|
+
await popup.close();
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Use `page.activate()` to make a page the target of browser-level helpers.
|
|
344
|
+
Most page methods switch to their window and switch back, so they never
|
|
345
|
+
disturb `browser.activePage()`; `activate()` is the deliberate exception:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const [, second] = await browser.pages();
|
|
349
|
+
await second.activate();
|
|
350
|
+
await browser.click('#confirm'); // acts on the second tab
|
|
340
351
|
```
|
|
341
352
|
|
|
342
353
|
> **Note.** `browser.openPage()` requires BiDi (the default). In Classic
|
|
@@ -378,8 +389,17 @@ await browser.saveState('./session.json');
|
|
|
378
389
|
|
|
379
390
|
// Load session from file
|
|
380
391
|
await browser.loadState('./session.json');
|
|
392
|
+
|
|
393
|
+
// Or use an in-memory SessionState object
|
|
394
|
+
await browser.loadState(await browser.storage.getState());
|
|
381
395
|
```
|
|
382
396
|
|
|
397
|
+
On Chrome/Chromium and Firefox BiDi, launch and `browser.loadState()` restore
|
|
398
|
+
cookies plus multi-origin localStorage before the first real navigation. In
|
|
399
|
+
Classic mode, launch rejects non-empty state; navigate to the sole origin, call
|
|
400
|
+
`browser.loadState()`, then reload. See
|
|
401
|
+
[Session Management](./session-management.md#browser-and-transport-support).
|
|
402
|
+
|
|
383
403
|
## Evaluate
|
|
384
404
|
|
|
385
405
|
Run JavaScript in the page and get the result back. Useful for reading app state,
|
package/docs/browser-context.md
CHANGED
|
@@ -120,9 +120,8 @@ Create a new isolated user context. Backed by BiDi
|
|
|
120
120
|
`browser.createUserContext`. Throws in Classic mode.
|
|
121
121
|
|
|
122
122
|
- `opts.storageState`: a `SessionState` object **or** a path to a JSON
|
|
123
|
-
file produced by `BrowserContext.saveStorageState()`. Cookies
|
|
124
|
-
|
|
125
|
-
each captured origin via an internal preload script.
|
|
123
|
+
file produced by `BrowserContext.saveStorageState()`. Cookies and every
|
|
124
|
+
captured localStorage origin are restored before `newContext()` resolves.
|
|
126
125
|
- `opts.baseURL`: a base URL applied to every relative `url` passed to
|
|
127
126
|
`ctx.newPage()` or `page.navigateTo()` inside the context. Absolute
|
|
128
127
|
URLs pass through unchanged. Lets your tests say `'/login'` instead
|
|
@@ -230,11 +229,17 @@ snapshot for inspection. See the auth-fixture example above.
|
|
|
230
229
|
### `BrowserContext.loadStorageState(source): Promise<void>`
|
|
231
230
|
|
|
232
231
|
Apply a previously-captured snapshot. `source` is a `SessionState`
|
|
233
|
-
object or a path to JSON produced by `saveStorageState`.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
232
|
+
object or a path to JSON produced by `saveStorageState`. localStorage is
|
|
233
|
+
seeded once per captured origin through a private, locally fulfilled document;
|
|
234
|
+
cookies are then applied to the same user-context partition. The method resolves
|
|
235
|
+
after hydration and cleanup, so the first real page script sees the state.
|
|
236
|
+
Calling `loadStorageState` again applies another ordered overlay; application
|
|
237
|
+
changes remain owned by the application and survive reload.
|
|
238
|
+
|
|
239
|
+
Restores in one context are serialized. Different user contexts may restore in
|
|
240
|
+
parallel. A runtime protocol failure on an existing context throws
|
|
241
|
+
`DRIVER_ERROR` with `detail.phase` and `detail.partialApplied`; use a newly
|
|
242
|
+
created context when failure isolation is required.
|
|
238
243
|
|
|
239
244
|
### `BrowserContext.close(): Promise<void>`
|
|
240
245
|
|
|
@@ -479,10 +484,11 @@ A default `browser.newContext()` (no options) is always cross-browser.
|
|
|
479
484
|
|
|
480
485
|
A few things that aren't bugs but bite if you don't know them:
|
|
481
486
|
|
|
482
|
-
- **`storageState` covers cookies + localStorage only.** Not
|
|
483
|
-
sessionStorage,
|
|
484
|
-
|
|
485
|
-
|
|
487
|
+
- **`storageState` covers cookies + localStorage only.** Not reusable
|
|
488
|
+
sessionStorage, IndexedDB, or Cache Storage. A non-empty sessionStorage
|
|
489
|
+
section is rejected by context/launch APIs rather than ignored. Use the
|
|
490
|
+
active-page `browser.loadState()` fallback for one matching sessionStorage
|
|
491
|
+
origin, or a custom login step.
|
|
486
492
|
- **Browser-level vs context-level emulation race.** `browser.setGeolocation(...)`
|
|
487
493
|
is a session-wide override; `ctx.setGeolocation(...)` is a per-context
|
|
488
494
|
override. If both are set, the context-level one wins inside the
|