Skip to content

Commit

Permalink
Merge pull request #88 from chanzuckerberg/configure
Browse files Browse the repository at this point in the history
Add "config" parameter, which is passed to axe.configure
  • Loading branch information
ahuth authored Feb 12, 2024
2 parents 3fd7dc2 + 418ee67 commit 45ead33
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- [new] Add `config` parameter, which is passed to `axe.configure`

# 7.1.4 (2024-02-09)

- [fix] Update all deps
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ yarn storybook:axe --headless false --browser firefox

Stories can use parameters to configure how axe-storybook-testing handles them.

You can provide these wherever Storybook accepts [parameters](https://storybook.js.org/docs/writing-stories/parameters) (story, component, or global).

### disabledRules

Prevent axe-storybook-testing from running specific Axe rules on a story by using the `disabledRules` parameter.
Expand Down Expand Up @@ -160,7 +162,25 @@ export const SomeStory = {
...
}
}
};
}
}
```

### config

Axe configuration, which is passed to [axe.configure](https://www.deque.com/axe/core-documentation/api-documentation/#api-name-axeconfigure).

```jsx
export const SomeStory = {
parameters: {
axe: {
config: {
checks: [...],
...
}
}
}
}
```

### skip
Expand Down
23 changes: 23 additions & 0 deletions demo/src/advanced.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

export default {
title: 'advanced',
component: 'button',
};

// Testing out passing config to `axe.configure`.
// See https://github.com/chanzuckerberg/axe-storybook-testing/issues/87.
export const branding = {
render: () => (
<button style={{backgroundColor: 'red', color: 'hotpink'}}>
hello world
</button>
),
parameters: {
axe: {
config: {
branding: 'my-branding',
},
},
},
};
8 changes: 6 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {RunOptions} from 'axe-core';
import type {RunOptions, Spec} from 'axe-core';

export declare type AxeParams = {
/**
Expand All @@ -22,10 +22,14 @@ export declare type AxeParams = {
*/
timeout?: number;
/**
* Allows use of optional axe.run options for a given story
* Run options passed to `axe.run`.
* @see https://www.deque.com/axe/core-documentation/api-documentation/#options-parameter
*/
runOptions?: RunOptions;
/**
* Config passed to `axe.configure`.
*/
config?: Spec;
/**
* @deprecated
* Legacy way of waiting for a selector before running Axe.
Expand Down
20 changes: 20 additions & 0 deletions src/ProcessedStory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,23 @@ describe('runOptions', () => {
);
});
});

describe('config', () => {
it('is undefined when the config parameter is missing', () => {
const parameters = {axe: {}};
const rawStory = {id: 'button--a', title: 'button', name: 'a', parameters};
const processedStory = new ProcessedStory(rawStory);
expect(processedStory.config).toBeUndefined();
});

it('parses config', () => {
const parameters = {
axe: {config: {branding: 'hi'}},
};
const rawStory = {id: 'button--a', title: 'button', name: 'a', parameters};
const processedStory = new ProcessedStory(rawStory);
expect(processedStory.config).toEqual({
branding: 'hi',
});
});
});
24 changes: 22 additions & 2 deletions src/ProcessedStory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type {RunOptions} from 'axe-core';
import type {RunOptions, Spec} from 'axe-core';
import {z as zod} from 'zod';
import type {StorybookStory} from './browser/StorybookPage';

type Params = {
disabledRules: string[];
mode: 'off' | 'warn' | 'error';
runOptions?: RunOptions;
config?: Spec;
skip: boolean;
timeout: number;
/** @deprecated */
Expand Down Expand Up @@ -41,6 +42,7 @@ export default class ProcessedStory {
rawStory.parameters?.axe?.runOptions,
rawStory,
),
config: normalizeConfig(rawStory.parameters?.axe?.config, rawStory),
};
}

Expand All @@ -66,13 +68,21 @@ export default class ProcessedStory {
}

/**
* All optional run options used for a given story
* All optional run options used for a given story. Passed to `axe.run`.
* @see https://www.deque.com/axe/core-documentation/api-documentation/#options-parameter
*/
get runOptions() {
return this.parameters.runOptions;
}

/**
* All optional config used to configure axe-core. Passed to `axe.configure`.
* @see https://www.deque.com/axe/core-documentation/api-documentation/#api-name-axeconfigure
*/
get config() {
return this.parameters.config;
}

/**
* Timeout override for a test triggered in runSuite()
*/
Expand Down Expand Up @@ -128,6 +138,8 @@ const runOptionsSchema = zod.optional(
}),
);

const configSchema = zod.object({}).passthrough().optional();

function normalizeSkip(skip: unknown, rawStory: StorybookStory) {
return parseWithFriendlyError(
() => skipSchema.parse(skip) || false,
Expand Down Expand Up @@ -163,6 +175,14 @@ function normalizeRunOptions(runOptions: unknown, rawStory: StorybookStory) {
);
}

function normalizeConfig(config: unknown, rawStory: StorybookStory) {
return parseWithFriendlyError(
() => configSchema.parse(config),
rawStory,
'config',
);
}

function normalizeWaitForSelector(
waitForSelector: unknown,
rawStory: StorybookStory,
Expand Down
7 changes: 6 additions & 1 deletion src/Result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ export default class Result {
*/
static async fromPage(page: Page, story: ProcessedStory) {
const disabledRules = [...defaultDisabledRules, ...story.disabledRules];
const axeResults = await analyze(page, disabledRules, story.runOptions);
const axeResults = await analyze(
page,
disabledRules,
story.runOptions,
story.config,
);
return new Result(axeResults.violations);
}

Expand Down
34 changes: 30 additions & 4 deletions src/browser/AxePage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {AxeResults, RuleObject, RunOptions} from 'axe-core';
import type {AxeResults, RuleObject, RunOptions, Spec} from 'axe-core';
import type {Page} from 'playwright';

/**
Expand All @@ -17,14 +17,40 @@ export function analyze(
page: Page,
disabledRules: string[] = [],
runOptions: RunOptions = {},
config?: Spec,
): Promise<AxeResults> {
return page.evaluate(runAxe, getRunOptions(runOptions, disabledRules));
return page.evaluate(runAxe, {
options: getRunOptions(runOptions, disabledRules),
config,
});
}

function runAxe(options: RunOptions): Promise<AxeResults> {
function runAxe({
options,
config,
}: {
options: RunOptions;
config?: Spec;
}): Promise<AxeResults> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This function executes in a browser context.
return window.axeQueue.add(() => window.axe.run(document, options));
return window.axeQueue.add(() => {
// Always reset the axe config, so if one story sets its own config it doesn't affect the
// others.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This function executes in a browser context.
window.axe.reset();

if (config) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This function executes in a browser context.
window.axe.configure(config);
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This function executes in a browser context.
return window.axe.run(document, options);
});
}

export function getRunOptions(
Expand Down
Loading

0 comments on commit 45ead33

Please sign in to comment.