Skip to content

Commit 682ba11

Browse files
authored
feat: retry option (#151)
1 parent 91c72d2 commit 682ba11

File tree

4 files changed

+52
-14
lines changed

4 files changed

+52
-14
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,12 @@ Defines the scope of the analysis - the part of the DOM that you would like to a
134134

135135
Set of options passed into rules or checks, temporarily modifying them. This contrasts with axe.configure, which is more permanent.
136136

137-
The keys consist of [those accepted by `axe.run`'s options argument](https://www.deque.com/axe/documentation/api-documentation/#parameters-axerun) as well as a custom `includedImpacts` key.
137+
The keys consist of [those accepted by `axe.run`'s options argument](https://www.deque.com/axe/documentation/api-documentation/#parameters-axerun), a custom `includedImpacts` key, and `retries`/`interval` keys for retrying the check.
138138

139139
The `includedImpacts` key is an array of strings that map to `impact` levels in violations. Specifying this array will only include violations where the impact matches one of the included values. Possible impact values are "minor", "moderate", "serious", or "critical".
140140

141+
The `retries` key is an integer that specifies how many times to retry the check if there are initial findings. The `interval` key is an integer that specifies the number of milliseconds to wait between retries, and defaults to `1000` (one second). If `retries` is not specified, the check will only be run once. Use this option to account for dynamic content that may not be fully loaded when the check is first run.
142+
141143
Filtering based on impact in combination with the `skipFailures` argument allows you to introduce `cypress-axe` into tests for a legacy application without failing in CI before you have an opportunity to address accessibility issues. Ideally, you would steadily move towards stricter testing as you address issues.
142144

143145
##### violationCallback (optional)
@@ -192,6 +194,14 @@ it('Only logs a11y violations while allowing the test to pass', () => {
192194
// Do not fail the test when there are accessibility failures
193195
cy.checkA11y(null, null, null, true)
194196
})
197+
198+
it('Has no a11y violations after asynchronous load', () => {
199+
// Retry the check if there are initial failures
200+
cy.checkA11y(null, {
201+
retries: 3,
202+
interval: 100
203+
})
204+
})
195205
```
196206

197207
#### Using the violationCallback argument

cypress/e2e/test.cy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
it('works!', () => {
22
cy.visit('/');
33
cy.injectAxe();
4-
cy.checkA11y();
4+
cy.checkA11y(undefined, { retries: 20, interval: 10 });
55
});

src/index.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ declare global {
1919

2020
export interface Options extends axe.RunOptions {
2121
includedImpacts?: string[];
22+
interval?: number;
23+
retries?: number;
2224
}
2325

2426
export interface InjectOptions {
@@ -51,6 +53,17 @@ function isEmptyObjectorNull(value: any) {
5153
return Object.entries(value).length === 0 && value.constructor === Object;
5254
}
5355

56+
function summarizeResults(
57+
includedImpacts: string[] | undefined,
58+
violations: axe.Result[]
59+
): axe.Result[] {
60+
return includedImpacts &&
61+
Array.isArray(includedImpacts) &&
62+
Boolean(includedImpacts.length)
63+
? violations.filter((v) => v.impact && includedImpacts.includes(v.impact))
64+
: violations;
65+
}
66+
5467
const checkA11y = (
5568
context?: axe.ElementContext,
5669
options?: Options,
@@ -68,18 +81,25 @@ const checkA11y = (
6881
if (isEmptyObjectorNull(violationCallback)) {
6982
violationCallback = undefined;
7083
}
71-
const { includedImpacts, ...axeOptions } = options || {};
72-
return win.axe
73-
.run(context || win.document, axeOptions)
74-
.then(({ violations }) => {
75-
return includedImpacts &&
76-
Array.isArray(includedImpacts) &&
77-
Boolean(includedImpacts.length)
78-
? violations.filter(
79-
(v) => v.impact && includedImpacts.includes(v.impact)
80-
)
81-
: violations;
82-
});
84+
const { includedImpacts, interval, retries, ...axeOptions } =
85+
options || {};
86+
let remainingRetries = retries || 0;
87+
function runAxeCheck(): Promise<axe.Result[]> {
88+
return win.axe
89+
.run(context || win.document, axeOptions)
90+
.then(({ violations }) => {
91+
const results = summarizeResults(includedImpacts, violations);
92+
if (results.length > 0 && remainingRetries > 0) {
93+
remainingRetries--;
94+
return new Promise((resolve) => {
95+
setTimeout(resolve, interval || 1000);
96+
}).then(runAxeCheck);
97+
} else {
98+
return results;
99+
}
100+
});
101+
}
102+
return runAxeCheck();
83103
})
84104
.then((violations) => {
85105
if (violations.length) {

test/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ <h1>Cypress-axe test</h1>
99
</header>
1010
<main>
1111
<p>This is cypress-axe test.</p>
12+
<a id="link" href="https://example.com"></a>
1213
</main>
14+
<script>
15+
setTimeout(() => {
16+
document
17+
.getElementById('link')
18+
.appendChild(document.createTextNode('Link'));
19+
}, 101);
20+
</script>
1321
</body>
1422
</html>

0 commit comments

Comments
 (0)