Skip to content

Commit f1fc866

Browse files
committed
feat(plugin-axe): add cacheable browser installation
1 parent 0a4113e commit f1fc866

File tree

7 files changed

+48
-17
lines changed

7 files changed

+48
-17
lines changed

e2e/plugin-axe-e2e/tests/__snapshots__/collect.e2e.test.ts.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ exports[`PLUGIN collect report with axe-plugin NPM package > should run plugin o
497497
"details": {
498498
"issues": [
499499
{
500-
"message": "[\`body > button\`] Fix any of the following: Element does not have inner text that is visible to screen readers aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute Element does not have an implicit (wrapped) <label> Element does not have an explicit <label> Element's default semantics were not overridden with role="none" or role="presentation" ([/Users/hanna.skryl/Dev/cli/tmp/e2e/plugin-axe-e2e/__test__/collect/default-setup/index.html](file:///<TEST_DIR>/index.html))",
500+
"message": "[\`body > button\`] Fix any of the following: Element does not have inner text that is visible to screen readers aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute Element does not have an implicit (wrapped) <label> Element does not have an explicit <label> Element's default semantics were not overridden with role="none" or role="presentation" ([/<TEST_DIR>/index.html](file:///<TEST_DIR>/index.html))",
501501
"severity": "error",
502502
},
503503
],
@@ -523,7 +523,7 @@ exports[`PLUGIN collect report with axe-plugin NPM package > should run plugin o
523523
"details": {
524524
"issues": [
525525
{
526-
"message": "[\`.low-contrast\`] Fix any of the following: Element has insufficient color contrast of 1.57 (foreground color: #777777, background color: #999999, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1 ([/Users/hanna.skryl/Dev/cli/tmp/e2e/plugin-axe-e2e/__test__/collect/default-setup/index.html](file:///<TEST_DIR>/index.html))",
526+
"message": "[\`.low-contrast\`] Fix any of the following: Element has insufficient color contrast of 1.57 (foreground color: #777777, background color: #999999, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1 ([/<TEST_DIR>/index.html](file:///<TEST_DIR>/index.html))",
527527
"severity": "error",
528528
},
529529
],
@@ -612,7 +612,7 @@ exports[`PLUGIN collect report with axe-plugin NPM package > should run plugin o
612612
"details": {
613613
"issues": [
614614
{
615-
"message": "[\`div[role="button"]\`] Fix any of the following: Invalid ARIA attribute name: aria-invalid-attribute ([/Users/hanna.skryl/Dev/cli/tmp/e2e/plugin-axe-e2e/__test__/collect/default-setup/index.html](file:///<TEST_DIR>/index.html))",
615+
"message": "[\`div[role="button"]\`] Fix any of the following: Invalid ARIA attribute name: aria-invalid-attribute ([/<TEST_DIR>/index.html](file:///<TEST_DIR>/index.html))",
616616
"severity": "error",
617617
},
618618
],
@@ -629,7 +629,7 @@ exports[`PLUGIN collect report with axe-plugin NPM package > should run plugin o
629629
"details": {
630630
"issues": [
631631
{
632-
"message": "[\`img\`] Fix any of the following: Element does not have an alt attribute aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute Element's default semantics were not overridden with role="none" or role="presentation" ([/Users/hanna.skryl/Dev/cli/tmp/e2e/plugin-axe-e2e/__test__/collect/default-setup/index.html](file:///<TEST_DIR>/index.html))",
632+
"message": "[\`img\`] Fix any of the following: Element does not have an alt attribute aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute Element's default semantics were not overridden with role="none" or role="presentation" ([/<TEST_DIR>/index.html](file:///<TEST_DIR>/index.html))",
633633
"severity": "error",
634634
},
635635
],
@@ -646,7 +646,7 @@ exports[`PLUGIN collect report with axe-plugin NPM package > should run plugin o
646646
"details": {
647647
"issues": [
648648
{
649-
"message": "[\`a\`] Fix all of the following: Element is in tab order and does not have accessible text Fix any of the following: Element does not have text that is visible to screen readers aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute ([/Users/hanna.skryl/Dev/cli/tmp/e2e/plugin-axe-e2e/__test__/collect/default-setup/index.html](file:///<TEST_DIR>/index.html))",
649+
"message": "[\`a\`] Fix all of the following: Element is in tab order and does not have accessible text Fix any of the following: Element does not have text that is visible to screen readers aria-label attribute does not exist or is empty aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty Element has no title attribute ([/<TEST_DIR>/index.html](file:///<TEST_DIR>/index.html))",
650650
"severity": "error",
651651
},
652652
],

e2e/plugin-axe-e2e/tests/collect.e2e.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ function sanitizeReportPaths(report: Report): Report {
1515
// Convert to JSON, replace paths, and parse back
1616
const reportJson = JSON.stringify(report);
1717
const sanitized = reportJson.replace(
18-
/file:\/{3}.+?\/index\.html/g,
19-
'file:///<TEST_DIR>/index.html',
18+
/\/(?:[^/\s"]+\/)+index\.html/g,
19+
'/<TEST_DIR>/index.html',
2020
);
21-
return JSON.parse(sanitized) as Report;
21+
return JSON.parse(sanitized);
2222
}
2323

2424
describe('PLUGIN collect report with axe-plugin NPM package', () => {

packages/plugin-axe/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ The plugin runs accessibility audits on web pages using [axe-core](https://githu
4646

4747
4. Run the CLI with `npx code-pushup collect` and view or upload the report (refer to [CLI docs](../cli/README.md)).
4848

49+
> **Note:** The plugin uses Playwright to run tests in Chromium. The browser will be installed automatically if not already present.
50+
4951
## Configuration
5052

5153
```ts

packages/plugin-axe/package.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,14 @@
4040
"access": "public"
4141
},
4242
"type": "module",
43-
"scripts": {
44-
"postinstall": "playwright-core install chromium --with-deps || playwright-core install chromium"
45-
},
4643
"dependencies": {
4744
"@axe-core/playwright": "^4.11.0",
4845
"@code-pushup/models": "0.86.0",
4946
"@code-pushup/utils": "0.86.0",
5047
"axe-core": "^4.11.0",
48+
"playwright-core": "^1.56.1",
5149
"zod": "^4.1.12"
5250
},
53-
"peerDependencies": {
54-
"playwright-core": "^1.56.1"
55-
},
5651
"files": [
5752
"src",
5853
"!**/*.tsbuildinfo"

packages/plugin-axe/project.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66
"tags": ["scope:plugin", "type:feature", "publishable"],
77
"// targets": "to see all targets run: nx show project plugin-axe --web",
88
"targets": {
9-
"build": {},
9+
"install-browser": {
10+
"command": "playwright-core install chromium",
11+
"cache": true,
12+
"inputs": [
13+
"sharedGlobals",
14+
{ "runtime": "playwright-core install chromium --dry-run" }
15+
]
16+
},
17+
"build": {
18+
"dependsOn": ["^build", "install-browser"]
19+
},
1020
"lint": {},
1121
"unit-test": {}
1222
}

packages/plugin-axe/src/lib/runner/run-axe.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import AxeBuilder from '@axe-core/playwright';
22
import { type Browser, chromium } from 'playwright-core';
33
import type { AuditOutputs } from '@code-pushup/models';
4-
import { logger, pluralizeToken, stringifyError } from '@code-pushup/utils';
4+
import {
5+
executeProcess,
6+
logger,
7+
pluralizeToken,
8+
stringifyError,
9+
} from '@code-pushup/utils';
510
import { toAuditOutputs } from './transform.js';
611

712
let browser: Browser | undefined;
13+
let browserChecked = false;
814

915
export async function runAxeForUrl(
1016
url: string,
@@ -13,6 +19,7 @@ export async function runAxeForUrl(
1319
): Promise<AuditOutputs> {
1420
try {
1521
if (!browser) {
22+
await ensureBrowserInstalled();
1623
logger.debug('Launching Chromium browser...');
1724
browser = await chromium.launch({ headless: true });
1825
}
@@ -63,3 +70,18 @@ export async function closeBrowser(): Promise<void> {
6370
browser = undefined;
6471
}
6572
}
73+
74+
async function ensureBrowserInstalled(): Promise<void> {
75+
if (browserChecked) {
76+
return;
77+
}
78+
79+
logger.debug('Checking Chromium browser installation...');
80+
81+
await executeProcess({
82+
command: 'npx',
83+
args: ['playwright-core', 'install', 'chromium'],
84+
});
85+
86+
browserChecked = true;
87+
}

project.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
},
2727
"code-pushup-jsdocs": {},
2828
"code-pushup-typescript": {},
29-
"code-pushup-axe": {},
29+
"code-pushup-axe": {
30+
"dependsOn": ["^install-browser"]
31+
},
3032
"code-pushup": {
3133
"dependsOn": ["code-pushup-*"],
3234
"executor": "nx:run-commands",

0 commit comments

Comments
 (0)