|
1 | 1 | import { expect } from 'chai' |
2 | 2 | import { ariaSnapshotFromAXNodes, ariaSnapshotFromTree, compactAriaSnapshot, diffAriaSnapshots } from '../../lib/aria.js' |
3 | 3 |
|
4 | | -const role = value => ({ type: 'role', value }) |
5 | | -const name = value => ({ type: 'computedString', value }) |
6 | | -const prop = (n, value) => ({ name: n, value: { type: 'token', value } }) |
7 | | - |
8 | | -const lightpandaNodes = [ |
9 | | - { nodeId: '1', backendDOMNodeId: 1, role: role('RootWebArea'), name: name('Checkout'), childIds: ['2'] }, |
10 | | - { nodeId: '2', backendDOMNodeId: 2, role: role('none'), childIds: ['3', '5', '7', '8'] }, |
11 | | - { nodeId: '3', backendDOMNodeId: 3, role: role('heading'), name: name('Checkout form'), properties: [{ name: 'level', value: { type: 'integer', value: 4 } }], childIds: ['4'] }, |
12 | | - { nodeId: '4', backendDOMNodeId: 4, role: role('StaticText'), name: name('Checkout form'), childIds: [] }, |
13 | | - { nodeId: '5', backendDOMNodeId: 5, role: role('form'), name: name(''), childIds: ['6', '9'] }, |
14 | | - { nodeId: '6', backendDOMNodeId: 6, role: role('textbox'), name: name('Promo code'), value: { type: 'string', value: 'SALE' }, properties: [prop('invalid', 'false')], childIds: [] }, |
15 | | - { nodeId: '7', backendDOMNodeId: 7, role: role('checkbox'), name: name('Same address'), value: { type: 'string', value: 'on' }, properties: [prop('checked', 'true')], childIds: [] }, |
16 | | - { nodeId: '8', backendDOMNodeId: 8, role: role('paragraph'), childIds: ['10'] }, |
17 | | - { nodeId: '9', backendDOMNodeId: 9, role: role('button'), name: name('Redeem'), childIds: ['11'] }, |
18 | | - { nodeId: '10', backendDOMNodeId: 10, role: role('StaticText'), name: name('Total: $20'), childIds: [] }, |
19 | | - { nodeId: '11', backendDOMNodeId: 11, role: role('StaticText'), name: name('Redeem'), childIds: [] }, |
20 | | -] |
21 | | - |
22 | | -const obscuraNodes = [ |
23 | | - { nodeId: '1', ignored: false, role: role('RootWebArea'), childIds: ['2'], backendDOMNodeId: 1 }, |
24 | | - { nodeId: '2', ignored: false, role: role('generic'), childIds: ['3', '5'], backendDOMNodeId: 2 }, |
25 | | - { nodeId: '3', ignored: false, role: role('button'), properties: [{ name: 'focusable', value: { type: 'boolean', value: true } }], childIds: ['4'], backendDOMNodeId: 3 }, |
26 | | - { nodeId: '4', ignored: false, role: role('StaticText'), name: { type: 'string', value: 'Redeem' }, backendDOMNodeId: 4 }, |
27 | | - { nodeId: '5', ignored: false, role: role('heading'), properties: [{ name: 'level', value: { type: 'integer', value: 2 } }], childIds: ['6'], backendDOMNodeId: 5 }, |
28 | | - { nodeId: '6', ignored: false, role: role('LabelText'), childIds: ['7'], backendDOMNodeId: 6 }, |
29 | | - { nodeId: '7', ignored: false, role: role('StaticText'), name: { type: 'string', value: 'Billing address' }, backendDOMNodeId: 7 }, |
| 4 | +const axNode = (nodeId, role, name, childIds = []) => ({ nodeId, backendDOMNodeId: Number(nodeId), role: { value: role }, name: name === undefined ? undefined : { value: name }, childIds }) |
| 5 | + |
| 6 | +const axNodes = [ |
| 7 | + axNode('1', 'generic', undefined, ['2', '4', '6']), |
| 8 | + axNode('2', 'heading', 'Checkout', ['3']), |
| 9 | + axNode('3', 'StaticText', 'Checkout'), |
| 10 | + axNode('4', 'form', '', ['5', '7']), |
| 11 | + axNode('5', 'textbox', 'Promo code'), |
| 12 | + axNode('6', 'StaticText', 'Total: $20'), |
| 13 | + axNode('7', 'button', undefined, ['8']), |
| 14 | + axNode('8', 'StaticText', 'Redeem'), |
30 | 15 | ] |
31 | 16 |
|
32 | 17 | describe('aria', () => { |
33 | | - describe('#ariaSnapshotFromAXNodes', () => { |
34 | | - it('renders a Lightpanda accessibility tree as an ARIA snapshot', () => { |
35 | | - expect(ariaSnapshotFromAXNodes(lightpandaNodes)).to.equal( |
36 | | - [ |
37 | | - '- heading "Checkout form" [level=4]', |
38 | | - '- form:', |
39 | | - ' - textbox "Promo code": SALE', |
40 | | - ' - button "Redeem"', |
41 | | - '- checkbox "Same address" [checked]', |
42 | | - '- paragraph:', |
43 | | - ' - text: "Total: $20"', |
44 | | - ].join('\n'), |
45 | | - ) |
46 | | - }) |
47 | | - |
48 | | - it('names buttons and headings from their text when the engine leaves names empty', () => { |
49 | | - expect(ariaSnapshotFromAXNodes(obscuraNodes)).to.equal(['- button "Redeem"', '- heading "Billing address" [level=2]'].join('\n')) |
50 | | - }) |
51 | | - |
52 | | - it('scopes the snapshot to an element by its backend node id', () => { |
53 | | - expect(ariaSnapshotFromAXNodes(lightpandaNodes, 5)).to.equal(['- form:', ' - textbox "Promo code": SALE', ' - button "Redeem"'].join('\n')) |
54 | | - }) |
55 | | - |
56 | | - it('returns an empty snapshot when the element is not in the tree', () => { |
57 | | - expect(ariaSnapshotFromAXNodes(lightpandaNodes, 999)).to.equal('') |
58 | | - }) |
59 | | - |
60 | | - it('produces snapshots that compact and diff like Playwright ones', () => { |
61 | | - const before = ariaSnapshotFromAXNodes(lightpandaNodes) |
62 | | - const after = ariaSnapshotFromAXNodes(lightpandaNodes.filter(node => node.nodeId !== '9')) |
63 | | - expect(compactAriaSnapshot(before)).to.include('button "Redeem"') |
64 | | - expect(diffAriaSnapshots(before, after)).to.include('removed: 1 interactive elements') |
65 | | - }) |
| 18 | + it('renders a CDP accessibility tree from a DOM node', () => { |
| 19 | + expect(ariaSnapshotFromAXNodes(axNodes, 1)).to.equal( |
| 20 | + ['- heading "Checkout"', '- form:', ' - textbox "Promo code"', ' - button:', ' - text: "Redeem"', '- text: "Total: $20"'].join('\n'), |
| 21 | + ) |
| 22 | + expect(ariaSnapshotFromAXNodes(axNodes, 4)).to.equal(['- form:', ' - textbox "Promo code"', ' - button:', ' - text: "Redeem"'].join('\n')) |
| 23 | + expect(ariaSnapshotFromAXNodes(axNodes, 999)).to.equal('') |
66 | 24 | }) |
67 | 25 |
|
68 | | - describe('#ariaSnapshotFromTree', () => { |
69 | | - it('renders a Puppeteer accessibility snapshot', () => { |
70 | | - const tree = { |
71 | | - role: 'RootWebArea', |
72 | | - name: 'Login', |
73 | | - children: [ |
74 | | - { role: 'heading', name: 'Sign "in"', level: 1, children: [{ role: 'StaticText', name: 'Sign "in"', children: [{ role: 'InlineTextBox', name: 'Sign "in"' }] }] }, |
75 | | - { role: 'textbox', name: 'Email', value: '' }, |
76 | | - { role: 'checkbox', name: 'Remember me', checked: 'mixed' }, |
77 | | - { role: 'button', name: 'Submit', disabled: true }, |
78 | | - { role: 'image', name: 'Logo' }, |
79 | | - ], |
80 | | - } |
81 | | - expect(ariaSnapshotFromTree(tree)).to.equal( |
82 | | - ['- heading "Sign \\"in\\"" [level=1]', '- textbox "Email"', '- checkbox "Remember me" [checked=mixed]', '- button "Submit" [disabled]', '- img "Logo"'].join('\n'), |
83 | | - ) |
84 | | - }) |
| 26 | + it('renders a Puppeteer accessibility tree', () => { |
| 27 | + const tree = { role: 'RootWebArea', children: [{ role: 'button', name: 'Submit', children: [{ role: 'StaticText', name: 'Submit' }] }] } |
| 28 | + expect(ariaSnapshotFromTree(tree)).to.equal('- button "Submit"') |
| 29 | + expect(ariaSnapshotFromTree(null)).to.equal('') |
| 30 | + }) |
85 | 31 |
|
86 | | - it('returns an empty snapshot for an empty tree', () => { |
87 | | - expect(ariaSnapshotFromTree(null)).to.equal('') |
88 | | - }) |
| 32 | + it('produces snapshots that compact and diff', () => { |
| 33 | + const before = ariaSnapshotFromAXNodes(axNodes, 1) |
| 34 | + const after = ariaSnapshotFromAXNodes(axNodes.filter(node => node.nodeId !== '5'), 1) |
| 35 | + expect(compactAriaSnapshot(before)).to.include('textbox "Promo code"') |
| 36 | + expect(diffAriaSnapshots(before, after)).to.include('removed: 1 interactive elements') |
89 | 37 | }) |
90 | 38 | }) |
0 commit comments