Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

[terra-functional-testing] Remove axe option from the Terra Service #466

Merged
merged 3 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
45 changes: 17 additions & 28 deletions packages/terra-functional-testing/src/commands/axe/run.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,38 @@
/* global browser, axe */
/* global browser, axe, Terra */
const injectAxe = require('./inject');

/**
* Executes axe on the browser.
* @param {Object} overrides - The axe options.
* @param {Array} overrides.rules - The rule overrides.
* @param {Object} options - The axe options.
* @param {Array} options.rules - The rule overrides.
*/
const runAxe = (overrides = {}) => {
// Extract the axe options for the Terra service from the global browser object.
const [, options = {}] = browser.options.services.find(([service]) => (
typeof service === 'function' && service.name === 'TerraService'
));

const { axe: axeOptions } = options;
const runAxe = (options = {}) => {
const isAxeUnavailable = browser.execute(() => window.axe === undefined);

// Inject axe-core onto the page if it has not already been initialized.
if (isAxeUnavailable) {
injectAxe(axeOptions);
}
/**
* Converts the global rule overrides into an array.
* The axe.configure API requires the rules to be an array of objects. The axe.run API requires
* the rules to be an object keyed by the rule ID.
*/
const globalRuleArray = Object.keys(Terra.axe.rules).map((rule) => (
{ ...Terra.axe.rules[rule], id: rule }
));
Comment on lines +19 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const globalRuleArray = Object.keys(Terra.axe.rules).map((rule) => (
{ ...Terra.axe.rules[rule], id: rule }
));
const globalRuleArray = Object.entries(Terra.axe.rules).map((rule, settings) => (
{ ...settings, id: rule }
));


/**
* This rule was introduced in axe-core v3.3 and causes failures in many Terra components.
* The solution to address this failure vary by component. It is being disabled until a solution is identified in the future.
*
* Reference: https://github.com/cerner/terra-framework/issues/991
*/
const ruleOverrides = {
'scrollable-region-focusable': { enabled: false },
};
injectAxe({ rules: globalRuleArray });
}

// Merge the global rules and overrides together.
const rules = {
...ruleOverrides,
...axeOptions && axeOptions.rules,
...overrides.rules,
};
// Merge the global rules and option overrides together.
const rules = { ...Terra.axe.rules, ...options.rules };

// eslint-disable-next-line prefer-arrow-callback, func-names
return browser.executeAsync(function (opts, done) {
// eslint-disable-next-line prefer-arrow-callback, func-names
axe.run(document, opts, function (error, result) {
done({ error, result });
});
}, { rules, restoreScroll: true, runOnly: ['wcag2a', 'wcag2aa', 'wcag21aa', 'section508'] });
}, { rules, runOnly: ['wcag2a', 'wcag2aa', 'wcag21aa', 'section508'] });
};

module.exports = runAxe;
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ exports.config = {
[TerraService, {
/* Use to change the form factor (test viewport) used in the wdio run. */
...FORM_FACTOR && { formFactor: FORM_FACTOR },
...THEME && { theme: THEME },
}],
[AssetServerService, {
...SITE && { site: SITE },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,54 @@ const hideInputCaret = require('../commands/hide-input-caret');

class TerraService {
constructor(options = {}) {
this.formFactor = options.formFactor;
const { formFactor, theme } = options;

this.formFactor = formFactor;
this.theme = theme || 'terra-default-theme';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #464 I'm exposing the entire service options object thru Terra.serviceOptions. I should probably default Terra.serviceOptions.theme to terrra-default-theme to keep them in sync. Would it be better to default the theme to terrra-default-theme in wdio.config.js or are we concerned that this config may get overridden by consumers.

Copy link
Contributor Author

@StephenEsser StephenEsser Nov 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the TerraService should default the option if it needs a default rather than requiring a theme be set in the wdio.config

}

/**
* Service hook executed prior to test execution.
* Initializes the Terra Service's custom commands.
*/
before(capabilities) {
// Add the Jest expect module the use the Jest matchers.
// Set Jest's expect module as the global assertion framework.
global.expect = expect;
global.expect.extend({ toBeAccessible });

// Add a Terra global with access to Mocha-Chai test helpers.
global.Terra = {
validates: { accessibility },

// viewports provides access Terra's list of test viewports.
// Provides access to Terra's list of supported testing viewports.
viewports: getViewports,

// describeViewports provides a custom describe block for looping test viewports.
// Provides a custom describe block for looping test viewports.
describeViewports,

// hideInputCaret hides the blinking input caret that appears in inputs or editable text areas.
// Hides the blinking input caret that appears in inputs or editable text areas.
hideInputCaret,

axe: {
/**
* Global rule overrides.
* Rules modified here will be applied globally for all tests.
*/
rules: {
/**
* This rule was introduced in axe-core v3.3 and causes failures in many Terra components.
* The solution to address this failure vary by component. It is being disabled until a solution is identified in the future.
*
* Reference: https://github.com/cerner/terra-framework/issues/991
*/
'scrollable-region-focusable': { enabled: false },
/**
* The lowlight theme adheres to a non-default color contrast ratio and fails the default ratio check.
* The color-contrast ratio check is disabled for lowlight theme testing.
*/
'color-contrast': { enabled: this.theme !== 'clinical-lowlight-theme' },
},
},
};

// IE driver takes longer to be ready for browser interactions.
Expand All @@ -46,10 +70,10 @@ class TerraService {
setViewport(this.formFactor);
}

afterCommand(commandName, args, result, error) {
afterCommand(commandName, _args, _result, error) {
if ((commandName === 'refresh' || commandName === 'url') && !error) {
try {
// This is only meant as a convenience so failure is not particularly concerning
// This is only meant as a convenience so failure is not particularly concerning.
global.Terra.hideInputCaret('body');

if (global.browser.$('[data-terra-dev-site-loading]').isExisting()) {
Expand All @@ -59,8 +83,7 @@ class TerraService {
});
}
} catch (err) {
// Intentionally blank
// If this fails we don't want to warn because the user can't fix the issue.
// Intentionally blank. If this fails we don't want to warn because the user can't fix the issue.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,21 @@ export.config = {

## Options

### axe
### formFactor

The axe configuration options for adding or customizing the rules used during testing. See the [axe API](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md) for more information on configuring rules.
Tests can be executed in a specific form factor by setting the `formFactor` configuration option or the `FORM_FACTOR` environment variable . The form factors can be any of the following supported viewports: `tiny`; `small`; `medium`; `large`; `huge`; `enormous`. In order for the tests to run in this form factor, it must be one that is already specified in the `Terra.describeViewport` block.

Type: `Object`
Type: `String`

Required: `false`

Default: `{}`
Default: `undefined`

Example:
Examples:

```js
"FORM_FACTOR=huge npm run test:wdio"
```

```js
// wdio.conf.js
Expand All @@ -51,32 +55,24 @@ export.config = {
// ...
services: [
[TerraService, {
axe: {
rules: {
'color-contrast': { enabled: true },
}
},
formFactor: 'huge',
}]
],
// ...
};
```

### formFactor
### theme

Tests can be executed in a specific form factor by setting the `formFactor` configuration option or the `FORM_FACTOR` environment variable . The form factors can be any of the following supported viewports: `tiny`; `small`; `medium`; `large`; `huge`; `enormous`. In order for the tests to run in this form factor, it must be one that is already specified in the `Terra.describeViewport` block.
An optional theme name that will be used to configure the testing environment. This option will flex the axe-core rules used during testing to account for the current theme.

Type: `String`
Type: `string`

Required: `false`

Default: `undefined`

Examples:
Default: `terra-default-theme`

```js
"FORM_FACTOR=huge npm run test:wdio"
```
Example:

```js
// wdio.conf.js
Expand All @@ -86,11 +82,9 @@ export.config = {
// ...
services: [
[TerraService, {
formFactor: 'huge',
theme: 'terra-theme-name',
}]
],
// ...
};
```


Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
jest.mock('../../../../src/commands/axe/inject');
const injectAxe = require('../../../../src/commands/axe/inject');
const runAxe = require('../../../../src/commands/axe/run');

jest.mock('../../../../src/commands/axe/inject');

describe('Run Axe', () => {
it('should inject axe if not already available', () => {
const mockAxeRun = jest.fn().mockImplementation((_document, _opts, func) => {
func(jest.fn(), jest.fn());
});

const mockExecuteAsync = jest.fn().mockImplementation((func, opts) => {
func(opts, jest.fn());
return {};
});

global.browser = {
execute: () => true,
executeAsync: mockExecuteAsync,
};

global.axe = {
run: mockAxeRun,
};

global.Terra = { axe: { rules: { 'scrollable-region-focusable': { enabled: false } } } };

runAxe();

const expectedRules = { rules: [{ enabled: false, id: 'scrollable-region-focusable' }] };

expect(injectAxe).toHaveBeenCalledWith(expectedRules);
});

it('should run axe on the document', () => {
const mockAxeRun = jest.fn().mockImplementation((_document, opts, func) => {
func(jest.fn(), jest.fn());
Expand All @@ -11,16 +41,13 @@ describe('Run Axe', () => {
return {};
});

const TerraService = () => { };

global.browser = {
execute: () => true,
executeAsync: mockExecuteAsync,
options: {
services: [[TerraService]],
},
};

global.Terra = { axe: { rules: {} } };

global.axe = {
run: mockAxeRun,
};
Expand All @@ -31,36 +58,26 @@ describe('Run Axe', () => {
});

it('should run axe with the service options', () => {
const TerraService = () => { };
const serviceOptions = { axe: { rules: { 'mock-rule': { enabled: true } } } };

const mockExecuteAsync = jest.fn().mockImplementation((func, opts) => {
const { rules } = opts;

const expectedRules = {
'mock-rule': { enabled: true },
'scrollable-region-focusable': { enabled: false },
};

expect(rules).toEqual(expectedRules);
expect(rules).toEqual({ 'mock-rule': { enabled: true } });
return {};
});

global.browser = {
execute: jest.fn(),
executeAsync: mockExecuteAsync,
options: {
services: [[TerraService, serviceOptions]],
},
};

global.Terra = { axe: { rules: { 'mock-rule': { enabled: true } } } };

runAxe();

expect.assertions(1);
});

it('should run axe with the options provided', () => {
const TerraService = () => { };
const mockExecuteAsync = jest.fn().mockImplementation((func, opts) => {
const { rules } = opts;

Expand All @@ -76,27 +93,22 @@ describe('Run Axe', () => {
global.browser = {
execute: jest.fn(),
executeAsync: mockExecuteAsync,
options: {
services: [[TerraService]],
},
};

global.Terra = { axe: { rules: { 'scrollable-region-focusable': { enabled: false } } } };

runAxe({ rules: { 'mock-rule': { enabled: true } } });

expect.assertions(1);
});

it('should run axe with merged rules from the service and from options provided', () => {
const TerraService = () => { };
const serviceOptions = { axe: { rules: { 'mock-rule-1': { enabled: true } } } };

const mockExecuteAsync = jest.fn().mockImplementation((func, opts) => {
const { rules } = opts;

const expectedRules = {
'mock-rule-1': { enabled: true },
'mock-rule-2': { enabled: true },
'scrollable-region-focusable': { enabled: false },
};

expect(rules).toEqual(expectedRules);
Expand All @@ -106,11 +118,10 @@ describe('Run Axe', () => {
global.browser = {
execute: jest.fn(),
executeAsync: mockExecuteAsync,
options: {
services: [[TerraService, serviceOptions]],
},
};

global.Terra = { axe: { rules: { 'mock-rule-1': { enabled: true } } } };

runAxe({ rules: { 'mock-rule-2': { enabled: true } } });

expect.assertions(1);
Expand Down
Loading