Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support shadow dom selectors #619

Merged
merged 6 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
],
"dependencies": {
"@axe-core/webdriverjs": "^4.5.2",
"axe-core": "^4.5.2",
"axe-core": "^4.6.0",
"chromedriver": "^106.0.1",
"colors": "^1.4.0",
"commander": "^9.4.1",
Expand Down
24 changes: 11 additions & 13 deletions packages/playwright/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/playwright/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@
"prepare": "npm run build"
},
"dependencies": {
"axe-core": "^4.5.2"
"axe-core": "^4.6.0"
},
"devDependencies": {
"@types/chai": "^4.3.3",
"@types/express": "^4.17.14",
"@types/mocha": "^10.0.0",
"@types/node": "^18.8.3",
"@types/test-listen": "^1.1.0",
"axe-test-fixtures": "github:dequelabs/axe-test-fixtures",
"axe-test-fixtures": "github:dequelabs/axe-test-fixtures#v1",
"chai": "^4.3.6",
"express": "^4.18.2",
"mocha": "^10.0.0",
Expand Down
18 changes: 11 additions & 7 deletions packages/playwright/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import type {
RunOptions,
AxeResults,
SerialContextObject,
PartialResults
PartialResults,
ContextObject,
SerialSelectorList,
SerialSelector,
SerialFrameSelector
} from 'axe-core';
import { source } from 'axe-core';
import { normalizeContext, analyzePage } from './utils';
Expand All @@ -16,11 +20,12 @@ import {
axeShadowSelect
} from './browser';
import AxePartialRunner from './AxePartialRunner';
type NewContextObject = ContextObject | SerialContextObject;

export default class AxeBuilder {
private page: Page;
private includes: string[][];
private excludes: string[][];
private includes: SerialSelectorList;
private excludes: SerialSelectorList;
private option: RunOptions;
private source: string;
private legacyMode = false;
Expand All @@ -43,8 +48,7 @@ export default class AxeBuilder {
* @returns this
*/

public include(selector: string | string[]): this {
selector = Array.isArray(selector) ? selector : [selector];
public include(selector: SerialFrameSelector): this {
this.includes.push(selector);
return this;
}
Expand All @@ -56,8 +60,7 @@ export default class AxeBuilder {
* @returns this
*/

public exclude(selector: string | string[]): this {
selector = Array.isArray(selector) ? selector : [selector];
public exclude(selector: SerialFrameSelector): this {
this.excludes.push(selector);
return this;
}
Expand Down Expand Up @@ -148,6 +151,7 @@ export default class AxeBuilder {
public async analyze(): Promise<AxeResults> {
const context = normalizeContext(this.includes, this.excludes);
const { page, option: options } = this;
console.log(JSON.stringify(context, null, 2));

page.evaluate(this.script());
const runPartialDefined = await page.evaluate<boolean>(
Expand Down
7 changes: 4 additions & 3 deletions packages/playwright/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import type {
AxeResults,
ContextObject,
CrossTreeSelector,
PartialResult
PartialResult,
SerialContextObject
} from 'axe-core';

export type PartialResults = PartialResult | null;

export interface AnalyzePageParams {
context: ContextObject;
options: RunOptions | null;
context: SerialContextObject;
options: RunOptions;
}

export interface AxePlaywrightParams {
Expand Down
13 changes: 9 additions & 4 deletions packages/playwright/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { AxeResults, SerialContextObject } from 'axe-core';
import type {
AxeResults,
SerialContextObject,
SerialSelectorList
} from 'axe-core';
import type { AnalyzePageParams, AnalyzePageResponse } from './types';

/**
Expand All @@ -9,11 +13,12 @@ import type { AnalyzePageParams, AnalyzePageResponse } from './types';
*/

export const normalizeContext = (
includes: string[][],
excludes: string[][]
includes: SerialSelectorList,
excludes: SerialSelectorList
): SerialContextObject => {
const base: SerialContextObject = {
exclude: []
exclude: [],
include: []
};
if (excludes.length && Array.isArray(base.exclude)) {
base.exclude.push(...excludes);
Expand Down
77 changes: 75 additions & 2 deletions packages/playwright/tests/axe-playwright.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import 'mocha';
import fs from 'fs';
import playwright from 'playwright';
import express from 'express';
import type { AxeResults } from 'axe-core';
import type {
AxeResults,
Result,
SerialSelector,
SerialSelectorList
} from 'axe-core';
import testListen from 'test-listen';
import { assert } from 'chai';
import path from 'path';
Expand Down Expand Up @@ -426,7 +431,7 @@ describe('@axe-core/playwright', () => {
return acc.concat(pass.nodes as any);
}, [])
.reduce((acc, node: any) => {
return acc.concat(node.target);
return acc.concat(node.target.flat(1));
}, []);
};
it('with include and exclude', async () => {
Expand Down Expand Up @@ -557,6 +562,74 @@ describe('@axe-core/playwright', () => {
assert.equal(res?.status(), 200);
assert.deepEqual(actual[0], expected);
});

it('with labelled frame', async () => {
await page.goto(`${addr}/external/context-include-exclude.html`);
const results = await new AxeBuilder({ page })
.include({ fromFrames: ['#ifr-inc-excl', 'html'] })
.exclude({ fromFrames: ['#ifr-inc-excl', '#foo-bar'] })
.include({ fromFrames: ['#ifr-inc-excl', '#foo-baz', 'html'] })
.exclude({ fromFrames: ['#ifr-inc-excl', '#foo-baz', 'input'] })
.analyze();
const labelResult = results.violations.find(
(r: Result) => r.id === 'label'
);
assert.isFalse(flatPassesTargets(results).includes('#foo-bar'));
assert.isFalse(flatPassesTargets(results).includes('input'));
assert.isUndefined(labelResult);
});

it('with include shadow DOM', async () => {
await page.goto(`${addr}/external/shadow-dom.html`);
const results = await new AxeBuilder({ page })
.include([['#shadow-root-1', '#shadow-button-1']])
.include([['#shadow-root-2', '#shadow-button-2']])
.analyze();
assert.isTrue(flatPassesTargets(results).includes('#shadow-button-1'));
assert.isTrue(flatPassesTargets(results).includes('#shadow-button-2'));
assert.isFalse(flatPassesTargets(results).includes('#button'));
});

it('with exclude shadow DOM', async () => {
await page.goto(`${addr}/external/shadow-dom.html`);
const results = await new AxeBuilder({ page })
.exclude([['#shadow-root-1', '#shadow-button-1']])
.exclude([['#shadow-root-2', '#shadow-button-2']])
.analyze();
assert.isFalse(flatPassesTargets(results).includes('#shadow-button-1'));
assert.isFalse(flatPassesTargets(results).includes('#shadow-button-2'));
assert.isTrue(flatPassesTargets(results).includes('#button'));
});

it('with labelled shadow DOM', async () => {
await page.goto(`${addr}/external/shadow-dom.html`);
const results = await new AxeBuilder({ page })
.include({ fromShadowDom: ['#shadow-root-1', '#shadow-button-1'] })
.exclude({ fromShadowDom: ['#shadow-root-2', '#shadow-button-2'] })
.analyze();
assert.isTrue(flatPassesTargets(results).includes('#shadow-button-1'));
assert.isFalse(flatPassesTargets(results).includes('#shadow-button-2'));
});

it('with labelled iframe and shadow DOM', async () => {
await page.goto(`${addr}/external/shadow-frames.html`);
const { violations } = await new AxeBuilder({ page })
.exclude({
fromFrames: [
{
fromShadowDom: ['#shadow-root', '#shadow-frame']
},
'input'
]
})
.options({ runOnly: 'label' })
.analyze();
assert.equal(violations[0].id, 'label');
assert.lengthOf(violations[0].nodes, 2);
const nodes = violations[0].nodes;
assert.deepEqual(nodes[0].target, ['#light-frame', 'input']);
assert.deepEqual(nodes[1].target, ['#slotted-frame', 'input']);
});
});

describe('axe.finishRun errors', () => {
Expand Down
24 changes: 11 additions & 13 deletions packages/puppeteer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading