wtr-a11y-tree-plugin 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Serhii Kulykov <iamkulykov@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,156 @@
1
- # Temporary Holding Version
1
+ # wtr-a11y-tree-plugin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Read the Chrome accessibility tree in Web Test Runner tests.
4
+
5
+ The package has three parts:
6
+
7
+ - Server plugin that reads a11y tree through the Chrome DevTools Protocol.
8
+ - `getA11yTree()` browser command that returns the tree of an element.
9
+ - Chai assertions: `matchA11yTree()` and `equalA11yTreeSnapshot()`.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ npm install --save-dev wtr-a11y-tree-plugin
15
+ ```
16
+
17
+ Pre-requisites: `@web/test-runner` and `@web/test-runner-playwright` (Chrome only).
18
+
19
+ ## Configuration
20
+
21
+ Add the plugin to the Web Test Runner config:
22
+
23
+ ```js
24
+ import { playwrightLauncher } from '@web/test-runner-playwright';
25
+ import { a11yTreePlugin } from 'wtr-a11y-tree-plugin';
26
+
27
+ export default {
28
+ nodeResolve: true,
29
+ browsers: [playwrightLauncher({ product: 'chromium' })],
30
+ plugins: [a11yTreePlugin()],
31
+ };
32
+ ```
33
+
34
+ Set `channel: 'chrome'` to use local Chrome e.g. in GitHub Actions:
35
+
36
+ ```js
37
+ import { playwrightLauncher } from '@web/test-runner-playwright';
38
+ import { a11yTreePlugin } from 'wtr-a11y-tree-plugin';
39
+
40
+ export default {
41
+ nodeResolve: true,
42
+ browsers: [
43
+ playwrightLauncher({
44
+ product: 'chromium',
45
+ launchOptions: {
46
+ channel: 'chrome',
47
+ headless: true,
48
+ },
49
+ }),
50
+ ],
51
+ plugins: [a11yTreePlugin()],
52
+ };
53
+ ```
54
+
55
+ Note: snapshots can differ depending on the Chrome or Playwright Chromium version.
56
+
57
+ ## Getting a11y tree
58
+
59
+ Call `getA11yTree()` with an element. The result is a list of `A11yNode` objects.
60
+
61
+ ```js
62
+ import { getA11yTree } from 'wtr-a11y-tree-plugin/browser';
63
+
64
+ const tree = await getA11yTree(document.querySelector('my-button'));
65
+ ```
66
+
67
+ Each node has these fields:
68
+
69
+ - `role`: the accessibility role.
70
+ - `name`: the accessible name.
71
+ - `value`: the value, when there is one.
72
+ - `description`: the accessible description, when there is one.
73
+ - `states`: the states, such as `expanded` or `pressed`.
74
+ - `relations`: the targets of `controls`, `owns` and similar relations, as `{ role, name }`.
75
+ - `children`: the child nodes.
76
+
77
+ Text nodes have the role `text` and only a name.
78
+
79
+ ## Chai assertions
80
+
81
+ Register the plugin with `chai.use()`. The plugin does not import chai. It uses the chai instance that you pass.
82
+
83
+ ```js
84
+ import * as chai from 'chai';
85
+ import { chaiA11yTree } from 'wtr-a11y-tree-plugin/chai';
86
+
87
+ chai.use(chaiA11yTree);
88
+ const { expect } = chai;
89
+
90
+ it('exposes the combobox', async () => {
91
+ await expect(element).to.matchA11yTree(`
92
+ - combobox "Size" [expanded]:
93
+ - /value: Medium
94
+ `);
95
+ });
96
+
97
+ it('keeps the tree', async () => {
98
+ await expect(element).to.equalA11yTreeSnapshot();
99
+ });
100
+ ```
101
+
102
+ TypeScript users need `@types/chai`. The `wtr-a11y-tree-plugin/chai` module adds both methods to `Chai.Assertion`.
103
+
104
+ ## Snapshot format
105
+
106
+ The format follows Playwright aria snapshots. Each line is a node.
107
+
108
+ ```text
109
+ - text: Size
110
+ - combobox "Size" [haspopup=listbox, expanded]:
111
+ - /value: Medium
112
+ - /activedescendant: option "Medium"
113
+ - /controls: listbox "Size"
114
+ - listbox "Size" [orientation=vertical]:
115
+ - option "Small"
116
+ - option "Medium" [selected]
117
+ ```
118
+
119
+ The rules are:
120
+
121
+ - **Role and name**: A node starts with its role. The name follows in double quotes.
122
+ - **States**: States appear in square brackets. A true state prints its lowercase name. Other states print as `name=value`.
123
+ - **False states**: Only `expanded=false` and `pressed=false` print. Other false states do not.
124
+ - **Value**: A value prints as `/value`. A single text child prints as an inline value.
125
+ - **Description**: A description prints as `/description`.
126
+ - **Relations**: Each relation target prints as `/relation: role "name"`.
127
+
128
+ ## Matching rules
129
+
130
+ `matchA11yTree()` checks that the expected tree is a subset of the actual tree. It does not require an exact match.
131
+
132
+ - **Subset**: The actual tree may have more nodes, states and properties.
133
+ - **Order**: Expected siblings must appear in the same order as in the actual tree.
134
+ - **Depth**: The first level of the expected tree may match nodes at any depth. Nested levels must match children.
135
+ - **Omitted parts**: A name or value that you do not write matches anything.
136
+
137
+ When the match fails, the assertion error holds the serialized actual tree. Test reporters show the difference.
138
+
139
+ ## Other exports
140
+
141
+ The `wtr-a11y-tree-plugin/format` module runs in the browser and in Node. It exports these functions:
142
+
143
+ - `serializeA11yTree(nodes)`: converts `A11yNode[]` to the snapshot text.
144
+ - `parseDisplayTree(text)`: parses the snapshot text.
145
+ - `matchDisplayList(expected, actual, deep)`: applies the matching rules.
146
+ - `dedent(text)`: removes the common indentation of a template literal.
147
+
148
+ ## Limitations
149
+
150
+ - Works in Playwright Chromium and Chrome only. Puppeteer and `@web/test-runner-chrome` are not supported.
151
+ - `equalA11yTreeSnapshot()` works in Mocha tests only. It uses the snapshot support of Web Test Runner.
152
+ - Pass `--update-snapshots` to Web Test Runner to rewrite saved snapshots.
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,15 @@
1
+ import type { A11yNode } from './types.ts';
2
+ export type { A11yNode } from './types.ts';
3
+ declare global {
4
+ var __a11yTreeTargets: Map<string, Element> | undefined;
5
+ }
6
+ /**
7
+ * Returns the Chromium accessibility tree of an element: the roles, names,
8
+ * descriptions, values, states and relations of the element and its
9
+ * descendants, including shadow DOM and slotted content.
10
+ *
11
+ * @example
12
+ * const tree = await getA11yTree(document.querySelector('vaadin-text-field'));
13
+ */
14
+ export declare function getA11yTree(element: Element): Promise<A11yNode[]>;
15
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,YAAY,CAAC;AAE5D,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACzD;AAID;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAWvE"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `getA11yTree()` is copied from vaadin/web-components test/test-runner-commands/index.js
3
+ * on proto/aria-snapshot at a9833531f6.
4
+ */
5
+ import { executeServerCommand } from '@web/test-runner-commands';
6
+ let a11yTreeId = 0;
7
+ /**
8
+ * Returns the Chromium accessibility tree of an element: the roles, names,
9
+ * descriptions, values, states and relations of the element and its
10
+ * descendants, including shadow DOM and slotted content.
11
+ *
12
+ * @example
13
+ * const tree = await getA11yTree(document.querySelector('vaadin-text-field'));
14
+ */
15
+ export async function getA11yTree(element) {
16
+ a11yTreeId += 1;
17
+ const id = `${a11yTreeId}`;
18
+ // Commands can only receive JSON, so the server reads the element from a global map.
19
+ globalThis.__a11yTreeTargets ||= new Map();
20
+ globalThis.__a11yTreeTargets.set(id, element);
21
+ try {
22
+ return (await executeServerCommand('get-a11y-tree', { id }));
23
+ }
24
+ finally {
25
+ globalThis.__a11yTreeTargets.delete(id);
26
+ }
27
+ }
28
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AASjE,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAgB;IAChD,UAAU,IAAI,CAAC,CAAC;IAChB,MAAM,EAAE,GAAG,GAAG,UAAU,EAAE,CAAC;IAC3B,qFAAqF;IACrF,UAAU,CAAC,iBAAiB,KAAK,IAAI,GAAG,EAAE,CAAC;IAC3C,UAAU,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,oBAAoB,CAAC,eAAe,EAAE,EAAE,EAAE,EAA4B,CAAC,CAAe,CAAC;IACvG,CAAC;YAAS,CAAC;QACT,UAAU,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC"}
package/dist/chai.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /// <reference types="chai" preserve="true" />
2
+ declare global {
3
+ namespace Chai {
4
+ interface Assertion {
5
+ /**
6
+ * Compares the Chromium accessibility tree of an element with the one saved for the current test.
7
+ */
8
+ equalA11yTreeSnapshot(): Promise<void>;
9
+ /**
10
+ * Checks that the Chromium accessibility tree of an element contains the nodes of the expected tree.
11
+ */
12
+ matchA11yTree(expected: string): Promise<void>;
13
+ }
14
+ }
15
+ }
16
+ /**
17
+ * Registers `matchA11yTree()` and `equalA11yTreeSnapshot()`. It does not
18
+ * import chai: `chai.use()` passes the instance of the consumer.
19
+ *
20
+ * @example
21
+ * import * as chai from 'chai';
22
+ * import { chaiA11yTree } from 'wtr-a11y-tree-plugin/chai';
23
+ *
24
+ * chai.use(chaiA11yTree);
25
+ */
26
+ export declare const chaiA11yTree: Chai.ChaiPlugin;
27
+ //# sourceMappingURL=chai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chai.d.ts","sourceRoot":"","sources":["../src/chai.ts"],"names":[],"mappings":";AAgBA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,IAAI,CAAC;QACb,UAAU,SAAS;YACjB;;eAEG;YACH,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;YAEvC;;eAEG;YACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;SAChD;KACF;CACF;AAwDD;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,IAAI,CAAC,UAG/B,CAAC"}
package/dist/chai.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Copied from vaadin/web-components test/plugins/index.js and index.d.ts
3
+ * on proto/aria-snapshot at a9833531f6.
4
+ */
5
+ /// <reference types="chai" preserve="true" />
6
+ import { getSnapshot, getSnapshotConfig, saveSnapshot } from '@web/test-runner-commands';
7
+ import { getA11yTree } from "./browser.js";
8
+ import { dedent, matchDisplayList, parseDisplayTree, serializeA11yTree, serializeDisplayTree, toDisplayTree, } from "./format.js";
9
+ /**
10
+ * Adds `matchA11yTree(expected)`, which checks that the Chromium accessibility
11
+ * tree of an element contains the nodes of the expected tree. The expected tree
12
+ * uses the snapshot format and lists only what the test cares about.
13
+ */
14
+ const chaiA11yTreeMatch = (chai, utils) => {
15
+ async function matchA11yTree(expected) {
16
+ const element = utils.flag(this, 'object');
17
+ const actual = toDisplayTree(await getA11yTree(element));
18
+ if (!matchDisplayList(parseDisplayTree(expected), actual, true)) {
19
+ throw new chai.AssertionError('expected accessibility tree to contain the expected nodes', { actual: serializeDisplayTree(actual), expected: dedent(expected), showDiff: true }, utils.flag(this, 'ssfi'));
20
+ }
21
+ }
22
+ utils.addMethod(chai.Assertion.prototype, 'matchA11yTree', matchA11yTree);
23
+ };
24
+ /**
25
+ * Adds `equalA11yTreeSnapshot()`, which compares the Chromium accessibility
26
+ * tree of an element with the one saved for the current test, and saves it
27
+ * when there is none or when the test runner is called with `--update-snapshots`.
28
+ */
29
+ const chaiA11yTreeSnapshot = (chai, utils) => {
30
+ async function equalA11yTreeSnapshot() {
31
+ const runner = globalThis.__WTR_MOCHA_RUNNER__;
32
+ if (!runner) {
33
+ throw new Error('equalA11yTreeSnapshot() works only in Mocha tests run by Web Test Runner.');
34
+ }
35
+ const element = utils.flag(this, 'object');
36
+ const name = runner.test.titlePath().join(' ');
37
+ const snapshot = serializeA11yTree(await getA11yTree(element));
38
+ const currentSnapshot = await getSnapshot({ name });
39
+ const config = await getSnapshotConfig();
40
+ if (currentSnapshot && !config.updateSnapshots) {
41
+ if (currentSnapshot !== snapshot) {
42
+ throw new chai.AssertionError(`Accessibility tree snapshot ${name} does not match the saved snapshot on disk`, { actual: snapshot, expected: currentSnapshot, showDiff: true }, utils.flag(this, 'ssfi'));
43
+ }
44
+ }
45
+ else if (currentSnapshot !== snapshot) {
46
+ await saveSnapshot({ name, content: snapshot });
47
+ }
48
+ }
49
+ utils.addMethod(chai.Assertion.prototype, 'equalA11yTreeSnapshot', equalA11yTreeSnapshot);
50
+ };
51
+ /**
52
+ * Registers `matchA11yTree()` and `equalA11yTreeSnapshot()`. It does not
53
+ * import chai: `chai.use()` passes the instance of the consumer.
54
+ *
55
+ * @example
56
+ * import * as chai from 'chai';
57
+ * import { chaiA11yTree } from 'wtr-a11y-tree-plugin/chai';
58
+ *
59
+ * chai.use(chaiA11yTree);
60
+ */
61
+ export const chaiA11yTree = (chai, utils) => {
62
+ chaiA11yTreeMatch(chai, utils);
63
+ chaiA11yTreeSnapshot(chai, utils);
64
+ };
65
+ //# sourceMappingURL=chai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chai.js","sourceRoot":"","sources":["../src/chai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Copied from vaadin/web-components test/plugins/index.js
3
+ * on proto/aria-snapshot at a9833531f6.
4
+ */
5
+ import type { A11yNode } from './types.ts';
6
+ export type DisplayNode = {
7
+ role: string;
8
+ name?: string;
9
+ states: string[];
10
+ value?: string;
11
+ props: Array<[string, string]>;
12
+ children: DisplayNode[];
13
+ };
14
+ /**
15
+ * Converts the tree returned by `getA11yTree()` into the nodes that the
16
+ * snapshot prints: formatted states, descriptions and relations as properties,
17
+ * and a single text child as the inline value.
18
+ */
19
+ export declare const toDisplayTree: (nodes: A11yNode[]) => DisplayNode[];
20
+ /**
21
+ * Serializes an accessibility tree to YAML in the format of Playwright aria
22
+ * snapshots. Descriptions and relations are printed as `/description` and
23
+ * `/<relation>` properties, like `/url` in Playwright.
24
+ */
25
+ export declare const serializeDisplayTree: (nodes: DisplayNode[], indent?: string) => string;
26
+ export declare const serializeA11yTree: (nodes: A11yNode[]) => string;
27
+ /**
28
+ * Removes the common indentation and the blank first and last lines of a
29
+ * template literal.
30
+ */
31
+ export declare const dedent: (text: string) => string;
32
+ /**
33
+ * Parses a tree in the snapshot format into display nodes. Values that are
34
+ * not written, such as a missing name, are left undefined and match anything.
35
+ */
36
+ export declare const parseDisplayTree: (text: string) => DisplayNode[];
37
+ /**
38
+ * Checks that the expected nodes appear in the actual nodes in the same order.
39
+ * Other nodes, states and properties are allowed. With `deep`, an expected node
40
+ * can match an actual node at any depth, which is used for the top level only,
41
+ * so that the expected tree can start below containers.
42
+ */
43
+ export declare function matchDisplayList(expected: DisplayNode[], actual: DisplayNode[], deep?: boolean): boolean;
44
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAiCF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,QAAQ,EAAE,KAAG,WAAW,EA4BzD,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,WAAW,EAAE,EAAE,eAAW,KAAG,MAuB1D,CAAC;AAEhB,eAAO,MAAM,iBAAiB,GAAI,OAAO,QAAQ,EAAE,KAAG,MAAoD,CAAC;AAK3G;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,MAIrC,CAAC;AAQF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,WAAW,EA2C1D,CAAC;AAmBF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,UAAQ,GAAG,OAAO,CAoBtG"}
package/dist/format.js ADDED
@@ -0,0 +1,182 @@
1
+ // States printed even when `false`, as `false` differs from a missing state.
2
+ const A11Y_FALSE_STATES = new Set(['expanded', 'pressed']);
3
+ // Snapshots are stored in template literals, which turn escape sequences
4
+ // into characters on read, so quote without escaping.
5
+ const quote = (text) => `"${text}"`;
6
+ const formatText = (text) => (/^[\w$.,!?()•@ ]+$/u.test(text) && !/^\d/u.test(text) ? text : quote(text));
7
+ const formatStates = (node) => Object.entries(node.states || {})
8
+ .map(([key, value]) => {
9
+ if (value === true || value === 'true') {
10
+ return key.toLowerCase();
11
+ }
12
+ if (value === false || value === 'false') {
13
+ return A11Y_FALSE_STATES.has(key) ? `${key}=false` : null;
14
+ }
15
+ if (value === '' || (key === 'valuetext' && String(value) === node.value)) {
16
+ return null;
17
+ }
18
+ return `${key.toLowerCase()}=${value}`;
19
+ })
20
+ .filter((state) => state !== null);
21
+ /**
22
+ * Removes text that repeats the parent name, such as the label of a button.
23
+ */
24
+ const normalizeChildren = (children, parentName) => (children || []).filter((child) => !(child.role === 'text' && child.name === parentName));
25
+ /**
26
+ * Converts the tree returned by `getA11yTree()` into the nodes that the
27
+ * snapshot prints: formatted states, descriptions and relations as properties,
28
+ * and a single text child as the inline value.
29
+ */
30
+ export const toDisplayTree = (nodes) => normalizeChildren(nodes).map((node) => {
31
+ if (node.role === 'text') {
32
+ return { role: 'text', name: node.name, states: [], props: [], children: [] };
33
+ }
34
+ let children = normalizeChildren(node.children, node.name);
35
+ let value = node.value;
36
+ if (value === undefined && children.length === 1 && children[0].role === 'text') {
37
+ value = children[0].name;
38
+ children = [];
39
+ }
40
+ const props = [];
41
+ if (node.description) {
42
+ props.push(['description', node.description]);
43
+ }
44
+ Object.entries(node.relations || {}).forEach(([key, targets]) => {
45
+ targets.forEach(({ role, name }) => {
46
+ props.push([key, `${role}${name ? ` ${quote(name)}` : ''}`]);
47
+ });
48
+ });
49
+ return {
50
+ role: node.role,
51
+ name: node.name,
52
+ states: formatStates(node),
53
+ value,
54
+ props,
55
+ children: toDisplayTree(children),
56
+ };
57
+ });
58
+ /**
59
+ * Serializes an accessibility tree to YAML in the format of Playwright aria
60
+ * snapshots. Descriptions and relations are printed as `/description` and
61
+ * `/<relation>` properties, like `/url` in Playwright.
62
+ */
63
+ export const serializeDisplayTree = (nodes, indent = '') => nodes
64
+ .map((node) => {
65
+ if (node.role === 'text') {
66
+ return `${indent}- text: ${formatText(node.name)}`;
67
+ }
68
+ let line = `${indent}- ${node.role}`;
69
+ if (node.name) {
70
+ line += ` ${quote(node.name)}`;
71
+ }
72
+ if (node.states.length) {
73
+ line += ` [${node.states.join(', ')}]`;
74
+ }
75
+ const props = node.props.map(([key, value]) => `${indent} - /${key}: ${key === 'description' ? formatText(value) : value}`);
76
+ const nested = node.children.length ? serializeDisplayTree(node.children, `${indent} `) : '';
77
+ if (!props.length && !nested) {
78
+ return node.value === undefined ? line : `${line}: ${formatText(node.value)}`;
79
+ }
80
+ const head = node.value === undefined ? `${line}:` : `${line}:\n${indent} - /value: ${formatText(node.value)}`;
81
+ return [head, ...props, nested].filter(Boolean).join('\n');
82
+ })
83
+ .join('\n');
84
+ export const serializeA11yTree = (nodes) => serializeDisplayTree(toDisplayTree(nodes));
85
+ const unquote = (text) => text.length > 1 && text.startsWith('"') && text.endsWith('"') ? text.slice(1, -1) : text;
86
+ /**
87
+ * Removes the common indentation and the blank first and last lines of a
88
+ * template literal.
89
+ */
90
+ export const dedent = (text) => {
91
+ const lines = text.replace(/^\s*\n|\n\s*$/gu, '').split('\n');
92
+ const indent = Math.min(...lines.filter((line) => line.trim()).map((line) => line.match(/^ */u)[0].length));
93
+ return lines.map((line) => line.slice(indent)).join('\n');
94
+ };
95
+ const NODE_PATTERN = /^(?<role>[\w-]+)(?: "(?<name>[^"]*)")?(?: \[(?<states>[^\]]*)\])?(?::(?: (?<value>.+))?)?$/u;
96
+ /**
97
+ * Parses a tree in the snapshot format into display nodes. Values that are
98
+ * not written, such as a missing name, are left undefined and match anything.
99
+ */
100
+ export const parseDisplayTree = (text) => {
101
+ const root = { children: [] };
102
+ const stack = [{ indent: -1, node: root }];
103
+ dedent(text)
104
+ .split('\n')
105
+ .filter((line) => line.trim())
106
+ .forEach((line) => {
107
+ const indent = line.match(/^ */u)[0].length;
108
+ const content = line.trim().replace(/^- /u, '');
109
+ while (stack[stack.length - 1].indent >= indent) {
110
+ stack.pop();
111
+ }
112
+ const parent = stack[stack.length - 1].node;
113
+ const prop = content.match(/^\/(?<key>[\w-]+): (?<value>.+)$/u);
114
+ if (prop) {
115
+ const { key, value } = prop.groups;
116
+ if (key === 'value') {
117
+ parent.value = unquote(value);
118
+ }
119
+ else {
120
+ parent.props.push([key, key === 'description' ? unquote(value) : value]);
121
+ }
122
+ return;
123
+ }
124
+ const match = content.match(NODE_PATTERN);
125
+ if (!match) {
126
+ throw new Error(`Cannot parse accessibility tree line: ${line}`);
127
+ }
128
+ const { role, name, states, value } = match.groups;
129
+ const node = role === 'text'
130
+ ? { role, name: value === undefined ? undefined : unquote(value) }
131
+ : {
132
+ role,
133
+ name,
134
+ states: states ? states.split(', ') : [],
135
+ value: value === undefined ? undefined : unquote(value),
136
+ props: [],
137
+ children: [],
138
+ };
139
+ parent.children.push(node);
140
+ stack.push({ indent, node });
141
+ });
142
+ return root.children;
143
+ };
144
+ /**
145
+ * Lists the nodes of a tree in document order, each with the index after its
146
+ * subtree, so that a match can skip the rest of the subtree.
147
+ */
148
+ const flattenDisplayTree = (nodes, result = []) => {
149
+ nodes.forEach((node) => {
150
+ const entry = { node, end: 0 };
151
+ result.push(entry);
152
+ flattenDisplayTree(node.children || [], result);
153
+ entry.end = result.length;
154
+ });
155
+ return result;
156
+ };
157
+ /**
158
+ * Checks that the expected nodes appear in the actual nodes in the same order.
159
+ * Other nodes, states and properties are allowed. With `deep`, an expected node
160
+ * can match an actual node at any depth, which is used for the top level only,
161
+ * so that the expected tree can start below containers.
162
+ */
163
+ export function matchDisplayList(expected, actual, deep = false) {
164
+ const matchNode = (exp, act) => exp.role === act.role &&
165
+ (exp.name === undefined || exp.name === act.name) &&
166
+ (exp.role === 'text' ||
167
+ ((exp.value === undefined || exp.value === act.value) &&
168
+ exp.states.every((state) => act.states.includes(state)) &&
169
+ exp.props.every(([key, value]) => act.props.some(([k, v]) => k === key && v === value)) &&
170
+ matchDisplayList(exp.children, act.children)));
171
+ const flat = deep ? flattenDisplayTree(actual) : actual.map((node, i) => ({ node, end: i + 1 }));
172
+ let cursor = 0;
173
+ return expected.every((node) => {
174
+ const index = flat.findIndex((entry, i) => i >= cursor && matchNode(node, entry.node));
175
+ if (index === -1) {
176
+ return false;
177
+ }
178
+ cursor = flat[index].end;
179
+ return true;
180
+ });
181
+ }
182
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `a11yTreePlugin()` is copied from vaadin/web-components wtr-utils.js
3
+ * on proto/aria-snapshot at a9833531f6.
4
+ */
5
+ import type { TestRunnerPlugin } from '@web/test-runner';
6
+ import type { A11yTreePayload } from './types.ts';
7
+ export type { A11yNode } from './types.ts';
8
+ /**
9
+ * A test runner command that returns the Chromium accessibility tree of the
10
+ * element marked by `getA11yTree()`. It works in Playwright Chromium only.
11
+ */
12
+ export declare const a11yTreePlugin: () => TestRunnerPlugin<A11yTreePayload>;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAO,gBAAgB,CAAC,eAAe,CA6BhE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ import { toA11yTree } from "./tree.js";
2
+ /**
3
+ * A test runner command that returns the Chromium accessibility tree of the
4
+ * element marked by `getA11yTree()`. It works in Playwright Chromium only.
5
+ */
6
+ export const a11yTreePlugin = () => ({
7
+ name: 'a11y-tree-command',
8
+ async executeCommand({ command, payload, session }) {
9
+ if (command !== 'get-a11y-tree') {
10
+ return undefined;
11
+ }
12
+ const { id } = payload;
13
+ const browser = session.browser;
14
+ if (browser.type !== 'playwright' || browser.name !== 'Chromium') {
15
+ throw new Error(`Accessibility tree is not supported for browser ${browser.name}.`);
16
+ }
17
+ const page = browser.getPage(session.id);
18
+ const cdp = await page.context().newCDPSession(page);
19
+ try {
20
+ // The browser side stores the element in a global map, which also works
21
+ // for elements inside shadow roots.
22
+ const { result } = await cdp.send('Runtime.evaluate', {
23
+ expression: `globalThis.__a11yTreeTargets.get(${JSON.stringify(id)})`,
24
+ });
25
+ if (!result.objectId) {
26
+ throw new Error(`Cannot find the element for accessibility tree request ${id}.`);
27
+ }
28
+ const { node } = await cdp.send('DOM.describeNode', { objectId: result.objectId });
29
+ const { nodes } = await cdp.send('Accessibility.getFullAXTree');
30
+ return toA11yTree(nodes, node.backendNodeId);
31
+ }
32
+ finally {
33
+ await cdp.detach();
34
+ }
35
+ },
36
+ });
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAKvC;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAsC,EAAE,CAAC,CAAC;IACtE,IAAI,EAAE,mBAAmB;IACzB,KAAK,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE;QAChD,IAAI,OAAO,KAAK,eAAe,EAAE,CAAC;YAChC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,EAAE,EAAE,EAAE,GAAG,OAAQ,CAAC;QACxB,MAAM,OAAO,GAAG,OAAO,CAAC,OAA6B,CAAC;QACtD,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,mDAAmD,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC;YACH,wEAAwE;YACxE,oCAAoC;YACpC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;gBACpD,UAAU,EAAE,oCAAoC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG;aACtE,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,0DAA0D,EAAE,GAAG,CAAC,CAAC;YACnF,CAAC;YACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnF,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAChE,OAAO,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/C,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
package/dist/tree.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `toA11yTree()` is copied from vaadin/web-components wtr-utils.js
3
+ * on proto/aria-snapshot at a9833531f6.
4
+ */
5
+ import type { CDPSession } from 'playwright-core';
6
+ import type { A11yNode } from './types.ts';
7
+ declare const cdp: CDPSession;
8
+ declare const fullAXTree: ReturnType<typeof cdp.send<'Accessibility.getFullAXTree'>>;
9
+ type AXNode = Awaited<typeof fullAXTree>['nodes'][number];
10
+ /**
11
+ * Converts the CDP accessibility tree below the given DOM node into a list of
12
+ * plain nodes. Ignored nodes, unnamed generic containers and noisy roles are
13
+ * skipped, and their children move up. Relations are resolved to the role and
14
+ * name of the target, as generated IDs change between runs.
15
+ */
16
+ export declare const toA11yTree: (axNodes: AXNode[], backendNodeId: number) => A11yNode[];
17
+ export {};
18
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,CAAC,MAAM,GAAG,EAAE,UAAU,CAAC;AAC9B,OAAO,CAAC,MAAM,UAAU,EAAE,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;AACrF,KAAK,MAAM,GAAG,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAyC1D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,EAAE,eAAe,MAAM,KAAG,QAAQ,EAsD7E,CAAC"}
package/dist/tree.js ADDED
@@ -0,0 +1,92 @@
1
+ // Accessibility tree properties kept in `get-a11y-tree` results. Others,
2
+ // such as `focusable` or `editable`, are set on most nodes and add noise.
3
+ const A11Y_TREE_STATES = new Set([
4
+ 'autocomplete',
5
+ 'checked',
6
+ 'disabled',
7
+ 'expanded',
8
+ 'hasPopup',
9
+ 'invalid',
10
+ 'level',
11
+ 'modal',
12
+ 'multiselectable',
13
+ 'orientation',
14
+ 'pressed',
15
+ 'readonly',
16
+ 'required',
17
+ 'selected',
18
+ 'valuemax',
19
+ 'valuemin',
20
+ 'valuetext',
21
+ ]);
22
+ const A11Y_TREE_RELATIONS = new Set(['activedescendant', 'controls', 'details', 'errormessage', 'owns']);
23
+ // Roles with no meaningful descendants: their text is already in the name or value.
24
+ const A11Y_TREE_LEAF_ROLES = new Set([
25
+ 'checkbox',
26
+ 'combobox',
27
+ 'radio',
28
+ 'searchbox',
29
+ 'slider',
30
+ 'spinbutton',
31
+ 'switch',
32
+ 'textbox',
33
+ ]);
34
+ // Roles that only add noise to the tree. Their children are kept.
35
+ const A11Y_TREE_SKIPPED_ROLES = new Set(['InlineTextBox', 'LabelText', 'LineBreak', 'none']);
36
+ /**
37
+ * Converts the CDP accessibility tree below the given DOM node into a list of
38
+ * plain nodes. Ignored nodes, unnamed generic containers and noisy roles are
39
+ * skipped, and their children move up. Relations are resolved to the role and
40
+ * name of the target, as generated IDs change between runs.
41
+ */
42
+ export const toA11yTree = (axNodes, backendNodeId) => {
43
+ const byId = new Map(axNodes.map((node) => [node.nodeId, node]));
44
+ const byBackendId = new Map(axNodes.map((node) => [node.backendDOMNodeId, node]));
45
+ const describe = (node) => ({
46
+ role: String(node.role?.value),
47
+ name: node.name?.value ?? '',
48
+ });
49
+ const convert = (node) => {
50
+ const role = node.role?.value;
51
+ const name = node.name?.value ?? '';
52
+ const children = A11Y_TREE_LEAF_ROLES.has(role)
53
+ ? []
54
+ : (node.childIds || []).flatMap((id) => (byId.has(id) ? convert(byId.get(id)) : []));
55
+ if (node.ignored || A11Y_TREE_SKIPPED_ROLES.has(role) || (role === 'generic' && !name)) {
56
+ // Join the text of one container, such as a label, into one node. Text
57
+ // from different containers stays separate.
58
+ if (children.length > 1 && children.every((child) => child.role === 'text')) {
59
+ return [{ role: 'text', name: children.map((child) => child.name).join(' ') }];
60
+ }
61
+ return children;
62
+ }
63
+ if (role === 'StaticText') {
64
+ return name ? [{ role: 'text', name }] : [];
65
+ }
66
+ const states = {};
67
+ const relations = {};
68
+ const result = { role: role, name, states, relations, children };
69
+ const value = node.value?.value;
70
+ if (value !== undefined && value !== '') {
71
+ result.value = String(value);
72
+ }
73
+ if (node.description?.value) {
74
+ result.description = node.description.value;
75
+ }
76
+ (node.properties || []).forEach(({ name: key, value: { value: stateValue, relatedNodes } }) => {
77
+ if (A11Y_TREE_STATES.has(key)) {
78
+ states[key] = stateValue;
79
+ }
80
+ else if (A11Y_TREE_RELATIONS.has(key) && relatedNodes) {
81
+ relations[key] = relatedNodes
82
+ .map((related) => byBackendId.get(related.backendDOMNodeId))
83
+ .filter((related) => !!related)
84
+ .map(describe);
85
+ }
86
+ });
87
+ return [result];
88
+ };
89
+ const root = byBackendId.get(backendNodeId);
90
+ return root ? convert(root) : [];
91
+ };
92
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export type A11yNode = {
2
+ role: string;
3
+ name: string;
4
+ value?: string;
5
+ description?: string;
6
+ states?: Record<string, boolean | number | string>;
7
+ relations?: Record<string, Array<{
8
+ role: string;
9
+ name: string;
10
+ }>>;
11
+ children?: A11yNode[];
12
+ };
13
+ export type A11yTreePayload = {
14
+ id: string;
15
+ };
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "wtr-a11y-tree-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Chromium accessibility tree commands and chai assertions for Web Test Runner",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": ">=22"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./browser": {
17
+ "types": "./dist/browser.d.ts",
18
+ "default": "./dist/browser.js"
19
+ },
20
+ "./chai": {
21
+ "types": "./dist/chai.d.ts",
22
+ "default": "./dist/chai.js"
23
+ },
24
+ "./format": {
25
+ "types": "./dist/format.d.ts",
26
+ "default": "./dist/format.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -p tsconfig.build.json",
34
+ "lint": "tsc && prettier --check .",
35
+ "test": "npm run test:unit && npm run test:browser",
36
+ "test:unit": "node --test \"test/*.test.ts\"",
37
+ "test:browser": "npm run build && web-test-runner",
38
+ "prepack": "npm run build"
39
+ },
40
+ "dependencies": {
41
+ "@web/test-runner-commands": "^1.0.1"
42
+ },
43
+ "peerDependencies": {
44
+ "@types/chai": "*",
45
+ "@web/test-runner": "^1.0.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@types/chai": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "@types/chai": "^5.0.0",
54
+ "@types/mocha": "^10.0.10",
55
+ "@types/node": "^24.0.0",
56
+ "@web/test-runner": "^1.0.0",
57
+ "@web/test-runner-playwright": "^1.0.0",
58
+ "chai": "^6.2.2",
59
+ "playwright": "1.64.1",
60
+ "playwright-core": "1.64.1",
61
+ "prettier": "^3.9.4",
62
+ "typescript": "^6.0.3"
63
+ }
64
+ }