diff --git a/.github/actions/auth/README.md b/.github/actions/auth/README.md index b857e0af..a6fb9484 100644 --- a/.github/actions/auth/README.md +++ b/.github/actions/auth/README.md @@ -21,6 +21,15 @@ Authenticate with Playwright and save session state. Supports HTTP Basic and for > [!IMPORTANT] > Don’t put passwords in your workflow as plain text; instead reference a [repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository). +#### `ignore_https_errors` + +**Optional** Bool - whether to ignore HTTPS errors when logging in to hosts with self-signed certificates. + +[!WARNING] +Enabling this disables TLS certificate validation for the authentication step itself. If an attacker can intercept traffic, they can present an untrusted certificate and capture submitted credentials (including HTTP Basic auth and form passwords) and resulting session cookies/storage. + +Use this only in trusted test/staging environments. For shared workflows, prefer a separate explicit opt-in for auth flows (for example, `auth_ignore_https_errors`) so certificate checks are not unintentionally disabled during credentialed requests. + ### Outputs #### `auth_context` diff --git a/.github/actions/auth/action.yml b/.github/actions/auth/action.yml index c68b3331..fbf913cb 100644 --- a/.github/actions/auth/action.yml +++ b/.github/actions/auth/action.yml @@ -11,6 +11,10 @@ inputs: password: description: "Password" required: true + ignore_https_errors: + description: "Whether to ignore HTTPS errors when logging in to hosts with self-signed certificates" + required: false + default: "false" outputs: auth_context: diff --git a/.github/actions/auth/src/index.ts b/.github/actions/auth/src/index.ts index 68ff4e82..01f08eeb 100644 --- a/.github/actions/auth/src/index.ts +++ b/.github/actions/auth/src/index.ts @@ -15,6 +15,7 @@ export default async function () { const username = core.getInput('username', {required: true}) const password = core.getInput('password', {required: true}) core.setSecret(password) + const ignoreHttpsErrors = core.getInput('ignore_https_errors', {required: false}).toLowerCase() === 'true' // Launch a headless browser browser = await playwright.chromium.launch({ @@ -27,6 +28,7 @@ export default async function () { username, password, }, + ...(ignoreHttpsErrors ? {ignoreHTTPSErrors: true} : {}), }) page = await context.newPage() diff --git a/.github/actions/file/src/generateIssueBody.ts b/.github/actions/file/src/generateIssueBody.ts index ad703d51..b9f234af 100644 --- a/.github/actions/file/src/generateIssueBody.ts +++ b/.github/actions/file/src/generateIssueBody.ts @@ -52,6 +52,7 @@ ${standardsLine} const body = `${categoryNotice}## What ${describeFinding(finding)} +${finding.ruleId ? `**Rule:** \`${finding.scannerType} rule: ${finding.ruleId}\`\n\n` : ''} ${screenshotSection ?? ''} To fix this, ${finding.solutionShort}. ${solutionLong ? `\nSpecifically:\n\n${solutionLong}` : ''} diff --git a/.github/actions/file/src/openIssue.ts b/.github/actions/file/src/openIssue.ts index 67128d08..f22a4a35 100644 --- a/.github/actions/file/src/openIssue.ts +++ b/.github/actions/file/src/openIssue.ts @@ -1,11 +1,28 @@ import type {Octokit} from '@octokit/core' import type {Finding} from './types.d.js' import {generateIssueBody} from './generateIssueBody.js' +import {createHash} from 'node:crypto' import * as url from 'node:url' const URL = url.URL /** Max length for GitHub issue titles */ const GITHUB_ISSUE_TITLE_MAX_LENGTH = 256 +/** Max length for GitHub label names */ +const GITHUB_LABEL_NAME_MAX_LENGTH = 50 +const RULE_LABEL_HASH_LENGTH = 4 + +function createRuleLabel(scannerType: string, ruleId: string): string { + const label = `${scannerType} rule: ${ruleId}` + const characters = Array.from(label) + + if (characters.length <= GITHUB_LABEL_NAME_MAX_LENGTH) { + return label + } + + const hash = createHash('sha256').update(label).digest('hex').slice(0, RULE_LABEL_HASH_LENGTH) + const prefixLength = GITHUB_LABEL_NAME_MAX_LENGTH - hash.length - 1 + return `${characters.slice(0, prefixLength).join('')}-${hash}` +} /** * Truncates text to a maximum length, adding an ellipsis if truncated. @@ -25,7 +42,7 @@ export async function openIssue(octokit: Octokit, repoWithOwner: string, finding const labels = [`${primary.scannerType}-scanning-issue`] // Only include a ruleId label when it's defined if (primary.ruleId) { - labels.push(`${primary.scannerType} rule: ${primary.ruleId}`) + labels.push(createRuleLabel(primary.scannerType, primary.ruleId)) } // Flag non-WCAG findings so they can be filtered or triaged separately if (primary.category && primary.category !== 'wcag') { diff --git a/.github/actions/file/tests/generateIssueBody.test.ts b/.github/actions/file/tests/generateIssueBody.test.ts index 60a06db7..dfa78e64 100644 --- a/.github/actions/file/tests/generateIssueBody.test.ts +++ b/.github/actions/file/tests/generateIssueBody.test.ts @@ -24,12 +24,26 @@ describe('generateIssueBody', () => { const body = generateIssueBody(baseFinding, 'github/accessibility-scanner') expect(body).toContain('## What') + expect(body).toContain('**Rule:** `axe rule: color-contrast`') expect(body).toContain('## Acceptance Criteria') expect(body).toContain('The specific violation reported in this issue is no longer reproducible.') expect(body).toContain('The fix MUST meet WCAG 2.2 guidelines OR') expect(body).not.toContain('Specifically:') }) + it('omits the rule line when the finding has no ruleId', () => { + const body = generateIssueBody(findingWithEmptyOptionalFields, 'github/accessibility-scanner') + + expect(body).not.toContain('**Rule:**') + }) + + it('includes the full rule ID when its label would be shortened', () => { + const ruleId = 'keyboard-accessible/scrollable-region-with-an-extra-long-description' + const body = generateIssueBody({...baseFinding, ruleId}, 'github/accessibility-scanner') + + expect(body).toContain(`**Rule:** \`axe rule: ${ruleId}\``) + }) + it('formats solutionLong lines into bullets while preserving Fix any/Fix all lines', () => { const body = generateIssueBody( { diff --git a/.github/actions/file/tests/openIssue.test.ts b/.github/actions/file/tests/openIssue.test.ts index 890f8438..af4b54b4 100644 --- a/.github/actions/file/tests/openIssue.test.ts +++ b/.github/actions/file/tests/openIssue.test.ts @@ -1,4 +1,5 @@ import {describe, it, expect, vi} from 'vitest' +import {createHash} from 'node:crypto' // Mock generateIssueBody so we can inspect what screenshotRepo is passed vi.mock('../src/generateIssueBody.js', () => ({ @@ -65,6 +66,33 @@ describe('openIssue', () => { ) }) + it('shortens long rule labels with a deterministic hash suffix', async () => { + const octokit = mockOctokit() + const ruleId = 'keyboard-accessible/scrollable-region-with-an-extra-long-description' + const fullLabel = `axe rule: ${ruleId}` + await openIssue(octokit, 'org/repo', [{...baseFinding, ruleId}]) + + const labels = octokit.request.mock.calls[0][1].labels + const ruleLabel = labels[1] + const hash = createHash('sha256').update(fullLabel).digest('hex').slice(0, 4) + + expect(ruleLabel).toBe(`${fullLabel.slice(0, 45)}-${hash}`) + expect(Array.from(ruleLabel)).toHaveLength(50) + }) + + it('produces distinct labels for different long rule IDs', async () => { + const firstOctokit = mockOctokit() + const secondOctokit = mockOctokit() + const commonPrefix = 'keyboard-accessible/scrollable-region-with-a-long-description-' + await openIssue(firstOctokit, 'org/repo', [{...baseFinding, ruleId: `${commonPrefix}first`}]) + await openIssue(secondOctokit, 'org/repo', [{...baseFinding, ruleId: `${commonPrefix}second`}]) + + const firstLabel = firstOctokit.request.mock.calls[0][1].labels[1] + const secondLabel = secondOctokit.request.mock.calls[0][1].labels[1] + expect(firstLabel.slice(0, 45)).toBe(secondLabel.slice(0, 45)) + expect(firstLabel).not.toBe(secondLabel) + }) + it('adds a category label for non-WCAG findings', async () => { const octokit = mockOctokit() await openIssue(octokit, 'org/repo', [{...baseFinding, category: 'best-practice'}]) diff --git a/.github/actions/find/README.md b/.github/actions/find/README.md index 0a0be1f1..651babf8 100644 --- a/.github/actions/find/README.md +++ b/.github/actions/find/README.md @@ -35,6 +35,10 @@ configuration option. [`colorScheme`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-color-scheme) configuration option. +#### `ignore_https_errors` + +**Optional** Bool - whether to ignore HTTPS errors when scanning URLs with self-signed certificates. + #### `include_screenshots` **Optional** Bool - whether to capture screenshots of scanned pages and include links to them in the issue diff --git a/.github/actions/find/action.yml b/.github/actions/find/action.yml index 6662d863..9edff453 100644 --- a/.github/actions/find/action.yml +++ b/.github/actions/find/action.yml @@ -25,6 +25,11 @@ inputs: color_scheme: description: 'Playwright colorScheme setting: https://playwright.dev/docs/api/class-browser#browser-new-context-option-color-scheme' required: false + ignore_https_errors: + description: 'Whether to ignore HTTPS errors when scanning URLs with self-signed certificates' + required: false + default: 'false' + outputs: findings_file: description: 'Path to a JSON file containing the list of potential accessibility gaps' diff --git a/.github/actions/find/src/findForUrl.ts b/.github/actions/find/src/findForUrl.ts index 14c4e30e..ebbf0b54 100644 --- a/.github/actions/find/src/findForUrl.ts +++ b/.github/actions/find/src/findForUrl.ts @@ -23,6 +23,7 @@ export async function findForUrl( includeScreenshots: boolean = false, reducedMotion?: ReducedMotionPreference, colorScheme?: ColorSchemePreference, + ignoreHttpsErrors: boolean = false, ): Promise { const {url, excludeSelectors, waitForSelectors} = urlConfig const browser = await playwright.chromium.launch({ @@ -33,6 +34,7 @@ export async function findForUrl( ...(authContext?.toPlaywrightBrowserContextOptions() ?? {}), ...(reducedMotion ? {reducedMotion} : {}), ...(colorScheme ? {colorScheme} : {}), + ...(ignoreHttpsErrors ? {ignoreHTTPSErrors: true} : {}), } let context: playwright.BrowserContext | undefined try { diff --git a/.github/actions/find/src/index.ts b/.github/actions/find/src/index.ts index 138307c0..836161a2 100644 --- a/.github/actions/find/src/index.ts +++ b/.github/actions/find/src/index.ts @@ -11,6 +11,7 @@ export default async function () { const urls = loadUrls({urlConfigs}) const reducedMotion = loadReducedMotion() const colorScheme = loadColorScheme() + const ignoreHttpsErrors = loadIgnoreHttpsErrors() const actualUrls = urlConfigs || urls || [] @@ -22,7 +23,14 @@ export default async function () { for (const urlConfig of actualUrls) { const {url} = urlConfig core.info(`Preparing to scan ${url}`) - const findingsForUrl = await findForUrl(urlConfig, authContext, includeScreenshots, reducedMotion, colorScheme) + const findingsForUrl = await findForUrl( + urlConfig, + authContext, + includeScreenshots, + reducedMotion, + colorScheme, + ignoreHttpsErrors, + ) if (findingsForUrl.length === 0) { core.info(`No accessibility gaps were found on ${url}`) continue @@ -108,3 +116,7 @@ function loadColorScheme() { return colorSchemeInput as ColorSchemePreference } + +function loadIgnoreHttpsErrors() { + return core.getInput('ignore_https_errors', {required: false}).toLowerCase() === 'true' +} diff --git a/.github/actions/find/tests/findForUrl.test.ts b/.github/actions/find/tests/findForUrl.test.ts index a8de52ed..387e2360 100644 --- a/.github/actions/find/tests/findForUrl.test.ts +++ b/.github/actions/find/tests/findForUrl.test.ts @@ -392,4 +392,35 @@ describe('findForUrl', () => { expect(await categoryFor(['wcag2a', 'experimental'])).toBe('experimental') }) }) + + describe('when ignoreHttpsErrors is enabled', () => { + it('forwards ignoreHTTPSErrors to the browser context', async () => { + actionInput = '' + clearAll() + + await findForUrl({url: 'test.com'}, undefined, false, undefined, undefined, true) + + expect(playwrightMocks.browserNewContext).toHaveBeenCalledWith({ignoreHTTPSErrors: true}) + }) + }) + + describe('when ignoreHttpsErrors is not enabled', () => { + it('does not forward ignoreHTTPSErrors by default', async () => { + actionInput = '' + clearAll() + + await findForUrl({url: 'test.com'}) + + expect(playwrightMocks.browserNewContext).toHaveBeenCalledWith({}) + }) + + it('does not forward ignoreHTTPSErrors when explicitly disabled', async () => { + actionInput = '' + clearAll() + + await findForUrl({url: 'test.com'}, undefined, false, undefined, undefined, false) + + expect(playwrightMocks.browserNewContext).toHaveBeenCalledWith({}) + }) + }) }) diff --git a/.github/actions/find/tests/index.test.ts b/.github/actions/find/tests/index.test.ts index f5d760fc..13cf72f8 100644 --- a/.github/actions/find/tests/index.test.ts +++ b/.github/actions/find/tests/index.test.ts @@ -43,7 +43,7 @@ describe('url_configs', () => { await find() - expect(mocks.findForUrl).toHaveBeenCalledWith(urlConfig, expect.anything(), false, undefined, undefined) + expect(mocks.findForUrl).toHaveBeenCalledWith(urlConfig, expect.anything(), false, undefined, undefined, false) }) it('rejects waitForSelectors values that are not arrays of strings', async () => { diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 446e8c84..a880f2b3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24 cache: npm diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f90a8505..6ca74608 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Ruby - uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 + uses: ruby/setup-ruby@26e5b7d197ebf60545ee9667bcbace0be11ccaec with: ruby-version: '3.4' bundler-cache: true @@ -80,7 +80,7 @@ jobs: repository: ${{ env.TESTING_REPOSITORY }} token: ${{ secrets.GH_TOKEN }} cache_key: ${{ steps.cache_key.outputs.cache_key }} - scans: '["axe", "reflow-scan"]' + scans: '["axe", "reflow-scan", "accesslint"]' - name: Retrieve cached results uses: ./.github/actions/gh-cache/restore diff --git a/README.md b/README.md index 5fac654a..f374ec4c 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ jobs: # dry_run: false # Optional: Set to true to scan and log what would be filed without creating/closing issues or writing the cache # reduced_motion: no-preference # Optional: Playwright reduced motion configuration option # color_scheme: light # Optional: Playwright color scheme configuration option + # ignore_https_errors: false # Optional: Set to true to ignore HTTPS errors when scanning URLs with self-signed certificates # scans: '["axe","accesslint","reflow-scan"]' # Optional: An array of scans (or plugins) to be performed. Built-in engines are 'axe' and 'accesslint'; any other entry is a plugin name. If not provided, only Axe will be performed. # url_configs: '[{"url":"https://example.com","excludeSelectors":["iframe","#widget"],"waitForSelectors":["#app","[data-ready]"]}]' # Optional: Per-URL config with CSS selectors to exclude from Axe or wait for before scanning. When provided, takes precedence over 'urls'. ``` @@ -137,6 +138,7 @@ Trigger the workflow manually or automatically based on your configuration. The | `file_experimental_issues` | No | Whether to file issues for experimental findings (checks that are not yet stable). Set to `false` to suppress new experimental issues; existing ones are left untouched. Default: `true` | `false` | | `reduced_motion` | No | Playwright `reducedMotion` setting for scan contexts. Allowed values: `reduce`, `no-preference` | `reduce` | | `color_scheme` | No | Playwright `colorScheme` setting for scan contexts. Allowed values: `light`, `dark`, `no-preference` | `dark` | +| `ignore_https_errors` | No | Whether to ignore HTTPS errors when scanning URLs with self-signed certificates. Default: `false` | `true` | | `scans` | No | An array of scans (or plugins) to be performed. Built-in engines are `axe` and `accesslint`; any other entry is treated as a plugin name. If not provided, only Axe will be performed. | `'["axe", "accesslint", ...other plugins]'` | | `dry_run` | No | When `true`, scan and log the issues that _would_ be filed without opening, closing, reopening, or assigning any issues — and without writing to the `gh-cache` branch. Useful for safely previewing results. Default: `false` | `true` | | `url_configs` | No | A stringified JSON array of URL config objects. Each object must have a `url` field and may have `excludeSelectors` (selectors to exclude from Axe) and `waitForSelectors` (selectors that must become visible within 30 seconds before scanning). When provided, takes precedence over the `urls` input. | `'[{"url":"https://example.com","excludeSelectors":["iframe","#widget"],"waitForSelectors":["#app"]}]'` | diff --git a/action.yml b/action.yml index 56b4160b..8f305a3a 100644 --- a/action.yml +++ b/action.yml @@ -63,6 +63,10 @@ inputs: color_scheme: description: 'Playwright colorScheme setting: https://playwright.dev/docs/api/class-browser#browser-new-context-option-color-scheme' required: false + ignore_https_errors: + description: 'Whether to ignore HTTPS errors when scanning URLs with self-signed certificates' + required: false + default: 'false' scans: description: "Stringified JSON array of scans to perform. Core engines and local plugins use string names. Allowlisted NPM plugins use an object with 'name', 'package', and optional 'version'. If not provided, only Axe will be performed" required: false @@ -123,6 +127,7 @@ runs: login_url: ${{ inputs.login_url }} username: ${{ inputs.username }} password: ${{ inputs.password }} + ignore_https_errors: ${{ inputs.ignore_https_errors }} - name: Find id: find uses: ./../../_actions/github/accessibility-scanner/current/.github/actions/find @@ -133,6 +138,7 @@ runs: include_screenshots: ${{ inputs.include_screenshots }} reduced_motion: ${{ inputs.reduced_motion }} color_scheme: ${{ inputs.color_scheme }} + ignore_https_errors: ${{ inputs.ignore_https_errors }} scans: ${{ inputs.scans }} - name: File id: file diff --git a/package-lock.json b/package-lock.json index bf4c2478..25551af5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -480,9 +480,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", - "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", + "version": "0.154.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.154.0.tgz", + "integrity": "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==", "dev": true, "license": "MIT", "peer": true, @@ -491,9 +491,9 @@ } }, "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", - "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.14.tgz", + "integrity": "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==", "cpu": [ "arm" ], @@ -509,9 +509,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", - "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.14.tgz", + "integrity": "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ==", "cpu": [ "arm64" ], @@ -527,9 +527,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", - "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.14.tgz", + "integrity": "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw==", "cpu": [ "arm64" ], @@ -545,9 +545,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", - "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.14.tgz", + "integrity": "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA==", "cpu": [ "x64" ], @@ -563,9 +563,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", - "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.14.tgz", + "integrity": "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA==", "cpu": [ "x64" ], @@ -581,9 +581,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", - "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.14.tgz", + "integrity": "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg==", "cpu": [ "arm" ], @@ -599,9 +599,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", - "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.14.tgz", + "integrity": "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ==", "cpu": [ "arm64" ], @@ -620,9 +620,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", - "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.14.tgz", + "integrity": "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg==", "cpu": [ "arm64" ], @@ -641,9 +641,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", - "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.14.tgz", + "integrity": "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q==", "cpu": [ "ppc64" ], @@ -662,9 +662,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", - "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.14.tgz", + "integrity": "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A==", "cpu": [ "s390x" ], @@ -683,9 +683,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", - "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.14.tgz", + "integrity": "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg==", "cpu": [ "x64" ], @@ -704,9 +704,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", - "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.14.tgz", + "integrity": "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg==", "cpu": [ "x64" ], @@ -725,9 +725,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", - "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.14.tgz", + "integrity": "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q==", "cpu": [ "arm64" ], @@ -743,9 +743,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", - "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.14.tgz", + "integrity": "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA==", "cpu": [ "arm64" ], @@ -761,9 +761,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", - "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.14.tgz", + "integrity": "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ==", "cpu": [ "x64" ], @@ -2025,9 +2025,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -2157,9 +2157,9 @@ } }, "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", "dev": true, "funding": [ { @@ -2178,9 +2178,9 @@ "license": "MIT", "peer": true, "dependencies": { - "nanoid": "^3.3.18", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2223,14 +2223,14 @@ } }, "node_modules/rolldown": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", - "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.14.tgz", + "integrity": "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@oxc-project/types": "=0.148.0", + "@oxc-project/types": "=0.154.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2240,21 +2240,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.7", - "@rolldown/binding-android-arm64": "1.2.7", - "@rolldown/binding-darwin-arm64": "1.2.7", - "@rolldown/binding-darwin-x64": "1.2.7", - "@rolldown/binding-freebsd-x64": "1.2.7", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", - "@rolldown/binding-linux-arm64-gnu": "1.2.7", - "@rolldown/binding-linux-arm64-musl": "1.2.7", - "@rolldown/binding-linux-ppc64-gnu": "1.2.7", - "@rolldown/binding-linux-s390x-gnu": "1.2.7", - "@rolldown/binding-linux-x64-gnu": "1.2.7", - "@rolldown/binding-linux-x64-musl": "1.2.7", - "@rolldown/binding-openharmony-arm64": "1.2.7", - "@rolldown/binding-win32-arm64-msvc": "1.2.7", - "@rolldown/binding-win32-x64-msvc": "1.2.7" + "@rolldown/binding-android-arm-eabi": "1.2.14", + "@rolldown/binding-android-arm64": "1.2.14", + "@rolldown/binding-darwin-arm64": "1.2.14", + "@rolldown/binding-darwin-x64": "1.2.14", + "@rolldown/binding-freebsd-x64": "1.2.14", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", + "@rolldown/binding-linux-arm64-gnu": "1.2.14", + "@rolldown/binding-linux-arm64-musl": "1.2.14", + "@rolldown/binding-linux-ppc64-gnu": "1.2.14", + "@rolldown/binding-linux-s390x-gnu": "1.2.14", + "@rolldown/binding-linux-x64-gnu": "1.2.14", + "@rolldown/binding-linux-x64-musl": "1.2.14", + "@rolldown/binding-openharmony-arm64": "1.2.14", + "@rolldown/binding-win32-arm64-msvc": "1.2.14", + "@rolldown/binding-win32-x64-msvc": "1.2.14" } }, "node_modules/semver": { @@ -2438,9 +2438,9 @@ } }, "node_modules/undici": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz", - "integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", + "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", "dev": true, "license": "MIT", "engines": { @@ -2472,17 +2472,17 @@ } }, "node_modules/vite": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", - "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.26", - "rolldown": "~1.2.4", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "bin": { @@ -2499,7 +2499,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/tests/site-with-errors.test.ts b/tests/site-with-errors.test.ts index 59c66146..ec0003a9 100644 --- a/tests/site-with-errors.test.ts +++ b/tests/site-with-errors.test.ts @@ -128,12 +128,14 @@ describe('site-with-errors', () => { }, ] // Check that: - // - every expected object exists (no more and no fewer), and + // - every expected Axe and reflow-scan object exists (no more and no fewer), and // - each object has all fields, and // - field values match expectations exactly // A specific order is _not_ enforced. - expect(actual).toHaveLength(expected.length) - expect(actual).toEqual(expect.arrayContaining(expected)) + const actualWithoutAccesslint = actual.filter(({scannerType}) => scannerType !== 'accesslint') + expect(actualWithoutAccesslint).toHaveLength(expected.length) + expect(actualWithoutAccesslint).toEqual(expect.arrayContaining(expected)) + expect(actual.some(({scannerType}) => scannerType === 'accesslint')).toBe(true) }) it('GITHUB_TOKEN environment variable is set', () => { @@ -164,7 +166,9 @@ describe('site-with-errors', () => { }) it('issues exist and have expected title, state, and assignee', async () => { - const actualTitles = issues.map(({title}) => title) + const actualTitles = issues + .filter((_, index) => results[index].findings[0].scannerType !== 'accesslint') + .map(({title}) => title) const expectedTitles = [ 'Accessibility issue: Elements must meet minimum color contrast ratio thresholds on /', 'Accessibility issue: Page should contain a level-one heading on /',