axe-playwright 1.1.11 → 1.2.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 +42 -9
- package/dist/index.d.ts +56 -0
- package/dist/index.js +34 -14
- package/dist/reporter/defaultTerminalReporter.d.ts +9 -0
- package/dist/reporter/defaultTerminalReporter.js +4 -3
- package/dist/reporter/terminalReporterV2.d.ts +7 -0
- package/dist/reporter/terminalReporterV2.js +69 -0
- package/dist/types.d.ts +45 -0
- package/dist/utils.d.ts +5 -0
- package/package.json +19 -14
- package/.circleci/config.yml +0 -19
- package/.github/dependabot.yml +0 -12
- package/index.d.ts +0 -93
- package/jest.config.js +0 -10
- package/src/index.ts +0 -115
- package/src/reporter/defaultTerminalReporter.ts +0 -43
- package/src/types.ts +0 -19
- package/src/utils.ts +0 -61
- package/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/axe-playwright)
|
|
7
7
|
[](https://www.npmjs.com/package/axe-playwright)
|
|
8
8
|
|
|
9
|
-
[Axe](https://www.deque.com/axe/) is an accessibility testing engine for websites and other HTML-based user interfaces. This package provides simple axe analyser commands which you can incorporate in your [Playwright](https://www.npmjs.com/package/playwright) tests.
|
|
9
|
+
[Axe](https://www.deque.com/axe/) is an accessibility testing engine for websites and other HTML-based user interfaces. This package provides simple axe analyser commands which you can incorporate in your [Playwright](https://www.npmjs.com/package/playwright) tests.
|
|
10
10
|
|
|
11
11
|
## Install and configure
|
|
12
12
|
|
|
@@ -95,11 +95,11 @@ The `page` instance of `playwright`.
|
|
|
95
95
|
|
|
96
96
|
Defines the scope of the analysis - the part of the DOM that you would like to analyze. This will typically be the document or a specific selector such as class name, ID, selector, etc.
|
|
97
97
|
|
|
98
|
-
#####
|
|
98
|
+
##### axeOptions (optional)
|
|
99
99
|
|
|
100
100
|
Set of options passed into rules or checks, temporarily modifying them. This contrasts with axe.configure, which is more permanent.
|
|
101
101
|
|
|
102
|
-
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 custom `includedImpacts`, `detailedReport`, and `detailedReportOptions` keys.
|
|
102
|
+
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 custom `includedImpacts`, `detailedReport`, `verbose`, and `detailedReportOptions` keys.
|
|
103
103
|
|
|
104
104
|
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".
|
|
105
105
|
|
|
@@ -116,14 +116,21 @@ The `detailedReport` key is a boolean whether to print the more detailed report
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
The `verbose` key is a boolean to whether to print the message `No accessibility violations detected!` when there aren't accessibility violations present in the test. For the `DefaultTerminalReporter` this is true and for the `v2 Reporter` this is false.
|
|
120
|
+
|
|
119
121
|
##### reporter (optional)
|
|
120
122
|
|
|
121
|
-
A class instance that implements the `Reporter` interface
|
|
123
|
+
A class instance that implements the `Reporter` interface or values `default` and `v2`. Custom reporter instances can be supplied to override default reporting behaviour dictated by `DefaultTerminalReporter` set by the value `default`. `v2` is the new TerminalReporter inspired by the reports from [jest-axe](https://github.com/nickcolley/jest-axe).
|
|
122
124
|
|
|
123
125
|
##### skipFailures (optional, defaults to false)
|
|
124
126
|
|
|
125
127
|
Disables assertions based on violations and only logs violations to the console output. If you set `skipFailures` as `true`, although accessibility check is not passed, your test will not fail. It will simply print the violations in the console, but will not make the test fail.
|
|
126
128
|
|
|
129
|
+
##### options (dedicated for axe-html-reporter)
|
|
130
|
+
|
|
131
|
+
Options dedicated for HTML reporter.
|
|
132
|
+
[axe-html-reporter](https://www.npmjs.com/package/axe-html-reporter)
|
|
133
|
+
|
|
127
134
|
### getAxeResults
|
|
128
135
|
|
|
129
136
|
This will run axe against the document at the point in which it is called, then returns the full set of results as reported by `axe.run`.
|
|
@@ -213,11 +220,9 @@ describe('Playwright web page accessibility test', () => {
|
|
|
213
220
|
|
|
214
221
|
it('gets and reports a11y for the specific element', async () => {
|
|
215
222
|
const violations = await getViolations(page, 'input[name="password"]', {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
values: ['wcag2a'],
|
|
220
|
-
},
|
|
223
|
+
runOnly: {
|
|
224
|
+
type: 'tag',
|
|
225
|
+
values: ['wcag2a'],
|
|
221
226
|
},
|
|
222
227
|
})
|
|
223
228
|
|
|
@@ -280,6 +285,34 @@ describe('Playwright web page accessibility test', () => {
|
|
|
280
285
|
|
|
281
286
|

|
|
282
287
|
|
|
288
|
+
#### HTML Report
|
|
289
|
+
|
|
290
|
+
Thanks to [axe-html-reporter](https://www.npmjs.com/package/axe-html-reporter) you can generate HTML report(s).
|
|
291
|
+
From default HTML file(s) will be generated under `/artifacts/accessibilityReport.html`.
|
|
292
|
+
Report's options can customized from `checkAlly` level:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
await checkA11y(
|
|
296
|
+
page,
|
|
297
|
+
'form',
|
|
298
|
+
{
|
|
299
|
+
axeOptions: {
|
|
300
|
+
runOnly: {
|
|
301
|
+
type: 'tag',
|
|
302
|
+
values: ['wcag2a'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
true, 'default',
|
|
307
|
+
{
|
|
308
|
+
outputDirPath: 'results',
|
|
309
|
+
outputDir: 'accessibility',
|
|
310
|
+
reportFileName: 'accessibility-audit.html'
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
|
|
283
316
|
## Before you Go
|
|
284
317
|
|
|
285
318
|
If it works for you , leave a [Star](https://github.com/abhinaba-ghosh/axe-playwright)! :star:
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Page } from 'playwright';
|
|
2
|
+
import { AxeResults, ElementContext, Result, RunOptions } from 'axe-core';
|
|
3
|
+
import DefaultTerminalReporter from './reporter/defaultTerminalReporter';
|
|
4
|
+
import Reporter, { ConfigOptions, AxeOptions } from './types';
|
|
5
|
+
import { Options } from 'axe-html-reporter';
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
axe: any;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
declare module 'axe-core' {
|
|
12
|
+
interface Node {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Injects axe executable commands in the active window
|
|
17
|
+
* @param page
|
|
18
|
+
*/
|
|
19
|
+
export declare const injectAxe: (page: Page) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Configures axe runtime options
|
|
22
|
+
* @param page
|
|
23
|
+
* @param configurationOptions
|
|
24
|
+
*/
|
|
25
|
+
export declare const configureAxe: (page: Page, configurationOptions?: ConfigOptions) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Runs axe-core tools on the relevant page and returns all results
|
|
28
|
+
* @param page
|
|
29
|
+
* @param context
|
|
30
|
+
* @param options
|
|
31
|
+
*/
|
|
32
|
+
export declare const getAxeResults: (page: Page, context?: ElementContext, options?: RunOptions) => Promise<AxeResults>;
|
|
33
|
+
/**
|
|
34
|
+
* Runs axe-core tools on the relevant page and returns all accessibility violations detected on the page
|
|
35
|
+
* @param page
|
|
36
|
+
* @param context
|
|
37
|
+
* @param options
|
|
38
|
+
*/
|
|
39
|
+
export declare const getViolations: (page: Page, context?: ElementContext, options?: RunOptions) => Promise<Result[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Report violations given the reporter.
|
|
42
|
+
* @param violations
|
|
43
|
+
* @param reporter
|
|
44
|
+
*/
|
|
45
|
+
export declare const reportViolations: (violations: Result[], reporter: Reporter) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Performs Axe validations
|
|
48
|
+
* @param page
|
|
49
|
+
* @param context
|
|
50
|
+
* @param axeOptions
|
|
51
|
+
* @param skipFailures
|
|
52
|
+
* @param reporter
|
|
53
|
+
* @param options
|
|
54
|
+
*/
|
|
55
|
+
export declare const checkA11y: (page: Page, context?: ElementContext | undefined, axeOptions?: AxeOptions | undefined, skipFailures?: boolean, reporter?: Reporter | 'default' | 'v2', options?: Options | undefined) => Promise<void>;
|
|
56
|
+
export { DefaultTerminalReporter };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -31,10 +35,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
36
|
};
|
|
33
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.checkA11y = exports.reportViolations = exports.getViolations = exports.getAxeResults = exports.configureAxe = exports.injectAxe = void 0;
|
|
38
|
+
exports.DefaultTerminalReporter = exports.checkA11y = exports.reportViolations = exports.getViolations = exports.getAxeResults = exports.configureAxe = exports.injectAxe = void 0;
|
|
35
39
|
const fs = __importStar(require("fs"));
|
|
36
40
|
const utils_1 = require("./utils");
|
|
37
41
|
const defaultTerminalReporter_1 = __importDefault(require("./reporter/defaultTerminalReporter"));
|
|
42
|
+
exports.DefaultTerminalReporter = defaultTerminalReporter_1.default;
|
|
43
|
+
const terminalReporterV2_1 = __importDefault(require("./reporter/terminalReporterV2"));
|
|
44
|
+
const axe_html_reporter_1 = require("axe-html-reporter");
|
|
38
45
|
/**
|
|
39
46
|
* Injects axe executable commands in the active window
|
|
40
47
|
* @param page
|
|
@@ -73,7 +80,7 @@ exports.getAxeResults = getAxeResults;
|
|
|
73
80
|
* @param options
|
|
74
81
|
*/
|
|
75
82
|
const getViolations = (page, context, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
|
-
const result = yield exports.getAxeResults(page, context, options);
|
|
83
|
+
const result = yield (0, exports.getAxeResults)(page, context, options);
|
|
77
84
|
return result.violations;
|
|
78
85
|
});
|
|
79
86
|
exports.getViolations = getViolations;
|
|
@@ -90,19 +97,32 @@ exports.reportViolations = reportViolations;
|
|
|
90
97
|
* Performs Axe validations
|
|
91
98
|
* @param page
|
|
92
99
|
* @param context
|
|
93
|
-
* @param
|
|
100
|
+
* @param axeOptions
|
|
94
101
|
* @param skipFailures
|
|
95
102
|
* @param reporter
|
|
103
|
+
* @param options
|
|
96
104
|
*/
|
|
97
|
-
const checkA11y = (page, context,
|
|
98
|
-
var _a;
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const checkA11y = (page, context = undefined, axeOptions = undefined, skipFailures = false, reporter = 'default', options = undefined) => __awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
+
var _a, _b, _c;
|
|
107
|
+
const violations = yield (0, exports.getViolations)(page, context, axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.axeOptions);
|
|
108
|
+
if (violations.length > 0) {
|
|
109
|
+
yield (0, axe_html_reporter_1.createHtmlReport)({ results: { violations }, options });
|
|
110
|
+
}
|
|
111
|
+
else
|
|
112
|
+
console.log("There were no violations to save in report");
|
|
113
|
+
const impactedViolations = (0, utils_1.getImpactedViolations)(violations, axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.includedImpacts);
|
|
114
|
+
let reporterWithOptions;
|
|
115
|
+
if (reporter === 'default') {
|
|
116
|
+
reporterWithOptions = new defaultTerminalReporter_1.default(axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.detailedReport, (_a = axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.detailedReportOptions) === null || _a === void 0 ? void 0 : _a.html, (_b = axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.verbose) !== null && _b !== void 0 ? _b : true);
|
|
117
|
+
}
|
|
118
|
+
else if (reporter === 'v2') {
|
|
119
|
+
reporterWithOptions = new terminalReporterV2_1.default((_c = axeOptions === null || axeOptions === void 0 ? void 0 : axeOptions.verbose) !== null && _c !== void 0 ? _c : false);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
reporterWithOptions = reporter;
|
|
123
|
+
}
|
|
124
|
+
yield (0, exports.reportViolations)(impactedViolations, reporterWithOptions);
|
|
125
|
+
if (reporter !== 'v2')
|
|
126
|
+
(0, utils_1.testResultDependsOnViolations)(impactedViolations, skipFailures);
|
|
107
127
|
});
|
|
108
128
|
exports.checkA11y = checkA11y;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Reporter from '../types';
|
|
2
|
+
import { Result } from 'axe-core';
|
|
3
|
+
export default class DefaultTerminalReporter implements Reporter {
|
|
4
|
+
protected detailedReport: boolean | undefined;
|
|
5
|
+
protected includeHtml: boolean | undefined;
|
|
6
|
+
protected verbose: boolean | undefined;
|
|
7
|
+
constructor(detailedReport: boolean | undefined, includeHtml: boolean | undefined, verbose: boolean | undefined);
|
|
8
|
+
report(violations: Result[]): Promise<void>;
|
|
9
|
+
}
|
|
@@ -11,9 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
class DefaultTerminalReporter {
|
|
14
|
-
constructor(detailedReport, includeHtml) {
|
|
14
|
+
constructor(detailedReport, includeHtml, verbose) {
|
|
15
15
|
this.detailedReport = detailedReport;
|
|
16
16
|
this.includeHtml = includeHtml;
|
|
17
|
+
this.verbose = verbose;
|
|
17
18
|
}
|
|
18
19
|
report(violations) {
|
|
19
20
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -29,7 +30,7 @@ class DefaultTerminalReporter {
|
|
|
29
30
|
// summary
|
|
30
31
|
console.table(violationData);
|
|
31
32
|
if (this.detailedReport) {
|
|
32
|
-
const nodeViolations = utils_1.describeViolations(violations).map(({ target, html, violations }) => {
|
|
33
|
+
const nodeViolations = (0, utils_1.describeViolations)(violations).map(({ target, html, violations }) => {
|
|
33
34
|
if (!this.includeHtml) {
|
|
34
35
|
return {
|
|
35
36
|
target,
|
|
@@ -43,7 +44,7 @@ class DefaultTerminalReporter {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
|
-
console.log(`No accessibility violations detected!`);
|
|
47
|
+
this.verbose && console.log(`No accessibility violations detected!`);
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Reporter from '../types';
|
|
2
|
+
import { Result } from 'axe-core';
|
|
3
|
+
export default class TerminalReporterV2 implements Reporter {
|
|
4
|
+
protected verbose: boolean | undefined;
|
|
5
|
+
constructor(verbose: boolean | undefined);
|
|
6
|
+
report(violations: Result[]): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const assert_1 = __importDefault(require("assert"));
|
|
16
|
+
const picocolors_1 = __importDefault(require("picocolors"));
|
|
17
|
+
class TerminalReporterV2 {
|
|
18
|
+
constructor(verbose) {
|
|
19
|
+
this.verbose = verbose;
|
|
20
|
+
}
|
|
21
|
+
report(violations) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const lineBreak = '\n\n';
|
|
24
|
+
const message = violations.length === 0
|
|
25
|
+
? 'No accessibility violations detected!'
|
|
26
|
+
: `Found ${violations.length} accessibility violations: \n`;
|
|
27
|
+
const horizontalLine = picocolors_1.default.bold('-'.repeat(message.length));
|
|
28
|
+
const reporter = (violations) => {
|
|
29
|
+
if (violations.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return violations
|
|
33
|
+
.map((violation) => {
|
|
34
|
+
const errorBody = violation.nodes
|
|
35
|
+
.map((node) => {
|
|
36
|
+
const selector = node.target.join(', ');
|
|
37
|
+
const expectedText = `Expected the HTML found at $('${selector}') to have no violations:` +
|
|
38
|
+
'\n';
|
|
39
|
+
return (picocolors_1.default.bold(expectedText) +
|
|
40
|
+
picocolors_1.default.gray(node.html) +
|
|
41
|
+
lineBreak +
|
|
42
|
+
`Received:\n` +
|
|
43
|
+
picocolors_1.default.red(`${violation.help} (${violation.id})`) +
|
|
44
|
+
lineBreak +
|
|
45
|
+
picocolors_1.default.bold(picocolors_1.default.yellow(node.failureSummary)) +
|
|
46
|
+
lineBreak +
|
|
47
|
+
(violation.helpUrl
|
|
48
|
+
? `You can find more information on this issue here: \n${picocolors_1.default.bold(picocolors_1.default.blue(violation.helpUrl))}`
|
|
49
|
+
: '') +
|
|
50
|
+
'\n' +
|
|
51
|
+
horizontalLine);
|
|
52
|
+
})
|
|
53
|
+
.join(lineBreak);
|
|
54
|
+
return errorBody;
|
|
55
|
+
})
|
|
56
|
+
.join(lineBreak);
|
|
57
|
+
};
|
|
58
|
+
const formatedViolations = reporter(violations);
|
|
59
|
+
const pass = formatedViolations.length === 0;
|
|
60
|
+
if (pass) {
|
|
61
|
+
this.verbose && console.log(message);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
assert_1.default.fail(message + horizontalLine + '\n' + formatedViolations);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.default = TerminalReporterV2;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Result, Check, ImpactValue, Locale, Rule, RunOptions } from 'axe-core';
|
|
2
|
+
export interface NodeViolation {
|
|
3
|
+
target: string;
|
|
4
|
+
html: string;
|
|
5
|
+
violations: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Aggregate {
|
|
8
|
+
[key: string]: {
|
|
9
|
+
target: string;
|
|
10
|
+
html: string;
|
|
11
|
+
violations: number[];
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export default interface Reporter {
|
|
15
|
+
report(violations: Result[]): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface axeOptionsConfig {
|
|
18
|
+
axeOptions?: RunOptions;
|
|
19
|
+
}
|
|
20
|
+
export interface ConfigOptions {
|
|
21
|
+
branding?: {
|
|
22
|
+
brand?: string;
|
|
23
|
+
application?: string;
|
|
24
|
+
};
|
|
25
|
+
reporter?: 'v1' | 'v2' | 'no-passes';
|
|
26
|
+
checks?: Check[];
|
|
27
|
+
rules?: Rule[];
|
|
28
|
+
locale?: Locale;
|
|
29
|
+
axeVersion?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Implement this interface to be able to specific custom reporting behaviour for checkA11y method.
|
|
33
|
+
* @see checkA11y
|
|
34
|
+
*/
|
|
35
|
+
export default interface Reporter {
|
|
36
|
+
report(violations: Result[]): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export type AxeOptions = {
|
|
39
|
+
includedImpacts?: ImpactValue[];
|
|
40
|
+
detailedReport?: boolean;
|
|
41
|
+
detailedReportOptions?: {
|
|
42
|
+
html?: boolean;
|
|
43
|
+
};
|
|
44
|
+
verbose?: boolean;
|
|
45
|
+
} & axeOptionsConfig;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { NodeViolation } from './types';
|
|
2
|
+
import { ImpactValue, Result } from 'axe-core';
|
|
3
|
+
export declare const getImpactedViolations: (violations: Result[], includedImpacts?: ImpactValue[]) => Result[];
|
|
4
|
+
export declare const testResultDependsOnViolations: (violations: Result[], skipFailures: boolean) => void;
|
|
5
|
+
export declare const describeViolations: (violations: Result[]) => NodeViolation[];
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axe-playwright",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Custom Playwright commands to inject axe-core and test for a11y",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"types": "index.d.ts",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
7
11
|
"keywords": [
|
|
8
12
|
"a11y",
|
|
9
13
|
"accessibility",
|
|
@@ -20,24 +24,25 @@
|
|
|
20
24
|
"release": "npm cache clean --force && npm version patch && npm publish --force"
|
|
21
25
|
},
|
|
22
26
|
"peerDependencies": {
|
|
23
|
-
"playwright
|
|
27
|
+
"playwright": ">1.0.0"
|
|
24
28
|
},
|
|
25
29
|
"author": "Abhinaba Ghosh",
|
|
26
30
|
"license": "MIT",
|
|
27
31
|
"devDependencies": {
|
|
28
|
-
"@types/
|
|
29
|
-
"@types/
|
|
30
|
-
"
|
|
31
|
-
"jest": "^
|
|
32
|
-
"jest-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"typescript": "^4.1.3"
|
|
32
|
+
"@types/jest": "^28.1.1",
|
|
33
|
+
"@types/node": "^18.11.9",
|
|
34
|
+
"jest": "^28.1.3",
|
|
35
|
+
"jest-each": "^28.1.3",
|
|
36
|
+
"jest-playwright-preset": "^2.0.0",
|
|
37
|
+
"playwright": "^1.27.1",
|
|
38
|
+
"prettier": "^2.7.1",
|
|
39
|
+
"ts-jest": "^28.0.8",
|
|
40
|
+
"typescript": "^4.8.4"
|
|
38
41
|
},
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"
|
|
43
|
+
"picocolors": "^1.0.0",
|
|
44
|
+
"axe-core": "^4.5.1",
|
|
45
|
+
"axe-html-reporter": "2.2.3"
|
|
41
46
|
},
|
|
42
47
|
"repository": {
|
|
43
48
|
"type": "git",
|
package/.circleci/config.yml
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
jobs:
|
|
3
|
-
build:
|
|
4
|
-
docker:
|
|
5
|
-
- image: qawolf/playwright-ci:v1.0.0
|
|
6
|
-
steps:
|
|
7
|
-
- checkout
|
|
8
|
-
|
|
9
|
-
- run:
|
|
10
|
-
command: npm install
|
|
11
|
-
|
|
12
|
-
- run:
|
|
13
|
-
command: |
|
|
14
|
-
# # Start local server
|
|
15
|
-
# npm run start &
|
|
16
|
-
# npx wait-on http://localhost:3000
|
|
17
|
-
# replace below with command you want to run, example for running a script below
|
|
18
|
-
# node myScript.js
|
|
19
|
-
npm test
|
package/.github/dependabot.yml
DELETED
package/index.d.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { AxeResults, Check, ElementContext, ImpactValue, Locale, Result, Rule, RunOptions } from 'axe-core'
|
|
2
|
-
import { Page } from 'playwright'
|
|
3
|
-
|
|
4
|
-
export interface axeOptionsConfig {
|
|
5
|
-
axeOptions: RunOptions
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface ConfigOptions {
|
|
9
|
-
branding?: {
|
|
10
|
-
brand?: string
|
|
11
|
-
application?: string
|
|
12
|
-
}
|
|
13
|
-
reporter?: 'v1' | 'v2' | 'no-passes'
|
|
14
|
-
checks?: Check[]
|
|
15
|
-
rules?: Rule[]
|
|
16
|
-
locale?: Locale
|
|
17
|
-
axeVersion?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Implement this interface to be able to specific custom reporting behaviour for checkA11y method.
|
|
22
|
-
* @see checkA11y
|
|
23
|
-
*/
|
|
24
|
-
export default interface Reporter {
|
|
25
|
-
report(violations: Result[]): Promise<void>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Default implementation of a reporter which prints a summary to the console.
|
|
30
|
-
*/
|
|
31
|
-
export class DefaultTerminalReporter implements Reporter {
|
|
32
|
-
constructor(detailedReport: boolean | undefined, includeHtml: boolean | undefined)
|
|
33
|
-
report(violations: Result[]): Promise<void>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type Options = {
|
|
37
|
-
includedImpacts?: ImpactValue[]
|
|
38
|
-
detailedReport?: boolean
|
|
39
|
-
detailedReportOptions?: { html?: boolean }
|
|
40
|
-
} & axeOptionsConfig
|
|
41
|
-
|
|
42
|
-
declare module 'axe-core' {
|
|
43
|
-
interface Node {}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Injects axe into browser-context
|
|
48
|
-
*/
|
|
49
|
-
export function injectAxe(page: Page): Promise<void>
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Performs accessibility checks in the web page
|
|
53
|
-
* @param page
|
|
54
|
-
* @param context
|
|
55
|
-
* @param options
|
|
56
|
-
* @param skipFailures
|
|
57
|
-
*/
|
|
58
|
-
export function checkA11y(
|
|
59
|
-
page: Page,
|
|
60
|
-
context?: ElementContext,
|
|
61
|
-
options?: Options,
|
|
62
|
-
skipFailures?: boolean,
|
|
63
|
-
): Promise<void>
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* configure different axe configurations
|
|
67
|
-
* @param page
|
|
68
|
-
* @param options
|
|
69
|
-
*/
|
|
70
|
-
export function configureAxe(page: Page, options?: ConfigOptions): Promise<void>
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Runs axe-core tools on the relevant page and returns all results
|
|
74
|
-
* @param page
|
|
75
|
-
* @param context
|
|
76
|
-
* @param options
|
|
77
|
-
*/
|
|
78
|
-
export function getAxeResults(page: Page, context?: ElementContext, options?: RunOptions): Promise<AxeResults>
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Runs axe-core tools on the relevant page and returns all accessibility violations detected on the page
|
|
82
|
-
* @param page
|
|
83
|
-
* @param context
|
|
84
|
-
* @param options
|
|
85
|
-
*/
|
|
86
|
-
export function getViolations(page: Page, context?: ElementContext, options?: RunOptions): Promise<Result[]>
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Report violations given the reporter.
|
|
90
|
-
* @param violations
|
|
91
|
-
* @param reporter
|
|
92
|
-
*/
|
|
93
|
-
export function reportViolations(violations: Result[], reporter: Reporter): Promise<void>
|
package/jest.config.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
preset: 'jest-playwright-preset',
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
|
|
5
|
-
testPathIgnorePatterns: ['/node_modules/'],
|
|
6
|
-
testTimeout: 30000,
|
|
7
|
-
transform: {
|
|
8
|
-
'^.+\\.tsx?$': 'ts-jest',
|
|
9
|
-
},
|
|
10
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { Page } from 'playwright'
|
|
2
|
-
import * as fs from 'fs'
|
|
3
|
-
import { AxeResults, ElementContext, Result, RunOptions, Spec } from 'axe-core'
|
|
4
|
-
import { ConfigOptions, Options } from '../index'
|
|
5
|
-
import { getImpactedViolations, testResultDependsOnViolations } from './utils'
|
|
6
|
-
import DefaultTerminalReporter from './reporter/defaultTerminalReporter'
|
|
7
|
-
import Reporter from './types'
|
|
8
|
-
|
|
9
|
-
declare global {
|
|
10
|
-
interface Window {
|
|
11
|
-
axe: any
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Injects axe executable commands in the active window
|
|
17
|
-
* @param page
|
|
18
|
-
*/
|
|
19
|
-
export const injectAxe = async (page: Page): Promise<void> => {
|
|
20
|
-
const axe: string = fs.readFileSync(
|
|
21
|
-
require.resolve('axe-core/axe.min.js'),
|
|
22
|
-
'utf8',
|
|
23
|
-
)
|
|
24
|
-
await page.evaluate((axe: string) => window.eval(axe), axe)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Configures axe runtime options
|
|
29
|
-
* @param page
|
|
30
|
-
* @param configurationOptions
|
|
31
|
-
*/
|
|
32
|
-
export const configureAxe = async (
|
|
33
|
-
page: Page,
|
|
34
|
-
configurationOptions: ConfigOptions = {},
|
|
35
|
-
): Promise<void> => {
|
|
36
|
-
await page.evaluate(
|
|
37
|
-
(configOptions: Spec) => window.axe.configure(configOptions),
|
|
38
|
-
configurationOptions as Spec,
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Runs axe-core tools on the relevant page and returns all results
|
|
44
|
-
* @param page
|
|
45
|
-
* @param context
|
|
46
|
-
* @param options
|
|
47
|
-
*/
|
|
48
|
-
export const getAxeResults = async (
|
|
49
|
-
page: Page,
|
|
50
|
-
context?: ElementContext,
|
|
51
|
-
options?: RunOptions,
|
|
52
|
-
): Promise<AxeResults> => {
|
|
53
|
-
const result = await page.evaluate(
|
|
54
|
-
([context, options]) => {
|
|
55
|
-
return window.axe.run(context || window.document, options)
|
|
56
|
-
},
|
|
57
|
-
[context, options],
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
return result
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Runs axe-core tools on the relevant page and returns all accessibility violations detected on the page
|
|
65
|
-
* @param page
|
|
66
|
-
* @param context
|
|
67
|
-
* @param options
|
|
68
|
-
*/
|
|
69
|
-
export const getViolations = async (
|
|
70
|
-
page: Page,
|
|
71
|
-
context?: ElementContext,
|
|
72
|
-
options?: RunOptions,
|
|
73
|
-
): Promise<Result[]> => {
|
|
74
|
-
const result = await getAxeResults(page, context, options)
|
|
75
|
-
return result.violations
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Report violations given the reporter.
|
|
80
|
-
* @param violations
|
|
81
|
-
* @param reporter
|
|
82
|
-
*/
|
|
83
|
-
export const reportViolations = async (violations: Result[], reporter: Reporter): Promise<void> => {
|
|
84
|
-
await reporter.report(violations)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Performs Axe validations
|
|
89
|
-
* @param page
|
|
90
|
-
* @param context
|
|
91
|
-
* @param options
|
|
92
|
-
* @param skipFailures
|
|
93
|
-
* @param reporter
|
|
94
|
-
*/
|
|
95
|
-
export const checkA11y = async (
|
|
96
|
-
page: Page,
|
|
97
|
-
context: ElementContext | undefined = undefined,
|
|
98
|
-
options: Options | undefined = undefined,
|
|
99
|
-
skipFailures: boolean = false,
|
|
100
|
-
reporter: Reporter = new DefaultTerminalReporter(options?.detailedReport, options?.detailedReportOptions?.html),
|
|
101
|
-
): Promise<void> => {
|
|
102
|
-
const violations = await getViolations(page, context, options?.axeOptions)
|
|
103
|
-
|
|
104
|
-
const impactedViolations = getImpactedViolations(
|
|
105
|
-
violations,
|
|
106
|
-
options?.includedImpacts,
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
await reportViolations(
|
|
110
|
-
impactedViolations,
|
|
111
|
-
reporter,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
testResultDependsOnViolations(impactedViolations, skipFailures)
|
|
115
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import Reporter from '../types'
|
|
2
|
-
import { Result } from 'axe-core'
|
|
3
|
-
import { describeViolations } from '../utils'
|
|
4
|
-
|
|
5
|
-
export default class DefaultTerminalReporter implements Reporter {
|
|
6
|
-
constructor(
|
|
7
|
-
protected detailedReport: boolean | undefined,
|
|
8
|
-
protected includeHtml: boolean | undefined,
|
|
9
|
-
) {}
|
|
10
|
-
|
|
11
|
-
async report(violations: Result[]): Promise<void> {
|
|
12
|
-
const violationData = violations.map(({ id, impact, description, nodes }) => {
|
|
13
|
-
return {
|
|
14
|
-
id,
|
|
15
|
-
impact,
|
|
16
|
-
description,
|
|
17
|
-
nodes: nodes.length,
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
if (violationData.length > 0) {
|
|
22
|
-
// summary
|
|
23
|
-
console.table(violationData)
|
|
24
|
-
if (this.detailedReport) {
|
|
25
|
-
const nodeViolations = describeViolations(violations).map(
|
|
26
|
-
({ target, html, violations }) => {
|
|
27
|
-
if (!this.includeHtml) {
|
|
28
|
-
return {
|
|
29
|
-
target,
|
|
30
|
-
violations,
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return { target, html, violations }
|
|
34
|
-
},
|
|
35
|
-
)
|
|
36
|
-
// per node
|
|
37
|
-
console.table(nodeViolations)
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
console.log(`No accessibility violations detected!`)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Result } from 'axe-core'
|
|
2
|
-
|
|
3
|
-
export interface NodeViolation {
|
|
4
|
-
target: string
|
|
5
|
-
html: string
|
|
6
|
-
violations: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface Aggregate {
|
|
10
|
-
[key: string]: {
|
|
11
|
-
target: string
|
|
12
|
-
html: string
|
|
13
|
-
violations: number[]
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default interface Reporter {
|
|
18
|
-
report(violations: Result[]): Promise<void>
|
|
19
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Aggregate, NodeViolation } from './types'
|
|
2
|
-
import { ImpactValue, Result } from 'axe-core'
|
|
3
|
-
import assert from 'assert'
|
|
4
|
-
|
|
5
|
-
export const getImpactedViolations = (
|
|
6
|
-
violations: Result[],
|
|
7
|
-
includedImpacts: ImpactValue[] = [],
|
|
8
|
-
): Result[] => {
|
|
9
|
-
return Array.isArray(includedImpacts) && includedImpacts.length
|
|
10
|
-
? violations.filter(
|
|
11
|
-
(v: Result) => v.impact && includedImpacts.includes(v.impact),
|
|
12
|
-
)
|
|
13
|
-
: violations
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const testResultDependsOnViolations = (
|
|
17
|
-
violations: Result[],
|
|
18
|
-
skipFailures: boolean,
|
|
19
|
-
) => {
|
|
20
|
-
if (!skipFailures) {
|
|
21
|
-
assert.strictEqual(
|
|
22
|
-
violations.length,
|
|
23
|
-
0,
|
|
24
|
-
`${violations.length} accessibility violation${
|
|
25
|
-
violations.length === 1 ? '' : 's'
|
|
26
|
-
} ${violations.length === 1 ? 'was' : 'were'} detected`,
|
|
27
|
-
)
|
|
28
|
-
} else {
|
|
29
|
-
if (violations.length) {
|
|
30
|
-
console.warn({
|
|
31
|
-
name: 'a11y violation summary',
|
|
32
|
-
message: `${violations.length} accessibility violation${
|
|
33
|
-
violations.length === 1 ? '' : 's'
|
|
34
|
-
} ${violations.length === 1 ? 'was' : 'were'} detected`,
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const describeViolations = (violations: Result[]): NodeViolation[] => {
|
|
41
|
-
const aggregate: Aggregate = {}
|
|
42
|
-
|
|
43
|
-
violations.map(({ nodes }, index) => {
|
|
44
|
-
nodes.forEach(({ target, html }) => {
|
|
45
|
-
const key = JSON.stringify(target) + html
|
|
46
|
-
|
|
47
|
-
if (aggregate[key]) {
|
|
48
|
-
aggregate[key].violations.push(index)
|
|
49
|
-
} else {
|
|
50
|
-
aggregate[key] = {
|
|
51
|
-
target: JSON.stringify(target),
|
|
52
|
-
html,
|
|
53
|
-
violations: [index],
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
return Object.values(aggregate).map(({ target, html, violations }) => {
|
|
59
|
-
return { target, html, violations: JSON.stringify(violations) }
|
|
60
|
-
})
|
|
61
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es6",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"strict": true,
|
|
6
|
-
"declaration": false,
|
|
7
|
-
"rootDir": "src",
|
|
8
|
-
"moduleResolution": "node",
|
|
9
|
-
"typeRoots": ["node_modules/@types"],
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"outDir": "dist",
|
|
12
|
-
"noImplicitAny": false,
|
|
13
|
-
"types": ["node", "jest"]
|
|
14
|
-
},
|
|
15
|
-
"exclude": ["node_modules"],
|
|
16
|
-
"include": ["src/"]
|
|
17
|
-
}
|