Skip to content

Commit

Permalink
feat: retry option (component-driven#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnhwhite authored Feb 24, 2023
1 parent 91c72d2 commit 682ba11
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 14 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ Defines the scope of the analysis - the part of the DOM that you would like to a

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

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.
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.

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".

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.

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.

##### violationCallback (optional)
Expand Down Expand Up @@ -192,6 +194,14 @@ it('Only logs a11y violations while allowing the test to pass', () => {
// Do not fail the test when there are accessibility failures
cy.checkA11y(null, null, null, true)
})

it('Has no a11y violations after asynchronous load', () => {
// Retry the check if there are initial failures
cy.checkA11y(null, {
retries: 3,
interval: 100
})
})
```

#### Using the violationCallback argument
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/test.cy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
it('works!', () => {
cy.visit('/');
cy.injectAxe();
cy.checkA11y();
cy.checkA11y(undefined, { retries: 20, interval: 10 });
});
44 changes: 32 additions & 12 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ declare global {

export interface Options extends axe.RunOptions {
includedImpacts?: string[];
interval?: number;
retries?: number;
}

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

function summarizeResults(
includedImpacts: string[] | undefined,
violations: axe.Result[]
): axe.Result[] {
return includedImpacts &&
Array.isArray(includedImpacts) &&
Boolean(includedImpacts.length)
? violations.filter((v) => v.impact && includedImpacts.includes(v.impact))
: violations;
}

const checkA11y = (
context?: axe.ElementContext,
options?: Options,
Expand All @@ -68,18 +81,25 @@ const checkA11y = (
if (isEmptyObjectorNull(violationCallback)) {
violationCallback = undefined;
}
const { includedImpacts, ...axeOptions } = options || {};
return win.axe
.run(context || win.document, axeOptions)
.then(({ violations }) => {
return includedImpacts &&
Array.isArray(includedImpacts) &&
Boolean(includedImpacts.length)
? violations.filter(
(v) => v.impact && includedImpacts.includes(v.impact)
)
: violations;
});
const { includedImpacts, interval, retries, ...axeOptions } =
options || {};
let remainingRetries = retries || 0;
function runAxeCheck(): Promise<axe.Result[]> {
return win.axe
.run(context || win.document, axeOptions)
.then(({ violations }) => {
const results = summarizeResults(includedImpacts, violations);
if (results.length > 0 && remainingRetries > 0) {
remainingRetries--;
return new Promise((resolve) => {
setTimeout(resolve, interval || 1000);
}).then(runAxeCheck);
} else {
return results;
}
});
}
return runAxeCheck();
})
.then((violations) => {
if (violations.length) {
Expand Down
8 changes: 8 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ <h1>Cypress-axe test</h1>
</header>
<main>
<p>This is cypress-axe test.</p>
<a id="link" href="https://example.com"></a>
</main>
<script>
setTimeout(() => {
document
.getElementById('link')
.appendChild(document.createTextNode('Link'));
}, 101);
</script>
</body>
</html>

0 comments on commit 682ba11

Please sign in to comment.