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

[terra-functional-testing] Add Accessibility Reporter #456

Merged
merged 10 commits into from
Nov 9, 2020

Conversation

StephenEsser
Copy link
Contributor

@StephenEsser StephenEsser commented Oct 27, 2020

Summary

This PR creates a Custom WebdriverIO Reporter that tracks accessibility warnings and writes them to the console at the end of the test run.

Using the axe-core API certain rules are set to reviewOnFail. This configuration option will mark rules to be returned as incomplete upon failure instead of a violation.

reviewOnFail - boolean(option, default false). Override the result of a rule to return "Needs Review" rather than "Violation" if the rule fails.

When running axe if warnings are found the Terra.validates.accessibility() command emits a process message the reporter subscribes to for communication. The reporter stores this information and uses it at the end of the test run to generate an accessibility report that logs warnings for any tests that reported them.

const ruleOverrides = {
  'color-contrast': { reviewOnFail: true }
}

Example Output:

image

Additional Details

Warnings do not fail the test run.

This approach only works globally when using axe.configure. Warnings cannot be set as options in the Terra.report.accessibility() command which run through axe.run which does not support the reviewOnFail option.

@cerner/terra

@StephenEsser StephenEsser self-assigned this Oct 27, 2020
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van October 27, 2020 14:17 Inactive
@@ -43,7 +47,7 @@ const runAxe = (overrides = {}) => {
axe.run(document, opts, function (error, result) {
done({ error, result });
});
}, { rules, restoreScroll: true, runOnly: ['wcag2a', 'wcag2aa', 'wcag21aa', 'section508'] });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

restoreScroll was removed: dequelabs/axe-core#2388

Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't know the scrolling that it used to do was only for color-contrast.

// Rules that fail but are marked for review are returned in the incomplete array.
// https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#results-object
if (incomplete && incomplete.length > 0) {
process.emit('terra:report:accessibility', { incomplete });
Copy link
Contributor

@emilyrohrbough emilyrohrbough Oct 28, 2020

Choose a reason for hiding this comment

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

Should we also add violations to the report's output? I could see two ways:

  • one report, two sections
  • two axe reports for incomplete and violations

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Violations are printed to the console when the expect fails; adding to the reporter also would print them again. We'd want to change how violations are printed if we wanted to pursue this, keeping in mind the expect would fail the test and print a message and then the reporter would print a message

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to add violations to the report. The failed test run should be indication enough. I see the report as valuable on non-failed test runs.

* @param {array} results.incomplete - Incomplete accessibility results.
*/
onReportAccessibility(results) {
this.accessibilityResults[this.currentTest] = results;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to know the suite info here instead of recursively building it out at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably, but I personally think traveling the tree is actually easier than tracking additional test data manually. A suite can be nested infinitely inside of other suites

* @param {RunnerStats} runner - The test runner.
*/
printReport(runner) {
const warnings = Object.keys(this.accessibilityResults);
Copy link
Contributor

@emilyrohrbough emilyrohrbough Oct 28, 2020

Choose a reason for hiding this comment

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

Is there any way to write to the console that an aggregated report can be found in the results dir

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can write to the console, but we don't currently write any files to the results dir

const output = this.travelSuite(rootSuite);
const spec = runner.specs[0];

this.write(`Spec: ${spec}\n${output}`);
Copy link
Contributor

@emilyrohrbough emilyrohrbough Oct 28, 2020

Choose a reason for hiding this comment

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

Can we output this as a md/json report as well that can be pulled in as an build artifact and as a release site artifact?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We'll probably want something like this, but I think that work will likely happen inside of the WDIO 6 reporter that generates the reports (which may or may not be the same reporter)

}

/**
* Travels the suite tree to generate the accessibility report.
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean by travel?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Travels though each node in the tree

@@ -0,0 +1,75 @@
{
"title": "(root)",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be the spec file that was ran?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, this was generated directly by the test runner and copy pasted into this file. The root suite is always assigned '(root)':

https://github.com/webdriverio/webdriverio/blob/master/packages/wdio-reporter/src/index.ts#L58

});

describe('travelSuite', () => {
it('should travel the suite tree and generate an accessibility report', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The recursiveness of this func, was that for nested suites?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test is using data generated from nested suites and has 100% jest coverage. Is there something specific you'd like to see explicit test coverage for?

Copy link
Contributor

Choose a reason for hiding this comment

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

it's not obvious from that was what being tested here by reading through the tests. Maybe update the description to indicate that then.

}

constructor(options) {
super(options);
Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we defined the outputDir and logFile option to pass to super ? It seems there is only one option for output files? maybe, which option 2 from this comment: https://github.com/cerner/terra-toolkit/pull/456/files#r513483163

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the discussion in our WDIO 6 sync I'd say this PR is not going to write out to a file, but it is something we can discuss and circle back on down the road if we think it would be beneficial

@emilyrohrbough
Copy link
Contributor

emilyrohrbough commented Oct 28, 2020

Quick question:

To set rules for review they can be set in the Terra Service axe options

I thought we discussed in the sync last week that we no longer wanted allow axe configure axe via the Service and it would be managed at the command level?

@StephenEsser
Copy link
Contributor Author

I thought we discussed in the sync last week that we no longer wanted allow axe configure axe via the Service and it would be managed at the command level?

My understanding was that we didn't want to expose an additional API for consumers to set the axe options via the service when they are extending our wdio.conf which is still the case here.

The axe API hasn't been removed from the service options, this is just using that existing pass through, this would work today without the changes on this PR.

Do we want to remove the axe option entirely from the service?

@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van October 30, 2020 20:53 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van October 30, 2020 20:55 Inactive
*/
const runAxe = (overrides = {}) => {
const runAxe = (options = {}) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we bind this to the Terra service so we don't need to pull these config options from a global?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll experiment with different ways to retrieve the service options in a more direct manner, but I don't think those changes should hold up this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

The viewport helpers and validate elements also need to look up the service options. Making them classes and initialize with the options was not the easiest to do so I set the service options in the Terra global object. That's what I going with for now unless we have other better ways to do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created a PR to address these changes, I'd like to move axe configuration discussions onto that PR:

#466

...axeOptions && axeOptions.rules,
...overrides.rules,
...GLOBAL_RULE_OVERRIDES,
...axeServiceOptions.rules,
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we decided to drop this Service Level configuration option in our sync last Wednesday? So no axe configuration when initialized the TerraService or flat on the wdio object, but instead allow per test overrides and then the global be defined like you have in GLOBAL_RULE_OVERRIDES.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I investigated removing the axe configuration from the service options, but we're going to need it to dynamically flex the color contrast rule while in lowlight theme. Either to disable it or adjust the contrast ratio.

Here's how we dynamically flex the rules today:

https://github.com/cerner/terra-toolkit-boneyard/blob/main/config/wdio/wdio.conf.js#L102

Here's an example of what removing the axe option looks like:

https://github.com/cerner/terra-toolkit/compare/remove-terra-service-axe-option

We might be able to flex the option within the service, but until we have a solution we'll need to keep it as a service option. These changes are just cleaning up the file, they're not introducing any new functionality

Copy link
Contributor

Choose a reason for hiding this comment

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

I assumed we'd be flex that in the service like how we are overriding the scroll rule. It's always be disabled (or warn) for the lowlight theme.

Copy link
Contributor Author

@StephenEsser StephenEsser Nov 2, 2020

Choose a reason for hiding this comment

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

The scroll rule is static and never changes, but the color contrast rule would be dynamic based on the theme.

Removing the axe option from the service is outside the scope of this PR but I'll do additional investigation into removing the axe options while still dynamically flexing rules based on the theme. If I come up with a solution I'll make a separate PR. Removing the axe options from the service doesn't impact the intended functionality of the accessibility reporter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created a PR to address these changes, I'd like to move axe configuration discussions onto that PR:

#466

I've removed all changes not directly associated to the accessibility reporter from this PR

/**
* Formats an accessibility warning summary for a test.
* @param {string} uid - The unique identifier of the test.
* @param {number} indent - The indentation the report should have,
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you intend to have more comment at the end or should it be a period?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be a period, updated:

51b3981

tests.forEach((test) => {
const { uid } = test;

if (this.accessibilityResults[uid]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to ensure that this result specifically has the incomplete type or is it guaranteed that the type is incomplete by the time it gets here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's guaranteed, with the current implementation the results are only stored if they contain an incomplete array

@@ -15,14 +15,14 @@ describe('Inject Axe', () => {

it('should inject axe into the document with the provided options', () => {
const mockExecute = jest.fn();
const mockRules = { rules: [{ id: 'mock-rule', enabled: true }] };
const mockRules = { rules: [{ enabled: true, id: 'mock-rule' }] };
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious what the reason is for swapping these? It's more natural to me to see the rule name first followed by their attributes.

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.

This is the order the objects will be defined for the real axe inject due to how the map generates the rules array and the order matters for the strict string comparison below. The id is always appended. For this test it doesn't matter, I've reverted the changes to how it was before

@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 3, 2020 21:08 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 4, 2020 00:38 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 4, 2020 00:47 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 5, 2020 15:07 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 9, 2020 17:28 Inactive
@mjhenkes mjhenkes temporarily deployed to terra-toolki-terra-acce-i76van November 9, 2020 19:27 Inactive
@StephenEsser StephenEsser merged commit f7f1ce9 into main Nov 9, 2020
@StephenEsser StephenEsser deleted the terra-accessibility-reporter branch November 9, 2020 21:58
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants