@code-pushup/axe-plugin 0.87.0
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 +175 -0
- package/package.json +58 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +3 -0
- package/src/index.js.map +1 -0
- package/src/lib/axe-plugin.d.ts +11 -0
- package/src/lib/axe-plugin.js +36 -0
- package/src/lib/axe-plugin.js.map +1 -0
- package/src/lib/config.d.ts +12 -0
- package/src/lib/config.js +18 -0
- package/src/lib/config.js.map +1 -0
- package/src/lib/constants.d.ts +4 -0
- package/src/lib/constants.js +9 -0
- package/src/lib/constants.js.map +1 -0
- package/src/lib/meta/transform.d.ts +6 -0
- package/src/lib/meta/transform.js +102 -0
- package/src/lib/meta/transform.js.map +1 -0
- package/src/lib/processing.d.ts +7 -0
- package/src/lib/processing.js +17 -0
- package/src/lib/processing.js.map +1 -0
- package/src/lib/runner/run-axe.d.ts +3 -0
- package/src/lib/runner/run-axe.js +64 -0
- package/src/lib/runner/run-axe.js.map +1 -0
- package/src/lib/runner/runner.d.ts +2 -0
- package/src/lib/runner/runner.js +41 -0
- package/src/lib/runner/runner.js.map +1 -0
- package/src/lib/runner/transform.d.ts +7 -0
- package/src/lib/runner/transform.js +67 -0
- package/src/lib/runner/transform.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# @code-pushup/plugin-axe
|
|
2
|
+
|
|
3
|
+
🕵️ **Code PushUp plugin for automated accessibility testing with Axe.** 🌐
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The plugin runs accessibility audits on web pages using [axe-core](https://github.com/dequelabs/axe-core) via Playwright. It identifies [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) violations and best practice recommendations, providing **100+ accessibility metrics** with detailed guidance for fixing problems.
|
|
8
|
+
|
|
9
|
+
**Why accessibility testing matters:** The [European Accessibility Act (EAA)](https://ec.europa.eu/social/main.jsp?catId=1202) requires digital products and services in the EU to meet accessibility standards closely aligned with the Web Content Accessibility Guidelines (WCAG). This plugin provides comprehensive automated testing to help meet compliance requirements and build inclusive experiences.
|
|
10
|
+
|
|
11
|
+
## Getting started
|
|
12
|
+
|
|
13
|
+
1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file.
|
|
14
|
+
|
|
15
|
+
2. Install as a dev dependency with your package manager:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install --save-dev @code-pushup/plugin-axe
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
yarn add --dev @code-pushup/plugin-axe
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pnpm add --save-dev @code-pushup/plugin-axe
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.ts`).
|
|
30
|
+
|
|
31
|
+
Pass in the URL you want to test:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import axePlugin from '@code-pushup/plugin-axe';
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
// ...
|
|
38
|
+
plugins: [
|
|
39
|
+
// ...
|
|
40
|
+
axePlugin('https://example.com'),
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
By default, the plugin runs **WCAG 2.1 Level AA** audits. You can customize this using [presets](#presets).
|
|
46
|
+
|
|
47
|
+
4. Run the CLI with `npx code-pushup collect` and view or upload the report (refer to [CLI docs](../cli/README.md)).
|
|
48
|
+
|
|
49
|
+
> **Note:** The plugin uses Playwright to run tests in Chromium. The browser will be installed automatically if not already present.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
axePlugin(urls: PluginUrls, options?: AxePluginOptions)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Parameters:**
|
|
58
|
+
|
|
59
|
+
- `urls` - URL(s) to test. See [Multiple URLs](#multiple-urls) section below, or [`PluginUrls`](../../packages/models/docs/models-reference.md#pluginurls) reference
|
|
60
|
+
- `options` - Optional configuration object
|
|
61
|
+
|
|
62
|
+
**Options:**
|
|
63
|
+
|
|
64
|
+
| Property | Type | Default | Description |
|
|
65
|
+
| -------------- | ----------- | ------------ | ----------------------------------------- |
|
|
66
|
+
| `preset` | `AxePreset` | `'wcag21aa'` | Accessibility ruleset preset |
|
|
67
|
+
| `scoreTargets` | `object` | `undefined` | Pass/fail thresholds for audits or groups |
|
|
68
|
+
|
|
69
|
+
See [Presets](#presets) for the list of available presets and [Preset details](#preset-details) for what each preset includes.
|
|
70
|
+
|
|
71
|
+
## Multiple URLs
|
|
72
|
+
|
|
73
|
+
The plugin supports testing single or multiple URLs:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// Single URL (string)
|
|
77
|
+
axePlugin('https://example.com');
|
|
78
|
+
|
|
79
|
+
// Multiple URLs (array)
|
|
80
|
+
axePlugin(['https://example.com', 'https://example.com/about']);
|
|
81
|
+
|
|
82
|
+
// Weighted URLs (record)
|
|
83
|
+
axePlugin({
|
|
84
|
+
'https://example.com': 3, // homepage has 3x weight
|
|
85
|
+
'https://example.com/about': 1, // about page has 1x weight
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
URLs with higher weights contribute more to overall scores. For example, a URL with weight 3 has three times the influence of a URL with weight 1.
|
|
90
|
+
|
|
91
|
+
## Presets
|
|
92
|
+
|
|
93
|
+
Choose which accessibility ruleset to test against using the `preset` option:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
axePlugin('https://example.com', {
|
|
97
|
+
preset: 'wcag22aa',
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Available presets:
|
|
102
|
+
|
|
103
|
+
| Preset | Description | Use case |
|
|
104
|
+
| --------------- | --------------------------------------------- | ------------------------------------- |
|
|
105
|
+
| `wcag21aa` | **WCAG 2.1 Level A and AA** (default) | Standard web accessibility compliance |
|
|
106
|
+
| `wcag22aa` | **WCAG 2.2 Level A and AA** | Latest WCAG standard |
|
|
107
|
+
| `best-practice` | **Best practices** (non-WCAG recommendations) | Deque's additional recommendations |
|
|
108
|
+
| `all` | **All available rules** | Comprehensive testing |
|
|
109
|
+
|
|
110
|
+
### Preset details
|
|
111
|
+
|
|
112
|
+
**`wcag21aa`** (default)
|
|
113
|
+
|
|
114
|
+
- Tests compliance with [WCAG 2.1 Level A and Level AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_overview&levels=a%2Caaa)
|
|
115
|
+
- Required for [European Accessibility Act (EAA)](https://ec.europa.eu/social/main.jsp?catId=1202) compliance
|
|
116
|
+
- Covers fundamentals: keyboard navigation, color contrast, form labels, alt text, etc.
|
|
117
|
+
|
|
118
|
+
**`wcag22aa`**
|
|
119
|
+
|
|
120
|
+
- Tests compliance with [WCAG 2.2 Level A and Level AA](https://www.w3.org/WAI/WCAG22/quickref/?currentsidebar=%23col_overview&levels=a%2Caaa)
|
|
121
|
+
- Includes all WCAG 2.1 rules plus new success criteria
|
|
122
|
+
- Future-proof for upcoming regulations
|
|
123
|
+
|
|
124
|
+
**`best-practice`**
|
|
125
|
+
|
|
126
|
+
- Non-WCAG recommendations from Deque Systems
|
|
127
|
+
- Goes beyond legal requirements
|
|
128
|
+
- Helps create more inclusive experiences
|
|
129
|
+
|
|
130
|
+
**`all`**
|
|
131
|
+
|
|
132
|
+
- Runs every rule available in axe-core
|
|
133
|
+
- Includes WCAG 2.0, 2.1, 2.2, experimental rules, and best practices
|
|
134
|
+
- Most comprehensive testing
|
|
135
|
+
|
|
136
|
+
> **Note:** Different pages may trigger different sets of rules depending on their content. For example, a page without video won't run video-related audits.
|
|
137
|
+
|
|
138
|
+
### Groups
|
|
139
|
+
|
|
140
|
+
The plugin automatically creates groups to organize audits:
|
|
141
|
+
|
|
142
|
+
**WCAG presets** (`wcag21aa`, `wcag22aa`):
|
|
143
|
+
|
|
144
|
+
- `wcag21-level-a` - WCAG 2.1 Level A audits
|
|
145
|
+
- `wcag21-level-aa` - WCAG 2.1 Level AA audits
|
|
146
|
+
- `wcag22-level-a` - WCAG 2.2 Level A audits (wcag22aa only)
|
|
147
|
+
- `wcag22-level-aa` - WCAG 2.2 Level AA audits (wcag22aa only)
|
|
148
|
+
|
|
149
|
+
**Best practice preset** (`best-practice`):
|
|
150
|
+
|
|
151
|
+
- `aria` - ARIA
|
|
152
|
+
- `color` - Color & Contrast
|
|
153
|
+
- `forms` - Forms
|
|
154
|
+
- `keyboard` - Keyboard
|
|
155
|
+
- `language` - Language
|
|
156
|
+
- `name-role-value` - Names & Labels
|
|
157
|
+
- `parsing` - Parsing
|
|
158
|
+
- `semantics` - Semantics
|
|
159
|
+
- `sensory-and-visual-cues` - Visual Cues
|
|
160
|
+
- `structure` - Structure
|
|
161
|
+
- `tables` - Tables
|
|
162
|
+
- `text-alternatives` - Text Alternatives
|
|
163
|
+
- `time-and-media` - Media
|
|
164
|
+
|
|
165
|
+
**All preset** (`all`):
|
|
166
|
+
|
|
167
|
+
- Combines all WCAG groups and best practice category groups
|
|
168
|
+
|
|
169
|
+
Use `npx code-pushup print-config --onlyPlugins=axe` to list all audits and groups for your configuration.
|
|
170
|
+
|
|
171
|
+
## Resources
|
|
172
|
+
|
|
173
|
+
- **[Axe-core rules](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md)** - Complete list of accessibility rules
|
|
174
|
+
- **[Deque University](https://dequeuniversity.com/rules/axe/)** - Detailed explanations and remediation guidance
|
|
175
|
+
- **[WCAG Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)** - Web Content Accessibility Guidelines
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@code-pushup/axe-plugin",
|
|
3
|
+
"version": "0.87.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Code PushUp plugin for detecting accessibility issues using Axe 🌐",
|
|
6
|
+
"homepage": "https://github.com/code-pushup/cli/tree/main/packages/plugin-axe#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/code-pushup/cli/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug%20label%3A%22🧩%20axe-plugin%22"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/code-pushup/cli.git",
|
|
13
|
+
"directory": "packages/plugin-axe"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"CLI",
|
|
17
|
+
"Code PushUp",
|
|
18
|
+
"plugin",
|
|
19
|
+
"axe",
|
|
20
|
+
"axe-core",
|
|
21
|
+
"accessibility",
|
|
22
|
+
"a11y",
|
|
23
|
+
"WCAG",
|
|
24
|
+
"compliance",
|
|
25
|
+
"testing",
|
|
26
|
+
"quality",
|
|
27
|
+
"automation",
|
|
28
|
+
"runtime analysis",
|
|
29
|
+
"audit",
|
|
30
|
+
"score monitoring",
|
|
31
|
+
"developer tools",
|
|
32
|
+
"conformance",
|
|
33
|
+
"KPI tracking",
|
|
34
|
+
"tech debt",
|
|
35
|
+
"automated feedback",
|
|
36
|
+
"regression guard",
|
|
37
|
+
"actionable feedback"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"type": "module",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@axe-core/playwright": "^4.11.0",
|
|
45
|
+
"@code-pushup/models": "0.87.0",
|
|
46
|
+
"@code-pushup/utils": "0.87.0",
|
|
47
|
+
"axe-core": "^4.11.0",
|
|
48
|
+
"playwright-core": "^1.56.1",
|
|
49
|
+
"zod": "^4.1.12"
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"src",
|
|
53
|
+
"!**/*.tsbuildinfo"
|
|
54
|
+
],
|
|
55
|
+
"types": "./src/index.d.ts",
|
|
56
|
+
"module": "./src/index.js",
|
|
57
|
+
"main": "./src/index.js"
|
|
58
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type PluginConfig, type PluginUrls } from '@code-pushup/models';
|
|
2
|
+
import { type AxePluginOptions } from './config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Code PushUp plugin for accessibility testing using axe-core.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
* @param urls - {@link PluginUrls} URL(s) to test
|
|
8
|
+
* @param options - {@link AxePluginOptions} Plugin options
|
|
9
|
+
* @returns Plugin configuration
|
|
10
|
+
*/
|
|
11
|
+
export declare function axePlugin(urls: PluginUrls, options?: AxePluginOptions): PluginConfig;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { validate, } from '@code-pushup/models';
|
|
3
|
+
import { normalizeUrlInput } from '@code-pushup/utils';
|
|
4
|
+
import { axePluginOptionsSchema } from './config.js';
|
|
5
|
+
import { AXE_PLUGIN_SLUG } from './constants.js';
|
|
6
|
+
import { processAuditsAndGroups } from './processing.js';
|
|
7
|
+
import { createRunnerFunction } from './runner/runner.js';
|
|
8
|
+
/**
|
|
9
|
+
* Code PushUp plugin for accessibility testing using axe-core.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
* @param urls - {@link PluginUrls} URL(s) to test
|
|
13
|
+
* @param options - {@link AxePluginOptions} Plugin options
|
|
14
|
+
* @returns Plugin configuration
|
|
15
|
+
*/
|
|
16
|
+
export function axePlugin(urls, options = {}) {
|
|
17
|
+
const { preset, scoreTargets, timeout } = validate(axePluginOptionsSchema, options);
|
|
18
|
+
const { urls: normalizedUrls, context } = normalizeUrlInput(urls);
|
|
19
|
+
const { audits, groups, ruleIds } = processAuditsAndGroups(normalizedUrls, preset);
|
|
20
|
+
const packageJson = createRequire(import.meta.url)('../../package.json');
|
|
21
|
+
return {
|
|
22
|
+
slug: AXE_PLUGIN_SLUG,
|
|
23
|
+
packageName: packageJson.name,
|
|
24
|
+
version: packageJson.version,
|
|
25
|
+
title: 'Axe Accessibility',
|
|
26
|
+
icon: 'folder-syntax',
|
|
27
|
+
description: 'Official Code PushUp Axe plugin for automated accessibility testing',
|
|
28
|
+
docsUrl: 'https://www.npmjs.com/package/@code-pushup/axe-plugin',
|
|
29
|
+
audits,
|
|
30
|
+
groups,
|
|
31
|
+
runner: createRunnerFunction(normalizedUrls, ruleIds, timeout),
|
|
32
|
+
context,
|
|
33
|
+
...(scoreTargets && { scoreTargets }),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=axe-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axe-plugin.js","sourceRoot":"","sources":["../../../src/lib/axe-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAGL,QAAQ,GACT,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAyB,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CACvB,IAAgB,EAChB,UAA4B,EAAE;IAE9B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,CAChD,sBAAsB,EACtB,OAAO,CACR,CAAC;IAEF,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAElE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,sBAAsB,CACxD,cAAc,EACd,MAAM,CACP,CAAC;IAEF,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAChD,oBAAoB,CACkB,CAAC;IAEzC,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,WAAW,CAAC,IAAI;QAC7B,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,KAAK,EAAE,mBAAmB;QAC1B,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,qEAAqE;QACvE,OAAO,EAAE,uDAAuD;QAChE,MAAM;QACN,MAAM;QACN,MAAM,EAAE,oBAAoB,CAAC,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC;QAC9D,OAAO;QACP,GAAG,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const axePluginOptionsSchema: z.ZodObject<{
|
|
3
|
+
preset: z.ZodDefault<z.ZodEnum<{
|
|
4
|
+
wcag21aa: "wcag21aa";
|
|
5
|
+
wcag22aa: "wcag22aa";
|
|
6
|
+
"best-practice": "best-practice";
|
|
7
|
+
all: "all";
|
|
8
|
+
}>>;
|
|
9
|
+
scoreTargets: z.ZodOptional<z.ZodOptional<z.ZodUnion<readonly [z.ZodOptional<z.ZodNumber>, z.ZodRecord<z.ZodString, z.ZodNonOptional<z.ZodOptional<z.ZodNumber>>>]>>>;
|
|
10
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type AxePluginOptions = z.input<typeof axePluginOptionsSchema>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { pluginScoreTargetsSchema, positiveIntSchema, } from '@code-pushup/models';
|
|
3
|
+
import { AXE_DEFAULT_PRESET, AXE_PRESETS } from './constants.js';
|
|
4
|
+
export const axePluginOptionsSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
preset: z.enum(AXE_PRESETS).default(AXE_DEFAULT_PRESET).meta({
|
|
7
|
+
description: 'Accessibility ruleset preset (default: wcag21aa for WCAG 2.1 Level AA compliance)',
|
|
8
|
+
}),
|
|
9
|
+
scoreTargets: pluginScoreTargetsSchema.optional(),
|
|
10
|
+
timeout: positiveIntSchema.default(30_000).meta({
|
|
11
|
+
description: 'Page navigation timeout in milliseconds (default: 30000ms / 30s)',
|
|
12
|
+
}),
|
|
13
|
+
})
|
|
14
|
+
.meta({
|
|
15
|
+
title: 'AxePluginOptions',
|
|
16
|
+
description: 'Configuration options for the Axe plugin',
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC;IACN,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC;QAC3D,WAAW,EACT,mFAAmF;KACtF,CAAC;IACF,YAAY,EAAE,wBAAwB,CAAC,QAAQ,EAAE;IACjD,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;QAC9C,WAAW,EACT,kEAAkE;KACrE,CAAC;CACH,CAAC;KACD,IAAI,CAAC;IACJ,KAAK,EAAE,kBAAkB;IACzB,WAAW,EAAE,0CAA0C;CACxD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAErC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,UAAU;IACV,UAAU;IACV,eAAe;IACf,KAAK;CACG,CAAC;AAIX,MAAM,CAAC,MAAM,kBAAkB,GAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import axe from 'axe-core';
|
|
2
|
+
import type { Audit, Group } from '@code-pushup/models';
|
|
3
|
+
import type { AxePreset } from '../constants.js';
|
|
4
|
+
export declare function loadAxeRules(preset: AxePreset): axe.RuleMetadata[];
|
|
5
|
+
export declare function transformRulesToAudits(rules: axe.RuleMetadata[]): Audit[];
|
|
6
|
+
export declare function transformRulesToGroups(rules: axe.RuleMetadata[], preset: AxePreset): Group[];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import axe from 'axe-core';
|
|
2
|
+
import { capitalize } from '@code-pushup/utils';
|
|
3
|
+
const WCAG_LEVEL_A_TAGS = ['wcag2a', 'wcag21a'];
|
|
4
|
+
const WCAG_LEVEL_AA_TAGS_21 = ['wcag2aa', 'wcag21aa'];
|
|
5
|
+
const WCAG_LEVEL_AA_TAGS_22 = ['wcag2aa', 'wcag21aa', 'wcag22aa'];
|
|
6
|
+
const CATEGORY_TITLES = {
|
|
7
|
+
'cat.aria': 'ARIA',
|
|
8
|
+
'cat.color': 'Color & Contrast',
|
|
9
|
+
'cat.forms': 'Forms',
|
|
10
|
+
'cat.keyboard': 'Keyboard',
|
|
11
|
+
'cat.language': 'Language',
|
|
12
|
+
'cat.name-role-value': 'Names & Labels',
|
|
13
|
+
'cat.parsing': 'Parsing',
|
|
14
|
+
'cat.semantics': 'Semantics',
|
|
15
|
+
'cat.sensory-and-visual-cues': 'Visual Cues',
|
|
16
|
+
'cat.structure': 'Structure',
|
|
17
|
+
'cat.tables': 'Tables',
|
|
18
|
+
'cat.text-alternatives': 'Text Alternatives',
|
|
19
|
+
'cat.time-and-media': 'Media',
|
|
20
|
+
};
|
|
21
|
+
export function loadAxeRules(preset) {
|
|
22
|
+
const tags = getPresetTags(preset);
|
|
23
|
+
return tags.length === 0 ? axe.getRules() : axe.getRules(tags);
|
|
24
|
+
}
|
|
25
|
+
export function transformRulesToAudits(rules) {
|
|
26
|
+
return rules.map(rule => ({
|
|
27
|
+
slug: rule.ruleId,
|
|
28
|
+
title: rule.help,
|
|
29
|
+
description: rule.description,
|
|
30
|
+
docsUrl: rule.helpUrl,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
export function transformRulesToGroups(rules, preset) {
|
|
34
|
+
const groups = (() => {
|
|
35
|
+
switch (preset) {
|
|
36
|
+
case 'wcag21aa':
|
|
37
|
+
return createWcagGroups(rules, '2.1');
|
|
38
|
+
case 'wcag22aa':
|
|
39
|
+
return createWcagGroups(rules, '2.2');
|
|
40
|
+
case 'best-practice':
|
|
41
|
+
return createCategoryGroups(rules);
|
|
42
|
+
case 'all':
|
|
43
|
+
return [
|
|
44
|
+
...createWcagGroups(rules, '2.2'),
|
|
45
|
+
...createCategoryGroups(rules),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
return groups.filter(({ refs }) => refs.length > 0);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Maps preset to corresponding axe-core tags.
|
|
53
|
+
*
|
|
54
|
+
* WCAG tags are non-cumulative - each rule has exactly one WCAG version tag.
|
|
55
|
+
* To include all rules up to a version/level, multiple tags must be combined.
|
|
56
|
+
*/
|
|
57
|
+
function getPresetTags(preset) {
|
|
58
|
+
switch (preset) {
|
|
59
|
+
case 'wcag21aa':
|
|
60
|
+
return [...WCAG_LEVEL_A_TAGS, ...WCAG_LEVEL_AA_TAGS_21];
|
|
61
|
+
case 'wcag22aa':
|
|
62
|
+
return [...WCAG_LEVEL_A_TAGS, ...WCAG_LEVEL_AA_TAGS_22];
|
|
63
|
+
case 'best-practice':
|
|
64
|
+
return ['best-practice'];
|
|
65
|
+
case 'all':
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function createGroup(slug, title, rules) {
|
|
70
|
+
return {
|
|
71
|
+
slug,
|
|
72
|
+
title,
|
|
73
|
+
refs: rules.map(({ ruleId }) => ({ slug: ruleId, weight: 1 })),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function createWcagGroups(rules, version) {
|
|
77
|
+
const aTags = WCAG_LEVEL_A_TAGS;
|
|
78
|
+
const aaTags = version === '2.1' ? WCAG_LEVEL_AA_TAGS_21 : WCAG_LEVEL_AA_TAGS_22;
|
|
79
|
+
const levelARules = rules.filter(({ tags }) => tags.some(tag => aTags.includes(tag)));
|
|
80
|
+
const levelAARules = rules.filter(({ tags }) => tags.some(tag => aaTags.includes(tag)));
|
|
81
|
+
const versionSlug = version.replace('.', '');
|
|
82
|
+
return [
|
|
83
|
+
createGroup(`wcag${versionSlug}-level-a`, `WCAG ${version} Level A`, levelARules),
|
|
84
|
+
createGroup(`wcag${versionSlug}-level-aa`, `WCAG ${version} Level AA`, levelAARules),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
function createCategoryGroups(rules) {
|
|
88
|
+
const categoryTags = new Set(rules.flatMap(({ tags }) => tags.filter(tag => tag.startsWith('cat.'))));
|
|
89
|
+
return Array.from(categoryTags).map(tag => {
|
|
90
|
+
const slug = tag.replace('cat.', '');
|
|
91
|
+
const title = formatCategoryTitle(tag, slug);
|
|
92
|
+
const categoryRules = rules.filter(({ tags }) => tags.includes(tag));
|
|
93
|
+
return createGroup(slug, title, categoryRules);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function formatCategoryTitle(tag, slug) {
|
|
97
|
+
if (CATEGORY_TITLES[tag]) {
|
|
98
|
+
return CATEGORY_TITLES[tag];
|
|
99
|
+
}
|
|
100
|
+
return slug.split('-').map(capitalize).join(' ');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../../src/lib/meta/transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { expandAuditsForUrls, expandGroupsForUrls, shouldExpandForUrls, } from '@code-pushup/utils';
|
|
2
|
+
import { loadAxeRules, transformRulesToAudits, transformRulesToGroups, } from './meta/transform.js';
|
|
3
|
+
export function processAuditsAndGroups(urls, preset) {
|
|
4
|
+
const rules = loadAxeRules(preset);
|
|
5
|
+
const ruleIds = rules.map(({ ruleId }) => ruleId);
|
|
6
|
+
const audits = transformRulesToAudits(rules);
|
|
7
|
+
const groups = transformRulesToGroups(rules, preset);
|
|
8
|
+
if (!shouldExpandForUrls(urls.length)) {
|
|
9
|
+
return { audits, groups, ruleIds };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
audits: expandAuditsForUrls(audits, urls),
|
|
13
|
+
groups: expandGroupsForUrls(groups, urls),
|
|
14
|
+
ruleIds,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=processing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processing.js","sourceRoot":"","sources":["../../../src/lib/processing.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,UAAU,sBAAsB,CACpC,IAAc,EACd,MAAiB;IAMjB,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAErD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC;IAED,OAAO;QACL,MAAM,EAAE,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC;QACzC,MAAM,EAAE,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC;QACzC,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
2
|
+
import { chromium } from 'playwright-core';
|
|
3
|
+
import { executeProcess, logger, pluralizeToken, stringifyError, } from '@code-pushup/utils';
|
|
4
|
+
import { toAuditOutputs } from './transform.js';
|
|
5
|
+
let browser;
|
|
6
|
+
let browserChecked = false;
|
|
7
|
+
export async function runAxeForUrl(url, ruleIds, timeout) {
|
|
8
|
+
try {
|
|
9
|
+
if (!browser) {
|
|
10
|
+
await ensureBrowserInstalled();
|
|
11
|
+
logger.debug('Launching Chromium browser...');
|
|
12
|
+
browser = await chromium.launch({ headless: true });
|
|
13
|
+
}
|
|
14
|
+
const context = await browser.newContext();
|
|
15
|
+
try {
|
|
16
|
+
const page = await context.newPage();
|
|
17
|
+
try {
|
|
18
|
+
await page.goto(url, {
|
|
19
|
+
waitUntil: 'networkidle',
|
|
20
|
+
timeout,
|
|
21
|
+
});
|
|
22
|
+
const axeBuilder = new AxeBuilder({ page });
|
|
23
|
+
// Use withRules() to include experimental/deprecated rules
|
|
24
|
+
if (ruleIds.length > 0) {
|
|
25
|
+
axeBuilder.withRules(ruleIds);
|
|
26
|
+
}
|
|
27
|
+
const results = await axeBuilder.analyze();
|
|
28
|
+
const incompleteCount = results.incomplete.length;
|
|
29
|
+
if (incompleteCount > 0) {
|
|
30
|
+
logger.warn(`Axe returned ${pluralizeToken('incomplete result', incompleteCount)} for ${url}`);
|
|
31
|
+
}
|
|
32
|
+
return toAuditOutputs(results, url);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await page.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await context.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.error(`Axe execution failed for ${url}: ${stringifyError(error)}`);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function closeBrowser() {
|
|
48
|
+
if (browser) {
|
|
49
|
+
await browser.close();
|
|
50
|
+
browser = undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function ensureBrowserInstalled() {
|
|
54
|
+
if (browserChecked) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
logger.debug('Checking Chromium browser installation...');
|
|
58
|
+
await executeProcess({
|
|
59
|
+
command: 'npx',
|
|
60
|
+
args: ['playwright-core', 'install', 'chromium'],
|
|
61
|
+
});
|
|
62
|
+
browserChecked = true;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=run-axe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-axe.js","sourceRoot":"","sources":["../../../../src/lib/runner/run-axe.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAgB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EACL,cAAc,EACd,MAAM,EACN,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,IAAI,OAA4B,CAAC;AACjC,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,OAAiB,EACjB,OAAe;IAEf,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,sBAAsB,EAAE,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAC9C,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;oBACnB,SAAS,EAAE,aAAa;oBACxB,OAAO;iBACR,CAAC,CAAC;gBAEH,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBAE5C,2DAA2D;gBAC3D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAChC,CAAC;gBAED,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;gBAE3C,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;gBAElD,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CACT,gBAAgB,cAAc,CAAC,mBAAmB,EAAE,eAAe,CAAC,QAAQ,GAAG,EAAE,CAClF,CAAC;gBACJ,CAAC;gBAED,OAAO,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YACtC,CAAC;oBAAS,CAAC;gBACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACrB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,4BAA4B,GAAG,KAAK,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,GAAG,SAAS,CAAC;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,sBAAsB;IACnC,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO;IACT,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAE1D,MAAM,cAAc,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,iBAAiB,EAAE,SAAS,EAAE,UAAU,CAAC;KACjD,CAAC,CAAC;IAEH,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { addIndex, logger, pluralizeToken, shouldExpandForUrls, stringifyError, } from '@code-pushup/utils';
|
|
2
|
+
import { closeBrowser, runAxeForUrl } from './run-axe.js';
|
|
3
|
+
export function createRunnerFunction(urls, ruleIds, timeout) {
|
|
4
|
+
return async (_runnerArgs) => {
|
|
5
|
+
const urlCount = urls.length;
|
|
6
|
+
const isSingleUrl = !shouldExpandForUrls(urlCount);
|
|
7
|
+
logger.info(`Running Axe accessibility checks for ${pluralizeToken('URL', urlCount)}...`);
|
|
8
|
+
try {
|
|
9
|
+
const allResults = await urls.reduce(async (prev, url, index) => {
|
|
10
|
+
const acc = await prev;
|
|
11
|
+
logger.debug(`Testing URL ${index + 1}/${urlCount}: ${url}`);
|
|
12
|
+
try {
|
|
13
|
+
const auditOutputs = await runAxeForUrl(url, ruleIds, timeout);
|
|
14
|
+
const processedOutputs = isSingleUrl
|
|
15
|
+
? auditOutputs
|
|
16
|
+
: auditOutputs.map(audit => ({
|
|
17
|
+
...audit,
|
|
18
|
+
slug: addIndex(audit.slug, index),
|
|
19
|
+
}));
|
|
20
|
+
return [...acc, ...processedOutputs];
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.warn(stringifyError(error));
|
|
24
|
+
return acc;
|
|
25
|
+
}
|
|
26
|
+
}, Promise.resolve([]));
|
|
27
|
+
const totalAuditCount = allResults.length;
|
|
28
|
+
if (totalAuditCount === 0) {
|
|
29
|
+
throw new Error(isSingleUrl
|
|
30
|
+
? 'Axe did not produce any results.'
|
|
31
|
+
: 'Axe failed to produce results for all URLs.');
|
|
32
|
+
}
|
|
33
|
+
logger.info(`Completed Axe accessibility checks with ${pluralizeToken('audit', totalAuditCount)}`);
|
|
34
|
+
return allResults;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await closeBrowser();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../../src/lib/runner/runner.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,QAAQ,EACR,MAAM,EACN,cAAc,EACd,mBAAmB,EACnB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,UAAU,oBAAoB,CAClC,IAAc,EACd,OAAiB,EACjB,OAAe;IAEf,OAAO,KAAK,EAAE,WAAwB,EAAyB,EAAE;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7B,MAAM,WAAW,GAAG,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAEnD,MAAM,CAAC,IAAI,CACT,wCAAwC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAC7E,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;gBAC9D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC;gBAEvB,MAAM,CAAC,KAAK,CAAC,eAAe,KAAK,GAAG,CAAC,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC,CAAC;gBAE7D,IAAI,CAAC;oBACH,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;oBAE/D,MAAM,gBAAgB,GAAG,WAAW;wBAClC,CAAC,CAAC,YAAY;wBACd,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;4BACzB,GAAG,KAAK;4BACR,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;yBAClC,CAAC,CAAC,CAAC;oBAER,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,gBAAgB,CAAC,CAAC;gBACvC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;oBACnC,OAAO,GAAG,CAAC;gBACb,CAAC;YACH,CAAC,EAAE,OAAO,CAAC,OAAO,CAAe,EAAE,CAAC,CAAC,CAAC;YAEtC,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,CAAC;YAE1C,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,WAAW;oBACT,CAAC,CAAC,kCAAkC;oBACpC,CAAC,CAAC,6CAA6C,CAClD,CAAC;YACJ,CAAC;YAED,MAAM,CAAC,IAAI,CACT,2CAA2C,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,CACtF,CAAC;YAEF,OAAO,UAAU,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,MAAM,YAAY,EAAE,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AxeResults } from 'axe-core';
|
|
2
|
+
import type { AuditOutputs } from '@code-pushup/models';
|
|
3
|
+
/**
|
|
4
|
+
* Transforms Axe results into audit outputs.
|
|
5
|
+
* Priority: violations > incomplete > passes > inapplicable
|
|
6
|
+
*/
|
|
7
|
+
export declare function toAuditOutputs({ passes, violations, incomplete, inapplicable }: AxeResults, url: string): AuditOutputs;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { formatIssueSeverities, getUrlIdentifier, pluralizeToken, truncateIssueMessage, } from '@code-pushup/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Transforms Axe results into audit outputs.
|
|
4
|
+
* Priority: violations > incomplete > passes > inapplicable
|
|
5
|
+
*/
|
|
6
|
+
export function toAuditOutputs({ passes, violations, incomplete, inapplicable }, url) {
|
|
7
|
+
const auditMap = new Map([
|
|
8
|
+
...inapplicable.map(res => [res.id, toAuditOutput(res, url, 1)]),
|
|
9
|
+
...passes.map(res => [res.id, toAuditOutput(res, url, 1)]),
|
|
10
|
+
...incomplete.map(res => [res.id, toAuditOutput(res, url, 0)]),
|
|
11
|
+
...violations.map(res => [res.id, toAuditOutput(res, url, 0)]),
|
|
12
|
+
]);
|
|
13
|
+
return Array.from(auditMap.values());
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* For failing audits (score 0), includes detailed issues with locations and severities.
|
|
17
|
+
* For passing audits (score 1), only includes element count.
|
|
18
|
+
*/
|
|
19
|
+
function toAuditOutput(result, url, score) {
|
|
20
|
+
const base = {
|
|
21
|
+
slug: result.id,
|
|
22
|
+
score,
|
|
23
|
+
value: result.nodes.length,
|
|
24
|
+
};
|
|
25
|
+
if (score === 0 && result.nodes.length > 0) {
|
|
26
|
+
const issues = result.nodes.map(node => toIssue(node, result, url));
|
|
27
|
+
return {
|
|
28
|
+
...base,
|
|
29
|
+
displayValue: formatIssueSeverities(issues),
|
|
30
|
+
details: { issues },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...base,
|
|
35
|
+
displayValue: pluralizeToken('element', result.nodes.length),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function formatSelector(selector) {
|
|
39
|
+
if (typeof selector === 'string') {
|
|
40
|
+
return selector;
|
|
41
|
+
}
|
|
42
|
+
return selector.join(' >> ');
|
|
43
|
+
}
|
|
44
|
+
function toIssue(node, result, url) {
|
|
45
|
+
const selector = formatSelector(node.target?.[0] || node.html);
|
|
46
|
+
const rawMessage = node.failureSummary || result.help;
|
|
47
|
+
const cleanedMessage = rawMessage.replace(/\s+/g, ' ').trim();
|
|
48
|
+
const message = `[\`${selector}\`] ${cleanedMessage} ([${getUrlIdentifier(url)}](${url}))`;
|
|
49
|
+
return {
|
|
50
|
+
message: truncateIssueMessage(message),
|
|
51
|
+
severity: impactToSeverity(node.impact),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function impactToSeverity(impact) {
|
|
55
|
+
switch (impact) {
|
|
56
|
+
case 'critical':
|
|
57
|
+
case 'serious':
|
|
58
|
+
return 'error';
|
|
59
|
+
case 'moderate':
|
|
60
|
+
return 'warning';
|
|
61
|
+
case 'minor':
|
|
62
|
+
case null:
|
|
63
|
+
case undefined:
|
|
64
|
+
return 'info';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../../src/lib/runner/transform.ts"],"names":[],"mappings":"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"}
|