Skip to content

Commit f1c0b61

Browse files
committed
test(plugin-axe): use jest-extended
1 parent 1e7d304 commit f1c0b61

File tree

7 files changed

+136
-193
lines changed

7 files changed

+136
-193
lines changed

packages/plugin-axe/src/lib/config.unit.test.ts

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,36 @@ describe('axePluginOptionsSchema', () => {
66
expect(axePluginOptionsSchema.parse({}).preset).toBe('wcag21aa');
77
});
88

9-
it('should accept wcag21aa preset', () => {
10-
expect(() =>
11-
axePluginOptionsSchema.parse({ preset: 'wcag21aa' }),
12-
).not.toThrow();
13-
});
14-
15-
it('should accept wcag22aa preset', () => {
16-
expect(() =>
17-
axePluginOptionsSchema.parse({ preset: 'wcag22aa' }),
18-
).not.toThrow();
19-
});
20-
21-
it('should accept best-practice preset', () => {
22-
expect(() =>
23-
axePluginOptionsSchema.parse({ preset: 'best-practice' }),
24-
).not.toThrow();
25-
});
26-
27-
it('should accept all preset', () => {
28-
expect(() => axePluginOptionsSchema.parse({ preset: 'all' })).not.toThrow();
29-
});
30-
31-
it('should accept number scoreTargets', () => {
9+
it.each(['wcag21aa', 'wcag22aa', 'best-practice', 'all'])(
10+
'should validate %j as a valid preset value',
11+
preset => {
12+
expect(() => axePluginOptionsSchema.parse({ preset })).not.toThrow();
13+
},
14+
);
15+
16+
it('should accept scoreTargets as a number between 0 and 1', () => {
3217
expect(() =>
3318
axePluginOptionsSchema.parse({ scoreTargets: 0.99 }),
3419
).not.toThrow();
3520
});
3621

37-
it('should accept object scoreTargets', () => {
22+
it('should accept scoreTargets as an audit-specific score map', () => {
3823
expect(() =>
3924
axePluginOptionsSchema.parse({
4025
scoreTargets: { 'color-contrast': 0.99 },
4126
}),
4227
).not.toThrow();
4328
});
4429

45-
it('should throw for invalid preset', () => {
30+
it('should reject invalid preset values', () => {
4631
expect(() => axePluginOptionsSchema.parse({ preset: 'wcag3aa' })).toThrow();
4732
});
4833

49-
it('should throw for invalid scoreTargets value', () => {
34+
it('should reject scoreTargets values greater than 1', () => {
5035
expect(() => axePluginOptionsSchema.parse({ scoreTargets: 1.5 })).toThrow();
5136
});
5237

53-
it('should throw for negative scoreTargets', () => {
38+
it('should reject negative scoreTargets', () => {
5439
expect(() =>
5540
axePluginOptionsSchema.parse({ scoreTargets: -0.1 }),
5641
).toThrow();
Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,30 @@
11
import { describe, expect, it } from 'vitest';
2-
import type { Audit } from '@code-pushup/models';
32
import { loadAxeRules, transformRulesToAudits } from './transform.js';
43

54
describe('transformRulesToAudits', () => {
6-
describe('wcag21aa preset', () => {
7-
it('should return approximately 67 audits', () => {
8-
const audits = transformRulesToAudits(loadAxeRules('wcag21aa'));
9-
10-
expect(audits.length).toBeGreaterThanOrEqual(65);
11-
expect(audits.length).toBeLessThanOrEqual(70);
12-
});
13-
});
14-
15-
describe('wcag22aa preset', () => {
16-
it('should return approximately 68 audits', () => {
17-
const audits = transformRulesToAudits(loadAxeRules('wcag22aa'));
18-
19-
expect(audits.length).toBeGreaterThanOrEqual(66);
20-
expect(audits.length).toBeLessThanOrEqual(72);
21-
});
22-
});
23-
24-
describe('best-practice preset', () => {
25-
it('should return approximately 30 audits', () => {
26-
const audits = transformRulesToAudits(loadAxeRules('best-practice'));
27-
28-
expect(audits.length).toBeGreaterThanOrEqual(25);
29-
expect(audits.length).toBeLessThanOrEqual(35);
30-
});
31-
});
32-
33-
describe('all preset', () => {
34-
it('should return approximately 104 audits', () => {
35-
const audits = transformRulesToAudits(loadAxeRules('all'));
36-
37-
expect(audits.length).toBeGreaterThanOrEqual(100);
38-
expect(audits.length).toBeLessThanOrEqual(110);
39-
});
40-
});
41-
42-
describe('audit structure', () => {
43-
it('should have slug, title, description, and docsUrl', () => {
44-
const audit = transformRulesToAudits(
45-
loadAxeRules('wcag21aa'),
46-
)[0] as Audit;
47-
48-
expect(audit.slug).toBeTruthy();
49-
expect(audit.title).toBeTruthy();
50-
expect(audit.description).toBeTruthy();
51-
expect(audit.docsUrl).toMatch(/^https:\/\//);
5+
it.each([
6+
['wcag21aa' as const, 65, 70],
7+
['wcag22aa' as const, 66, 72],
8+
['best-practice' as const, 25, 35],
9+
['all' as const, 100, 110],
10+
])(
11+
'should transform %j preset rules into audits within expected range',
12+
(preset, min, max) => {
13+
expect(transformRulesToAudits(loadAxeRules(preset))).toBeInRange(
14+
min,
15+
max,
16+
);
17+
},
18+
);
19+
20+
it('should include required metadata fields for all transformed audits', () => {
21+
const audit = transformRulesToAudits(loadAxeRules('wcag21aa'))[0]!;
22+
23+
expect(audit).toMatchObject({
24+
slug: expect.any(String),
25+
title: expect.any(String),
26+
description: expect.any(String),
27+
docsUrl: expect.stringMatching(/^https:\/\//),
5228
});
5329
});
5430
});
Lines changed: 76 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,105 @@
11
import type { RuleMetadata } from 'axe-core';
22
import { describe, expect, it } from 'vitest';
3+
import type { Group } from '@code-pushup/models';
34
import { loadAxeRules, transformRulesToGroups } from './transform.js';
45

56
describe('transformRulesToGroups', () => {
6-
describe('wcag21aa preset', () => {
7-
it('should create WCAG 2.1 Level A and AA groups', () => {
8-
const groups = transformRulesToGroups(
9-
loadAxeRules('wcag21aa'),
10-
'wcag21aa',
11-
);
12-
13-
expect(groups.map(({ slug }) => slug)).toEqual([
14-
'wcag21-level-a',
15-
'wcag21-level-aa',
16-
]);
17-
expect(groups.map(({ title }) => title)).toEqual([
18-
'WCAG 2.1 Level A',
19-
'WCAG 2.1 Level AA',
20-
]);
21-
});
7+
it('should create WCAG 2.1 Level A and AA groups for "wcag21aa" preset', () => {
8+
const groups = transformRulesToGroups(loadAxeRules('wcag21aa'), 'wcag21aa');
229

23-
it('should have refs in WCAG groups', () => {
24-
transformRulesToGroups(loadAxeRules('wcag21aa'), 'wcag21aa').forEach(
25-
({ refs }) => {
26-
expect(refs.length).toBeGreaterThan(0);
27-
},
28-
);
10+
expect(groups).toBeArrayOfSize(2);
11+
expect(groups).toPartiallyContain({
12+
slug: 'wcag21-level-a',
13+
title: 'WCAG 2.1 Level A',
14+
});
15+
expect(groups).toPartiallyContain({
16+
slug: 'wcag21-level-aa',
17+
title: 'WCAG 2.1 Level AA',
2918
});
3019
});
3120

32-
describe('wcag22aa preset', () => {
33-
it('should create WCAG 2.2 Level A and AA groups', () => {
34-
const groups = transformRulesToGroups(
35-
loadAxeRules('wcag22aa'),
36-
'wcag22aa',
37-
);
38-
39-
expect(groups.map(({ slug }) => slug)).toEqual([
40-
'wcag22-level-a',
41-
'wcag22-level-aa',
42-
]);
43-
expect(groups.map(({ title }) => title)).toEqual([
44-
'WCAG 2.2 Level A',
45-
'WCAG 2.2 Level AA',
46-
]);
47-
});
21+
it('should populate group refs with audit references for "wcag21aa" preset', () => {
22+
const groups = transformRulesToGroups(loadAxeRules('wcag21aa'), 'wcag21aa');
23+
24+
expect(groups[0]!.refs).not.toBeEmpty();
25+
expect(groups[1]!.refs).not.toBeEmpty();
4826
});
4927

50-
describe('best-practice preset', () => {
51-
it('should create multiple category groups', () => {
52-
expect(
53-
transformRulesToGroups(loadAxeRules('best-practice'), 'best-practice')
54-
.length,
55-
).toBeGreaterThan(5);
28+
it('should create WCAG 2.2 Level A and AA groups for "wcag22aa" preset', () => {
29+
const groups = transformRulesToGroups(loadAxeRules('wcag22aa'), 'wcag22aa');
30+
31+
expect(groups).toBeArrayOfSize(2);
32+
expect(groups).toPartiallyContain({
33+
slug: 'wcag22-level-a',
34+
title: 'WCAG 2.2 Level A',
5635
});
36+
expect(groups).toPartiallyContain({
37+
slug: 'wcag22-level-aa',
38+
title: 'WCAG 2.2 Level AA',
39+
});
40+
});
5741

58-
it('should format category titles correctly', () => {
59-
const groups = transformRulesToGroups(
60-
loadAxeRules('best-practice'),
61-
'best-practice',
62-
);
42+
it('should create multiple category groups for "best-practice" preset', () => {
43+
expect(
44+
transformRulesToGroups(loadAxeRules('best-practice'), 'best-practice')
45+
.length,
46+
).toBeGreaterThan(5);
47+
});
6348

64-
expect(groups.find(({ slug }) => slug === 'aria')?.title).toBe('ARIA');
65-
expect(groups.find(({ slug }) => slug === 'name-role-value')?.title).toBe(
66-
'Names & Labels',
67-
);
49+
it('should format category titles using display names', () => {
50+
const groups = transformRulesToGroups(
51+
loadAxeRules('best-practice'),
52+
'best-practice',
53+
);
54+
55+
expect(groups).toPartiallyContain({ slug: 'aria', title: 'ARIA' });
56+
expect(groups).toPartiallyContain({
57+
slug: 'name-role-value',
58+
title: 'Names & Labels',
6859
});
60+
});
6961

70-
it('should format unknown category titles with title case', () => {
71-
const groups = transformRulesToGroups(
72-
[{ tags: ['cat.some-new-category', 'best-practice'] } as RuleMetadata],
73-
'best-practice',
74-
);
62+
it('should format unknown category titles with title case', () => {
63+
const groups = transformRulesToGroups(
64+
[{ tags: ['cat.some-new-category', 'best-practice'] } as RuleMetadata],
65+
'best-practice',
66+
);
7567

76-
expect(
77-
groups.find(({ slug }) => slug === 'some-new-category')?.title,
78-
).toBe('Some New Category');
68+
expect(groups).toPartiallyContain({
69+
slug: 'some-new-category',
70+
title: 'Some New Category',
7971
});
72+
});
8073

81-
it('should remove "cat." prefix from category slugs', () => {
82-
transformRulesToGroups(
83-
loadAxeRules('best-practice'),
84-
'best-practice',
85-
).forEach(({ slug }) => {
86-
expect(slug).not.toMatch(/^cat\./);
87-
});
88-
});
74+
it('should remove "cat." prefix from category slugs', () => {
75+
const groups = transformRulesToGroups(
76+
loadAxeRules('best-practice'),
77+
'best-practice',
78+
);
79+
80+
expect(groups).toSatisfyAll<Group>(({ slug }) => !slug.match(/^cat\./));
8981
});
9082

91-
describe('all preset', () => {
92-
it('should combine WCAG and category groups', () => {
93-
const groups = transformRulesToGroups(loadAxeRules('all'), 'all');
83+
it('should include both WCAG 2.2 and category groups for "all" preset', () => {
84+
const groups = transformRulesToGroups(loadAxeRules('all'), 'all');
9485

95-
expect(groups.filter(({ slug }) => slug.startsWith('wcag'))).toHaveLength(
96-
2,
97-
);
98-
expect(
99-
groups.filter(({ slug }) => !slug.startsWith('wcag')).length,
100-
).toBeGreaterThan(5);
101-
});
86+
expect(groups).toPartiallyContain({ slug: 'wcag22-level-a' });
87+
expect(groups).toPartiallyContain({ slug: 'wcag22-level-aa' });
10288

103-
it('should use WCAG 2.2 for all preset', () => {
104-
const groups = transformRulesToGroups(loadAxeRules('all'), 'all');
89+
expect(groups).toSatisfyAny(({ slug }) => !slug.startsWith('wcag'));
90+
});
10591

106-
expect(groups.some(({ slug }) => slug === 'wcag22-level-a')).toBe(true);
107-
expect(groups.some(({ slug }) => slug === 'wcag22-level-aa')).toBe(true);
108-
});
92+
it('should assign equal weight to all audit references within groups', () => {
93+
const groups = transformRulesToGroups(loadAxeRules('wcag21aa'), 'wcag21aa');
94+
95+
expect(groups).toSatisfyAll<Group>(({ refs }) =>
96+
refs.every(({ weight }) => weight === 1),
97+
);
10998
});
11099

111-
describe('group structure', () => {
112-
it('should have all refs with weight 1', () => {
113-
transformRulesToGroups(loadAxeRules('wcag21aa'), 'wcag21aa').forEach(
114-
({ refs }) => {
115-
refs.forEach(({ weight }) => {
116-
expect(weight).toBe(1);
117-
});
118-
},
119-
);
120-
});
100+
it('should filter out empty groups', () => {
101+
const groups = transformRulesToGroups(loadAxeRules('all'), 'all');
121102

122-
it('should filter out empty groups', () => {
123-
transformRulesToGroups(loadAxeRules('all'), 'all').forEach(({ refs }) => {
124-
expect(refs.length).toBeGreaterThan(0);
125-
});
126-
});
103+
expect(groups).toSatisfyAll<Group>(({ refs }) => refs.length > 0);
127104
});
128105
});

packages/plugin-axe/src/lib/processing.unit.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,32 @@ import { describe, expect, it } from 'vitest';
22
import { processAuditsAndGroups } from './processing';
33

44
describe('processAuditsAndGroups', () => {
5-
it('should return audits and groups without expansion for single URL', () => {
5+
it('should return audits and groups without expansion when analyzing single URL', () => {
66
const { audits, groups } = processAuditsAndGroups(
77
['https://example.com'],
88
'wcag21aa',
99
);
1010

11-
expect(audits.length).toBeGreaterThan(0);
12-
expect(groups.length).toBeGreaterThan(0);
11+
expect(audits).not.toBeEmpty();
12+
expect(groups).not.toBeEmpty();
1313

14-
expect(audits[0]?.slug).not.toContain('-1');
15-
expect(groups[0]?.slug).not.toContain('-1');
14+
expect(audits[0]!.slug).not.toContain('-1');
15+
expect(groups[0]!.slug).not.toContain('-1');
1616
});
1717

18-
it('should expand audits and groups for multiple URLs', () => {
18+
it('should expand audits and groups when analyzing multiple URLs', () => {
1919
const { audits, groups } = processAuditsAndGroups(
2020
['https://example.com', 'https://another-example.com'],
2121
'wcag21aa',
2222
);
2323

24-
expect(audits.length).toBeGreaterThan(0);
25-
expect(groups.length).toBeGreaterThan(0);
24+
expect(audits).not.toBeEmpty();
25+
expect(groups).not.toBeEmpty();
2626

27-
expect(audits[0]?.slug).toContain('-1');
28-
expect(groups[0]?.slug).toContain('-1');
27+
expect(audits[0]!.slug).toContain('-1');
28+
expect(groups[0]!.slug).toContain('-1');
2929

30-
expect(audits[0]?.title).toContain('(example.com)');
31-
expect(groups[0]?.title).toContain('(example.com)');
30+
expect(audits[0]!.title).toContain('(example.com)');
31+
expect(groups[0]!.title).toContain('(example.com)');
3232
});
3333
});

0 commit comments

Comments
 (0)