Skip to content

Commit

Permalink
fix(webdriverio): include/exclude chaining and iframe selectors (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zidious committed Dec 17, 2021
1 parent 23b848c commit ca8aa31
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 40 deletions.
2 changes: 1 addition & 1 deletion packages/webdriverio/package-lock.json

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

13 changes: 8 additions & 5 deletions packages/webdriverio/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import type {
CallbackFunction,
WdioBrowser,
WdioElement,
PartialResults
PartialResults,
Selector
} from './types';

export default class AxeBuilder {
private client: Browser<'async'>;
private axeSource: string;
private includes: string[] = [];
private excludes: string[] = [];
private includes: Selector[] = [];
private excludes: Selector[] = [];
private option: RunOptions = {};
private disableFrameSelectors: string[] = [];
private legacyMode = false;
Expand Down Expand Up @@ -68,7 +69,8 @@ export default class AxeBuilder {
* Selector to include in analysis.
* This may be called any number of times.
*/
public include(selector: string): this {
public include(selector: Selector): this {
selector = Array.isArray(selector) ? selector : [selector];
this.includes.push(selector);
return this;
}
Expand All @@ -77,7 +79,8 @@ export default class AxeBuilder {
* Selector to exclude in analysis.
* This may be called any number of times.
*/
public exclude(selector: string): this {
public exclude(selector: Selector): this {
selector = Array.isArray(selector) ? selector : [selector];
this.excludes.push(selector);
return this;
}
Expand Down
141 changes: 111 additions & 30 deletions packages/webdriverio/src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import delay from 'delay';
import AxeBuilder from '.';
import { logOrRethrowError } from './utils';
import { WdioBrowser } from './types';
import type { AxeResults } from 'axe-core';

const connectToChromeDriver = (port: number): Promise<void> => {
let socket: net.Socket;
Expand Down Expand Up @@ -853,48 +854,128 @@ describe('@axe-core/webdriverio', () => {
});

describe('include/exclude', () => {
const flatPassesTargets = (results: AxeResults): string[] => {
return results.passes
.reduce((acc, pass) => {
return acc.concat(pass.nodes as any);
}, [])
.reduce((acc, node: any) => {
return acc.concat(node.target);
}, []);
};

it('with include and exclude', async () => {
let error: unknown = null;
await client.url(`${addr}/nested-iframes.html`);
const builder = new AxeBuilder({ client })
.include('#ifr-foo')
.exclude('#ifr-bar');
await client.url(`${addr}/context-include-exclude.html`);

try {
await builder.analyze();
} catch (e) {
error = e;
}
const builder = new AxeBuilder({ client })
.include('.include')
.exclude('.exclude');
const results = await builder.analyze();

assert.strictEqual(error, null);
assert.isTrue(flatPassesTargets(results).includes('.include'));
assert.isFalse(flatPassesTargets(results).includes('.exclude'));
});

it('with only include', async () => {
let error: unknown = null;
await client.url(`${addr}/nested-iframes.html`);
const builder = new AxeBuilder({ client }).include('#ifr-foo');
await client.url(`${addr}/context-include-exclude.html`);

try {
await builder.analyze();
} catch (e) {
error = e;
}
const builder = new AxeBuilder({ client }).include('.include');
const results = await builder.analyze();

assert.strictEqual(error, null);
assert.isTrue(flatPassesTargets(results).includes('.include'));
});

it('wth only exclude', async () => {
let error: unknown = null;
await client.url(`${addr}/nested-iframes.html`);
const builder = new AxeBuilder({ client }).exclude('#ifr-bar');
it('with only exclude', async () => {
await client.url(`${addr}/context-include-exclude.html`);

try {
await builder.analyze();
} catch (e) {
error = e;
}
const builder = new AxeBuilder({ client }).exclude('.exclude');
const results = await builder.analyze();

assert.isFalse(flatPassesTargets(results).includes('.exclude'));
});

it('with only chaining include', async () => {
await client.url(`${addr}/context-include-exclude.html`);

assert.strictEqual(error, null);
const builder = new AxeBuilder({ client })
.include('.include')
.include('.include2');

const results = await builder.analyze();

assert.isTrue(flatPassesTargets(results).includes('.include'));
assert.isTrue(flatPassesTargets(results).includes('.include2'));
});

it('with only chaining exclude', async () => {
await client.url(`${addr}/context-include-exclude.html`);

const builder = new AxeBuilder({ client })
.exclude('.exclude')
.exclude('.exclude2');

const results = await builder.analyze();

assert.isFalse(flatPassesTargets(results).includes('.exclude'));
assert.isFalse(flatPassesTargets(results).includes('.exclude2'));
});

it('with chaining include and exclude', async () => {
await client.url(`${addr}/context-include-exclude.html`);

const builder = new AxeBuilder({ client })
.include('.include')
.include('.include2')
.exclude('.exclude')
.exclude('.exclude2');

const results = await builder.analyze();

assert.isTrue(flatPassesTargets(results).includes('.include'));
assert.isTrue(flatPassesTargets(results).includes('.include2'));
assert.isFalse(flatPassesTargets(results).includes('.exclude'));
assert.isFalse(flatPassesTargets(results).includes('.exclude2'));
});

it('with include and exclude iframes', async () => {
await client.url(`${addr}/context-include-exclude.html`);

const builder = new AxeBuilder({ client })
.include(['#ifr-inc-excl', 'html'])
.exclude(['#ifr-inc-excl', '#foo-bar'])
.include(['#ifr-inc-excl', '#foo-baz', 'html'])
.exclude(['#ifr-inc-excl', '#foo-baz', 'input']);

const results = await builder.analyze();
const labelResult = results.incomplete.find(
({ id }) => id === 'label'
);

assert.isFalse(flatPassesTargets(results).includes('#foo-bar'));
assert.isFalse(flatPassesTargets(results).includes('input'));
assert.isUndefined(labelResult);
});

it('with include and exclude iframes', async () => {
await client.url(`${addr}/context-include-exclude.html`);

const builder = new AxeBuilder({ client })
.include(['#ifr-inc-excl', '#foo-baz', 'html'])
.include(['#ifr-inc-excl', '#foo-baz', 'input'])
// does not exist
.include(['#hazaar', 'html']);

const results = await builder.analyze();
const labelResult = results.violations.find(
({ id }) => id === 'label'
);
assert.isTrue(flatPassesTargets(results).includes('#ifr-inc-excl'));
assert.isTrue(flatPassesTargets(results).includes('#foo-baz'));
assert.isTrue(flatPassesTargets(results).includes('input'));
assert.isFalse(flatPassesTargets(results).includes('#foo-bar'));
// does not exist
assert.isFalse(flatPassesTargets(results).includes('#hazaar'));
assert.isDefined(labelResult);
});
});

Expand Down
4 changes: 3 additions & 1 deletion packages/webdriverio/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Browser, MultiRemoteBrowser, Element } from 'webdriverio';
import type { AxeResults, ElementContext, RunOptions, Spec } from 'axe-core';
import type { AxeResults, BaseSelector } from 'axe-core';
import * as axe from 'axe-core';

export type WdioBrowser =
Expand Down Expand Up @@ -32,3 +32,5 @@ declare global {
}

export type PartialResults = Parameters<typeof axe.finishRun>[0];

export type Selector = BaseSelector | BaseSelector[];
6 changes: 3 additions & 3 deletions packages/webdriverio/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
Spec,
PartialResults
} from 'axe-core';
import type { WdioBrowser } from './types';
import type { Selector, WdioBrowser } from './types';

/**
* Validates that the client provided is WebdriverIO v5 or v6.
Expand All @@ -33,8 +33,8 @@ export const isWebdriverClient = (client: WdioBrowser): boolean => {
* Get running context
*/
export const normalizeContext = (
includes: string[],
excludes: string[],
includes: Selector[],
excludes: Selector[],
disabledFrameSelectors: string[]
): ContextObject => {
const base: ContextObject = {
Expand Down

0 comments on commit ca8aa31

Please sign in to comment.