axe-playwright 1.1.7 → 1.1.10

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.
@@ -0,0 +1,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "23:30"
8
+ open-pull-requests-limit: 10
9
+ ignore:
10
+ - dependency-name: "@types/node"
11
+ versions:
12
+ - 15.0.0
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  [![Build Status](https://circleci.com/gh/abhinaba-ghosh/axe-playwright.svg?style=shield&branch-=master)](https://app.circleci.com/pipelines/github/abhinaba-ghosh/axe-playwright)
6
6
  [![NPM release](https://img.shields.io/npm/v/axe-playwright.svg 'NPM release')](https://www.npmjs.com/package/axe-playwright)
7
+ [![NPM Downloads](https://img.shields.io/npm/dt/axe-playwright.svg?style=flat-square)](https://www.npmjs.com/package/axe-playwright)
7
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.The idea is highly inspired by [Andy Van Slaars](https://github.com/avanslaars) cypress-axe project.
9
10
 
@@ -115,17 +116,63 @@ The `detailedReport` key is a boolean whether to print the more detailed report
115
116
  }
116
117
  ```
117
118
 
119
+ ##### reporter (optional)
120
+
121
+ A class instance that implements the `Reporter` interface. Custom reporter instances can be supplied to override default reporting behaviour dictated by `DefaultTerminalReporter`.
122
+
118
123
  ##### skipFailures (optional, defaults to false)
119
124
 
120
125
  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.
121
126
 
127
+ ### getAxeResults
128
+
129
+ 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`.
130
+
131
+ #### Parameters on getAxeResults
132
+
133
+ ##### page (mandatory)
134
+
135
+ The `page` instance of `playwright`.
136
+
137
+ ##### context (optional)
138
+
139
+ 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.
140
+
141
+ ##### options (optional)
142
+
143
+ Set of options passed into rules or checks, temporarily modifying them. This contrasts with axe.configure, which is more permanent.
144
+
145
+ The object is of the same type which is [accepted by `axe.run`'s options argument](https://www.deque.com/axe/documentation/api-documentation/#parameters-axerun) and directly forwarded to it.
146
+
147
+ ### getViolations
148
+
149
+ This will run axe against the document at the point in which it is called, then return you an array of accessibility violations (i.e. the `violations` array included in the `getAxeResults` result).
150
+
151
+ #### Parameters on getViolations (axe.run)
152
+
153
+ Identical to [parameters of getAxeResults](#parameters-on-getAxeResults).
154
+
155
+ ### reportViolations
156
+
157
+ Reports violations based on the `Reporter` concrete implementation behaviour.
158
+
159
+ #### Parameters on reportViolations
160
+
161
+ ##### violations (mandatory)
162
+
163
+ An array of Axe violations to be printed.
164
+
165
+ ##### reporter (mandatory)
166
+
167
+ A class instance that implements the `Reporter` interface. Custom reporter instances can be supplied to override default reporting behaviour dictated by `DefaultTerminalReporter`.
168
+
122
169
  ### Examples
123
170
 
124
171
  #### Basic usage
125
172
 
126
- ```js
173
+ ```ts
127
174
  import { chromium, Browser, Page } from 'playwright'
128
- import { injectAxe, checkA11y } from 'axe-playwright'
175
+ import { injectAxe, checkA11y, getViolations, reportViolations } from 'axe-playwright'
129
176
 
130
177
  let browser: Browser
131
178
  let page: Page
@@ -164,6 +211,21 @@ describe('Playwright web page accessibility test', () => {
164
211
  })
165
212
  })
166
213
 
214
+ it('gets and reports a11y for the specific element', async () => {
215
+ const violations = await getViolations(page, 'input[name="password"]', {
216
+ axeOptions: {
217
+ runOnly: {
218
+ type: 'tag',
219
+ values: ['wcag2a'],
220
+ },
221
+ },
222
+ })
223
+
224
+ reportViolations(violations, new YourAwesomeCsvReporter('accessibility-report.csv'))
225
+
226
+ expect(violations.length).toBe(0)
227
+ })
228
+
167
229
  afterAll(async () => {
168
230
  await browser.close()
169
231
  })
@@ -178,7 +240,7 @@ This custom logging behavior results in terminal output like this:
178
240
 
179
241
  The detailed report is disabled by default, but can be enabled by including the `detailedReport` property in the `checkAlly` options.
180
242
 
181
- ```js
243
+ ```ts
182
244
  import { chromium, Browser, Page } from 'playwright'
183
245
  import { injectAxe, checkA11y } from 'axe-playwright'
184
246
 
package/dist/index.js CHANGED
@@ -27,10 +27,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
27
27
  step((generator = generator.apply(thisArg, _arguments || [])).next());
28
28
  });
29
29
  };
30
+ var __importDefault = (this && this.__importDefault) || function (mod) {
31
+ return (mod && mod.__esModule) ? mod : { "default": mod };
32
+ };
30
33
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.checkA11y = exports.configureAxe = exports.injectAxe = void 0;
34
+ exports.checkA11y = exports.reportViolations = exports.getViolations = exports.configureAxe = exports.injectAxe = void 0;
32
35
  const fs = __importStar(require("fs"));
33
36
  const utils_1 = require("./utils");
37
+ const defaultTerminalReporter_1 = __importDefault(require("./reporter/defaultTerminalReporter"));
34
38
  /**
35
39
  * Injects axe executable commands in the active window
36
40
  * @param page
@@ -50,20 +54,45 @@ const configureAxe = (page, configurationOptions = {}) => __awaiter(void 0, void
50
54
  });
51
55
  exports.configureAxe = configureAxe;
52
56
  /**
53
- * Performs Axe validations
57
+ * Runs axe-core tools on the relevant page and returns all accessibility violations detected on the page
54
58
  * @param page
55
59
  * @param context
56
60
  * @param options
57
- * @param skipFailures
58
61
  */
59
- const checkA11y = (page, context = undefined, options = undefined, skipFailures = false) => __awaiter(void 0, void 0, void 0, function* () {
60
- let axeResults = yield page.evaluate(([context, options]) => {
62
+ const getViolations = (page, context, options) => __awaiter(void 0, void 0, void 0, function* () {
63
+ const result = yield page.evaluate(([context, options]) => {
61
64
  const axeOptions = options ? options['axeOptions'] : {};
62
65
  return window.axe.run(context || window.document, axeOptions);
63
66
  }, [context, options]);
64
- const { includedImpacts, detailedReport = false, detailedReportOptions = { html: false }, } = options || {};
65
- const violations = utils_1.getImpactedViolations(axeResults.violations, includedImpacts);
66
- utils_1.printViolationTerminal(violations, detailedReport, !!detailedReportOptions.html);
67
- utils_1.testResultDependsOnViolations(violations, skipFailures);
67
+ return result.violations;
68
+ });
69
+ exports.getViolations = getViolations;
70
+ /**
71
+ * Report violations given the reporter.
72
+ * @param violations
73
+ * @param reporter
74
+ */
75
+ const reportViolations = (violations, reporter) => __awaiter(void 0, void 0, void 0, function* () {
76
+ yield reporter.report(violations);
77
+ });
78
+ exports.reportViolations = reportViolations;
79
+ /**
80
+ * Performs Axe validations
81
+ * @param page
82
+ * @param context
83
+ * @param options
84
+ * @param skipFailures
85
+ * @param reporter
86
+ */
87
+ const checkA11y = (page, context, options, skipFailures, reporter) => __awaiter(void 0, void 0, void 0, function* () {
88
+ var _a;
89
+ if (context === void 0) { context = undefined; }
90
+ if (options === void 0) { options = undefined; }
91
+ if (skipFailures === void 0) { skipFailures = false; }
92
+ if (reporter === void 0) { reporter = new defaultTerminalReporter_1.default(options === null || options === void 0 ? void 0 : options.detailedReport, (_a = options === null || options === void 0 ? void 0 : options.detailedReportOptions) === null || _a === void 0 ? void 0 : _a.html); }
93
+ const violations = yield exports.getViolations(page, context, options);
94
+ const impactedViolations = utils_1.getImpactedViolations(violations, options === null || options === void 0 ? void 0 : options.includedImpacts);
95
+ yield exports.reportViolations(impactedViolations, reporter);
96
+ utils_1.testResultDependsOnViolations(impactedViolations, skipFailures);
68
97
  });
69
98
  exports.checkA11y = checkA11y;
@@ -0,0 +1,51 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const utils_1 = require("../utils");
13
+ class DefaultTerminalReporter {
14
+ constructor(detailedReport, includeHtml) {
15
+ this.detailedReport = detailedReport;
16
+ this.includeHtml = includeHtml;
17
+ }
18
+ report(violations) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const violationData = violations.map(({ id, impact, description, nodes }) => {
21
+ return {
22
+ id,
23
+ impact,
24
+ description,
25
+ nodes: nodes.length,
26
+ };
27
+ });
28
+ if (violationData.length > 0) {
29
+ // summary
30
+ console.table(violationData);
31
+ if (this.detailedReport) {
32
+ const nodeViolations = utils_1.describeViolations(violations).map(({ target, html, violations }) => {
33
+ if (!this.includeHtml) {
34
+ return {
35
+ target,
36
+ violations,
37
+ };
38
+ }
39
+ return { target, html, violations };
40
+ });
41
+ // per node
42
+ console.table(nodeViolations);
43
+ }
44
+ }
45
+ else {
46
+ console.log(`No accessibility violations detected!`);
47
+ }
48
+ });
49
+ }
50
+ }
51
+ exports.default = DefaultTerminalReporter;
package/dist/utils.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.printViolationTerminal = exports.describeViolations = exports.testResultDependsOnViolations = exports.getImpactedViolations = void 0;
6
+ exports.describeViolations = exports.testResultDependsOnViolations = exports.getImpactedViolations = void 0;
7
7
  const assert_1 = __importDefault(require("assert"));
8
8
  const getImpactedViolations = (violations, includedImpacts = []) => {
9
9
  return Array.isArray(includedImpacts) && includedImpacts.length
@@ -47,34 +47,3 @@ const describeViolations = (violations) => {
47
47
  });
48
48
  };
49
49
  exports.describeViolations = describeViolations;
50
- const printViolationTerminal = (violations, detailedReport, includeHtml) => {
51
- const violationData = violations.map(({ id, impact, description, nodes }) => {
52
- return {
53
- id,
54
- impact,
55
- description,
56
- nodes: nodes.length,
57
- };
58
- });
59
- if (violationData.length > 0) {
60
- // summary
61
- console.table(violationData);
62
- if (detailedReport) {
63
- const nodeViolations = exports.describeViolations(violations).map(({ target, html, violations }) => {
64
- if (!includeHtml) {
65
- return {
66
- target,
67
- violations,
68
- };
69
- }
70
- return { target, html, violations };
71
- });
72
- // per node
73
- console.table(nodeViolations);
74
- }
75
- }
76
- else {
77
- console.log(`No accessibility violations detected!`);
78
- }
79
- };
80
- exports.printViolationTerminal = printViolationTerminal;
package/index.d.ts CHANGED
@@ -1,11 +1,4 @@
1
- import {
2
- Check,
3
- ElementContext,
4
- ImpactValue,
5
- Locale,
6
- Rule,
7
- RunOptions,
8
- } from 'axe-core'
1
+ import { AxeResults, Check, ElementContext, ImpactValue, Locale, Result, Rule, RunOptions } from 'axe-core'
9
2
  import { Page } from 'playwright'
10
3
 
11
4
  export interface axeOptionsConfig {
@@ -24,6 +17,22 @@ export interface ConfigOptions {
24
17
  axeVersion?: string
25
18
  }
26
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
+
27
36
  export type Options = {
28
37
  includedImpacts?: ImpactValue[]
29
38
  detailedReport?: boolean
@@ -59,3 +68,26 @@ export function checkA11y(
59
68
  * @param options
60
69
  */
61
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axe-playwright",
3
- "version": "1.1.7",
3
+ "version": "1.1.10",
4
4
  "description": "Custom Playwright commands to inject axe-core and test for a11y",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.d.ts",
@@ -25,11 +25,11 @@
25
25
  "author": "Abhinaba Ghosh",
26
26
  "license": "MIT",
27
27
  "devDependencies": {
28
- "@types/is-ci": "^2.0.0",
29
- "@types/jest": "^26.0.10",
30
- "@types/node": "^14.14.25",
28
+ "@types/is-ci": "^3.0.0",
29
+ "@types/jest": "^27.0.3",
30
+ "@types/node": "^17.0.4",
31
31
  "jest": "^26.6.3",
32
- "jest-each": "^26.6.2",
32
+ "jest-each": "^27.0.2",
33
33
  "jest-playwright-preset": "^1.3.1",
34
34
  "playwright": "^1.1.1",
35
35
  "prettier": "^2.0.5",
package/src/index.ts CHANGED
@@ -2,11 +2,9 @@ import { Page } from 'playwright'
2
2
  import * as fs from 'fs'
3
3
  import { AxeResults, ElementContext, Result, RunOptions, Spec } from 'axe-core'
4
4
  import { ConfigOptions, Options } from '../index'
5
- import {
6
- getImpactedViolations,
7
- printViolationTerminal,
8
- testResultDependsOnViolations,
9
- } from './utils'
5
+ import { getImpactedViolations, testResultDependsOnViolations } from './utils'
6
+ import DefaultTerminalReporter from './reporter/defaultTerminalReporter'
7
+ import Reporter from './types'
10
8
 
11
9
  declare global {
12
10
  interface Window {
@@ -41,41 +39,77 @@ export const configureAxe = async (
41
39
  )
42
40
  }
43
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
+
44
87
  /**
45
88
  * Performs Axe validations
46
89
  * @param page
47
90
  * @param context
48
91
  * @param options
49
92
  * @param skipFailures
93
+ * @param reporter
50
94
  */
51
95
  export const checkA11y = async (
52
96
  page: Page,
53
97
  context: ElementContext | undefined = undefined,
54
98
  options: Options | undefined = undefined,
55
99
  skipFailures: boolean = false,
100
+ reporter: Reporter = new DefaultTerminalReporter(options?.detailedReport, options?.detailedReportOptions?.html),
56
101
  ): Promise<void> => {
57
- let axeResults: AxeResults = await page.evaluate(
58
- ([context, options]) => {
59
- const axeOptions: RunOptions = options ? options['axeOptions'] : {}
60
- return window.axe.run(context || window.document, axeOptions)
61
- },
62
- [context, options],
63
- )
102
+ const violations = await getViolations(page, context, options?.axeOptions)
64
103
 
65
- const {
66
- includedImpacts,
67
- detailedReport = false,
68
- detailedReportOptions = { html: false },
69
- } = options || {}
70
- const violations: Result[] = getImpactedViolations(
71
- axeResults.violations,
72
- includedImpacts,
104
+ const impactedViolations = getImpactedViolations(
105
+ violations,
106
+ options?.includedImpacts,
73
107
  )
74
108
 
75
- printViolationTerminal(
76
- violations,
77
- detailedReport,
78
- !!detailedReportOptions.html,
109
+ await reportViolations(
110
+ impactedViolations,
111
+ reporter,
79
112
  )
80
- testResultDependsOnViolations(violations, skipFailures)
113
+
114
+ testResultDependsOnViolations(impactedViolations, skipFailures)
81
115
  }
@@ -0,0 +1,43 @@
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 CHANGED
@@ -1,3 +1,5 @@
1
+ import { Result } from 'axe-core'
2
+
1
3
  export interface NodeViolation {
2
4
  target: string
3
5
  html: string
@@ -11,3 +13,7 @@ export interface Aggregate {
11
13
  violations: number[]
12
14
  }
13
15
  }
16
+
17
+ export default interface Reporter {
18
+ report(violations: Result[]): Promise<void>
19
+ }
package/src/utils.ts CHANGED
@@ -5,11 +5,11 @@ import assert from 'assert'
5
5
  export const getImpactedViolations = (
6
6
  violations: Result[],
7
7
  includedImpacts: ImpactValue[] = [],
8
- ) => {
8
+ ): Result[] => {
9
9
  return Array.isArray(includedImpacts) && includedImpacts.length
10
10
  ? violations.filter(
11
- (v: Result) => v.impact && includedImpacts.includes(v.impact),
12
- )
11
+ (v: Result) => v.impact && includedImpacts.includes(v.impact),
12
+ )
13
13
  : violations
14
14
  }
15
15
 
@@ -59,40 +59,3 @@ export const describeViolations = (violations: Result[]): NodeViolation[] => {
59
59
  return { target, html, violations: JSON.stringify(violations) }
60
60
  })
61
61
  }
62
-
63
- export const printViolationTerminal = (
64
- violations: Result[],
65
- detailedReport: boolean,
66
- includeHtml: boolean,
67
- ) => {
68
- const violationData = violations.map(({ id, impact, description, nodes }) => {
69
- return {
70
- id,
71
- impact,
72
- description,
73
- nodes: nodes.length,
74
- }
75
- })
76
-
77
- if (violationData.length > 0) {
78
- // summary
79
- console.table(violationData)
80
- if (detailedReport) {
81
- const nodeViolations = describeViolations(violations).map(
82
- ({ target, html, violations }) => {
83
- if (!includeHtml) {
84
- return {
85
- target,
86
- violations,
87
- }
88
- }
89
- return { target, html, violations }
90
- },
91
- )
92
- // per node
93
- console.table(nodeViolations)
94
- }
95
- } else {
96
- console.log(`No accessibility violations detected!`)
97
- }
98
- }