Skip to content

Commit 8409eef

Browse files
uzirthapaclaude
andcommitted
test: Add e2e tests for Adaptive Card screen reader a11y fix
- hack.roleMod.ariaLabelFromTextContent.html: Tests that aria-label is derived from text content when speak is not set, and that speak value is used when present - attachmentRow.focusable.html: Tests that stacked layout attachment rows have tabIndex="0" and are focusable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d6b191e commit 8409eef

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<main id="webchat"></main>
11+
<script>
12+
run(async function () {
13+
const { directLine, store } = testHelpers.createDirectLineEmulator();
14+
15+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
16+
17+
await pageConditions.uiConnected();
18+
19+
await directLine.emulateIncomingActivity({
20+
attachments: [
21+
{
22+
content: {
23+
type: 'AdaptiveCard',
24+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
25+
version: '1.5',
26+
body: [
27+
{
28+
type: 'TextBlock',
29+
text: 'First card content'
30+
}
31+
]
32+
},
33+
contentType: 'application/vnd.microsoft.card.adaptive'
34+
}
35+
]
36+
});
37+
38+
await directLine.emulateIncomingActivity({
39+
attachments: [
40+
{
41+
content: {
42+
type: 'AdaptiveCard',
43+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
44+
version: '1.5',
45+
body: [
46+
{
47+
type: 'TextBlock',
48+
text: 'Second card content'
49+
}
50+
]
51+
},
52+
contentType: 'application/vnd.microsoft.card.adaptive'
53+
}
54+
]
55+
});
56+
57+
await pageConditions.numActivitiesShown(2);
58+
59+
const attachmentRows = document.querySelectorAll('[aria-roledescription="attachment"]');
60+
61+
// Both attachment rows should be focusable via tabIndex.
62+
expect(attachmentRows).toHaveProperty('length', 2);
63+
expect(attachmentRows[0].getAttribute('tabindex')).toBe('0');
64+
expect(attachmentRows[1].getAttribute('tabindex')).toBe('0');
65+
66+
// Both should have role="group".
67+
expect(attachmentRows[0].getAttribute('role')).toBe('group');
68+
expect(attachmentRows[1].getAttribute('role')).toBe('group');
69+
70+
// Focus on the first attachment row and verify it receives focus.
71+
attachmentRows[0].focus();
72+
73+
await pageConditions.became(
74+
'focus is on the first attachment row',
75+
() => document.activeElement === attachmentRows[0],
76+
1000
77+
);
78+
79+
// The Adaptive Card inside should have an aria-label derived from text content.
80+
const card = attachmentRows[0].querySelector('.ac-adaptiveCard');
81+
82+
expect(card.getAttribute('aria-label')).toContain('First card content');
83+
expect(card.getAttribute('role')).toBeTruthy();
84+
});
85+
</script>
86+
</body>
87+
</html>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<main id="webchat"></main>
11+
<script>
12+
run(async function () {
13+
const { directLine, store } = testHelpers.createDirectLineEmulator();
14+
15+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
16+
17+
await pageConditions.uiConnected();
18+
19+
await directLine.emulateIncomingActivity({
20+
attachments: [
21+
{
22+
content: {
23+
type: 'AdaptiveCard',
24+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
25+
version: '1.5',
26+
body: [
27+
{
28+
type: 'TextBlock',
29+
text: 'Flight Status Update'
30+
},
31+
{
32+
type: 'TextBlock',
33+
text: 'Flight AA1234 from Seattle to New York'
34+
}
35+
]
36+
},
37+
contentType: 'application/vnd.microsoft.card.adaptive'
38+
},
39+
{
40+
content: {
41+
type: 'AdaptiveCard',
42+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
43+
version: '1.5',
44+
speak: 'Custom speak text for screen readers',
45+
body: [
46+
{
47+
type: 'TextBlock',
48+
text: 'This text should not be the aria-label'
49+
}
50+
]
51+
},
52+
contentType: 'application/vnd.microsoft.card.adaptive'
53+
},
54+
{
55+
content: {
56+
type: 'AdaptiveCard',
57+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
58+
version: '1.5',
59+
body: [
60+
{
61+
type: 'Input.Text',
62+
id: 'name',
63+
label: 'Your Name',
64+
placeholder: 'Enter your name'
65+
}
66+
]
67+
},
68+
contentType: 'application/vnd.microsoft.card.adaptive'
69+
}
70+
]
71+
});
72+
73+
await pageConditions.numActivitiesShown(1);
74+
75+
const [cardNoSpeak, cardWithSpeak, cardFormNoSpeak] = Array.from(
76+
document.querySelectorAll('.ac-adaptiveCard')
77+
);
78+
79+
// Card without speak: aria-label should be derived from visible text content.
80+
expect(cardNoSpeak.getAttribute('aria-label')).toContain('Flight Status Update');
81+
expect(cardNoSpeak.getAttribute('aria-label')).toContain('Flight AA1234');
82+
expect(cardNoSpeak.getAttribute('role')).toBe('figure');
83+
84+
// Card with speak: aria-label should use the speak property value.
85+
expect(cardWithSpeak.getAttribute('aria-label')).toBe('Custom speak text for screen readers');
86+
expect(cardWithSpeak.getAttribute('role')).toBe('figure');
87+
88+
// Card with form inputs and no speak: aria-label should be derived from text content,
89+
// and role should be "form" because it has inputs and an aria-label.
90+
expect(cardFormNoSpeak.getAttribute('aria-label')).toBeTruthy();
91+
expect(cardFormNoSpeak.getAttribute('role')).toBe('form');
92+
});
93+
</script>
94+
</body>
95+
</html>

0 commit comments

Comments
 (0)