aria-ease 6.11.0 → 6.12.1
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/README.md +3 -3
- package/dist/{ComboboxComponentStrategy-OGRVZXAF.js → ComboboxComponentStrategy-DU342VMB.js} +7 -7
- package/dist/RelativeTargetResolver-DJAITO6D.js +7 -0
- package/dist/{audit-RM6TCZ5C.js → audit-JYEPKLHR.js} +5 -0
- package/dist/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +0 -23
- package/dist/{chunk-NI3MQCAS.js → chunk-GJGUY643.js} +2 -2
- package/dist/chunk-GLT43UVH.js +43 -0
- package/dist/cli.cjs +147 -84
- package/dist/cli.js +5 -5
- package/dist/{configLoader-DWHOHXHL.js → configLoader-Q7N5XV4P.js} +2 -2
- package/dist/{configLoader-UJZHQBYS.js → configLoader-REHK3S3Q.js} +1 -1
- package/dist/{contractTestRunnerPlaywright-QDXSK3FE.js → contractTestRunnerPlaywright-H24LQ45R.js} +113 -72
- package/dist/{contractTestRunnerPlaywright-WNWQYSXZ.js → contractTestRunnerPlaywright-NL3JNJYH.js} +113 -72
- package/dist/index.cjs +248 -112
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +124 -41
- package/dist/src/combobox/index.cjs +1 -0
- package/dist/src/combobox/index.js +1 -0
- package/dist/src/utils/test/{ComboboxComponentStrategy-5AECQSRN.js → ComboboxComponentStrategy-XKQ72RFD.js} +7 -7
- package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +1 -0
- package/dist/src/utils/test/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +1 -23
- package/dist/src/utils/test/chunk-GLT43UVH.js +41 -0
- package/dist/src/utils/test/{configLoader-SHJSRG2A.js → configLoader-NA7IBCS3.js} +2 -2
- package/dist/src/utils/test/{contractTestRunnerPlaywright-Z2AHXSNM.js → contractTestRunnerPlaywright-5FT6K2WN.js} +111 -71
- package/dist/src/utils/test/dsl/index.cjs +106 -29
- package/dist/src/utils/test/dsl/index.d.cts +3 -0
- package/dist/src/utils/test/dsl/index.d.ts +3 -0
- package/dist/src/utils/test/dsl/index.js +106 -29
- package/dist/src/utils/test/index.cjs +135 -76
- package/dist/src/utils/test/index.js +17 -11
- package/dist/{test-O3J4ZPQR.js → test-FYSJXQWO.js} +17 -12
- package/package.json +3 -4
- package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +0 -290
- package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +0 -463
- package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +0 -562
- package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +0 -361
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { normalizeStrictness, closeSharedBrowser, ContractReporter,
|
|
1
|
+
import { normalizeStrictness, closeSharedBrowser, ContractReporter, normalizeLevel, resolveEnforcement } from './chunk-4DU5Z5BR.js';
|
|
2
2
|
import { axe } from 'jest-axe';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
|
|
6
|
-
async function runContractTests(componentName, component, strictness) {
|
|
6
|
+
async function runContractTests(contractPath, componentName, component, strictness) {
|
|
7
7
|
const reporter = new ContractReporter(false);
|
|
8
8
|
const strictnessMode = normalizeStrictness(strictness);
|
|
9
|
-
const contractTyped = contract_default;
|
|
10
|
-
const contractPath = contractTyped[componentName]?.path;
|
|
11
9
|
if (!contractPath) {
|
|
12
|
-
throw new Error(`No contract
|
|
10
|
+
throw new Error(`No contract path provided for component: ${componentName}`);
|
|
13
11
|
}
|
|
14
|
-
const
|
|
15
|
-
const contractData = await fs.readFile(resolvedPath, "utf-8");
|
|
12
|
+
const contractData = await fs.readFile(contractPath, "utf-8");
|
|
16
13
|
const componentContract = JSON.parse(contractData);
|
|
17
14
|
const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
|
|
18
15
|
reporter.start(componentName, totalTests);
|
|
@@ -147,7 +144,7 @@ async function runContractTests(componentName, component, strictness) {
|
|
|
147
144
|
staticPassed += 1;
|
|
148
145
|
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
|
|
149
146
|
}
|
|
150
|
-
} else if (!attributeValue || !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
147
|
+
} else if (!attributeValue || typeof test.expectedValue === "string" && !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
151
148
|
const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
|
|
152
149
|
if (outcome.status === "fail") staticFailed += 1;
|
|
153
150
|
if (outcome.status === "warn") staticWarnings += 1;
|
|
@@ -209,7 +206,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
209
206
|
let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
|
|
210
207
|
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
211
208
|
try {
|
|
212
|
-
const { loadConfig } = await import('./configLoader-
|
|
209
|
+
const { loadConfig } = await import('./configLoader-NA7IBCS3.js');
|
|
213
210
|
const result2 = await loadConfig(process.cwd());
|
|
214
211
|
config = result2.config;
|
|
215
212
|
if (result2.configPath) {
|
|
@@ -231,7 +228,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
231
228
|
const devServerUrl = await checkDevServer(url);
|
|
232
229
|
if (devServerUrl) {
|
|
233
230
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
234
|
-
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-
|
|
231
|
+
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-5FT6K2WN.js');
|
|
235
232
|
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
|
|
236
233
|
} else {
|
|
237
234
|
throw new Error(
|
|
@@ -241,7 +238,16 @@ Please start your dev server and try again.`
|
|
|
241
238
|
}
|
|
242
239
|
} else if (component) {
|
|
243
240
|
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
244
|
-
|
|
241
|
+
const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
|
|
242
|
+
if (!contractPath) {
|
|
243
|
+
throw new Error(`\u274C No contract path found for component: ${componentName}`);
|
|
244
|
+
}
|
|
245
|
+
contract = await runContractTests(
|
|
246
|
+
path.resolve(configBaseDir, contractPath),
|
|
247
|
+
componentName,
|
|
248
|
+
component,
|
|
249
|
+
strictness
|
|
250
|
+
);
|
|
245
251
|
} else {
|
|
246
252
|
throw new Error("\u274C Either component or URL must be provided");
|
|
247
253
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractReporter,
|
|
3
3
|
closeSharedBrowser,
|
|
4
|
-
contract_default,
|
|
5
4
|
normalizeLevel,
|
|
6
5
|
normalizeStrictness,
|
|
7
6
|
resolveEnforcement
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4DU5Z5BR.js";
|
|
9
8
|
import "./chunk-I2KLQ2HA.js";
|
|
10
9
|
|
|
11
10
|
// src/utils/test/src/test.ts
|
|
@@ -13,16 +12,13 @@ import { axe } from "jest-axe";
|
|
|
13
12
|
|
|
14
13
|
// src/utils/test/src/contractTestRunner.ts
|
|
15
14
|
import fs from "fs/promises";
|
|
16
|
-
async function runContractTests(componentName, component, strictness) {
|
|
15
|
+
async function runContractTests(contractPath, componentName, component, strictness) {
|
|
17
16
|
const reporter = new ContractReporter(false);
|
|
18
17
|
const strictnessMode = normalizeStrictness(strictness);
|
|
19
|
-
const contractTyped = contract_default;
|
|
20
|
-
const contractPath = contractTyped[componentName]?.path;
|
|
21
18
|
if (!contractPath) {
|
|
22
|
-
throw new Error(`No contract
|
|
19
|
+
throw new Error(`No contract path provided for component: ${componentName}`);
|
|
23
20
|
}
|
|
24
|
-
const
|
|
25
|
-
const contractData = await fs.readFile(resolvedPath, "utf-8");
|
|
21
|
+
const contractData = await fs.readFile(contractPath, "utf-8");
|
|
26
22
|
const componentContract = JSON.parse(contractData);
|
|
27
23
|
const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
|
|
28
24
|
reporter.start(componentName, totalTests);
|
|
@@ -157,7 +153,7 @@ async function runContractTests(componentName, component, strictness) {
|
|
|
157
153
|
staticPassed += 1;
|
|
158
154
|
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
|
|
159
155
|
}
|
|
160
|
-
} else if (!attributeValue || !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
156
|
+
} else if (!attributeValue || typeof test.expectedValue === "string" && !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
161
157
|
const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
|
|
162
158
|
if (outcome.status === "fail") staticFailed += 1;
|
|
163
159
|
if (outcome.status === "warn") staticWarnings += 1;
|
|
@@ -222,7 +218,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
222
218
|
let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
|
|
223
219
|
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
224
220
|
try {
|
|
225
|
-
const { loadConfig } = await import("./configLoader-
|
|
221
|
+
const { loadConfig } = await import("./configLoader-REHK3S3Q.js");
|
|
226
222
|
const result2 = await loadConfig(process.cwd());
|
|
227
223
|
config = result2.config;
|
|
228
224
|
if (result2.configPath) {
|
|
@@ -244,7 +240,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
244
240
|
const devServerUrl = await checkDevServer(url);
|
|
245
241
|
if (devServerUrl) {
|
|
246
242
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
247
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
243
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-H24LQ45R.js");
|
|
248
244
|
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
|
|
249
245
|
} else {
|
|
250
246
|
throw new Error(
|
|
@@ -254,7 +250,16 @@ Please start your dev server and try again.`
|
|
|
254
250
|
}
|
|
255
251
|
} else if (component) {
|
|
256
252
|
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
257
|
-
|
|
253
|
+
const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
|
|
254
|
+
if (!contractPath) {
|
|
255
|
+
throw new Error(`\u274C No contract path found for component: ${componentName}`);
|
|
256
|
+
}
|
|
257
|
+
contract = await runContractTests(
|
|
258
|
+
path.resolve(configBaseDir, contractPath),
|
|
259
|
+
componentName,
|
|
260
|
+
component,
|
|
261
|
+
strictness
|
|
262
|
+
);
|
|
258
263
|
} else {
|
|
259
264
|
throw new Error("\u274C Either component or URL must be provided");
|
|
260
265
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aria-ease",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.12.1",
|
|
4
4
|
"description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
"build:test": "tsup ./src/utils/test/index.ts --format esm,cjs --dts --treeshake --external jest-axe --external @testing-library/react --external playwright --external @playwright/test --outDir dist/src/utils/test",
|
|
23
23
|
"build:dsl": "tsup ./src/utils/test/dsl/index.ts --format esm,cjs --dts --treeshake --outDir dist/src/utils/test/dsl",
|
|
24
24
|
"build:cli": "tsup ./src/utils/cli/cli.ts --format esm,cjs --dts --outDir dist --external commander --external chalk --external jest-axe --external @testing-library/react --external @axe-core/playwright --external playwright",
|
|
25
|
-
"build
|
|
26
|
-
"build": "npm run clean && npm run build:core && npm run build:modules && npm run build:test && npm run build:dsl && npm run build:cli && npm run build:contracts"
|
|
25
|
+
"build": "npm run clean && npm run build:core && npm run build:modules && npm run build:test && npm run build:dsl && npm run build:cli"
|
|
27
26
|
},
|
|
28
27
|
"repository": {
|
|
29
28
|
"type": "git",
|
|
@@ -47,7 +46,7 @@
|
|
|
47
46
|
"bugs": {
|
|
48
47
|
"url": "https://github.com/aria-ease/aria-ease/issues"
|
|
49
48
|
},
|
|
50
|
-
"homepage": "https://ariaease.site/
|
|
49
|
+
"homepage": "https://ariaease.site/",
|
|
51
50
|
"devDependencies": {
|
|
52
51
|
"@axe-core/playwright": "^4.11.0",
|
|
53
52
|
"@playwright/test": "^1.57.0",
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"id": "aria-ease.contract.accordion",
|
|
4
|
-
"version": "1.0.0",
|
|
5
|
-
"created": "09-02-2026",
|
|
6
|
-
"lastUpdated": "19-03-2026",
|
|
7
|
-
"description": "ARIA Accordion interaction contract. Validates the ARIA and interaction contract for a custom accordion component following the ARIA Authoring Practices Guide accordion with show/hide pattern.",
|
|
8
|
-
"source": {
|
|
9
|
-
"apg": "https://www.w3.org/WAI/ARIA/apg/patterns/accordion/",
|
|
10
|
-
"wcag": ["2.2 AA"]
|
|
11
|
-
},
|
|
12
|
-
"W3CName": "Accordion"
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
"selectors": {
|
|
16
|
-
"trigger": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
17
|
-
"focusable": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
18
|
-
"relative": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
19
|
-
"panel": "[role='region'][aria-labelledby], [role='region'][aria-labelledby]"
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
"observables": {
|
|
23
|
-
"observable": "focus | visible | attribute | role",
|
|
24
|
-
"target": "trigger | relative | panel | focusable",
|
|
25
|
-
"relative": "first | last | next | previous"
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
"relationships": [
|
|
29
|
-
{
|
|
30
|
-
"type": "aria-reference",
|
|
31
|
-
"from": "trigger",
|
|
32
|
-
"attribute": "aria-controls",
|
|
33
|
-
"to": "panel"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"type": "aria-reference",
|
|
37
|
-
"from": "panel",
|
|
38
|
-
"attribute": "aria-labelledby",
|
|
39
|
-
"to": "trigger"
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
|
|
43
|
-
"static": [
|
|
44
|
-
{
|
|
45
|
-
"assertions": [
|
|
46
|
-
{
|
|
47
|
-
"target": "trigger",
|
|
48
|
-
"assertion": "toHaveAttribute",
|
|
49
|
-
"attribute": "aria-expanded",
|
|
50
|
-
"expectedValue": "true | false",
|
|
51
|
-
"failureMessage": "Accordion trigger button doesn't conform to the ARIA Accordion Button pattern as specified in APG 1.2. Accordion trigger button should have 'aria-expanded=true | false' attribute. This helps assistive technology to keep track of the open or close state of the accordion panel div that the button controls."
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"target": "trigger",
|
|
55
|
-
"assertion": "toHaveAttribute",
|
|
56
|
-
"attribute": "aria-controls",
|
|
57
|
-
"failureMessage": "Accordion trigger button doesn't conform to the ARIA Accordion Button pattern as specified in APG 1.2. Accordion trigger button should have 'aria-controls' attribute that points to the id of the accordion panel div it controls with 'role=region'. This helps assistive technology to identify the accordion panel div that the button controls."
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"target": "panel",
|
|
61
|
-
"assertion": "toHaveAttribute",
|
|
62
|
-
"attribute": "aria-labelledby | aria-label",
|
|
63
|
-
"failureMessage": "Accordion panel doesn't conform to the ARIA Accordion pattern as specified in APG 1.2. Accordion panel should have 'aria-labelledby' attribute that points to the id of the trigger button that controls it."
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
|
|
69
|
-
"dynamic": [
|
|
70
|
-
{
|
|
71
|
-
"description": "Clicking the trigger expands a collapsed panel.",
|
|
72
|
-
"action": [
|
|
73
|
-
{ "type": "click", "target": "trigger" }
|
|
74
|
-
],
|
|
75
|
-
"assertions": [
|
|
76
|
-
{
|
|
77
|
-
"target": "panel",
|
|
78
|
-
"assertion": "toBeVisible",
|
|
79
|
-
"failureMessage": "Panel should be visible after clicking the trigger."
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"target": "trigger",
|
|
83
|
-
"assertion": "toHaveAttribute",
|
|
84
|
-
"attribute": "aria-expanded",
|
|
85
|
-
"expectedValue": "true",
|
|
86
|
-
"failureMessage": "Trigger's aria-expanded attribute should be true after clicking to expand."
|
|
87
|
-
}
|
|
88
|
-
]
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
"description": "Clicking the trigger again collapses an expanded panel.",
|
|
92
|
-
"action": [
|
|
93
|
-
{ "type": "click", "target": "trigger" },
|
|
94
|
-
{ "type": "click", "target": "trigger" }
|
|
95
|
-
],
|
|
96
|
-
"assertions": [
|
|
97
|
-
{
|
|
98
|
-
"target": "panel",
|
|
99
|
-
"assertion": "notToBeVisible",
|
|
100
|
-
"failureMessage": "Panel should not be visible after clicking the trigger twice."
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"target": "trigger",
|
|
104
|
-
"assertion": "toHaveAttribute",
|
|
105
|
-
"attribute": "aria-expanded",
|
|
106
|
-
"expectedValue": "false",
|
|
107
|
-
"failureMessage": "Trigger's aria-expanded attribute should be false after collapsing."
|
|
108
|
-
}
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"description": "Pressing Enter on trigger expands a collapsed panel.",
|
|
113
|
-
"action": [
|
|
114
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" }
|
|
115
|
-
],
|
|
116
|
-
"assertions": [
|
|
117
|
-
{
|
|
118
|
-
"target": "panel",
|
|
119
|
-
"assertion": "toBeVisible",
|
|
120
|
-
"failureMessage": "Panel should be visible after pressing Enter on trigger."
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
"target": "trigger",
|
|
124
|
-
"assertion": "toHaveAttribute",
|
|
125
|
-
"attribute": "aria-expanded",
|
|
126
|
-
"expectedValue": "true",
|
|
127
|
-
"failureMessage": "Trigger's should have aria-expanded=true after pressing Enter."
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
"description": "Pressing Space on trigger expands a collapsed panel.",
|
|
133
|
-
"action": [
|
|
134
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" }
|
|
135
|
-
],
|
|
136
|
-
"assertions": [
|
|
137
|
-
{
|
|
138
|
-
"target": "panel",
|
|
139
|
-
"assertion": "toBeVisible",
|
|
140
|
-
"failureMessage": "Panel should be visible after pressing Space on trigger."
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"target": "trigger",
|
|
144
|
-
"assertion": "toHaveAttribute",
|
|
145
|
-
"attribute": "aria-expanded",
|
|
146
|
-
"expectedValue": "true",
|
|
147
|
-
"failureMessage": "Trigger's should have aria-expanded=true after pressing Space."
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
"description": "Pressing Enter on trigger collapses an expanded panel.",
|
|
153
|
-
"action": [
|
|
154
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" },
|
|
155
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" }
|
|
156
|
-
],
|
|
157
|
-
"assertions": [
|
|
158
|
-
{
|
|
159
|
-
"target": "panel",
|
|
160
|
-
"assertion": "notToBeVisible",
|
|
161
|
-
"failureMessage": "Panel should not be visible after pressing Enter on expanded trigger."
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"target": "trigger",
|
|
165
|
-
"assertion": "toHaveAttribute",
|
|
166
|
-
"attribute": "aria-expanded",
|
|
167
|
-
"expectedValue": "false",
|
|
168
|
-
"failureMessage": "Trigger's aria-expanded should be false after collapsing with Enter."
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"description": "Pressing Space on trigger collapses an expanded panel.",
|
|
174
|
-
"action": [
|
|
175
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" },
|
|
176
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" }
|
|
177
|
-
],
|
|
178
|
-
"assertions": [
|
|
179
|
-
{
|
|
180
|
-
"target": "panel",
|
|
181
|
-
"assertion": "notToBeVisible",
|
|
182
|
-
"failureMessage": "Panel should not be visible after pressing Space on expanded trigger."
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
"target": "trigger",
|
|
186
|
-
"assertion": "toHaveAttribute",
|
|
187
|
-
"attribute": "aria-expanded",
|
|
188
|
-
"expectedValue": "false",
|
|
189
|
-
"failureMessage": "Trigger's aria-expanded should be false after collapsing with Space."
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
"description": "Down Arrow moves focus to next accordion trigger.",
|
|
195
|
-
"isMultiple": true,
|
|
196
|
-
"level": "optional",
|
|
197
|
-
"action": [
|
|
198
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowDown" }
|
|
199
|
-
],
|
|
200
|
-
"assertions": [
|
|
201
|
-
{
|
|
202
|
-
"target": "relative",
|
|
203
|
-
"assertion": "toHaveFocus",
|
|
204
|
-
"expectedValue": "second",
|
|
205
|
-
"failureMessage": "Focus should move to the next accordion trigger after pressing Down Arrow."
|
|
206
|
-
}
|
|
207
|
-
]
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
"description": "Up Arrow moves focus to previous accordion trigger.",
|
|
211
|
-
"isMultiple": true,
|
|
212
|
-
"level": "optional",
|
|
213
|
-
"action": [
|
|
214
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowUp" }
|
|
215
|
-
],
|
|
216
|
-
"assertions": [
|
|
217
|
-
{
|
|
218
|
-
"target": "relative",
|
|
219
|
-
"assertion": "toHaveFocus",
|
|
220
|
-
"expectedValue": "first",
|
|
221
|
-
"failureMessage": "Focus should move to the previous accordion trigger after pressing Up Arrow."
|
|
222
|
-
}
|
|
223
|
-
]
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"description": "Home moves focus to first accordion trigger.",
|
|
227
|
-
"isMultiple": true,
|
|
228
|
-
"level": "optional",
|
|
229
|
-
"action": [
|
|
230
|
-
{ "type": "keypress", "target": "focusable", "key": "Home" }
|
|
231
|
-
],
|
|
232
|
-
"assertions": [
|
|
233
|
-
{
|
|
234
|
-
"target": "relative",
|
|
235
|
-
"assertion": "toHaveFocus",
|
|
236
|
-
"expectedValue": "first",
|
|
237
|
-
"failureMessage": "Focus should move to the first accordion trigger after pressing Home."
|
|
238
|
-
}
|
|
239
|
-
]
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
"description": "End moves focus to last accordion trigger.",
|
|
243
|
-
"isMultiple": true,
|
|
244
|
-
"level": "optional",
|
|
245
|
-
"action": [
|
|
246
|
-
{ "type": "keypress", "target": "focusable", "key": "End" }
|
|
247
|
-
],
|
|
248
|
-
"assertions": [
|
|
249
|
-
{
|
|
250
|
-
"target": "relative",
|
|
251
|
-
"assertion": "toHaveFocus",
|
|
252
|
-
"expectedValue": "last",
|
|
253
|
-
"failureMessage": "Focus should move to the last accordion trigger after pressing End."
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
"description": "Down Arrow wraps focus from last trigger to first trigger.",
|
|
259
|
-
"isMultiple": true,
|
|
260
|
-
"level": "optional",
|
|
261
|
-
"action": [
|
|
262
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowDown" }
|
|
263
|
-
],
|
|
264
|
-
"assertions": [
|
|
265
|
-
{
|
|
266
|
-
"target": "relative",
|
|
267
|
-
"assertion": "toHaveFocus",
|
|
268
|
-
"expectedValue": "first",
|
|
269
|
-
"failureMessage": "Focus should wrap from last to first accordion trigger after pressing Down Arrow."
|
|
270
|
-
}
|
|
271
|
-
]
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
"description": "Up Arrow wraps focus from first trigger to last trigger.",
|
|
275
|
-
"isMultiple": true,
|
|
276
|
-
"level": "optional",
|
|
277
|
-
"action": [
|
|
278
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowUp" }
|
|
279
|
-
],
|
|
280
|
-
"assertions": [
|
|
281
|
-
{
|
|
282
|
-
"target": "relative",
|
|
283
|
-
"assertion": "toHaveFocus",
|
|
284
|
-
"expectedValue": "last",
|
|
285
|
-
"failureMessage": "Focus should wrap from first to last accordion trigger after pressing Up Arrow."
|
|
286
|
-
}
|
|
287
|
-
]
|
|
288
|
-
}
|
|
289
|
-
]
|
|
290
|
-
}
|