-
Notifications
You must be signed in to change notification settings - Fork 38
[terra-functional-testing] Add Accessibility Reporter #456
Conversation
@@ -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'] }); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 }); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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}`); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)", |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)':
}); | ||
|
||
describe('travelSuite', () => { | ||
it('should travel the suite tree and generate an accessibility report', () => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
Quick question:
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 |
*/ | ||
const runAxe = (overrides = {}) => { | ||
const runAxe = (options = {}) => { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
...axeOptions && axeOptions.rules, | ||
...overrides.rules, | ||
...GLOBAL_RULE_OVERRIDES, | ||
...axeServiceOptions.rules, |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:
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, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
tests.forEach((test) => { | ||
const { uid } = test; | ||
|
||
if (this.accessibilityResults[uid]) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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' }] }; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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 asincomplete
upon failure instead of aviolation
.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.Example Output:
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 throughaxe.run
which does not support thereviewOnFail
option.@cerner/terra