craftdriver 1.4.0 → 1.5.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 +10 -3
- package/README.md +4 -2
- package/dist/cli/parseArgs.d.ts +2 -2
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +13 -0
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +81 -32
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/browser.d.ts +27 -1
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +142 -74
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +2 -2
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +1 -1
- package/dist/lib/builder.d.ts +4 -1
- package/dist/lib/builder.d.ts.map +1 -1
- package/dist/lib/builder.js +20 -2
- package/dist/lib/builder.js.map +1 -1
- package/dist/lib/capabilities.d.ts +12 -2
- package/dist/lib/capabilities.d.ts.map +1 -1
- package/dist/lib/capabilities.js +84 -39
- package/dist/lib/capabilities.js.map +1 -1
- package/dist/lib/driver.d.ts +16 -1
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +64 -0
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/driverManager.d.ts +28 -0
- package/dist/lib/driverManager.d.ts.map +1 -1
- package/dist/lib/driverManager.js +60 -0
- package/dist/lib/driverManager.js.map +1 -1
- package/dist/lib/launchTarget.d.ts +1 -1
- package/dist/lib/launchTarget.d.ts.map +1 -1
- package/dist/lib/launchTarget.js +65 -3
- package/dist/lib/launchTarget.js.map +1 -1
- package/dist/lib/safari.d.ts +90 -0
- package/dist/lib/safari.d.ts.map +1 -0
- package/dist/lib/safari.js +136 -0
- package/dist/lib/safari.js.map +1 -0
- package/dist/lib/tracing.d.ts +1 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js.map +1 -1
- package/dist/lib/types.d.ts +41 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/vibiumTrace.d.ts +1 -1
- package/dist/lib/vibiumTrace.d.ts.map +1 -1
- package/dist/lib/webelement.d.ts.map +1 -1
- package/dist/lib/webelement.js +118 -2
- package/dist/lib/webelement.js.map +1 -1
- package/docs/api-reference.md +3 -1
- package/docs/driver-configuration.md +54 -0
- package/docs/getting-started.md +30 -10
- package/docs/index.md +1 -1
- package/docs/public/examples/displayed.html +86 -0
- package/docs/safari.md +118 -0
- package/docs/standards.md +13 -0
- package/docs/why-craftdriver.md +1 -1
- package/docs/zero-config-drivers.md +12 -0
- package/package.json +3 -1
- package/skills/craftdriver/SKILL.md +4 -1
- package/skills/craftdriver/cheatsheet.md +4 -1
package/dist/lib/webelement.js
CHANGED
|
@@ -1,6 +1,106 @@
|
|
|
1
1
|
import { HttpClient } from './http.js';
|
|
2
2
|
export const W3C_ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf';
|
|
3
3
|
export const LEGACY_ELEMENT_KEY = 'ELEMENT';
|
|
4
|
+
/**
|
|
5
|
+
* W3C-compatible "is element displayed" algorithm, executed via
|
|
6
|
+
* `POST /execute/sync`. Faithfully reproduces the behavior of Selenium's
|
|
7
|
+
* `bot.dom.isShown_` (the atom chromedriver and geckodriver's native
|
|
8
|
+
* `is displayed` commands are compiled from), so results match the browsers
|
|
9
|
+
* that currently answer via the legacy `/element/{id}/displayed` endpoint —
|
|
10
|
+
* while also working on Safari, whose command table omits that endpoint.
|
|
11
|
+
*
|
|
12
|
+
* Written in ES5-compatible style (plain `function`s, `var`, `for` loops) to
|
|
13
|
+
* match the other scripts sent to `/execute/sync` in this codebase and to run
|
|
14
|
+
* safely across every supported Classic remote end. Receives the element as
|
|
15
|
+
* `arguments[0]` and returns a boolean.
|
|
16
|
+
*
|
|
17
|
+
* Rules, each mirroring the corresponding `bot.dom` behavior:
|
|
18
|
+
* - `<input type="hidden">` and `<noscript>` are never shown.
|
|
19
|
+
* - `display:none` on the element OR any ancestor hides it. `display` is not
|
|
20
|
+
* inherited, so the ancestor chain must be walked explicitly.
|
|
21
|
+
* - `visibility:hidden`/`collapse` hides it. `visibility` IS inherited, and a
|
|
22
|
+
* descendant may override an ancestor back to `visible`, so we check only the
|
|
23
|
+
* element's OWN computed value (getComputedStyle already resolves
|
|
24
|
+
* inheritance) — this both matches `bot.dom` and is more correct than walking
|
|
25
|
+
* the chain for visibility.
|
|
26
|
+
* - No positive-area box hides it, unless a child has one (zero-size wrappers)
|
|
27
|
+
* or it is a `<br>` (a legitimately zero-width line break). Mirrors
|
|
28
|
+
* `bot.dom.positiveSize_`.
|
|
29
|
+
* - Being clipped out of view by an `overflow:hidden` ancestor hides it,
|
|
30
|
+
* matching `bot.dom.OverflowState.HIDDEN`. An element scrolled out of a
|
|
31
|
+
* `scroll`/`auto` ancestor is still considered shown (it can be scrolled
|
|
32
|
+
* into view — `bot.dom.OverflowState.SCROLL`), so it is not treated as
|
|
33
|
+
* hidden here.
|
|
34
|
+
*/
|
|
35
|
+
const IS_DISPLAYED_ATOM = `
|
|
36
|
+
var elem = arguments[0];
|
|
37
|
+
if (!elem || elem.nodeType !== 1) { return false; }
|
|
38
|
+
|
|
39
|
+
var win = elem.ownerDocument.defaultView || window;
|
|
40
|
+
function style(e, prop) {
|
|
41
|
+
var cs = win.getComputedStyle(e, null);
|
|
42
|
+
return cs ? cs.getPropertyValue(prop) : '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// <input type="hidden"> is never shown (bot.dom.isShown_).
|
|
46
|
+
var tag = elem.tagName ? elem.tagName.toUpperCase() : '';
|
|
47
|
+
if (tag === 'INPUT' && (elem.type || '').toLowerCase() === 'hidden') { return false; }
|
|
48
|
+
// <noscript> is not rendered.
|
|
49
|
+
if (tag === 'NOSCRIPT') { return false; }
|
|
50
|
+
|
|
51
|
+
// display:none on the element or any ancestor hides it. display is NOT
|
|
52
|
+
// inherited, so the whole chain must be checked (bot.dom.displayed_).
|
|
53
|
+
var node = elem;
|
|
54
|
+
while (node && node.nodeType === 1) {
|
|
55
|
+
if (style(node, 'display') === 'none') { return false; }
|
|
56
|
+
node = node.parentElement;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// visibility:hidden/collapse hides it. visibility IS inherited but overridable
|
|
60
|
+
// by descendants, so checking the element's own computed value is correct.
|
|
61
|
+
var vis = style(elem, 'visibility');
|
|
62
|
+
if (vis === 'hidden' || vis === 'collapse') { return false; }
|
|
63
|
+
|
|
64
|
+
// An element with no positive-area box is not shown, unless a child has one
|
|
65
|
+
// (zero-size wrapper) or it is a <br> (bot.dom.positiveSize_).
|
|
66
|
+
function positiveSize(e) {
|
|
67
|
+
var r = e.getBoundingClientRect();
|
|
68
|
+
if (r.width > 0 && r.height > 0) { return true; }
|
|
69
|
+
if (e.tagName && e.tagName.toUpperCase() === 'BR') { return true; }
|
|
70
|
+
var kids = e.childNodes;
|
|
71
|
+
for (var i = 0; i < kids.length; i++) {
|
|
72
|
+
var k = kids[i];
|
|
73
|
+
if (k.nodeType === 1 && positiveSize(k)) { return true; }
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (!positiveSize(elem)) { return false; }
|
|
78
|
+
|
|
79
|
+
// Clipped out of view by an overflow:hidden ancestor => hidden
|
|
80
|
+
// (bot.dom.OverflowState.HIDDEN). scroll/auto ancestors are treated as shown
|
|
81
|
+
// (OverflowState.SCROLL — scrollable into view). position:fixed escapes
|
|
82
|
+
// ancestor clipping, so skip the check for fixed elements.
|
|
83
|
+
if (style(elem, 'position') !== 'fixed') {
|
|
84
|
+
var rect = elem.getBoundingClientRect();
|
|
85
|
+
var parent = elem.parentElement;
|
|
86
|
+
while (parent) {
|
|
87
|
+
var ox = style(parent, 'overflow-x');
|
|
88
|
+
var oy = style(parent, 'overflow-y');
|
|
89
|
+
if (ox === 'hidden' || oy === 'hidden' || ox === 'clip' || oy === 'clip') {
|
|
90
|
+
var pr = parent.getBoundingClientRect();
|
|
91
|
+
var clippedX = (ox === 'hidden' || ox === 'clip') &&
|
|
92
|
+
(rect.right <= pr.left || rect.left >= pr.right);
|
|
93
|
+
var clippedY = (oy === 'hidden' || oy === 'clip') &&
|
|
94
|
+
(rect.bottom <= pr.top || rect.top >= pr.bottom);
|
|
95
|
+
if (clippedX || clippedY) { return false; }
|
|
96
|
+
}
|
|
97
|
+
if (style(parent, 'position') === 'fixed') { break; }
|
|
98
|
+
parent = parent.parentElement;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return true;
|
|
103
|
+
`;
|
|
4
104
|
export class WebElement {
|
|
5
105
|
endpoint;
|
|
6
106
|
sessionId;
|
|
@@ -43,10 +143,26 @@ export class WebElement {
|
|
|
43
143
|
return { [W3C_ELEMENT_KEY]: this.elementId, [LEGACY_ELEMENT_KEY]: this.elementId };
|
|
44
144
|
}
|
|
45
145
|
async isDisplayed() {
|
|
146
|
+
// The legacy `GET /element/{id}/displayed` endpoint is a pre-W3C JSON Wire
|
|
147
|
+
// Protocol command. chromedriver/geckodriver still accept it for backward
|
|
148
|
+
// compatibility, but it was never part of W3C WebDriver Classic and is
|
|
149
|
+
// absent from Apple's Safari 12+ command table. We therefore compute
|
|
150
|
+
// displayedness the same way every real driver does under the hood: by
|
|
151
|
+
// executing the well-known "displayedness atom" via the standard
|
|
152
|
+
// `POST /execute/sync` command, which every Classic remote end supports.
|
|
153
|
+
//
|
|
154
|
+
// The script below mirrors Selenium's `bot.dom.isShown_`
|
|
155
|
+
// (selenium/javascript/atoms/dom.js) — the exact algorithm chromedriver's
|
|
156
|
+
// and geckodriver's `is displayed` implementations are compiled from, and
|
|
157
|
+
// the same one wdio/Playwright rely on. Keeping it byte-for-byte faithful
|
|
158
|
+
// in behavior is what preserves existing Chrome/Chromium/Firefox results;
|
|
159
|
+
// this is a protocol change, not a semantics change. Each rule is annotated
|
|
160
|
+
// with the `bot.dom` behavior it reproduces so a reviewer can verify it.
|
|
46
161
|
const client = new HttpClient(this.endpoint);
|
|
47
162
|
const res = await client.send({
|
|
48
|
-
method: '
|
|
49
|
-
path: `/session/${this.sessionId}/
|
|
163
|
+
method: 'POST',
|
|
164
|
+
path: `/session/${this.sessionId}/execute/sync`,
|
|
165
|
+
body: { script: IS_DISPLAYED_ATOM, args: [this.toJSON()] },
|
|
50
166
|
});
|
|
51
167
|
const value = res?.value ?? res;
|
|
52
168
|
return Boolean(value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webelement.js","sourceRoot":"","sources":["../../src/lib/webelement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAIvC,MAAM,CAAC,MAAM,eAAe,GAAG,qCAAqC,CAAC;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAE5C,MAAM,OAAO,UAAU;IAEX;IACA;IACA;IAHV,YACU,QAA2B,EAC3B,SAAiB,EACjB,SAAiB;QAFjB,aAAQ,GAAR,QAAQ,CAAmB;QAC3B,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAAQ;IACvB,CAAC;IAEL,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,YAAY,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,SAAS,QAAQ;YAClE,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAsC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACnF,MAAM,MAAM,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,YAAY,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,SAAS,QAAQ;YAClE,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAS;YACpC,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,YAAY,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,SAAS,OAAO;SAClE,CAAC,CAAC;QACH,MAAM,KAAK,GAAI,GAA+B,EAAE,KAAK,IAAK,GAAyB,CAAC;QACpF,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAU;YACrC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"webelement.js","sourceRoot":"","sources":["../../src/lib/webelement.ts"],"names":[],"mappings":"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"}
|
package/docs/api-reference.md
CHANGED
|
@@ -153,6 +153,8 @@ Handle stable CraftDriver errors or customize browser driver services.
|
|
|
153
153
|
| `ChromeServiceOptions` | type | — | [getting-started](getting-started.md) |
|
|
154
154
|
| `FirefoxService` | class | — | [getting-started](getting-started.md) |
|
|
155
155
|
| `FirefoxServiceOptions` | type | — | [getting-started](getting-started.md) |
|
|
156
|
+
| `SafariService` | class | — | [getting-started](getting-started.md) |
|
|
157
|
+
| `SafariServiceOptions` | type | — | [getting-started](getting-started.md) |
|
|
156
158
|
|
|
157
159
|
## Other Exports
|
|
158
160
|
|
|
@@ -166,4 +168,4 @@ Exports that are public but not yet assigned to a feature group.
|
|
|
166
168
|
| `ElectronRemoteOptions` | type | Extra context ElectronRemote needs beyond the inspector endpoint. | [electron](electron.md) |
|
|
167
169
|
| `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) |
|
|
168
170
|
|
|
169
|
-
Total exports: **
|
|
171
|
+
Total exports: **84**.
|
|
@@ -279,3 +279,57 @@ const browser = await Browser.launch({
|
|
|
279
279
|
}),
|
|
280
280
|
});
|
|
281
281
|
```
|
|
282
|
+
|
|
283
|
+
## Safari (macOS): driver ships with the browser
|
|
284
|
+
|
|
285
|
+
Everything above (steps 1–9, the download chain, the TTL cache) resolves
|
|
286
|
+
**chromedriver**/**geckodriver** — binaries craftdriver may fetch and cache on
|
|
287
|
+
disk. `browserName: 'safari'` doesn't fit that model at all: Apple ships
|
|
288
|
+
`safaridriver` as part of macOS/Safari itself, so there is no download, no
|
|
289
|
+
version-matching problem, and no cache to warm. This page covers only driver
|
|
290
|
+
**resolution**; for setup (`safaridriver --enable`), capabilities, limitations,
|
|
291
|
+
and Safari Technology Preview, see the [**Safari guide**](./safari.md).
|
|
292
|
+
|
|
293
|
+
`SafariService` resolves the `safaridriver` binary through its own chain —
|
|
294
|
+
first match wins, and none of these steps ever triggers a network call:
|
|
295
|
+
|
|
296
|
+
| Step | Source |
|
|
297
|
+
|---|---|
|
|
298
|
+
| 1 | `binaryPath` passed to `new SafariService({ binaryPath })` |
|
|
299
|
+
| 2 | `CRAFTDRIVER_SAFARIDRIVER_PATH` env var |
|
|
300
|
+
| 3 | `CRAFTDRIVER_DRIVER_PATH` env var (generic fallback, shared with Chrome/Firefox) |
|
|
301
|
+
| 4 | `/usr/bin/safaridriver` (the fixed system location on macOS) |
|
|
302
|
+
| 5 | `safaridriver` on `PATH` |
|
|
303
|
+
| 6 | a clear, actionable error if nothing resolves — craftdriver never downloads a `safaridriver` |
|
|
304
|
+
|
|
305
|
+
To pin **Safari Technology Preview**, point `binaryPath` at its bundled driver
|
|
306
|
+
(`/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver`) —
|
|
307
|
+
see the [Safari guide](./safari.md#safari-technology-preview).
|
|
308
|
+
|
|
309
|
+
### Extra `safaridriver` arguments
|
|
310
|
+
|
|
311
|
+
`SafariService` accepts `args`, passed through verbatim after `--port <port>`.
|
|
312
|
+
The most useful one is `--diagnose`, which turns on `safaridriver`'s
|
|
313
|
+
diagnostic logging (written under `~/Library/Logs/com.apple.WebDriver/`) —
|
|
314
|
+
handy when a Safari session fails to start and the driver's own stdout/stderr
|
|
315
|
+
isn't enough to tell why:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const browser = await Browser.launch({
|
|
319
|
+
browserName: 'safari',
|
|
320
|
+
safariService: new SafariService({ args: ['--diagnose'] }),
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### What's different from Chrome/Firefox here
|
|
325
|
+
|
|
326
|
+
- No `CRAFTDRIVER_CACHE_DIR`, `CRAFTDRIVER_DRIVER_TTL`, or `CRAFTDRIVER_OFFLINE`
|
|
327
|
+
behavior applies to Safari — there's nothing cached or downloaded to
|
|
328
|
+
configure.
|
|
329
|
+
- No browser-binary resolution chain (`browserPath` / `CRAFTDRIVER_CHROME_PATH`-
|
|
330
|
+
style) exists for Safari — you select which Safari build's driver to use via
|
|
331
|
+
`SafariService.binaryPath` directly, as shown above, not via a separate
|
|
332
|
+
browser-path option.
|
|
333
|
+
- `enableBiDi: true` is rejected before launch — Safari has no documented
|
|
334
|
+
WebDriver BiDi endpoint to negotiate. See
|
|
335
|
+
[WebDriver Standards → Safari is Classic-only](./standards.md#safari-is-classic-only).
|
package/docs/getting-started.md
CHANGED
|
@@ -75,29 +75,49 @@ returns the same `Page` the shortcuts would target.
|
|
|
75
75
|
|
|
76
76
|
## Launch Options
|
|
77
77
|
|
|
78
|
+
The simplest launch runs Chrome:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const browser = await Browser.launch(); // Chrome by default
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Pick a browser with `browserName`:
|
|
85
|
+
|
|
78
86
|
```typescript
|
|
79
|
-
// Chrome (default)
|
|
80
87
|
const browser = await Browser.launch({
|
|
81
|
-
browserName: '
|
|
88
|
+
browserName: 'firefox', // 'chrome' | 'chromium' | 'firefox' | 'safari'
|
|
82
89
|
storageState: './session.json', // optional: pre-load saved session state
|
|
83
90
|
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
To switch browsers from an environment variable in your own tests, read it
|
|
94
|
+
yourself and pass it to `browserName` — craftdriver doesn't read any browser
|
|
95
|
+
env var for you:
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
```typescript
|
|
86
98
|
const browser = await Browser.launch({
|
|
87
|
-
browserName: '
|
|
99
|
+
browserName: process.env.BROWSER_NAME ?? 'chrome',
|
|
88
100
|
});
|
|
89
101
|
```
|
|
90
102
|
|
|
91
|
-
###
|
|
103
|
+
### Safari
|
|
92
104
|
|
|
93
|
-
|
|
105
|
+
Safari runs on macOS with the same API, as a smaller Classic-only tier. Enable
|
|
106
|
+
automation once (`safaridriver --enable`) and launch:
|
|
94
107
|
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
```typescript
|
|
109
|
+
const browser = await Browser.launch({ browserName: 'safari' });
|
|
110
|
+
await browser.navigateTo('http://127.0.0.1:8080/login.html');
|
|
111
|
+
await browser.fill('#username', 'alice');
|
|
112
|
+
await browser.click('#submit');
|
|
113
|
+
await browser.quit();
|
|
99
114
|
```
|
|
100
115
|
|
|
116
|
+
Safari is macOS-only, headed, and serial (one session at a time), and doesn't
|
|
117
|
+
support BiDi-only features (network mocking, tracing, emulation, …). See the
|
|
118
|
+
[**Safari guide**](./safari.md) for the full picture — setup, what works, and
|
|
119
|
+
Safari Technology Preview.
|
|
120
|
+
|
|
101
121
|
### Custom WebDriver binary or port
|
|
102
122
|
|
|
103
123
|
`Browser.launch()` resolves the driver binary automatically. To pin a
|
package/docs/index.md
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<title>Craftdriver Displayedness</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
font-family: system-ui, Arial, sans-serif;
|
|
11
|
+
margin: 1rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* An ancestor hidden via display:none. Its descendant #hidden-by-ancestor
|
|
15
|
+
has no style of its own but must count as not displayed. */
|
|
16
|
+
#display-none-ancestor {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Own visibility:hidden. */
|
|
21
|
+
#visibility-hidden {
|
|
22
|
+
visibility: hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Zero-sized content: no rendered box, overflow clipped. */
|
|
26
|
+
#zero-size {
|
|
27
|
+
width: 0;
|
|
28
|
+
height: 0;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* A scrollable-but-CLIPPING (overflow:hidden) container. The child at the
|
|
33
|
+
bottom is scrolled out of the container's viewport, so it is clipped and
|
|
34
|
+
not displayed (bot.dom OverflowState.HIDDEN). */
|
|
35
|
+
#clip-container {
|
|
36
|
+
width: 120px;
|
|
37
|
+
height: 80px;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
border: 1px solid #ccc;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#clip-spacer {
|
|
43
|
+
height: 400px;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
|
|
48
|
+
<body>
|
|
49
|
+
<div class="container">
|
|
50
|
+
<!-- Plainly visible baseline. -->
|
|
51
|
+
<div id="visible-el">I am visible</div>
|
|
52
|
+
|
|
53
|
+
<!-- Hidden by a display:none ancestor. -->
|
|
54
|
+
<div id="display-none-ancestor">
|
|
55
|
+
<div id="hidden-by-ancestor">Hidden because my ancestor is display:none</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- Own display:none. -->
|
|
59
|
+
<div id="display-none-self" style="display: none">Hidden by my own display:none</div>
|
|
60
|
+
|
|
61
|
+
<!-- Own visibility:hidden. -->
|
|
62
|
+
<div id="visibility-hidden">Hidden by my own visibility:hidden</div>
|
|
63
|
+
|
|
64
|
+
<!-- Visibility hidden ancestor with a visible descendant override:
|
|
65
|
+
the child sets visibility:visible and MUST count as displayed. -->
|
|
66
|
+
<div style="visibility: hidden">
|
|
67
|
+
<div id="visibility-visible-override" style="visibility: visible">
|
|
68
|
+
Visible again despite hidden ancestor
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Zero-sized content. -->
|
|
73
|
+
<div id="zero-size">unrenderable</div>
|
|
74
|
+
|
|
75
|
+
<!-- A <br> is legitimately zero-width but should count as displayed. -->
|
|
76
|
+
<br id="line-break" />
|
|
77
|
+
|
|
78
|
+
<!-- Element scrolled out of an overflow:hidden container's viewport. -->
|
|
79
|
+
<div id="clip-container">
|
|
80
|
+
<div id="clip-spacer"></div>
|
|
81
|
+
<div id="scrolled-out-of-clip">I am below the clipped fold</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</body>
|
|
85
|
+
|
|
86
|
+
</html>
|
package/docs/safari.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Safari
|
|
2
|
+
|
|
3
|
+
CraftDriver drives **real desktop Safari on macOS** through W3C WebDriver
|
|
4
|
+
Classic.
|
|
5
|
+
|
|
6
|
+
## One-time setup
|
|
7
|
+
|
|
8
|
+
Safari's driver (`safaridriver`) ships with macOS — **nothing to install or
|
|
9
|
+
download**. You only have to turn on automation **once per machine**:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
safaridriver --enable
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This is the single prerequisite.
|
|
16
|
+
|
|
17
|
+
CraftDriver never runs `--enable` for you. If automation isn't enabled, a
|
|
18
|
+
launch fails immediately with that exact remedy rather than hanging.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Browser } from 'craftdriver';
|
|
24
|
+
|
|
25
|
+
const browser = await Browser.launch({ browserName: 'safari' });
|
|
26
|
+
await browser.navigateTo('http://127.0.0.1:8080/login.html');
|
|
27
|
+
await browser.getByLabel('Username').fill('alice');
|
|
28
|
+
await browser.getByLabel('Password').fill('secret');
|
|
29
|
+
await browser.getByRole('button', { name: 'Sign in' }).click();
|
|
30
|
+
await browser.expect('#welcome').toHaveText('Welcome back, alice!');
|
|
31
|
+
await browser.quit();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Same API as every other browser — locators, actions, assertions, and
|
|
35
|
+
auto-waiting all work unchanged.
|
|
36
|
+
|
|
37
|
+
## What works
|
|
38
|
+
|
|
39
|
+
Navigation, locators, and element queries · click / fill / clear / keyboard /
|
|
40
|
+
desktop mouse · `evaluate()` (sync & async JS) · frames and iframes · window &
|
|
41
|
+
popup enumeration · **imperative dialogs** (`getAlertText` / `acceptAlert` /
|
|
42
|
+
`dismissAlert` / `sendAlertText`) · viewport and element screenshots · cookies
|
|
43
|
+
and storage (via the standard Classic cookie endpoints) · accessibility checks.
|
|
44
|
+
|
|
45
|
+
## What isn't supported
|
|
46
|
+
|
|
47
|
+
Safari exposes no WebDriver BiDi endpoint, so everything event- or
|
|
48
|
+
BiDi-driven is unavailable — and each of these throws a clear
|
|
49
|
+
[`UNSUPPORTED`](./error-codes.md) error immediately, never a silent hang:
|
|
50
|
+
|
|
51
|
+
- network mocking / interception and request/response waits
|
|
52
|
+
- console & JavaScript-error capture, tracing
|
|
53
|
+
- BiDi user contexts and emulation (permissions, geolocation,
|
|
54
|
+
locale/timezone, offline, color-scheme)
|
|
55
|
+
- init/preload scripts and the virtual clock
|
|
56
|
+
- event-driven dialogs (`onDialog` / `waitForDialog`) — use the imperative
|
|
57
|
+
methods above
|
|
58
|
+
- full-page screenshots and `openPage()`
|
|
59
|
+
- CraftDriver-managed downloads (`waitForDownload`)
|
|
60
|
+
|
|
61
|
+
Desktop Safari is also **not** iPhone/iPad Safari — there is no mobile or
|
|
62
|
+
touch emulation.
|
|
63
|
+
|
|
64
|
+
## Limitations to plan around
|
|
65
|
+
|
|
66
|
+
- **macOS-only.** There is no Safari for Linux or Windows, and none is coming.
|
|
67
|
+
Trying to launch Safari off macOS throws `UNSUPPORTED`.
|
|
68
|
+
- **Headed only.** No headless mode — every launch opens a visible automation
|
|
69
|
+
window (with a purple banner). Don't interact with it while a test runs.
|
|
70
|
+
Setting `HEADLESS=true` with Safari is rejected up front.
|
|
71
|
+
- **Serial only.** Only one Safari WebDriver session can be active on a Mac at
|
|
72
|
+
a time. A second concurrent launch is refused with an actionable error
|
|
73
|
+
(never a hang) — run Safari tests serially.
|
|
74
|
+
|
|
75
|
+
## Safari Technology Preview
|
|
76
|
+
|
|
77
|
+
STP ships its own `safaridriver`; point `SafariService` at it. This is also
|
|
78
|
+
the one way to get a **second concurrent session** on one Mac — stable Safari
|
|
79
|
+
and STP each run one session at the same time.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Browser, SafariService } from 'craftdriver';
|
|
83
|
+
|
|
84
|
+
const browser = await Browser.launch({
|
|
85
|
+
browserName: 'safari',
|
|
86
|
+
safariService: new SafariService({
|
|
87
|
+
binaryPath: '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver',
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Enable automation once for STP too: `/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver --enable`.
|
|
93
|
+
|
|
94
|
+
## Running Safari tests
|
|
95
|
+
|
|
96
|
+
Run Safari **serially** — one session at a time. With Vitest that's
|
|
97
|
+
`--maxWorkers=1`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
vitest run --maxWorkers=1
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Keep it headed (don't set `HEADLESS`), on macOS with Remote Automation enabled.
|
|
104
|
+
In CI, use a macOS runner; parallelism across Safari needs **separate macOS
|
|
105
|
+
hosts**.
|
|
106
|
+
|
|
107
|
+
## Troubleshooting
|
|
108
|
+
|
|
109
|
+
| Symptom | Fix |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `You must enable 'Allow remote automation'…` | Run `safaridriver --enable` once (see [setup](#one-time-setup)). |
|
|
112
|
+
| Second launch fails while one session is open | Expected — Safari is serial. Serialize your tests, or use STP for a second session. |
|
|
113
|
+
| `Could not find 'safaridriver'` | You're not on macOS, or Safari is missing. For a non-standard install, set `CRAFTDRIVER_SAFARIDRIVER_PATH` or `SafariService({ binaryPath })`. |
|
|
114
|
+
|
|
115
|
+
## See also
|
|
116
|
+
|
|
117
|
+
- [Driver Configuration → Safari](./driver-configuration.md#safari-macos-driver-ships-with-the-browser) — the no-download driver-resolution chain.
|
|
118
|
+
- [WebDriver Standards](./standards.md#safari-is-classic-only) — why Safari is Classic-only.
|
package/docs/standards.md
CHANGED
|
@@ -16,6 +16,19 @@ That protocol choice is the center of the project. CraftDriver tries to provide
|
|
|
16
16
|
|
|
17
17
|
CraftDriver picks the practical protocol path behind a single public API. You call `browser.click()`, `browser.navigateTo()`, `browser.network.mock()`, or `browser.startTrace()`; the library handles the transport details.
|
|
18
18
|
|
|
19
|
+
### Safari is Classic-only
|
|
20
|
+
|
|
21
|
+
`browserName: 'safari'` always uses WebDriver Classic and never negotiates
|
|
22
|
+
`webSocketUrl`. This isn't a temporary gap CraftDriver plans to close — Apple
|
|
23
|
+
does not currently document a supported WebDriver BiDi endpoint for Safari or
|
|
24
|
+
Safari Technology Preview, so there's no BiDi transport to opt into. CraftDriver
|
|
25
|
+
rejects `enableBiDi: true` for Safari with a clear `UNSUPPORTED` error before
|
|
26
|
+
launch rather than silently downgrading, and every BiDi-only method (network,
|
|
27
|
+
logs, tracing, contexts, permissions/geolocation/emulation, full-page
|
|
28
|
+
screenshots, event-driven dialogs) fails the same way when called on a Safari
|
|
29
|
+
session. See the [Safari guide](./safari.md) for what remains available on
|
|
30
|
+
Classic.
|
|
31
|
+
|
|
19
32
|
## Why It Matters
|
|
20
33
|
|
|
21
34
|
Standards-based automation gives projects a few useful properties:
|
package/docs/why-craftdriver.md
CHANGED
|
@@ -5,7 +5,7 @@ CraftDriver is for writing boringly reliable automation against real browsers, w
|
|
|
5
5
|
## What It Cares About
|
|
6
6
|
|
|
7
7
|
- 🍺 **Focused Node.js API** - browser automation without a giant framework around it.
|
|
8
|
-
- 🧭 **Real browsers** - drives installed Chrome, Chromium, and Firefox instead of patched browser engine builds.
|
|
8
|
+
- 🧭 **Real browsers** - drives installed Chrome, Chromium, and Firefox instead of patched browser engine builds, plus real ([Safari on macOS](./safari.md)).
|
|
9
9
|
- 🌐 **Standards that age well** - W3C WebDriver standards stay stable while browser-private protocols change.
|
|
10
10
|
- 🚦 **Readable, auto-waited flows** - role, label, text, test id, CSS, XPath, click, fill, and expect.
|
|
11
11
|
- 📡 **Network control** - mock, block, intercept, and wait for browser requests and responses.
|
|
@@ -62,3 +62,15 @@ Manual configuration is useful when:
|
|
|
62
62
|
[Driver Configuration → Browser Binary Configuration](./driver-configuration.md#browser-binary-configuration)
|
|
63
63
|
|
|
64
64
|
See [Driver Configuration](./driver-configuration.md) for the full reference.
|
|
65
|
+
|
|
66
|
+
## Safari: nothing to resolve, nothing to download
|
|
67
|
+
|
|
68
|
+
The auto-download model above doesn't apply to `browserName: 'safari'`.
|
|
69
|
+
`safaridriver` ships as part of macOS/Safari itself — there's no version to
|
|
70
|
+
match, no binary to fetch, and no cache to warm. CraftDriver only ever
|
|
71
|
+
**locates** the driver Apple already installed. The single prerequisite is
|
|
72
|
+
turning automation on once per Mac with `safaridriver --enable`.
|
|
73
|
+
|
|
74
|
+
See the [**Safari guide**](./safari.md) for setup and capabilities, and
|
|
75
|
+
[Driver Configuration → Safari](./driver-configuration.md#safari-macos-driver-ships-with-the-browser)
|
|
76
|
+
for the driver-resolution chain.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "craftdriver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Modern WebDriver automation library for NodeJS",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"test:firefox": "BROWSER_NAME=firefox HEADLESS=true vitest run --maxWorkers=2",
|
|
18
18
|
"test:chrome": "BROWSER_NAME=chrome vitest run",
|
|
19
19
|
"test:chromium": "BROWSER_NAME=chromium vitest run",
|
|
20
|
+
"test:safari": "BROWSER_NAME=safari vitest run tests/safari.test.ts --maxWorkers=1",
|
|
20
21
|
"test:recipes": "HEADLESS=true vitest run --config vitest.recipes.config.ts",
|
|
21
22
|
"test:electron": "vitest run --config vitest.electron.config.ts",
|
|
22
23
|
"bench": "HEADLESS=true vitest run --config vitest.perf.config.ts",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"firefox",
|
|
41
42
|
"chrome",
|
|
42
43
|
"chromium",
|
|
44
|
+
"safari",
|
|
43
45
|
"electron",
|
|
44
46
|
"electron-testing",
|
|
45
47
|
"desktop-app-testing",
|
|
@@ -36,7 +36,10 @@ break the moment markup shifts.
|
|
|
36
36
|
4. BiDi features (`network`, `logs`, tracing, init scripts, true load
|
|
37
37
|
events) work out of the box — `enableBiDi` defaults to `true`. They only
|
|
38
38
|
break if you explicitly pass `enableBiDi: false`; the error code on the
|
|
39
|
-
wrong transport is `UNSUPPORTED`.
|
|
39
|
+
wrong transport is `UNSUPPORTED`. Exception: `browserName: 'safari'`
|
|
40
|
+
defaults `enableBiDi` to `false` and rejects `true` — Safari is
|
|
41
|
+
Classic-only (macOS, headed, one session at a time). See
|
|
42
|
+
[docs/safari.md](../../docs/safari.md).
|
|
40
43
|
5. Tests fetch from the example server. Start it in a separate
|
|
41
44
|
terminal: `npm run examples:start` before `npm test`.
|
|
42
45
|
|
|
@@ -10,11 +10,14 @@ list.
|
|
|
10
10
|
import { Browser } from 'craftdriver';
|
|
11
11
|
|
|
12
12
|
const browser = await Browser.launch({
|
|
13
|
-
browserName: 'chrome', // 'chrome' | 'chromium' | 'firefox'
|
|
13
|
+
browserName: 'chrome', // 'chrome' | 'chromium' | 'firefox' | 'safari'
|
|
14
14
|
headless: true,
|
|
15
15
|
// enableBiDi defaults to true — network / logs / tracing / init scripts
|
|
16
16
|
// all need it, so only set enableBiDi: false if you must disable it.
|
|
17
17
|
});
|
|
18
|
+
// 'safari' is macOS-only, headed only (no `headless`),
|
|
19
|
+
// one session at a time, WebDriver Classic only (enableBiDi is rejected).
|
|
20
|
+
// See docs/safari.md.
|
|
18
21
|
try {
|
|
19
22
|
await browser.navigateTo('https://example.com');
|
|
20
23
|
// ...
|