craftdriver 0.2.1 → 1.0.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 +76 -0
- package/dist/lib/bidi/connection.d.ts +1 -1
- package/dist/lib/bidi/connection.d.ts.map +1 -1
- package/dist/lib/bidi/connection.js +3 -2
- package/dist/lib/bidi/connection.js.map +1 -1
- package/dist/lib/bidi/index.d.ts +40 -3
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +55 -18
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -2
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +9 -0
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +26 -11
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/browser.d.ts +51 -4
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +215 -66
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +7 -2
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +17 -8
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/builder.d.ts +2 -1
- package/dist/lib/builder.d.ts.map +1 -1
- package/dist/lib/builder.js +35 -5
- package/dist/lib/builder.js.map +1 -1
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +21 -6
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/driverManager.d.ts +6 -0
- package/dist/lib/driverManager.d.ts.map +1 -1
- package/dist/lib/driverManager.js +97 -22
- package/dist/lib/driverManager.js.map +1 -1
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +91 -229
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/firefox.d.ts.map +1 -1
- package/dist/lib/firefox.js +2 -1
- package/dist/lib/firefox.js.map +1 -1
- package/dist/lib/frame.d.ts.map +1 -1
- package/dist/lib/frame.js +4 -3
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/http.d.ts +7 -0
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +37 -0
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/loadState.d.ts +23 -0
- package/dist/lib/loadState.d.ts.map +1 -0
- package/dist/lib/loadState.js +25 -0
- package/dist/lib/loadState.js.map +1 -0
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +11 -4
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/mouse.d.ts.map +1 -1
- package/dist/lib/mouse.js +2 -1
- package/dist/lib/mouse.js.map +1 -1
- package/dist/lib/page.d.ts +14 -1
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +41 -9
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/service.d.ts +1 -0
- package/dist/lib/service.d.ts.map +1 -1
- package/dist/lib/service.js +21 -3
- package/dist/lib/service.js.map +1 -1
- package/dist/lib/timing.d.ts +104 -0
- package/dist/lib/timing.d.ts.map +1 -0
- package/dist/lib/timing.js +108 -0
- package/dist/lib/timing.js.map +1 -0
- package/dist/lib/wait.d.ts.map +1 -1
- package/dist/lib/wait.js +5 -4
- package/dist/lib/wait.js.map +1 -1
- package/docs/bidi-features.md +10 -2
- package/docs/browser-api.md +2 -1
- package/docs/browser-context.md +11 -14
- package/docs/dialogs.md +14 -3
- package/docs/driver-configuration.md +126 -8
- package/docs/emulation.md +8 -4
- package/docs/error-codes.md +8 -3
- package/docs/keyboard-mouse.md +20 -7
- package/docs/session-management.md +1 -1
- package/package.json +4 -1
- package/skills/craftdriver/SKILL.md +3 -2
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized timing constants: poll intervals, timeouts, fixed delays, and
|
|
3
|
+
* retry policy. Kept in one place so they are easy to find and tune rather than
|
|
4
|
+
* scattered as magic numbers across the codebase. All values are milliseconds.
|
|
5
|
+
*
|
|
6
|
+
* This module intentionally has no imports so any file can depend on it without
|
|
7
|
+
* risking an import cycle.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Base cadence for all auto-wait retry loops. Kept short so a condition that
|
|
11
|
+
* flips true mid-interval is noticed quickly rather than up to a full interval
|
|
12
|
+
* late. The first check of every loop runs immediately, so conditions that are
|
|
13
|
+
* already satisfied never pay it — it only affects genuine waits. Round trips
|
|
14
|
+
* to a local driver are cheap (~a few ms), so polling this often is fine.
|
|
15
|
+
*
|
|
16
|
+
* Used for element visibility/existence waits and `Locator` resolution.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_POLL_INTERVAL_MS = 25;
|
|
19
|
+
/**
|
|
20
|
+
* How often `expect(...)` assertion retries re-check their matcher (text,
|
|
21
|
+
* value, class, …). Same responsive cadence as {@link DEFAULT_POLL_INTERVAL_MS}
|
|
22
|
+
* so an assertion that becomes true shortly after a failed check passes almost
|
|
23
|
+
* immediately instead of waiting out a long interval.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ASSERTION_POLL_INTERVAL_MS = 25;
|
|
26
|
+
/** Poll interval for the driver service's `/status` readiness check. */
|
|
27
|
+
export declare const DRIVER_READINESS_POLL_INTERVAL_MS = 25;
|
|
28
|
+
/**
|
|
29
|
+
* Poll interval for state that changes at process/OS speed rather than DOM
|
|
30
|
+
* speed: Classic `document.readyState`, the Classic `waitForPage`
|
|
31
|
+
* window-handle fallback, and download-file polling. Kept at the same 25ms
|
|
32
|
+
* cadence for responsiveness — these checks (a script eval, a window-handle
|
|
33
|
+
* list, an `fs.stat`) are cheap enough to run this often.
|
|
34
|
+
*/
|
|
35
|
+
export declare const STATE_POLL_INTERVAL_MS = 25;
|
|
36
|
+
/**
|
|
37
|
+
* Per-attempt cap on how long one `expect(...)` retry waits for the element to
|
|
38
|
+
* *exist* before looping back to re-run the whole matcher. This does NOT govern
|
|
39
|
+
* how fast an assertion re-checks — the matcher is re-evaluated every
|
|
40
|
+
* {@link ASSERTION_POLL_INTERVAL_MS}, and an element that already exists returns
|
|
41
|
+
* from the existence wait immediately. Existence itself is polled at
|
|
42
|
+
* {@link DEFAULT_POLL_INTERVAL_MS} inside this window, so a slow-to-appear
|
|
43
|
+
* element is still caught within ~25ms; the cap only bounds a single attempt so
|
|
44
|
+
* the outer loop keeps its cadence and honours the overall timeout.
|
|
45
|
+
*/
|
|
46
|
+
export declare const ASSERTION_INNER_WAIT_CAP_MS = 250;
|
|
47
|
+
/** Default timeout for element interactions and waits (click, fill, find, …). */
|
|
48
|
+
export declare const DEFAULT_ELEMENT_TIMEOUT_MS = 5000;
|
|
49
|
+
/**
|
|
50
|
+
* Default timeout for navigation and network/event waits (navigateTo,
|
|
51
|
+
* waitForRequest/Response, log waits, download waits).
|
|
52
|
+
*/
|
|
53
|
+
export declare const DEFAULT_NAVIGATION_TIMEOUT_MS = 30000;
|
|
54
|
+
/** Default timeout for the BiDi WebSocket connect and for each BiDi command. */
|
|
55
|
+
export declare const BIDI_TIMEOUT_MS = 30000;
|
|
56
|
+
/** How long to wait for the driver service to report ready after spawning. */
|
|
57
|
+
export declare const DRIVER_READINESS_TIMEOUT_MS = 5000;
|
|
58
|
+
/**
|
|
59
|
+
* Readiness deadline for geckodriver specifically — it takes longer than
|
|
60
|
+
* chromedriver to spin up Marionette and start listening.
|
|
61
|
+
*/
|
|
62
|
+
export declare const FIREFOX_READINESS_TIMEOUT_MS = 15000;
|
|
63
|
+
/** Timeout for the `<driver|browser> --version` capability probe spawn. */
|
|
64
|
+
export declare const VERSION_PROBE_TIMEOUT_MS = 5000;
|
|
65
|
+
/** Timeout for the `which` / `where` PATH-probe spawn. */
|
|
66
|
+
export declare const PATH_PROBE_TIMEOUT_MS = 2000;
|
|
67
|
+
/** Best-effort settle used as the Classic fallback for `networkidle`. */
|
|
68
|
+
export declare const NETWORK_IDLE_SETTLE_MS = 500;
|
|
69
|
+
/**
|
|
70
|
+
* Pause between `driver.quit()` and stopping the driver service, giving the
|
|
71
|
+
* browser time to release its ports (notably Firefox's BiDi WebSocket) before a
|
|
72
|
+
* subsequent launch reuses them; without it a fresh launch can hit a 404.
|
|
73
|
+
*/
|
|
74
|
+
export declare const PORT_RELEASE_DELAY_MS = 500;
|
|
75
|
+
/** Delay before a BiDi connection auto-reconnect attempt. */
|
|
76
|
+
export declare const BIDI_RECONNECT_DELAY_MS = 1000;
|
|
77
|
+
/**
|
|
78
|
+
* BiDi connect retries. Firefox may not have finished binding its BiDi
|
|
79
|
+
* WebSocket right after the session is created; back off linearly between
|
|
80
|
+
* attempts (delay = {@link BIDI_CONNECT_BACKOFF_STEP_MS} × attempt).
|
|
81
|
+
*/
|
|
82
|
+
export declare const BIDI_CONNECT_MAX_ATTEMPTS = 8;
|
|
83
|
+
export declare const BIDI_CONNECT_BACKOFF_STEP_MS = 300;
|
|
84
|
+
/**
|
|
85
|
+
* WebDriver session-creation retries. Firefox's Marionette interface can lag
|
|
86
|
+
* geckodriver reporting healthy, so retry with linear backoff (delay =
|
|
87
|
+
* {@link SESSION_CREATE_BACKOFF_STEP_MS} × attempt); Chrome is reliable enough
|
|
88
|
+
* to need a single attempt.
|
|
89
|
+
*/
|
|
90
|
+
export declare const FIREFOX_SESSION_MAX_ATTEMPTS = 4;
|
|
91
|
+
export declare const CHROME_SESSION_MAX_ATTEMPTS = 1;
|
|
92
|
+
export declare const SESSION_CREATE_BACKOFF_STEP_MS = 500;
|
|
93
|
+
/**
|
|
94
|
+
* `evaluate()` retries on a transient "execution contexts cleared" BiDi error.
|
|
95
|
+
* A Classic-first navigate returns at `readyState === 'complete'`, which is not
|
|
96
|
+
* a barrier the BiDi side respects — an immediately following `{ context }`
|
|
97
|
+
* script call can land while BiDi is still swapping the old realm for the new
|
|
98
|
+
* one. The error is pre-execution (the script never ran, no side effects), so a
|
|
99
|
+
* short retry that re-resolves the context is safe. See
|
|
100
|
+
* plans/TODO-bidi-first-navigation.md (Option A).
|
|
101
|
+
*/
|
|
102
|
+
export declare const EVAL_REALM_RETRY_ATTEMPTS = 3;
|
|
103
|
+
export declare const EVAL_REALM_RETRY_DELAY_MS = 25;
|
|
104
|
+
//# sourceMappingURL=timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/lib/timing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,wEAAwE;AACxE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,gFAAgF;AAChF,eAAO,MAAM,eAAe,QAAS,CAAC;AAEtC,8EAA8E;AAC9E,eAAO,MAAM,2BAA2B,OAAQ,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,OAAQ,CAAC;AAI3C,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAI7C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,eAAO,MAAM,yBAAyB,KAAK,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized timing constants: poll intervals, timeouts, fixed delays, and
|
|
3
|
+
* retry policy. Kept in one place so they are easy to find and tune rather than
|
|
4
|
+
* scattered as magic numbers across the codebase. All values are milliseconds.
|
|
5
|
+
*
|
|
6
|
+
* This module intentionally has no imports so any file can depend on it without
|
|
7
|
+
* risking an import cycle.
|
|
8
|
+
*/
|
|
9
|
+
// ── Auto-wait / retry poll intervals ─────────────────────────────────────────
|
|
10
|
+
/**
|
|
11
|
+
* Base cadence for all auto-wait retry loops. Kept short so a condition that
|
|
12
|
+
* flips true mid-interval is noticed quickly rather than up to a full interval
|
|
13
|
+
* late. The first check of every loop runs immediately, so conditions that are
|
|
14
|
+
* already satisfied never pay it — it only affects genuine waits. Round trips
|
|
15
|
+
* to a local driver are cheap (~a few ms), so polling this often is fine.
|
|
16
|
+
*
|
|
17
|
+
* Used for element visibility/existence waits and `Locator` resolution.
|
|
18
|
+
*/
|
|
19
|
+
export const DEFAULT_POLL_INTERVAL_MS = 25;
|
|
20
|
+
/**
|
|
21
|
+
* How often `expect(...)` assertion retries re-check their matcher (text,
|
|
22
|
+
* value, class, …). Same responsive cadence as {@link DEFAULT_POLL_INTERVAL_MS}
|
|
23
|
+
* so an assertion that becomes true shortly after a failed check passes almost
|
|
24
|
+
* immediately instead of waiting out a long interval.
|
|
25
|
+
*/
|
|
26
|
+
export const ASSERTION_POLL_INTERVAL_MS = 25;
|
|
27
|
+
/** Poll interval for the driver service's `/status` readiness check. */
|
|
28
|
+
export const DRIVER_READINESS_POLL_INTERVAL_MS = 25;
|
|
29
|
+
/**
|
|
30
|
+
* Poll interval for state that changes at process/OS speed rather than DOM
|
|
31
|
+
* speed: Classic `document.readyState`, the Classic `waitForPage`
|
|
32
|
+
* window-handle fallback, and download-file polling. Kept at the same 25ms
|
|
33
|
+
* cadence for responsiveness — these checks (a script eval, a window-handle
|
|
34
|
+
* list, an `fs.stat`) are cheap enough to run this often.
|
|
35
|
+
*/
|
|
36
|
+
export const STATE_POLL_INTERVAL_MS = 25;
|
|
37
|
+
/**
|
|
38
|
+
* Per-attempt cap on how long one `expect(...)` retry waits for the element to
|
|
39
|
+
* *exist* before looping back to re-run the whole matcher. This does NOT govern
|
|
40
|
+
* how fast an assertion re-checks — the matcher is re-evaluated every
|
|
41
|
+
* {@link ASSERTION_POLL_INTERVAL_MS}, and an element that already exists returns
|
|
42
|
+
* from the existence wait immediately. Existence itself is polled at
|
|
43
|
+
* {@link DEFAULT_POLL_INTERVAL_MS} inside this window, so a slow-to-appear
|
|
44
|
+
* element is still caught within ~25ms; the cap only bounds a single attempt so
|
|
45
|
+
* the outer loop keeps its cadence and honours the overall timeout.
|
|
46
|
+
*/
|
|
47
|
+
export const ASSERTION_INNER_WAIT_CAP_MS = 250;
|
|
48
|
+
// ── Default timeouts ─────────────────────────────────────────────────────────
|
|
49
|
+
/** Default timeout for element interactions and waits (click, fill, find, …). */
|
|
50
|
+
export const DEFAULT_ELEMENT_TIMEOUT_MS = 5_000;
|
|
51
|
+
/**
|
|
52
|
+
* Default timeout for navigation and network/event waits (navigateTo,
|
|
53
|
+
* waitForRequest/Response, log waits, download waits).
|
|
54
|
+
*/
|
|
55
|
+
export const DEFAULT_NAVIGATION_TIMEOUT_MS = 30_000;
|
|
56
|
+
/** Default timeout for the BiDi WebSocket connect and for each BiDi command. */
|
|
57
|
+
export const BIDI_TIMEOUT_MS = 30_000;
|
|
58
|
+
/** How long to wait for the driver service to report ready after spawning. */
|
|
59
|
+
export const DRIVER_READINESS_TIMEOUT_MS = 5_000;
|
|
60
|
+
/**
|
|
61
|
+
* Readiness deadline for geckodriver specifically — it takes longer than
|
|
62
|
+
* chromedriver to spin up Marionette and start listening.
|
|
63
|
+
*/
|
|
64
|
+
export const FIREFOX_READINESS_TIMEOUT_MS = 15_000;
|
|
65
|
+
/** Timeout for the `<driver|browser> --version` capability probe spawn. */
|
|
66
|
+
export const VERSION_PROBE_TIMEOUT_MS = 5_000;
|
|
67
|
+
/** Timeout for the `which` / `where` PATH-probe spawn. */
|
|
68
|
+
export const PATH_PROBE_TIMEOUT_MS = 2_000;
|
|
69
|
+
// ── Fixed delays ─────────────────────────────────────────────────────────────
|
|
70
|
+
/** Best-effort settle used as the Classic fallback for `networkidle`. */
|
|
71
|
+
export const NETWORK_IDLE_SETTLE_MS = 500;
|
|
72
|
+
/**
|
|
73
|
+
* Pause between `driver.quit()` and stopping the driver service, giving the
|
|
74
|
+
* browser time to release its ports (notably Firefox's BiDi WebSocket) before a
|
|
75
|
+
* subsequent launch reuses them; without it a fresh launch can hit a 404.
|
|
76
|
+
*/
|
|
77
|
+
export const PORT_RELEASE_DELAY_MS = 500;
|
|
78
|
+
/** Delay before a BiDi connection auto-reconnect attempt. */
|
|
79
|
+
export const BIDI_RECONNECT_DELAY_MS = 1_000;
|
|
80
|
+
// ── Retry policy ─────────────────────────────────────────────────────────────
|
|
81
|
+
/**
|
|
82
|
+
* BiDi connect retries. Firefox may not have finished binding its BiDi
|
|
83
|
+
* WebSocket right after the session is created; back off linearly between
|
|
84
|
+
* attempts (delay = {@link BIDI_CONNECT_BACKOFF_STEP_MS} × attempt).
|
|
85
|
+
*/
|
|
86
|
+
export const BIDI_CONNECT_MAX_ATTEMPTS = 8;
|
|
87
|
+
export const BIDI_CONNECT_BACKOFF_STEP_MS = 300;
|
|
88
|
+
/**
|
|
89
|
+
* WebDriver session-creation retries. Firefox's Marionette interface can lag
|
|
90
|
+
* geckodriver reporting healthy, so retry with linear backoff (delay =
|
|
91
|
+
* {@link SESSION_CREATE_BACKOFF_STEP_MS} × attempt); Chrome is reliable enough
|
|
92
|
+
* to need a single attempt.
|
|
93
|
+
*/
|
|
94
|
+
export const FIREFOX_SESSION_MAX_ATTEMPTS = 4;
|
|
95
|
+
export const CHROME_SESSION_MAX_ATTEMPTS = 1;
|
|
96
|
+
export const SESSION_CREATE_BACKOFF_STEP_MS = 500;
|
|
97
|
+
/**
|
|
98
|
+
* `evaluate()` retries on a transient "execution contexts cleared" BiDi error.
|
|
99
|
+
* A Classic-first navigate returns at `readyState === 'complete'`, which is not
|
|
100
|
+
* a barrier the BiDi side respects — an immediately following `{ context }`
|
|
101
|
+
* script call can land while BiDi is still swapping the old realm for the new
|
|
102
|
+
* one. The error is pre-execution (the script never ran, no side effects), so a
|
|
103
|
+
* short retry that re-resolves the context is safe. See
|
|
104
|
+
* plans/TODO-bidi-first-navigation.md (Option A).
|
|
105
|
+
*/
|
|
106
|
+
export const EVAL_REALM_RETRY_ATTEMPTS = 3;
|
|
107
|
+
export const EVAL_REALM_RETRY_DELAY_MS = 25;
|
|
108
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/lib/timing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,wEAAwE;AACxE,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,gFAAgF;AAEhF,iFAAiF;AACjF,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAEpD,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AAEtC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAE3C,gFAAgF;AAEhF,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAE7C,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC"}
|
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;
|
|
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;AAED,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;CA8BtE;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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WebElement } from './webelement.js';
|
|
2
2
|
import { CraftdriverError, ErrorCode } from './errors.js';
|
|
3
|
+
import { DEFAULT_POLL_INTERVAL_MS, DEFAULT_ELEMENT_TIMEOUT_MS } from './timing.js';
|
|
3
4
|
export class WebDriverWait {
|
|
4
5
|
driver;
|
|
5
6
|
timeoutMs;
|
|
@@ -8,13 +9,13 @@ export class WebDriverWait {
|
|
|
8
9
|
constructor(driver, timeoutOrOptions, intervalMs) {
|
|
9
10
|
this.driver = driver;
|
|
10
11
|
if (typeof timeoutOrOptions === 'object') {
|
|
11
|
-
this.timeoutMs = timeoutOrOptions?.timeout ??
|
|
12
|
-
this.intervalMs = timeoutOrOptions?.interval ??
|
|
12
|
+
this.timeoutMs = timeoutOrOptions?.timeout ?? DEFAULT_ELEMENT_TIMEOUT_MS;
|
|
13
|
+
this.intervalMs = timeoutOrOptions?.interval ?? DEFAULT_POLL_INTERVAL_MS;
|
|
13
14
|
this.timeoutMsg = timeoutOrOptions?.timeoutMsg;
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
16
|
-
this.timeoutMs = timeoutOrOptions ??
|
|
17
|
-
this.intervalMs = intervalMs ??
|
|
17
|
+
this.timeoutMs = timeoutOrOptions ?? DEFAULT_ELEMENT_TIMEOUT_MS;
|
|
18
|
+
this.intervalMs = intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
async until(condition, message) {
|
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;
|
|
1
|
+
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"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"}
|
package/docs/bidi-features.md
CHANGED
|
@@ -19,8 +19,8 @@ in Classic mode.
|
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| Network mocking / interception | [`browser.network.*`](#network-mocking) | yes |
|
|
21
21
|
| Console & error log capture | [`browser.logs.*`](#console--error-logs) | yes |
|
|
22
|
-
| `waitForLoadState('load' \| 'domcontentloaded' \| 'networkidle')` | `browser.waitForLoadState()` |
|
|
23
|
-
| `navigateTo(..., { waitUntil })` real load events | `browser.navigateTo()` |
|
|
22
|
+
| `waitForLoadState('load' \| 'domcontentloaded' \| 'networkidle')` | `browser.waitForLoadState()` | no — event-driven over BiDi, polls `document.readyState` in Classic |
|
|
23
|
+
| `navigateTo(..., { waitUntil })` real load events | `browser.navigateTo()` | no — event-driven over BiDi, best-effort settle timer in Classic |
|
|
24
24
|
| `waitForRequest()` / `waitForResponse()` | `browser.waitForRequest()` / `waitForResponse()` | yes |
|
|
25
25
|
| Init scripts (run before any page script) | `browser.addInitScript()` | yes |
|
|
26
26
|
| Open new tab / popup | `browser.openPage()` | yes |
|
|
@@ -229,6 +229,14 @@ waitForResponse("**/api/users") timed out after 30000ms
|
|
|
229
229
|
|
|
230
230
|
Access browser console output and JavaScript errors via `browser.logs`.
|
|
231
231
|
|
|
232
|
+
> **Capture is lazy by default.** Craftdriver only subscribes to log events
|
|
233
|
+
> the first time you touch `browser.logs.onLog()` / `.onConsole()` /
|
|
234
|
+
> `.onError()` / `.on()` / `.waitForConsole()` / `.waitForError()` — messages
|
|
235
|
+
> emitted before that first touch are not captured, so a bare
|
|
236
|
+
> `browser.logs.getMessages()` right after `navigateTo()` can return `[]`.
|
|
237
|
+
> Either arm a listener before the action that logs, or launch with
|
|
238
|
+
> `Browser.launch({ captureLogs: true })` to start capture immediately.
|
|
239
|
+
|
|
232
240
|
### getMessages()
|
|
233
241
|
|
|
234
242
|
Get all console messages.
|
package/docs/browser-api.md
CHANGED
|
@@ -28,7 +28,8 @@ const browser = await Browser.launch({
|
|
|
28
28
|
| Option | Type | Default | Description |
|
|
29
29
|
|--------|------|---------|-------------|
|
|
30
30
|
| `browserName` | `'chrome' \| 'chromium' \| 'firefox'` | `'chrome'` | Browser to launch |
|
|
31
|
-
| `enableBiDi` | `boolean` | `true` | Use WebDriver BiDi. Set `false` only when running against a browser that does not support it. |
|
|
31
|
+
| `enableBiDi` | `boolean` | `true` | Use WebDriver BiDi. Set `false` only when running against a browser that does not support it — every BiDi-only feature then throws a `requires BiDi` error if called. |
|
|
32
|
+
| `captureLogs` | `boolean` | `false` | Start console/error log capture immediately at launch instead of lazily on first `browser.logs`/`onConsole`/`onError`/`waitForConsole` touch. See [BiDi Features — Console & Error Logs](./bidi-features.md#console--error-logs). |
|
|
32
33
|
| `storageState` | `string` | — | Path to a session-state JSON file to load on startup |
|
|
33
34
|
| `mobileEmulation` | `MobileEmulation \| DeviceName` | — | Mobile device emulation settings (Chrome/Chromium only) |
|
|
34
35
|
| `downloadsDir` | `string` | temp dir | Directory where downloaded files are saved |
|
package/docs/browser-context.md
CHANGED
|
@@ -333,13 +333,12 @@ Requests from other contexts are not seen.
|
|
|
333
333
|
|
|
334
334
|
- `pattern`: a URL substring, a `RegExp`, or a glob (`*` matches any
|
|
335
335
|
run of non-`/` characters; `**` matches any run of characters).
|
|
336
|
-
- `handler(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
- `abort(reason?)` — fail the request.
|
|
336
|
+
- `handler(request)`: called for every matched request, receiving an
|
|
337
|
+
`InterceptedRequest` (`url`, `method`, `headers`, `postData`, …).
|
|
338
|
+
Return a `MockResponse` (`{ status?, statusText?, headers?, body? }`)
|
|
339
|
+
to fulfill the request immediately without hitting the network, or
|
|
340
|
+
return/resolve `void` to let it continue to the network unmodified.
|
|
341
|
+
The handler may be async.
|
|
343
342
|
|
|
344
343
|
```typescript
|
|
345
344
|
// Real-world: serve a fixture to one tenant, let the real API answer
|
|
@@ -347,13 +346,11 @@ Requests from other contexts are not seen.
|
|
|
347
346
|
const admin = await browser.newContext();
|
|
348
347
|
const guest = await browser.newContext();
|
|
349
348
|
|
|
350
|
-
await admin.route('**/api/users',
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
});
|
|
356
|
-
});
|
|
349
|
+
await admin.route('**/api/users', (request) => ({
|
|
350
|
+
status: 200,
|
|
351
|
+
headers: { 'content-type': 'application/json' },
|
|
352
|
+
body: [{ id: 1, role: 'admin', name: 'Alice' }],
|
|
353
|
+
}));
|
|
357
354
|
|
|
358
355
|
const aPage = await admin.newPage({ url: '/users' });
|
|
359
356
|
const gPage = await guest.newPage({ url: '/users' });
|
package/docs/dialogs.md
CHANGED
|
@@ -11,6 +11,13 @@ or the automation handles them.
|
|
|
11
11
|
> failures so unexpected dialogs surface as broken tests rather than
|
|
12
12
|
> passing tests against a stuck browser.
|
|
13
13
|
|
|
14
|
+
> **`waitForDialog()` and `onDialog()` require BiDi.** Both are built on
|
|
15
|
+
> BiDi's dialog-open event; under Classic WebDriver (`enableBiDi: false`)
|
|
16
|
+
> `onDialog()` silently registers a no-op and `waitForDialog()` will simply
|
|
17
|
+
> time out rather than error, since Classic has no push notification for
|
|
18
|
+
> "a dialog opened." Use the imperative `acceptDialog()` / `dismissDialog()`
|
|
19
|
+
> / `getDialogMessage()` API instead if you're running Classic-only.
|
|
20
|
+
|
|
14
21
|
You handle a dialog one of three ways, depending on the shape of the
|
|
15
22
|
test:
|
|
16
23
|
|
|
@@ -89,9 +96,13 @@ await browser.dismissDialog(); // click Cancel / close
|
|
|
89
96
|
const text = await browser.getDialogMessage();
|
|
90
97
|
```
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
(
|
|
94
|
-
|
|
99
|
+
`acceptDialog` / `dismissDialog` use BiDi's `handleUserPrompt` when the
|
|
100
|
+
browser was launched with BiDi (the default), falling back to the W3C
|
|
101
|
+
Classic alert endpoints (`/alert/accept`, `/alert/dismiss`) only when
|
|
102
|
+
`enableBiDi: false`. `getDialogMessage` always uses the Classic
|
|
103
|
+
`/alert/text` endpoint — BiDi only surfaces the dialog message via the
|
|
104
|
+
`userPromptOpened` event, not a query, so there's nothing to query outside
|
|
105
|
+
`onDialog`/`waitForDialog`. All four throw if no dialog is currently open.
|
|
95
106
|
|
|
96
107
|
## `Dialog` interface
|
|
97
108
|
|
|
@@ -7,13 +7,25 @@ Craftdriver resolves the WebDriver binary through a chain — first match wins:
|
|
|
7
7
|
| 1 | `driverPath` option in `Browser.launch()` |
|
|
8
8
|
| 2 | `CRAFTDRIVER_CHROMEDRIVER_PATH` / `CRAFTDRIVER_GECKODRIVER_PATH` env var |
|
|
9
9
|
| 3 | `CRAFTDRIVER_DRIVER_PATH` env var (generic fallback for either browser) |
|
|
10
|
-
| 4 | `
|
|
11
|
-
| 5 |
|
|
12
|
-
| 6 |
|
|
10
|
+
| 4 | Legacy/Selenium-compatible env vars: `CHROMEDRIVER_PATH`, `SE_CHROMEDRIVER` (chromedriver) or `GECKODRIVER_PATH`, `GECKODRIVER_FILEPATH`, `SE_GECKODRIVER` (geckodriver) |
|
|
11
|
+
| 5 | **Cached auto-resolution** — the path a previous auto-resolve settled on, reused within the `CRAFTDRIVER_DRIVER_TTL` window |
|
|
12
|
+
| 6 | `chromedriver` / `geckodriver` in `node_modules/.bin` |
|
|
13
|
+
| 7 | `chromedriver` / `geckodriver` on `PATH` |
|
|
14
|
+
| 8 | **Auto-download from Chrome for Testing / GitHub** ← the zero-config default |
|
|
13
15
|
|
|
14
|
-
Downloaded drivers are cached in `~/.cache/craftdriver
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
Downloaded drivers are cached in `~/.cache/craftdriver`, and so is the
|
|
17
|
+
*resolution itself* — which driver path to use. Within the
|
|
18
|
+
`CRAFTDRIVER_DRIVER_TTL` window (default 24 h), craftdriver reuses the
|
|
19
|
+
resolved path directly and skips the system-browser probes it would
|
|
20
|
+
otherwise run on **every** launch: launching the browser binary just to read
|
|
21
|
+
its version string, and a `PATH` lookup. Both are blocking calls, so caching
|
|
22
|
+
the resolution measurably speeds up launch — most noticeably when several
|
|
23
|
+
browsers start in parallel (see [Performance](#performance)). After the TTL
|
|
24
|
+
expires, or if Chrome reports that the cached chromedriver is for the wrong
|
|
25
|
+
major version after a browser upgrade, the driver is re-resolved and
|
|
26
|
+
re-downloaded if needed. Only the driver binary is ever downloaded, never the
|
|
27
|
+
browser itself. Explicit configuration (steps 1–4) always takes precedence over
|
|
28
|
+
the cache.
|
|
17
29
|
|
|
18
30
|
## Environment variables
|
|
19
31
|
|
|
@@ -24,7 +36,7 @@ never the browser itself.
|
|
|
24
36
|
| `CRAFTDRIVER_DRIVER_PATH` | Generic fallback path (either browser) | — |
|
|
25
37
|
| `CRAFTDRIVER_CACHE_DIR` | Directory for cached driver downloads | `~/.cache/craftdriver` |
|
|
26
38
|
| `CRAFTDRIVER_OFFLINE` | Set to `1` to disable all network calls | — |
|
|
27
|
-
| `CRAFTDRIVER_DRIVER_TTL` |
|
|
39
|
+
| `CRAFTDRIVER_DRIVER_TTL` | Driver-resolution cache lifetime, in seconds (both browsers). `0` disables the cache | `86400` (24 h) |
|
|
28
40
|
|
|
29
41
|
## Examples
|
|
30
42
|
|
|
@@ -35,7 +47,7 @@ CRAFTDRIVER_CHROMEDRIVER_PATH=/usr/bin/chromedriver npm test
|
|
|
35
47
|
# Pin a specific geckodriver
|
|
36
48
|
CRAFTDRIVER_GECKODRIVER_PATH=/usr/local/bin/geckodriver npm test
|
|
37
49
|
|
|
38
|
-
# Never make a network call (requires a local driver to exist in steps 1–
|
|
50
|
+
# Never make a network call (requires a local driver to exist in steps 1–7)
|
|
39
51
|
CRAFTDRIVER_OFFLINE=1 npm test
|
|
40
52
|
|
|
41
53
|
# Change the cache location
|
|
@@ -45,6 +57,112 @@ CRAFTDRIVER_CACHE_DIR=/tmp/my-driver-cache npm test
|
|
|
45
57
|
CRAFTDRIVER_DRIVER_TTL=3600 npm test
|
|
46
58
|
```
|
|
47
59
|
|
|
60
|
+
## Performance
|
|
61
|
+
|
|
62
|
+
Most of the time `Browser.launch()` spends is the browser process starting up
|
|
63
|
+
(that's the same for WebDriver Classic and BiDi and there's little a client
|
|
64
|
+
library can do about it). The part craftdriver *does* control is resolving and
|
|
65
|
+
starting the driver, and it's tuned to stay out of the way:
|
|
66
|
+
|
|
67
|
+
- **Driver resolution is cached** (see above). Without a cache, resolving a
|
|
68
|
+
chromedriver means launching your Chrome binary just to read its version
|
|
69
|
+
string — a blocking call of a few hundred milliseconds on *every* launch.
|
|
70
|
+
The TTL cache skips that after the first launch.
|
|
71
|
+
- **Point at a driver explicitly to skip resolution entirely.** If you set
|
|
72
|
+
`CRAFTDRIVER_CHROMEDRIVER_PATH` / `CRAFTDRIVER_GECKODRIVER_PATH` (or pass
|
|
73
|
+
`chromeService: new ChromeService({ binaryPath })`), craftdriver uses it
|
|
74
|
+
directly — no version detection, no `PATH` lookup, no cache read. This is
|
|
75
|
+
the fastest and most deterministic option and is recommended for CI:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
CRAFTDRIVER_CHROMEDRIVER_PATH=/opt/chromedriver/chromedriver npm test
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- **Parallel runs benefit the most.** The resolution work that the cache (or an
|
|
82
|
+
explicit path) removes was synchronous and blocked the event loop, so it
|
|
83
|
+
serialized when several browsers were launched at once. Removing it lets
|
|
84
|
+
concurrent `Browser.launch()` calls overlap their startup (measured ~17–20%
|
|
85
|
+
faster wall time for a batch of concurrent launches).
|
|
86
|
+
|
|
87
|
+
### Concurrency and oversubscription
|
|
88
|
+
|
|
89
|
+
Once resolution is out of the way, the rest of launch time is the browser
|
|
90
|
+
process starting — and that is **CPU-bound**. Starting many browsers at once
|
|
91
|
+
on a machine with fewer CPU cores oversubscribes the CPU, and each browser's
|
|
92
|
+
startup slows down roughly in proportion. On an 8-core machine, for example, a
|
|
93
|
+
single launch is ~2s but 20 simultaneous launches take ~25s *each* (they still
|
|
94
|
+
finish sooner in aggregate than launching serially, just with diminishing
|
|
95
|
+
returns). This is not a craftdriver limitation — it's the browser competing for
|
|
96
|
+
CPU — and no client-side change removes it.
|
|
97
|
+
|
|
98
|
+
Practical guidance for parallel test suites (e.g. Vitest / Jest / Playwright
|
|
99
|
+
Test): **cap worker concurrency at roughly the number of CPU cores.** More
|
|
100
|
+
workers than cores mostly adds launch latency, and a launch that is merely slow
|
|
101
|
+
under heavy load can trip a short per-test/hook timeout and look like a hang.
|
|
102
|
+
With Vitest, set `maxWorkers` (or `poolOptions`) accordingly; a generous
|
|
103
|
+
`hookTimeout` for the `beforeAll` that launches the browser also helps on
|
|
104
|
+
loaded CI runners.
|
|
105
|
+
|
|
106
|
+
Indicative numbers from the `tests/perf/launch-critical-path.perf.ts`
|
|
107
|
+
benchmark (macOS, Chrome, headless — absolute values are machine-dependent,
|
|
108
|
+
but the direction holds):
|
|
109
|
+
|
|
110
|
+
| Scenario | Before | After |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `Browser.launch()` (BiDi) | ~2760ms | ~2240ms |
|
|
113
|
+
| `Browser.launch()` (Classic) | ~2400ms | ~1860ms |
|
|
114
|
+
| 4 browsers launched concurrently | ~6450ms | ~5130ms |
|
|
115
|
+
|
|
116
|
+
Run it yourself with `npm run bench -- launch-critical-path`.
|
|
117
|
+
|
|
118
|
+
### Browser startup flags (advanced, opt-in)
|
|
119
|
+
|
|
120
|
+
craftdriver launches the browser with **no performance flags of its own** — it
|
|
121
|
+
stays unopinionated so it never silently changes browser behavior underneath
|
|
122
|
+
you. If you want to experiment, you can pass extra browser command-line flags
|
|
123
|
+
via the `args` launch option (appended to `goog:chromeOptions.args` for
|
|
124
|
+
Chrome/Chromium, `moz:firefoxOptions.args` for Firefox):
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const browser = await Browser.launch({
|
|
128
|
+
browserName: 'chrome',
|
|
129
|
+
args: [
|
|
130
|
+
'--no-first-run',
|
|
131
|
+
'--no-default-browser-check',
|
|
132
|
+
'--disable-background-networking',
|
|
133
|
+
'--disable-component-update',
|
|
134
|
+
'--disable-default-apps',
|
|
135
|
+
'--disable-extensions',
|
|
136
|
+
'--disable-sync',
|
|
137
|
+
'--metrics-recording-only',
|
|
138
|
+
'--disable-background-timer-throttling',
|
|
139
|
+
'--disable-backgrounding-occluded-windows',
|
|
140
|
+
'--disable-renderer-backgrounding',
|
|
141
|
+
'--mute-audio',
|
|
142
|
+
'--no-service-autorun',
|
|
143
|
+
'--password-store=basic',
|
|
144
|
+
'--use-mock-keychain', // macOS: skip keychain access
|
|
145
|
+
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints',
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> `args` are **browser** flags. They are distinct from the **driver**
|
|
151
|
+
> (chromedriver/geckodriver) args you'd pass via `chromeService` /
|
|
152
|
+
> `firefoxService` below.
|
|
153
|
+
|
|
154
|
+
**Set expectations honestly:** on a normal local machine this set moved
|
|
155
|
+
`Browser.launch()` wall time by **~0.4% (≈8ms — noise)** in our measurements.
|
|
156
|
+
Cold browser startup is dominated by unavoidable process/engine init; these
|
|
157
|
+
flags mostly suppress *background* work (auto-updates, telemetry, background
|
|
158
|
+
networking, sync) that happens after startup rather than on the launch critical
|
|
159
|
+
path. So their real value is **determinism and avoiding intermittent stalls in
|
|
160
|
+
CI / constrained environments** (no update popups, no background network
|
|
161
|
+
calls), not raw local launch speed. If you adopt them, **measure on your own
|
|
162
|
+
environment** — the payoff is environment-dependent, and some flags can change
|
|
163
|
+
behavior (e.g. `--no-sandbox`, or `--disable-features=...` entries a page or
|
|
164
|
+
fixture relies on).
|
|
165
|
+
|
|
48
166
|
## Pinning via code
|
|
49
167
|
|
|
50
168
|
For tighter control (custom port, extra driver flags), pass a `ChromeService`
|
package/docs/emulation.md
CHANGED
|
@@ -25,12 +25,16 @@ await browser.emulate({
|
|
|
25
25
|
|
|
26
26
|
| Option | What the page sees | Transport | Cross-browser |
|
|
27
27
|
|---|---|---|---|
|
|
28
|
-
| `colorScheme` | `prefers-color-scheme` media query |
|
|
29
|
-
| `reducedMotion` | `prefers-reduced-motion` media query |
|
|
30
|
-
| `forcedColors` | `forced-colors` media query |
|
|
28
|
+
| `colorScheme` | `prefers-color-scheme` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
|
|
29
|
+
| `reducedMotion` | `prefers-reduced-motion` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
|
|
30
|
+
| `forcedColors` | `forced-colors` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
|
|
31
31
|
| `locale` | `navigator.language`, `Intl.*` formatting | BiDi `emulation.setLocaleOverride` | yes |
|
|
32
32
|
| `timezoneId` | `Intl.DateTimeFormat`, `Date` offsets | BiDi `emulation.setTimezoneOverride` | yes |
|
|
33
|
-
| `offline` | `navigator.onLine`, network stack |
|
|
33
|
+
| `offline` | `navigator.onLine`, network stack | CDP `Network.emulateNetworkConditions` (over the BiDi+CDP bridge) | Chromium only |
|
|
34
|
+
|
|
35
|
+
All options still require an active BiDi session (`enableBiDi: true`, the
|
|
36
|
+
default) — the four CDP-backed options ride over a CDP bridge that BiDi
|
|
37
|
+
opens, they don't work with BiDi disabled.
|
|
34
38
|
|
|
35
39
|
- Pass `null` for any field to clear that override.
|
|
36
40
|
- Calling `emulate({})` is a no-op.
|
package/docs/error-codes.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Error codes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Nearly every error thrown from the public craftdriver API is a
|
|
4
4
|
[`CraftdriverError`](../src/lib/errors.ts), carrying:
|
|
5
5
|
|
|
6
6
|
- `code` — a stable, machine-readable identifier from the table below.
|
|
@@ -12,6 +12,11 @@ Every error thrown from the public craftdriver API is a
|
|
|
12
12
|
`CraftdriverError extends Error`, so `instanceof Error` keeps working
|
|
13
13
|
and stack traces are preserved.
|
|
14
14
|
|
|
15
|
+
> A few edge cases still throw a plain `Error` instead — e.g. calling
|
|
16
|
+
> `startTrace()` again while a trace is already running, or without
|
|
17
|
+
> `outDir`. Guard against these with normal usage (don't call `startTrace()`
|
|
18
|
+
> twice without `stopTrace()`) rather than a `code` check.
|
|
19
|
+
|
|
15
20
|
```ts
|
|
16
21
|
import { CraftdriverError, ErrorCode } from 'craftdriver';
|
|
17
22
|
|
|
@@ -38,11 +43,11 @@ try {
|
|
|
38
43
|
| `TIMEOUT` | Generic `WebDriverWait.until(...)` timeout with no more specific code. | Same as above — check the condition. |
|
|
39
44
|
| `EXPECT_MISMATCH` | An `expect(locator).to…()` assertion failed after auto-waiting. | Inspect `error.detail` for the selector and observed value. |
|
|
40
45
|
| `A11Y_VIOLATIONS` | `browser.a11y.check()` (or scoped variants) found axe-core violations. | Iterate over `error.violations` — each has an `id`, `impact`, and `helpUrl`. |
|
|
41
|
-
| `EVAL_THREW` | The function passed to `evaluate()` threw inside the page. | The page-side exception text is in `error.detail.exception`. |
|
|
46
|
+
| `EVAL_THREW` | The function passed to `evaluate()` threw inside the page. Also fires for `browser.clock` methods (`tick()`, `setSystemTime()`, `runFor()`) called before `install()` — they run as in-page scripts under the hood. | The page-side exception text is in `error.detail.exception`. |
|
|
42
47
|
| `EVAL_BAD_ARG` | `evaluate()` / `addInitScript()` received a non-JSON-serializable argument (function, Symbol, DOM node…). | Pass plain JSON values. |
|
|
43
48
|
| `INVALID_ARGUMENT` | Caller passed an invalid value (bad enum, wrong shape, unparseable duration…). | Read the message; it lists the accepted forms. |
|
|
44
49
|
| `UNSUPPORTED` | Feature exists but is unavailable on this browser/transport (e.g. Chromium-only over Firefox, or a BiDi-only feature with BiDi disabled). | Enable BiDi (`enableBiDi: true`) or switch browser. |
|
|
45
|
-
| `STATE_INVALID` | Method called in the wrong state (e.g. `stopTrace()` without `startTrace()
|
|
50
|
+
| `STATE_INVALID` | Method called in the wrong state (e.g. `stopTrace()` without `startTrace()`). | Call the prerequisite first. |
|
|
46
51
|
| `DRIVER_ERROR` | Driver-level / transport-level failure surfaced to the caller. | Inspect `error.cause`. |
|
|
47
52
|
|
|
48
53
|
## Stability
|