craftdriver 1.8.0 → 1.9.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 +6 -5
- package/README.md +3 -2
- package/dist/cli/pageSemantics.d.ts +3 -39
- package/dist/cli/pageSemantics.d.ts.map +1 -1
- package/dist/cli/pageSemantics.js +3 -191
- package/dist/cli/pageSemantics.js.map +1 -1
- package/dist/cli/selector.d.ts +3 -3
- package/dist/cli/selector.d.ts.map +1 -1
- package/dist/cli/selector.js +6 -9
- package/dist/cli/selector.js.map +1 -1
- package/dist/cli/snapshot.d.ts +5 -6
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +59 -18
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/a11y.d.ts +6 -2
- package/dist/lib/a11y.d.ts.map +1 -1
- package/dist/lib/a11y.js +3 -1
- package/dist/lib/a11y.js.map +1 -1
- package/dist/lib/bidi/evaluate.d.ts +11 -0
- package/dist/lib/bidi/evaluate.d.ts.map +1 -0
- package/dist/lib/bidi/evaluate.js +25 -0
- package/dist/lib/bidi/evaluate.js.map +1 -0
- package/dist/lib/bidi/types.d.ts +2 -0
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +1 -1
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +46 -35
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +4 -6
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +5 -7
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +65 -27
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +47 -20
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/clearFastPath.d.ts.map +1 -1
- package/dist/lib/clearFastPath.js +2 -0
- package/dist/lib/clearFastPath.js.map +1 -1
- package/dist/lib/clickFastPath.d.ts.map +1 -1
- package/dist/lib/clickFastPath.js +2 -0
- package/dist/lib/clickFastPath.js.map +1 -1
- package/dist/lib/driver.d.ts +19 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +111 -4
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/elementHandle.d.ts +13 -10
- package/dist/lib/elementHandle.d.ts.map +1 -1
- package/dist/lib/elementHandle.js +119 -50
- package/dist/lib/elementHandle.js.map +1 -1
- package/dist/lib/errors.d.ts +5 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +5 -1
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/expect.d.ts +10 -0
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +76 -49
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/fillFastPath.d.ts.map +1 -1
- package/dist/lib/fillFastPath.js +2 -0
- package/dist/lib/fillFastPath.js.map +1 -1
- package/dist/lib/frame.d.ts.map +1 -1
- package/dist/lib/frame.js +29 -17
- package/dist/lib/frame.js.map +1 -1
- package/dist/lib/locator.d.ts +21 -54
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +165 -208
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +23 -13
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/pageSemantics.d.ts +42 -0
- package/dist/lib/pageSemantics.d.ts.map +1 -0
- package/dist/lib/pageSemantics.js +216 -0
- package/dist/lib/pageSemantics.js.map +1 -0
- package/dist/lib/query.d.ts +68 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +590 -0
- package/dist/lib/query.js.map +1 -0
- package/dist/lib/shadowRoot.d.ts +21 -0
- package/dist/lib/shadowRoot.d.ts.map +1 -0
- package/dist/lib/shadowRoot.js +29 -0
- package/dist/lib/shadowRoot.js.map +1 -0
- package/dist/lib/shadowRootLocator.d.ts +31 -0
- package/dist/lib/shadowRootLocator.d.ts.map +1 -0
- package/dist/lib/shadowRootLocator.js +55 -0
- package/dist/lib/shadowRootLocator.js.map +1 -0
- package/dist/lib/timing.d.ts +2 -2
- package/dist/lib/timing.js +2 -2
- package/docs/accessibility.md +9 -1
- package/docs/api-reference.md +4 -1
- package/docs/browser-api.md +3 -3
- package/docs/browser-context.md +3 -3
- package/docs/cli.md +10 -7
- package/docs/error-codes.md +3 -1
- package/docs/getting-started.md +1 -1
- package/docs/mcp.md +9 -5
- package/docs/public/examples/iframe-child.html +12 -1
- package/docs/public/examples/shadow-dom.html +137 -0
- package/docs/recipes/login-once-reuse-session.md +1 -1
- package/docs/selectors.md +61 -0
- package/docs/session-management.md +17 -10
- package/package.json +3 -3
- package/skills/craftdriver/patterns.md +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
# [1.
|
|
1
|
+
# [1.9.0](https://github.com/dtopuzov/craftdriver/compare/v1.8.0...v1.9.0) (2026-07-24)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* add open Shadow DOM support ([#49](https://github.com/dtopuzov/craftdriver/issues/49)) ([fadd962](https://github.com/dtopuzov/craftdriver/commit/fadd962c6d1a583501d9efb7c8117d42ce3710a6)), closes [#40](https://github.com/dtopuzov/craftdriver/issues/40)
|
|
7
|
+
|
|
8
|
+
# [1.8.0](https://github.com/dtopuzov/craftdriver/compare/v1.7.0...v1.8.0) (2026-07-21)
|
|
7
9
|
|
|
8
|
-
## Unreleased
|
|
9
10
|
|
|
10
11
|
### Features
|
|
11
12
|
|
|
12
|
-
* **agent CLI:** add a persistent browser exploration workflow with semantic snapshots, safe temporary refs, live-validated durable locators, manual QA actions, console/network evidence, mocks, traces, and reusable login state
|
|
13
|
+
* **agent CLI:** add a persistent browser exploration workflow with semantic snapshots, safe temporary refs, live-validated durable locators, manual QA actions, console/network evidence, mocks, traces, and reusable login state ([2a0f079](https://github.com/dtopuzov/craftdriver/commit/2a0f079b327ffd1545fa0f1919e06045e89422dc))
|
|
13
14
|
* **auth state:** restore cookies and multi-origin localStorage before the first navigation on BiDi launch and contexts, with path/object inputs and one-time hydration that preserves later application writes
|
|
14
15
|
* **sessions:** isolate browsers, cookies, pages, refs, and artifacts in named sessions under a project-scoped daemon
|
|
15
16
|
* **MCP:** add a validated stdio tool server with automatic post-action snapshot diffs, bounded responses, and quota-controlled file artifacts for token-efficient context
|
|
@@ -220,7 +221,7 @@ Fixes: https://github.com/dtopuzov/craftdriver/issues/20
|
|
|
220
221
|
|
|
221
222
|
# [0.1.0](https://github.com/dtopuzov/craftdriver/compare/v0.0.3...v0.1.0) (2026-02-08)
|
|
222
223
|
|
|
223
|
-
|
|
224
|
+
### Changes
|
|
224
225
|
|
|
225
226
|
- docs: refresh the README as a product front door, add a VitePress documentation site with GitHub Pages deployment, add proof pages/launch kit/social card, and add contribution files (issue forms, PR template, code of conduct).
|
|
226
227
|
- **breaking** (pre-1.0): BiDi console/error log capture is lazy by default. Messages emitted before the first `browser.logs` / `onConsole` / `onError` / `waitForConsole` touch are no longer captured unless `Browser.launch({ captureLogs: true })` is used to arm logging at launch.
|
package/README.md
CHANGED
|
@@ -89,11 +89,11 @@ await browser.network.mock('**/api/users', {
|
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
// Save and reuse login state
|
|
92
|
-
await browser.saveState('
|
|
92
|
+
await browser.saveState('.auth/session.json');
|
|
93
93
|
|
|
94
94
|
await Browser.launch({
|
|
95
95
|
browserName: 'chrome',
|
|
96
|
-
storageState: '
|
|
96
|
+
storageState: '.auth/session.json',
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// Built-in accessibility checks via axe-core
|
|
@@ -113,6 +113,7 @@ explicit single-active-origin fallback after navigation; see
|
|
|
113
113
|
| Driver management | Zero-config driver resolution, cache behavior, env vars, offline mode | [Driver configuration](https://dtopuzov.github.io/craftdriver/driver-configuration) |
|
|
114
114
|
| Browser control | Navigation, tabs, popups, iframes, content helpers, evaluate, init scripts | [Browser API](https://dtopuzov.github.io/craftdriver/browser-api) |
|
|
115
115
|
| Locators | CSS, XPath, text, role, label, test id, and composable `locator()` chains | [Selectors](https://dtopuzov.github.io/craftdriver/selectors) |
|
|
116
|
+
| Open Shadow DOM | Explicit lazy `shadowRoot()` boundaries over WebDriver Classic and BiDi | [Shadow DOM](https://dtopuzov.github.io/craftdriver/selectors#open-shadow-dom) |
|
|
116
117
|
| Element actions | Click, fill, upload, inspect, and interact through element handles | [Element API](https://dtopuzov.github.io/craftdriver/element-api) |
|
|
117
118
|
| Assertions | Built-in `expect(...)`, retries, visibility, text, attributes, and timing behavior | [Assertions](https://dtopuzov.github.io/craftdriver/assertions) |
|
|
118
119
|
| Input | Low-level key presses, mouse movement, hover, drag, and pointer input | [Keyboard and mouse](https://dtopuzov.github.io/craftdriver/keyboard-mouse) |
|
|
@@ -1,42 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* These two surfaces must agree, and both must agree with `By.role` — that is
|
|
6
|
-
* the whole point of the module. The snapshot tells an agent an element is a
|
|
7
|
-
* `listbox`; the candidate builder proposes `role=listbox[name=Colors]`; and
|
|
8
|
-
* `By.role('listbox', { name: 'Colors' })` has to actually resolve it in the
|
|
9
|
-
* committed test. When those three drifted apart, the recommended locator for
|
|
10
|
-
* a multi-select was a brittle `text=RedBlue`.
|
|
11
|
-
*
|
|
12
|
-
* So the mapping lives here once, `src/lib/by.ts` stays the resolution
|
|
13
|
-
* authority, and `tests/cli/role-conformance.test.ts` pins the agreement
|
|
14
|
-
* against a real browser.
|
|
15
|
-
*
|
|
16
|
-
* Emitted as JS **source text**: both callers assemble a body for
|
|
17
|
-
* `page.evaluate`, and the build has no DOM lib. Keep it dependency-free and
|
|
18
|
-
* side-effect-free — it is concatenated into larger scripts.
|
|
19
|
-
*
|
|
20
|
-
* ## The ARIA role / display token split
|
|
21
|
-
*
|
|
22
|
-
* `ariaRole` returns a real ARIA role or `null`. `null` is not a failure: an
|
|
23
|
-
* `<input type="password">` genuinely has no ARIA role, and pretending it is a
|
|
24
|
-
* `textbox` produces a `role=textbox[name=Password]` that resolves to nothing.
|
|
25
|
-
* The snapshot falls back to `displayRole` (the tag name) so the element still
|
|
26
|
-
* appears, while the candidate builder proposes a role locator only when
|
|
27
|
-
* `ariaRole` is non-null. Honest over convenient.
|
|
2
|
+
* Compatibility re-export. The page-side semantic evaluator is shared by the
|
|
3
|
+
* public locator engine and CLI snapshot/candidate surfaces.
|
|
28
4
|
*/
|
|
29
|
-
|
|
30
|
-
* Function declarations injected into a page-side script.
|
|
31
|
-
*
|
|
32
|
-
* Provides `firstRoleToken`, `hasSectioningAncestor`, `ariaRole`,
|
|
33
|
-
* `displayRole`, `associatedLabel` and `accName`.
|
|
34
|
-
*/
|
|
35
|
-
export declare const PAGE_SEMANTICS_JS = "\nfunction firstRoleToken(el) {\n const role = (el.getAttribute('role') || '').trim();\n return role ? role.split(/\\s+/)[0] : null;\n}\n// Mirrors HAS_SECTIONING_ANCESTOR in src/lib/by.ts: header/footer are only\n// banner/contentinfo when not nested in sectioning content.\nfunction hasSectioningAncestor(el) {\n for (let p = el.parentElement; p; p = p.parentElement) {\n const t = p.tagName.toLowerCase();\n if (t === 'article' || t === 'aside' || t === 'main' || t === 'nav' || t === 'section') {\n return true;\n }\n const role = firstRoleToken(p);\n if (\n role === 'article' ||\n role === 'complementary' ||\n role === 'main' ||\n role === 'navigation' ||\n role === 'region'\n ) {\n return true;\n }\n }\n return false;\n}\n// The ARIA role, or null when the element has none that By.role can resolve.\n// Every branch here must have a counterpart in IMPLICIT_ROLE_XPATH.\nfunction ariaRole(el) {\n const explicit = firstRoleToken(el);\n if (explicit) return explicit;\n const t = el.tagName.toLowerCase();\n if (t === 'a' || t === 'area') return el.hasAttribute('href') ? 'link' : null;\n if (t === 'button' || t === 'summary') return 'button';\n if (t === 'input') {\n const it = (el.getAttribute('type') || 'text').toLowerCase();\n if (it === 'submit' || it === 'button' || it === 'reset' || it === 'image') return 'button';\n if (it === 'checkbox') return 'checkbox';\n if (it === 'radio') return 'radio';\n if (it === 'search') return 'searchbox';\n if (it === 'number') return 'spinbutton';\n if (it === 'range') return 'slider';\n if (it === 'text' || it === 'email' || it === 'tel' || it === 'url') return 'textbox';\n // password, date, file, color, hidden\u2026 have no corresponding ARIA role.\n return el.hasAttribute('type') ? null : 'textbox';\n }\n if (t === 'textarea') return 'textbox';\n // multiple/size are reflected IDL attributes, so reading the properties\n // stays equivalent to the attribute test By.role compiles to XPath.\n if (t === 'select') return el.multiple || el.size > 1 ? 'listbox' : 'combobox';\n if (/^h[1-6]$/.test(t)) return 'heading';\n if (t === 'img') return 'img';\n if (t === 'ul' || t === 'ol') return 'list';\n if (t === 'li') return 'listitem';\n if (t === 'table') return 'table';\n if (t === 'tr') return 'row';\n if (t === 'td') return 'cell';\n if (t === 'th') return 'columnheader';\n if (t === 'nav') return 'navigation';\n if (t === 'main') return 'main';\n if (t === 'article') return 'article';\n if (t === 'form') return 'form';\n if (t === 'header') return hasSectioningAncestor(el) ? null : 'banner';\n if (t === 'footer') return hasSectioningAncestor(el) ? null : 'contentinfo';\n return null;\n}\n// What the snapshot prints. Falls back to the tag name so an element with no\n// ARIA role is still listed and still addressable by ref.\nfunction displayRole(el) {\n return ariaRole(el) || el.tagName.toLowerCase();\n}\n// The <label> a user reads for a form control, independent of accName \u2014\n// By.labelText resolves against exactly this.\nfunction associatedLabel(el) {\n const t = el.tagName;\n if (t !== 'INPUT' && t !== 'TEXTAREA' && t !== 'SELECT') return null;\n if (el.id) {\n const forLabel = document.querySelector('label[for=' + JSON.stringify(el.id) + ']');\n if (forLabel) return (forLabel.textContent || '').trim() || null;\n }\n const parentLabel = el.closest('label');\n if (parentLabel) return (parentLabel.textContent || '').trim() || null;\n return null;\n}\n// Approximates the accessible name, in the same source order By.role's name\n// predicate checks. Sources By.role cannot match must not appear here.\nfunction accName(el) {\n const aria = el.getAttribute('aria-label');\n if (aria && aria.trim()) return aria.trim();\n const labelledby = el.getAttribute('aria-labelledby');\n if (labelledby) {\n // Single id only \u2014 By.role's @aria-labelledby test is an equality match.\n const ref = document.getElementById(labelledby);\n if (ref) {\n const t = (ref.textContent || '').trim();\n if (t) return t;\n }\n }\n const label = associatedLabel(el);\n if (label) return label;\n const tag = el.tagName;\n if (tag === 'INPUT') {\n const it = (el.getAttribute('type') || 'text').toLowerCase();\n // Native buttons carry their name in value=, not in text content.\n if (it === 'submit' || it === 'button' || it === 'reset') {\n const v = el.getAttribute('value');\n if (v && v.trim()) return v.trim();\n }\n if (it === 'image') {\n const alt = el.getAttribute('alt');\n if (alt && alt.trim()) return alt.trim();\n }\n }\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {\n const placeholder = el.getAttribute('placeholder');\n if (placeholder && placeholder.trim()) return placeholder.trim();\n }\n if (tag === 'IMG') {\n const alt = el.getAttribute('alt');\n if (alt && alt.trim()) return alt.trim();\n }\n const text = (el.textContent || '').trim().replace(/\\s+/g, ' ');\n if (text) return text;\n const title = el.getAttribute('title');\n if (title && title.trim()) return title.trim();\n return '';\n}\n";
|
|
36
|
-
/**
|
|
37
|
-
* Every role `ariaRole` can emit for a native element. The conformance test
|
|
38
|
-
* walks this list, so a new branch above without a `By.role` counterpart
|
|
39
|
-
* fails CI rather than shipping an unresolvable recommendation.
|
|
40
|
-
*/
|
|
41
|
-
export declare const EMITTABLE_ROLES: readonly ["link", "button", "checkbox", "radio", "searchbox", "spinbutton", "slider", "textbox", "listbox", "combobox", "heading", "img", "list", "listitem", "table", "row", "cell", "columnheader", "navigation", "main", "article", "form", "banner", "contentinfo"];
|
|
5
|
+
export { PAGE_SEMANTICS_JS, EMITTABLE_ROLES } from '../lib/pageSemantics.js';
|
|
42
6
|
//# sourceMappingURL=pageSemantics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageSemantics.d.ts","sourceRoot":"","sources":["../../src/cli/pageSemantics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pageSemantics.d.ts","sourceRoot":"","sources":["../../src/cli/pageSemantics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -1,194 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* These two surfaces must agree, and both must agree with `By.role` — that is
|
|
6
|
-
* the whole point of the module. The snapshot tells an agent an element is a
|
|
7
|
-
* `listbox`; the candidate builder proposes `role=listbox[name=Colors]`; and
|
|
8
|
-
* `By.role('listbox', { name: 'Colors' })` has to actually resolve it in the
|
|
9
|
-
* committed test. When those three drifted apart, the recommended locator for
|
|
10
|
-
* a multi-select was a brittle `text=RedBlue`.
|
|
11
|
-
*
|
|
12
|
-
* So the mapping lives here once, `src/lib/by.ts` stays the resolution
|
|
13
|
-
* authority, and `tests/cli/role-conformance.test.ts` pins the agreement
|
|
14
|
-
* against a real browser.
|
|
15
|
-
*
|
|
16
|
-
* Emitted as JS **source text**: both callers assemble a body for
|
|
17
|
-
* `page.evaluate`, and the build has no DOM lib. Keep it dependency-free and
|
|
18
|
-
* side-effect-free — it is concatenated into larger scripts.
|
|
19
|
-
*
|
|
20
|
-
* ## The ARIA role / display token split
|
|
21
|
-
*
|
|
22
|
-
* `ariaRole` returns a real ARIA role or `null`. `null` is not a failure: an
|
|
23
|
-
* `<input type="password">` genuinely has no ARIA role, and pretending it is a
|
|
24
|
-
* `textbox` produces a `role=textbox[name=Password]` that resolves to nothing.
|
|
25
|
-
* The snapshot falls back to `displayRole` (the tag name) so the element still
|
|
26
|
-
* appears, while the candidate builder proposes a role locator only when
|
|
27
|
-
* `ariaRole` is non-null. Honest over convenient.
|
|
2
|
+
* Compatibility re-export. The page-side semantic evaluator is shared by the
|
|
3
|
+
* public locator engine and CLI snapshot/candidate surfaces.
|
|
28
4
|
*/
|
|
29
|
-
|
|
30
|
-
* Function declarations injected into a page-side script.
|
|
31
|
-
*
|
|
32
|
-
* Provides `firstRoleToken`, `hasSectioningAncestor`, `ariaRole`,
|
|
33
|
-
* `displayRole`, `associatedLabel` and `accName`.
|
|
34
|
-
*/
|
|
35
|
-
export const PAGE_SEMANTICS_JS = `
|
|
36
|
-
function firstRoleToken(el) {
|
|
37
|
-
const role = (el.getAttribute('role') || '').trim();
|
|
38
|
-
return role ? role.split(/\\s+/)[0] : null;
|
|
39
|
-
}
|
|
40
|
-
// Mirrors HAS_SECTIONING_ANCESTOR in src/lib/by.ts: header/footer are only
|
|
41
|
-
// banner/contentinfo when not nested in sectioning content.
|
|
42
|
-
function hasSectioningAncestor(el) {
|
|
43
|
-
for (let p = el.parentElement; p; p = p.parentElement) {
|
|
44
|
-
const t = p.tagName.toLowerCase();
|
|
45
|
-
if (t === 'article' || t === 'aside' || t === 'main' || t === 'nav' || t === 'section') {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
const role = firstRoleToken(p);
|
|
49
|
-
if (
|
|
50
|
-
role === 'article' ||
|
|
51
|
-
role === 'complementary' ||
|
|
52
|
-
role === 'main' ||
|
|
53
|
-
role === 'navigation' ||
|
|
54
|
-
role === 'region'
|
|
55
|
-
) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
// The ARIA role, or null when the element has none that By.role can resolve.
|
|
62
|
-
// Every branch here must have a counterpart in IMPLICIT_ROLE_XPATH.
|
|
63
|
-
function ariaRole(el) {
|
|
64
|
-
const explicit = firstRoleToken(el);
|
|
65
|
-
if (explicit) return explicit;
|
|
66
|
-
const t = el.tagName.toLowerCase();
|
|
67
|
-
if (t === 'a' || t === 'area') return el.hasAttribute('href') ? 'link' : null;
|
|
68
|
-
if (t === 'button' || t === 'summary') return 'button';
|
|
69
|
-
if (t === 'input') {
|
|
70
|
-
const it = (el.getAttribute('type') || 'text').toLowerCase();
|
|
71
|
-
if (it === 'submit' || it === 'button' || it === 'reset' || it === 'image') return 'button';
|
|
72
|
-
if (it === 'checkbox') return 'checkbox';
|
|
73
|
-
if (it === 'radio') return 'radio';
|
|
74
|
-
if (it === 'search') return 'searchbox';
|
|
75
|
-
if (it === 'number') return 'spinbutton';
|
|
76
|
-
if (it === 'range') return 'slider';
|
|
77
|
-
if (it === 'text' || it === 'email' || it === 'tel' || it === 'url') return 'textbox';
|
|
78
|
-
// password, date, file, color, hidden… have no corresponding ARIA role.
|
|
79
|
-
return el.hasAttribute('type') ? null : 'textbox';
|
|
80
|
-
}
|
|
81
|
-
if (t === 'textarea') return 'textbox';
|
|
82
|
-
// multiple/size are reflected IDL attributes, so reading the properties
|
|
83
|
-
// stays equivalent to the attribute test By.role compiles to XPath.
|
|
84
|
-
if (t === 'select') return el.multiple || el.size > 1 ? 'listbox' : 'combobox';
|
|
85
|
-
if (/^h[1-6]$/.test(t)) return 'heading';
|
|
86
|
-
if (t === 'img') return 'img';
|
|
87
|
-
if (t === 'ul' || t === 'ol') return 'list';
|
|
88
|
-
if (t === 'li') return 'listitem';
|
|
89
|
-
if (t === 'table') return 'table';
|
|
90
|
-
if (t === 'tr') return 'row';
|
|
91
|
-
if (t === 'td') return 'cell';
|
|
92
|
-
if (t === 'th') return 'columnheader';
|
|
93
|
-
if (t === 'nav') return 'navigation';
|
|
94
|
-
if (t === 'main') return 'main';
|
|
95
|
-
if (t === 'article') return 'article';
|
|
96
|
-
if (t === 'form') return 'form';
|
|
97
|
-
if (t === 'header') return hasSectioningAncestor(el) ? null : 'banner';
|
|
98
|
-
if (t === 'footer') return hasSectioningAncestor(el) ? null : 'contentinfo';
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
// What the snapshot prints. Falls back to the tag name so an element with no
|
|
102
|
-
// ARIA role is still listed and still addressable by ref.
|
|
103
|
-
function displayRole(el) {
|
|
104
|
-
return ariaRole(el) || el.tagName.toLowerCase();
|
|
105
|
-
}
|
|
106
|
-
// The <label> a user reads for a form control, independent of accName —
|
|
107
|
-
// By.labelText resolves against exactly this.
|
|
108
|
-
function associatedLabel(el) {
|
|
109
|
-
const t = el.tagName;
|
|
110
|
-
if (t !== 'INPUT' && t !== 'TEXTAREA' && t !== 'SELECT') return null;
|
|
111
|
-
if (el.id) {
|
|
112
|
-
const forLabel = document.querySelector('label[for=' + JSON.stringify(el.id) + ']');
|
|
113
|
-
if (forLabel) return (forLabel.textContent || '').trim() || null;
|
|
114
|
-
}
|
|
115
|
-
const parentLabel = el.closest('label');
|
|
116
|
-
if (parentLabel) return (parentLabel.textContent || '').trim() || null;
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
// Approximates the accessible name, in the same source order By.role's name
|
|
120
|
-
// predicate checks. Sources By.role cannot match must not appear here.
|
|
121
|
-
function accName(el) {
|
|
122
|
-
const aria = el.getAttribute('aria-label');
|
|
123
|
-
if (aria && aria.trim()) return aria.trim();
|
|
124
|
-
const labelledby = el.getAttribute('aria-labelledby');
|
|
125
|
-
if (labelledby) {
|
|
126
|
-
// Single id only — By.role's @aria-labelledby test is an equality match.
|
|
127
|
-
const ref = document.getElementById(labelledby);
|
|
128
|
-
if (ref) {
|
|
129
|
-
const t = (ref.textContent || '').trim();
|
|
130
|
-
if (t) return t;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const label = associatedLabel(el);
|
|
134
|
-
if (label) return label;
|
|
135
|
-
const tag = el.tagName;
|
|
136
|
-
if (tag === 'INPUT') {
|
|
137
|
-
const it = (el.getAttribute('type') || 'text').toLowerCase();
|
|
138
|
-
// Native buttons carry their name in value=, not in text content.
|
|
139
|
-
if (it === 'submit' || it === 'button' || it === 'reset') {
|
|
140
|
-
const v = el.getAttribute('value');
|
|
141
|
-
if (v && v.trim()) return v.trim();
|
|
142
|
-
}
|
|
143
|
-
if (it === 'image') {
|
|
144
|
-
const alt = el.getAttribute('alt');
|
|
145
|
-
if (alt && alt.trim()) return alt.trim();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') {
|
|
149
|
-
const placeholder = el.getAttribute('placeholder');
|
|
150
|
-
if (placeholder && placeholder.trim()) return placeholder.trim();
|
|
151
|
-
}
|
|
152
|
-
if (tag === 'IMG') {
|
|
153
|
-
const alt = el.getAttribute('alt');
|
|
154
|
-
if (alt && alt.trim()) return alt.trim();
|
|
155
|
-
}
|
|
156
|
-
const text = (el.textContent || '').trim().replace(/\\s+/g, ' ');
|
|
157
|
-
if (text) return text;
|
|
158
|
-
const title = el.getAttribute('title');
|
|
159
|
-
if (title && title.trim()) return title.trim();
|
|
160
|
-
return '';
|
|
161
|
-
}
|
|
162
|
-
`;
|
|
163
|
-
/**
|
|
164
|
-
* Every role `ariaRole` can emit for a native element. The conformance test
|
|
165
|
-
* walks this list, so a new branch above without a `By.role` counterpart
|
|
166
|
-
* fails CI rather than shipping an unresolvable recommendation.
|
|
167
|
-
*/
|
|
168
|
-
export const EMITTABLE_ROLES = [
|
|
169
|
-
'link',
|
|
170
|
-
'button',
|
|
171
|
-
'checkbox',
|
|
172
|
-
'radio',
|
|
173
|
-
'searchbox',
|
|
174
|
-
'spinbutton',
|
|
175
|
-
'slider',
|
|
176
|
-
'textbox',
|
|
177
|
-
'listbox',
|
|
178
|
-
'combobox',
|
|
179
|
-
'heading',
|
|
180
|
-
'img',
|
|
181
|
-
'list',
|
|
182
|
-
'listitem',
|
|
183
|
-
'table',
|
|
184
|
-
'row',
|
|
185
|
-
'cell',
|
|
186
|
-
'columnheader',
|
|
187
|
-
'navigation',
|
|
188
|
-
'main',
|
|
189
|
-
'article',
|
|
190
|
-
'form',
|
|
191
|
-
'banner',
|
|
192
|
-
'contentinfo',
|
|
193
|
-
];
|
|
5
|
+
export { PAGE_SEMANTICS_JS, EMITTABLE_ROLES } from '../lib/pageSemantics.js';
|
|
194
6
|
//# sourceMappingURL=pageSemantics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pageSemantics.js","sourceRoot":"","sources":["../../src/cli/pageSemantics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pageSemantics.js","sourceRoot":"","sources":["../../src/cli/pageSemantics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/cli/selector.d.ts
CHANGED
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
* placeholder=Search… → By.placeholder('Search…')
|
|
18
18
|
* alt=Logo → By.altText('Logo')
|
|
19
19
|
* title=Help → By.title('Help')
|
|
20
|
-
* ref=e5 → By.
|
|
20
|
+
* ref=e5 → By.ref('e5') (resolved through the page identity registry)
|
|
21
21
|
* (refs come from `craftdriver snapshot` /
|
|
22
|
-
* browser_snapshot; invalidated by
|
|
23
|
-
*
|
|
22
|
+
* browser_snapshot; invalidated by detachment,
|
|
23
|
+
* navigation, or page replacement)
|
|
24
24
|
*/
|
|
25
25
|
import { By } from '../lib/by.js';
|
|
26
26
|
export declare function parseSelector(input: string): By;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/cli/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAKlC,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/cli/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAKlC,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,EAAE,CA2E/C;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAE/C"}
|
package/dist/cli/selector.js
CHANGED
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
* placeholder=Search… → By.placeholder('Search…')
|
|
18
18
|
* alt=Logo → By.altText('Logo')
|
|
19
19
|
* title=Help → By.title('Help')
|
|
20
|
-
* ref=e5 → By.
|
|
20
|
+
* ref=e5 → By.ref('e5') (resolved through the page identity registry)
|
|
21
21
|
* (refs come from `craftdriver snapshot` /
|
|
22
|
-
* browser_snapshot; invalidated by
|
|
23
|
-
*
|
|
22
|
+
* browser_snapshot; invalidated by detachment,
|
|
23
|
+
* navigation, or page replacement)
|
|
24
24
|
*/
|
|
25
25
|
import { By } from '../lib/by.js';
|
|
26
26
|
import { CraftdriverError, ErrorCode } from '../lib/errors.js';
|
|
@@ -76,16 +76,13 @@ export function parseSelector(input) {
|
|
|
76
76
|
case 'data-testid':
|
|
77
77
|
return By.testId(valueRaw);
|
|
78
78
|
case 'ref': {
|
|
79
|
-
// Refs
|
|
80
|
-
//
|
|
81
|
-
// snapshot or navigation. We resolve them as a plain CSS
|
|
82
|
-
// attribute selector so all the usual auto-waiting paths work
|
|
83
|
-
// unchanged.
|
|
79
|
+
// Refs resolve through the page's identity registry. The marker
|
|
80
|
+
// attribute is diagnostic only and cannot cross a shadow boundary.
|
|
84
81
|
const v = valueRaw.trim();
|
|
85
82
|
if (!/^e\d+$/.test(v)) {
|
|
86
83
|
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, `parseSelector: invalid ref "${valueRaw}"`, { hint: 'refs look like e1, e2, … (take a snapshot to see them)' });
|
|
87
84
|
}
|
|
88
|
-
return By.
|
|
85
|
+
return By.ref(v);
|
|
89
86
|
}
|
|
90
87
|
default:
|
|
91
88
|
// Treat unknown prefix as part of a CSS selector (e.g. attribute
|
package/dist/cli/selector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/cli/selector.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/cli/selector.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/snapshot.d.ts
CHANGED
|
@@ -27,17 +27,16 @@
|
|
|
27
27
|
* makes every pre-navigation ref detectably stale instead of silently
|
|
28
28
|
* rebound.
|
|
29
29
|
*
|
|
30
|
-
* The element marker attribute (`data-craftdriver-ref`) is
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `cloneNode`) never resolves.
|
|
30
|
+
* The element marker attribute (`data-craftdriver-ref`) is diagnostic only.
|
|
31
|
+
* Resolution goes directly through the registry, which works for elements
|
|
32
|
+
* inside open shadow trees and cannot be confused by authored or cloned
|
|
33
|
+
* marker attributes.
|
|
35
34
|
*/
|
|
36
35
|
import type { Browser } from '../lib/browser.js';
|
|
37
36
|
export interface SnapshotShape {
|
|
38
37
|
url: string;
|
|
39
38
|
title: string;
|
|
40
|
-
/**
|
|
39
|
+
/** Visible semantic nodes plus indented open-shadow boundary lines. */
|
|
41
40
|
lines: string[];
|
|
42
41
|
/**
|
|
43
42
|
* Identity of the document the refs belong to. Changes on navigation and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAYjD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,+DAA+D;AAC/D,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,aAAa,GACb,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,SAAI,GACT,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAwB/B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOzE;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAAC,QAAQ,CAAC,CAgBnB;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAU3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,aAAa,GAAG,IAAI,EAC1B,IAAI,EAAE,aAAa,GAAG,IAAI,GACzB,MAAM,CA8CR;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAO7D;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IAEtB,iEAAiE;IACjE,IAAI,UAAU,IAAI,MAAM,GAAG,IAAI,CAE9B;IAED,IAAI,OAAO,IAAI,aAAa,GAAG,IAAI,CAElC;IAED;;;;;;OAMG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,yEAAyE;IACzE,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI;IAQxD,4DAA4D;IAC5D,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM;IAO3C,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/cli/snapshot.js
CHANGED
|
@@ -117,7 +117,7 @@ export function renderDelta(prev, next) {
|
|
|
117
117
|
if (prev.documentId !== next.documentId || prev.url !== next.url) {
|
|
118
118
|
return `${header}\n${next.lines.join('\n')}`;
|
|
119
119
|
}
|
|
120
|
-
const strip = (l) => l.replace(/^e\d+:\s*/, '');
|
|
120
|
+
const strip = (l) => l.replace(/^(\s*)e\d+:\s*/, '$1');
|
|
121
121
|
// Count occurrences rather than testing membership: a page with three
|
|
122
122
|
// identical "Remove" buttons must still report a change when one of them
|
|
123
123
|
// goes, and a set-difference silently calls that "no changes".
|
|
@@ -230,10 +230,13 @@ if (expected !== null && reg.doc !== expected) {
|
|
|
230
230
|
}
|
|
231
231
|
const el = reg.map.get(ref);
|
|
232
232
|
if (!el) return { status: 'unknown-ref', documentId: reg.doc };
|
|
233
|
-
if (!
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
if (!el.isConnected || el.ownerDocument !== document) {
|
|
234
|
+
return { status: 'detached', documentId: reg.doc };
|
|
235
|
+
}
|
|
236
|
+
// The reverse map is the exact identity index. A mismatch is only possible for a
|
|
237
|
+
// corrupt/legacy registry; marker attributes deliberately do not participate.
|
|
238
|
+
if (reg.reverse && reg.reverse.get(el) !== ref) {
|
|
239
|
+
return { status: 'ambiguous', documentId: reg.doc };
|
|
237
240
|
}
|
|
238
241
|
return { status: 'ok', documentId: reg.doc };
|
|
239
242
|
`;
|
|
@@ -243,7 +246,7 @@ return { status: 'ok', documentId: reg.doc };
|
|
|
243
246
|
*
|
|
244
247
|
* Strategy:
|
|
245
248
|
* 1. Get or create the per-document ref registry.
|
|
246
|
-
* 2. Walk
|
|
249
|
+
* 2. Walk the composed tree, entering open shadow roots and flattened slots.
|
|
247
250
|
* 3. Skip nodes outside the viewport-ish bounds and zero-sized nodes.
|
|
248
251
|
* 4. Reuse each node's existing ref, or issue the next unused one.
|
|
249
252
|
* 5. Cap output at MAX_NODES so the snapshot stays bounded regardless
|
|
@@ -262,9 +265,18 @@ function newDocId(epoch) {
|
|
|
262
265
|
function registry() {
|
|
263
266
|
let reg = window.__craftdriverRefs;
|
|
264
267
|
if (!reg || !reg.map) {
|
|
265
|
-
reg = {
|
|
268
|
+
reg = {
|
|
269
|
+
doc: newDocId(1), epoch: 1, next: 1, issued: 0,
|
|
270
|
+
map: new Map(), reverse: new WeakMap()
|
|
271
|
+
};
|
|
266
272
|
window.__craftdriverRefs = reg;
|
|
267
273
|
}
|
|
274
|
+
if (!reg.reverse) {
|
|
275
|
+
reg.reverse = new WeakMap();
|
|
276
|
+
reg.map.forEach(function (el, ref) {
|
|
277
|
+
if (el) reg.reverse.set(el, ref);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
268
280
|
// Never reissue a number the session has already handed out. A fresh
|
|
269
281
|
// document starts its counter above the session high-water mark, so a
|
|
270
282
|
// ref captured before a navigation can never match a new element.
|
|
@@ -278,24 +290,26 @@ function registry() {
|
|
|
278
290
|
// re-key on every single snapshot — invalidating refs the instant they
|
|
279
291
|
// were handed out.
|
|
280
292
|
if (reg.issued >= MAX_REFS) {
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
reg.map.forEach(function (el) {
|
|
294
|
+
if (el && el.removeAttribute) el.removeAttribute('data-craftdriver-ref');
|
|
295
|
+
});
|
|
283
296
|
reg.epoch += 1;
|
|
284
297
|
reg.doc = newDocId(reg.epoch);
|
|
285
298
|
reg.issued = 0;
|
|
286
299
|
reg.map = new Map();
|
|
300
|
+
reg.reverse = new WeakMap();
|
|
287
301
|
}
|
|
288
302
|
return reg;
|
|
289
303
|
}
|
|
290
304
|
function refFor(reg, el) {
|
|
291
|
-
const existing =
|
|
292
|
-
// Trust the attribute only when the registry agrees it names this node;
|
|
293
|
-
// that rejects authored markup and cloneNode duplicates.
|
|
305
|
+
const existing = reg.reverse.get(el);
|
|
294
306
|
if (existing && reg.map.get(existing) === el) return existing;
|
|
295
307
|
const ref = 'e' + reg.next;
|
|
296
308
|
reg.next += 1;
|
|
297
309
|
reg.issued += 1;
|
|
298
310
|
reg.map.set(ref, el);
|
|
311
|
+
reg.reverse.set(el, ref);
|
|
312
|
+
// Diagnostic only. Exact ref resolution never queries this attribute.
|
|
299
313
|
el.setAttribute('data-craftdriver-ref', ref);
|
|
300
314
|
return ref;
|
|
301
315
|
}
|
|
@@ -323,22 +337,21 @@ const reg = registry();
|
|
|
323
337
|
const sel = 'a,button,input,select,textarea,h1,h2,h3,h4,h5,h6,[role],nav,main,header,footer,form,img,label';
|
|
324
338
|
const out = [];
|
|
325
339
|
const refs = [];
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (!visible(el)) continue;
|
|
340
|
+
const seen = new WeakSet();
|
|
341
|
+
function emit(el, depth) {
|
|
342
|
+
if (out.length >= MAX_NODES || !visible(el)) return;
|
|
330
343
|
// A <header>/<footer> inside sectioning content is not a landmark, and
|
|
331
344
|
// listing it as a bare tag would be pure noise. Everything else keeps a
|
|
332
345
|
// line even without an ARIA role, so controls stay addressable by ref.
|
|
333
346
|
const tag = el.tagName.toLowerCase();
|
|
334
|
-
if (!ariaRole(el) && (tag === 'header' || tag === 'footer'))
|
|
347
|
+
if (!ariaRole(el) && (tag === 'header' || tag === 'footer')) return;
|
|
335
348
|
const role = displayRole(el);
|
|
336
349
|
let name = accName(el);
|
|
337
350
|
if (name.length > MAX_NAME) name = name.slice(0, MAX_NAME - 1) + '…';
|
|
338
351
|
const hint = locatorHint(el);
|
|
339
352
|
const ref = refFor(reg, el);
|
|
340
353
|
refs.push(ref);
|
|
341
|
-
let line = ref + ': ' + role;
|
|
354
|
+
let line = ' '.repeat(depth) + ref + ': ' + role;
|
|
342
355
|
if (name) line += ' "' + name + '"';
|
|
343
356
|
if (hint) line += ' ' + hint;
|
|
344
357
|
// Annotate disabled/checked state, agents need it.
|
|
@@ -346,6 +359,34 @@ for (let i = 0; i < nodes.length && out.length < MAX_NODES; i++) {
|
|
|
346
359
|
if (el.checked) line += ' (checked)';
|
|
347
360
|
out.push(line);
|
|
348
361
|
}
|
|
362
|
+
function visitContainer(container, depth) {
|
|
363
|
+
const children = container && container.children ? Array.from(container.children) : [];
|
|
364
|
+
for (let i = 0; i < children.length && out.length < MAX_NODES; i++) {
|
|
365
|
+
visitElement(children[i], depth);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function visitElement(el, depth) {
|
|
369
|
+
if (!el || seen.has(el) || out.length >= MAX_NODES) return;
|
|
370
|
+
seen.add(el);
|
|
371
|
+
const openRoot = el.shadowRoot || null;
|
|
372
|
+
if (el.matches(sel) || openRoot) emit(el, depth);
|
|
373
|
+
if (openRoot) {
|
|
374
|
+
if (out.length < MAX_NODES) out.push(' '.repeat(depth + 1) + '#shadow-root (open)');
|
|
375
|
+
visitContainer(openRoot, depth + 2);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (el.tagName === 'SLOT') {
|
|
379
|
+
const assigned = el.assignedElements ? el.assignedElements({ flatten: true }) : [];
|
|
380
|
+
if (assigned.length) {
|
|
381
|
+
for (let i = 0; i < assigned.length && out.length < MAX_NODES; i++) {
|
|
382
|
+
visitElement(assigned[i], depth);
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
visitContainer(el, depth);
|
|
388
|
+
}
|
|
389
|
+
visitContainer(document, 0);
|
|
349
390
|
return { lines: out, refs: refs, documentId: reg.doc, nextRef: reg.next };
|
|
350
391
|
`;
|
|
351
392
|
}
|
package/dist/cli/snapshot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/cli/snapshot.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { type RemoteWebDriverOptions, type RemoteAuth } from './lib/remote.js';
|
|
|
4
4
|
export { Key } from './lib/keys.js';
|
|
5
5
|
export { By } from './lib/by.js';
|
|
6
6
|
export { Locator } from './lib/locator.js';
|
|
7
|
+
export { ShadowRootLocator } from './lib/shadowRootLocator.js';
|
|
7
8
|
export { Frame } from './lib/frame.js';
|
|
8
9
|
export { Page } from './lib/page.js';
|
|
9
10
|
export { BrowserContext, type ClearCookiesFilter, type ContextStorageStateOptions, type BrowserContextConfig, type BrowserContextHooks, type InitScriptHandle, type RoutePattern, } from './lib/browserContext.js';
|
|
@@ -21,5 +22,5 @@ export { type ElectronMock, type ElectronMockCall } from './lib/electronMock.js'
|
|
|
21
22
|
export { ElectronMainLogMonitor, type ElectronMainLog, type ElectronMainLogLevel, type ElectronMainLogHandler, } from './lib/electronMainLogs.js';
|
|
22
23
|
export { Clock, type ClockTime, type ClockInstallOptions, } from './lib/clock.js';
|
|
23
24
|
export { compareScreenshots, VisualMismatchError, type ScreenshotCompareOptions, type ExpectScreenshotOptions, type VisualScreenshotOptions, type VisualComparisonResult, type ScreenshotMatchResult, } from './lib/visual/index.js';
|
|
24
|
-
export { A11y, A11yError, type A11yOptions, type A11yResult, type A11yViolation, type A11yViolationNode, type A11yImpact, } from './lib/a11y.js';
|
|
25
|
+
export { A11y, A11yError, type A11yOptions, type A11yResult, type A11yViolation, type A11yViolationNode, type A11yImpact, type A11yShadowSelector, type A11yTarget, } from './lib/a11y.js';
|
|
25
26
|
//# sourceMappingURL=index.d.ts.map
|