-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathindex.ts
More file actions
126 lines (114 loc) · 3.06 KB
/
index.ts
File metadata and controls
126 lines (114 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import * as axe from 'axe-core';
declare global {
interface Window {
axe: typeof axe;
}
}
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
injectAxe: typeof injectAxe;
configureAxe: typeof configureAxe;
checkA11y: typeof checkA11y;
}
}
}
export interface Options extends axe.RunOptions {
includedImpacts?: string[];
}
export const injectAxe = () => {
const fileName =
typeof require?.resolve === 'function'
? require.resolve('axe-core/axe.min.js')
: 'node_modules/axe-core/axe.min.js';
cy.readFile<string>(fileName).then((source) =>
cy.window({ log: false }).then((window) => {
window.eval(source);
})
);
};
export const configureAxe = (configurationOptions: axe.Spec = {}) => {
cy.window({ log: false }).then((win) => {
return win.axe.configure(configurationOptions);
});
};
function isEmptyObjectorNull(value: any) {
if (value == null) {
return true;
}
return Object.entries(value).length === 0 && value.constructor === Object;
}
const checkA11y = (
context?: axe.ElementContext,
options?: Options,
violationCallback?: (violations: axe.Result[]) => void,
skipFailures = false
) => {
cy.window({ log: false })
.then((win) => {
if (isEmptyObjectorNull(context)) {
context = undefined;
}
if (isEmptyObjectorNull(options)) {
options = undefined;
}
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;
});
})
.then((violations) => {
if (violations.length) {
if (violationCallback) {
violationCallback(violations);
}
violations.forEach((v) => {
const selectors = v.nodes
.reduce<string[]>((acc, node) => acc.concat(node.target), [])
.join(', ');
Cypress.log({
$el: Cypress.$(selectors),
name: 'a11y error!',
consoleProps: () => v,
message: `${v.id} on ${v.nodes.length} Node${
v.nodes.length === 1 ? '' : 's'
}`,
});
});
}
return cy.wrap(violations, { log: false });
})
.then((violations) => {
if (!skipFailures) {
assert.equal(
violations.length,
0,
`${violations.length} accessibility violation${
violations.length === 1 ? '' : 's'
} ${violations.length === 1 ? 'was' : 'were'} detected`
);
} else if (violations.length) {
Cypress.log({
name: 'a11y violation summary',
message: `${violations.length} accessibility violation${
violations.length === 1 ? '' : 's'
} ${violations.length === 1 ? 'was' : 'were'} detected`,
});
}
});
};
Cypress.Commands.add('injectAxe', injectAxe);
Cypress.Commands.add('configureAxe', configureAxe);
Cypress.Commands.add('checkA11y', checkA11y);