Skip to content

Commit 51b03c7

Browse files
fix(ava.generate-tests) Add move verbose error messages to CircleCI/Jest (#4988)
* fix(ava.generate-tests) Add move verbose error messages to CircleCI/Jest * fix(ava.generate-tests) Add message for more info * fix(ava.generate-tests) Escape new lines and fix quotes * fix(ava.generate-tests) Make violation header more readable * fix(README) Add note to explain how to use the axe/a11y tests * fix(ava.generate-tests) Refactor generator to make editing tests easier. * Use a template.js file so that we don't have to edit stringified functions. * fix(ava.generate-tests) Add more information about how to view this error. Add link so that you don't have to run the whole test suite locally, which is very slow and resource heavy. Co-authored-by: Kim Flournoy <[email protected]>
1 parent cc237f2 commit 51b03c7

File tree

3 files changed

+98
-79
lines changed

3 files changed

+98
-79
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ Delete temporary build and local files.
108108

109109
### Tests
110110

111-
`npm test`: run all tests
111+
`npm test`: run all tests, except axe/a11y tests
112+
113+
`npm run test:a11y`: run axe tests for accessibility violations
114+
Results are saved into `./__tests__/a11y/results`.
112115

113116
### Release
114117

__tests__/a11y/ava.generate-tests.js

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,9 @@ const fs = require('fs').promises;
22
const del = require('del');
33
const playwright = require('playwright');
44

5-
//
65
const portNum = process.env.a11yPort || 9002;
76

8-
/* eslint-disable no-template-curly-in-string */
9-
// base content for tests file
10-
let tests = `const sa11yPresetRules = require('@sa11y/preset-rules');
11-
const axeReporter = require('axe-html-reporter').createHtmlReport;
12-
const AxeBuilder = require('@axe-core/playwright').default;
13-
const playwright = require('playwright');
14-
const test = require('ava').default;
15-
const browserPromise = playwright.chromium.launch();
16-
17-
// required macro to allow Playwright tests to run efficiently when using the AVA test runner
18-
async function pageMacro(t, callback) {
19-
const browser = await browserPromise;
20-
const context = await browser.newContext();
21-
const page = await context.newPage();
22-
try {
23-
await callback(t, page);
24-
} finally {
25-
await page.close();
26-
}
27-
}
28-
29-
// repeatable function to run accessibility(a11y) tests for a story
30-
async function axeTest(t, page, story) {
31-
try {
32-
const results = await new AxeBuilder({ page })
33-
.options(sa11yPresetRules.recommended)
34-
.include('#root')
35-
.analyze();
36-
37-
const numViolations = results.violations.length;
38-
39-
if (numViolations > 0) {
40-
axeReporter({
41-
results: {
42-
violations: results.violations,
43-
},
44-
options: {
45-
projectKey: 'SLDS a11y',
46-
outputDir: '__tests__/a11y/results',
47-
reportFileName: \`${'${story.id}.html'}\`,
48-
},
49-
});
50-
51-
// conditionally generate legible wording for the test failure message
52-
const plural = numViolations !== 1 ? 'rules have' : 'rule has';
53-
t.fail(\`${'» ${numViolations} ${plural} violations'}\`);
54-
}
55-
56-
t.pass('» No accessibility violations. YAY!');
57-
58-
} catch (e) {
59-
// do something with any other error
60-
t.fail('!! Error occurred: ' + e);
61-
}
62-
}
63-
`;
64-
65-
// repeatable template for each test
66-
function testTemplate(story) {
67-
return `
68-
test('» a11y: ${story.kind}/${story.name}', pageMacro, async (t, page) => {
69-
const story = {
70-
id: '${story.id}',
71-
kind: '${story.kind}',
72-
name: '${story.name}'
73-
};
74-
await page.goto('http://localhost:${portNum}/iframe.html?id=${story.id}&args=&viewMode=story', { timeout: 0 });
75-
await axeTest(t, page, story);
76-
});
77-
`;
78-
}
7+
const templatePath = './__tests__/a11y/ava.test-template.js';
798

809
// asynchronously build the accessibility(a11y) test files using Storybook as the source of truth
8110
(async () => {
@@ -110,17 +39,18 @@ test('» a11y: ${story.kind}/${story.name}', pageMacro, async (t, page) => {
11039

11140
// create test files with `numTests` tests per file
11241
for (let fileNum = 0; fileNum < numTestFiles; fileNum++) {
113-
let fileTests = tests;
11442
// create a test for each story
115-
stories.splice(0, numTests).forEach((story) => {
116-
const storyTest = testTemplate(story);
117-
fileTests += storyTest;
118-
});
43+
const s = stories.splice(0, numTests)
44+
45+
let template = await fs.readFile(templatePath, 'utf-8');
46+
47+
template = template.replace(/(?<=const stories = ).+(?=;)/,JSON.stringify(s.map(({name,id,kind})=>({name,id,kind})),null,2));
48+
template = template.replace(/(?<=a11yPort \|\| ).+(?=;)/,portNum);
11949

12050
// write tests to file
12151
await fs.writeFile(
12252
`./__tests__/a11y/__testfiles__/ava.a11y.${fileNum}.spec.js`,
123-
fileTests
53+
template
12454
);
12555
}
12656
})();

__tests__/a11y/ava.test-template.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const sa11yPresetRules = require('@sa11y/preset-rules');
2+
const axeReporter = require('axe-html-reporter').createHtmlReport;
3+
const AxeBuilder = require('@axe-core/playwright').default;
4+
const playwright = require('playwright');
5+
const test = require('ava').default;
6+
const browserPromise = playwright.chromium.launch();
7+
8+
// Will be replaced by ava.generate-tests.js
9+
const portNum = process.env.a11yPort || 9002;
10+
const stories = []; //[{name,id,type},...]
11+
12+
// required macro to allow Playwright tests to run efficiently when using the AVA test runner
13+
async function pageMacro(t, callback) {
14+
const browser = await browserPromise;
15+
const context = await browser.newContext();
16+
const page = await context.newPage();
17+
try {
18+
await callback(t, page);
19+
} finally {
20+
await page.close();
21+
}
22+
}
23+
24+
const prettyPrintObject = (obj) =>
25+
Object.keys(obj).map(key=>`${key}: ${obj[key]}`);
26+
27+
const generateNodeDescription = ({html, target, failureSummary}) =>
28+
prettyPrintObject({html,target,failureSummary});
29+
30+
const generateDescription = ({id,impact,nodes}, i) =>
31+
[
32+
`\nRULE ${i+1}:`,
33+
prettyPrintObject({id,impact}).join('\n'),
34+
'\nViolation:',
35+
nodes.map(generateNodeDescription).map(x=>x.join('\n')).flat().join('\n\n'),
36+
'\nFor more info run these tests locally with `npm run test:a11y`\n'
37+
];
38+
39+
const generateLink = (address) =>
40+
`To view this error, run \`npm start\` and then click this link ${address}`
41+
42+
// repeatable function to run accessibility(a11y) tests for a story
43+
async function axeTest(t, page, story, address) {
44+
try {
45+
const results = await new AxeBuilder({ page })
46+
.options(sa11yPresetRules.recommended)
47+
.include('#root')
48+
.analyze();
49+
50+
const numViolations = results.violations.length;
51+
52+
if (numViolations > 0) {
53+
axeReporter({
54+
results: {
55+
violations: results.violations,
56+
},
57+
options: {
58+
projectKey: 'SLDS a11y',
59+
outputDir: '__tests__/a11y/results',
60+
reportFileName: `${story.id}.html`,
61+
},
62+
});
63+
64+
// conditionally generate legible wording for the test failure message
65+
const plural = numViolations !== 1 ? 'rules have' : 'rule has';
66+
t.fail(${numViolations} ${plural} violations \n\n ${generateLink(address)} \n\n ${results.violations.map(generateDescription).flat().join("\n")}`);
67+
}
68+
69+
t.pass('» No accessibility violations. YAY!');
70+
71+
} catch (e) {
72+
// do something with any other error
73+
t.fail('!! Error occurred: ' + e);
74+
}
75+
}
76+
77+
const runTest = (story) => test(`» a11y: ${story.kind}/${story.name}`, pageMacro, async (t, page) => {
78+
const address = `http://localhost:${portNum}/iframe.html?id=${story.id}&args=&viewMode=story`;
79+
await page.goto(address, { timeout: 0 });
80+
await axeTest(t, page, story, address);
81+
});
82+
83+
// init
84+
(()=>{
85+
stories.forEach(runTest)
86+
})()

0 commit comments

Comments
 (0)