diff --git a/packages/plugin-axe/src/lib/runner/run-axe.ts b/packages/plugin-axe/src/lib/runner/run-axe.ts index 9f54eb23d..7aba6ea82 100644 --- a/packages/plugin-axe/src/lib/runner/run-axe.ts +++ b/packages/plugin-axe/src/lib/runner/run-axe.ts @@ -1,4 +1,6 @@ -import AxeBuilder from '@axe-core/playwright'; +import { AxeBuilder } from '@axe-core/playwright'; +import { createRequire } from 'node:module'; +import path from 'node:path'; import { type Browser, chromium } from 'playwright-core'; import type { AuditOutputs } from '@code-pushup/models'; import { @@ -9,8 +11,10 @@ import { } from '@code-pushup/utils'; import { toAuditOutputs } from './transform.js'; +/* eslint-disable functional/no-let */ let browser: Browser | undefined; let browserChecked = false; +/* eslint-enable functional/no-let */ export async function runAxeForUrl( url: string, @@ -71,6 +75,12 @@ export async function closeBrowser(): Promise { } } +/** + * Ensures Chromium browser binary is installed before running accessibility audits. + * + * Uses Node's module resolution and npm's bin specification to locate playwright-core CLI, + * working reliably with all package managers (npm, pnpm, yarn). + */ async function ensureBrowserInstalled(): Promise { if (browserChecked) { return; @@ -78,9 +88,14 @@ async function ensureBrowserInstalled(): Promise { logger.debug('Checking Chromium browser installation...'); + const require = createRequire(import.meta.url); + const pkgPath = require.resolve('playwright-core/package.json'); + const pkg = require(pkgPath); + const cliPath = path.join(path.dirname(pkgPath), pkg.bin['playwright-core']); + await executeProcess({ - command: 'npx', - args: ['playwright-core', 'install', 'chromium'], + command: 'node', + args: [cliPath, 'install', 'chromium'], }); browserChecked = true;