Skip to content

Commit c5db33e

Browse files
committed
fix: resolve key property (take 2)
1 parent d70ebcd commit c5db33e

File tree

2 files changed

+62
-24
lines changed

2 files changed

+62
-24
lines changed

src/aria/match.ts

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
AriaNode,
1414
AriaRegex,
1515
AriaTemplateNode,
16+
AriaTemplateRoleNode,
1617
} from './folk/isomorphic/ariaSnapshot'
1718
import { formatTextValue, formatNameValue } from './template'
1819

@@ -91,13 +92,9 @@ import { formatTextValue, formatNameValue } from './template'
9192
*
9293
* Invariant:
9394
* pass = true <=> resolved = expected
94-
* TODO: right? but this doesn't hold yet.
95-
* we only have one direction
96-
* pass = true <= resolved = expected
97-
* or equivalently
98-
* pass = false => resolved != expected
99-
* This shouldn't affect user facing behavior since
100-
* when pass = true, it won't show error diff nor create new snapshot.
95+
* TODO:
96+
* This holds all cases except `aria-expanded` tri-state behaviors,
97+
* which we inherited from playwright. We leave this case for now.
10198
*
10299
* Diff display (pass: false):
103100
* Use resolved vs expected. The user sees their original assertion
@@ -159,6 +156,49 @@ function renderKeyWithName(
159156
return key
160157
}
161158

159+
/** Build the resolved key through the template's lens:
160+
* only include name/attributes that the template mentions. */
161+
function renderResolvedKey(node: AriaNode, template: AriaTemplateRoleNode): string {
162+
let key = node.role as string
163+
164+
// Name: omit if template omits, adopt regex if matched, literal otherwise
165+
if (template.name === undefined) {
166+
// template doesn't care about name → omit
167+
} else if (
168+
isRegexName(template.name) &&
169+
matchesStringOrRegex(node.name, template.name)
170+
) {
171+
key += ` ${formatNameValue(template.name)}`
172+
} else {
173+
if (node.name) {
174+
key += ` ${JSON.stringify(node.name)}`
175+
}
176+
}
177+
178+
// Attributes: only render what the template mentions
179+
if (template.level !== undefined) key += ` [level=${node.level}]`
180+
if (template.checked !== undefined) {
181+
if (node.checked === true) key += ' [checked]'
182+
else if (node.checked === 'mixed') key += ' [checked=mixed]'
183+
}
184+
if (template.disabled !== undefined && node.disabled) {
185+
key += ' [disabled]'
186+
}
187+
if (template.expanded !== undefined) {
188+
if (node.expanded === true) key += ' [expanded]'
189+
else if (node.expanded === false) key += ' [expanded=false]'
190+
}
191+
if (template.pressed !== undefined) {
192+
if (node.pressed === true) key += ' [pressed]'
193+
else if (node.pressed === 'mixed') key += ' [pressed=mixed]'
194+
}
195+
if (template.selected !== undefined && node.selected) {
196+
key += ' [selected]'
197+
}
198+
199+
return key
200+
}
201+
162202
// --- Pairing + merge ---
163203

164204
function pairChildren(
@@ -290,18 +330,13 @@ function mergeNode(
290330
let namePass = matchesStringOrRegex(node.name, template.name)
291331

292332
// Resolved key (e.g. `- heading "Hello" [level=1]`):
293-
// adopt the template's lens for the name.
294-
// template omits name (e.g. `- heading`) → resolved omits it
333+
// adopt the template's lens for both name and attributes.
334+
// template omits name (e.g. `- heading`) → resolved omits it
295335
// template has regex (e.g. `- button /\d+/`) → resolved adopts regex if matched
296336
// template has literal (e.g. `- button "Save"`) → resolved uses literal
297-
let resolvedKey: string
298-
if (template.name === undefined) {
299-
resolvedKey = `${node.role}${renderAriaProps(node)}`
300-
} else if (namePass && isRegexName(template.name)) {
301-
resolvedKey = renderKeyWithName(node, template.name)
302-
} else {
303-
resolvedKey = createAriaKey(node)
304-
}
337+
// template omits attr (e.g. no [level]) → resolved omits it
338+
// template has attr (e.g. [level=1]) → resolved includes it
339+
const resolvedKey = renderResolvedKey(node, template)
305340

306341
// Recurse into children — if template omits children, the lens says
307342
// "don't care", so we skip (don't render children in resolved output).

test/aria.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ const match = vi.defineHelper(
3333
const actual = renderAriaTree(tree)
3434
const expected = renderAriaTemplate(templateTree)
3535
if (options?.assertInvariant !== false) {
36-
// TODO: not invariant yet
3736
if (r.pass) {
3837
expect(r.resolved, `when pass=true, resolved should equal expected`).toBe(
3938
expected
@@ -3700,8 +3699,7 @@ describe('matchAriaTree', () => {
37003699
- heading "title 2" [level=1]
37013700
",
37023701
"actualResolved": "
3703-
- heading "title" [level=1]
3704-
- heading "title 2" [level=1]
3702+
- heading "title"
37053703
",
37063704
"expected": "
37073705
- heading "title"
@@ -4141,19 +4139,21 @@ describe('aria-expanded', () => {
41414139
// TODO: `expected` should preserve [expanded=false] from the template
41424140
// when pass is true. Currently lost because mergedKey goes through
41434141
// createAriaKey/renderAriaProps which omits expanded=false.
4142+
// This is the only case that breaks invariant.
41444143
test('expanded=false is preserved in merge when template asserts it', () => {
41454144
expect(
41464145
match(
41474146
'<button aria-expanded="false">Menu</button>',
4148-
'- button "Menu" [expanded=false]'
4147+
'- button "Menu" [expanded=false]',
4148+
{ assertInvariant: false }
41494149
)
41504150
).toMatchInlineSnapshot(`
41514151
{
41524152
"actual": "
41534153
- button "Menu"
41544154
",
41554155
"actualResolved": "
4156-
- button "Menu"
4156+
- button "Menu" [expanded=false]
41574157
",
41584158
"expected": "
41594159
- button "Menu"
@@ -4164,8 +4164,11 @@ describe('aria-expanded', () => {
41644164
})
41654165

41664166
test('expanded=false vs expanded=undefined is a mismatch', () => {
4167-
expect(match('<button>Menu</button>', '- button "Menu" [expanded=false]'))
4168-
.toMatchInlineSnapshot(`
4167+
expect(
4168+
match('<button>Menu</button>', '- button "Menu" [expanded=false]', {
4169+
assertInvariant: false,
4170+
})
4171+
).toMatchInlineSnapshot(`
41694172
{
41704173
"actual": "
41714174
- button "Menu"

0 commit comments

Comments
 (0)