@public-ui/visual-tests 4.0.0-alpha.3 → 4.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,6 +56,7 @@ Add the following npm scripts to the theme's `package.json`:
56
56
  - `THEME_EXPERT`: Define the name of the export within the module. (e.g., `export const THEME_NAME = {/**/};`) Defaults to `default`.
57
57
  - `KOLIBRI_VISUAL_TESTS_TIMEOUT`: Define the Playwright [test timeout](https://playwright.dev/docs/test-timeouts).
58
58
  - `KOLIBRI_VISUAL_TESTS_EXPECT_TIMEOUT`: Define the Playwright [expect timeout](https://playwright.dev/docs/test-timeouts).
59
+ - `KOLIBRI_VISUAL_TESTS_COLOR_SCHEME`: Choose the [CSS color scheme](https://developer.mozilla.org/docs/Web/CSS/@media/prefers-color-scheme) for the browser context. Supported values are `light` (default) and `dark`.
59
60
 
60
61
  Run the tests with `npm test`. The first time, this will create a new folder `snapshots` which is supposed to be committed to the repository.
61
62
  In the following runs, new screenshots will be compared to this reference.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@public-ui/visual-tests",
3
- "version": "4.0.0-alpha.3",
3
+ "version": "4.0.0-alpha.5",
4
4
  "license": "EUPL-1.2",
5
5
  "homepage": "https://public-ui.github.io",
6
6
  "repository": {
@@ -25,10 +25,11 @@
25
25
  "kolibri-visual-test": "src/index.js"
26
26
  },
27
27
  "dependencies": {
28
- "axe-playwright": "2.2.2",
28
+ "@axe-core/playwright": "4.11.0",
29
+ "axe-html-reporter": "2.2.11",
29
30
  "portfinder": "1.0.38",
30
31
  "serve": "14.2.5",
31
- "@public-ui/sample-react": "4.0.0-alpha.3"
32
+ "@public-ui/sample-react": "4.0.0-alpha.5"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@babel/eslint-parser": "7.28.4",
@@ -37,7 +38,8 @@
37
38
  "@playwright/test": "1.56.0",
38
39
  "eslint": "8.57.1",
39
40
  "knip": "5.65.0",
40
- "prettier": "3.6.2"
41
+ "prettier": "3.6.2",
42
+ "prettier-plugin-organize-imports": "4.1.0"
41
43
  },
42
44
  "peerDependencies": {
43
45
  "@playwright/test": "1.56.0"
@@ -12,6 +12,16 @@ const EXPECT_TIMEOUT = parseInt(process.env.KOLIBRI_VISUAL_TESTS_EXPECT_TIMEOUT
12
12
  const BUILD_PATH = process.env.KOLIBRI_VISUAL_TESTS_BUILD_PATH ?? '';
13
13
  const THEME = (process.env.THEME_EXPORT || 'default').toLocaleLowerCase();
14
14
 
15
+ const VALID_COLOR_SCHEMES = ['light', 'dark'];
16
+ const colorSchemeInput = process.env.KOLIBRI_VISUAL_TESTS_COLOR_SCHEME;
17
+ const colorSchema = (colorSchemeInput || 'light').toLocaleLowerCase();
18
+
19
+ if (!VALID_COLOR_SCHEMES.includes(colorSchema)) {
20
+ throw new Error(
21
+ `Environment variable KOLIBRI_VISUAL_TESTS_COLOR_SCHEME must be one of "${VALID_COLOR_SCHEMES.join('", "')}" (received "${colorSchemeInput}").`,
22
+ );
23
+ }
24
+
15
25
  /**
16
26
  * See https://playwright.dev/docs/test-configuration.
17
27
  */
@@ -68,5 +78,5 @@ export default defineConfig({
68
78
  url: BASE_URL,
69
79
  reuseExistingServer: false,
70
80
  },
71
- snapshotPathTemplate: `{snapshotDir}/theme-${THEME}/{arg}-{projectName}-{platform}{ext}`,
81
+ snapshotPathTemplate: `{snapshotDir}/theme-${THEME}${colorSchema === 'light' ? '' : `-${colorSchema}`}/{arg}-{projectName}-{platform}{ext}`,
72
82
  });
package/src/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import child_process from 'node:child_process';
2
- import path from 'node:path';
3
- import { fileURLToPath, pathToFileURL } from 'url';
4
1
  import * as crypto from 'crypto';
5
- import { readFile } from 'fs/promises';
6
2
  import * as fs from 'fs';
3
+ import { readFile } from 'fs/promises';
4
+ import child_process from 'node:child_process';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
7
  import portfinder from 'portfinder';
8
8
  import * as process from 'process';
9
- import os from 'node:os';
9
+ import { fileURLToPath, pathToFileURL } from 'url';
10
10
 
11
11
  const tempDir = process.env.RUNNER_TEMP || process.env.TMPDIR || os.tmpdir(); // TODO: Check on Windows
12
12
 
@@ -1,7 +1,12 @@
1
+ import AxeBuilder from '@axe-core/playwright';
1
2
  import { test } from '@playwright/test';
2
- import { checkA11y, injectAxe } from 'axe-playwright';
3
- import { ROUTES } from './sample-app.routes.js';
3
+ import axeHtmlReporter from 'axe-html-reporter';
4
4
  import process from 'process';
5
+ import { ROUTES } from './sample-app.routes.js';
6
+
7
+ const { createHtmlReport } = axeHtmlReporter;
8
+
9
+ const AXE_TAGS = ['best-practices', 'wcag2a', 'wcag2aa', 'wcag21aa'];
5
10
 
6
11
  const themeName = (process.env.THEME_EXPORT || 'default').toLocaleLowerCase();
7
12
  const rename = (snapshotName) => {
@@ -25,6 +30,25 @@ const rename = (snapshotName) => {
25
30
  return result;
26
31
  };
27
32
 
33
+ const sanitizeRouteForReport = (route) => route.replace(/[/?]/g, '-').replace(/^-+/, '') || 'root';
34
+
35
+ const buildReportOptions = (testInfo, route) => ({
36
+ projectKey: `axe-${themeName}`,
37
+ reportFileName: `${sanitizeRouteForReport(route)}.html`,
38
+ outputDirPath: rename(testInfo.outputDir),
39
+ outputDir: `axe-${themeName}`,
40
+ });
41
+
42
+ const logViolations = (route, violations) => {
43
+ if (!violations?.length) {
44
+ return;
45
+ }
46
+ console.warn(`Axe found ${violations.length} violation(s) on ${route}`);
47
+ for (const violation of violations) {
48
+ console.warn(`- ${violation.id}: ${violation.help} (${violation.nodes.length} nodes)`);
49
+ }
50
+ };
51
+
28
52
  // https://playwright.dev/docs/emulation
29
53
  test.use({
30
54
  colorScheme: 'light',
@@ -43,7 +67,6 @@ ROUTES.forEach((options, route) => {
43
67
  return;
44
68
  }
45
69
  test(`snapshot for ${route}`, async ({ page }, testInfo) => {
46
- const outputPath = rename(testInfo.outputDir);
47
70
  const hideMenusParam = `${route.includes('?') ? '&' : '?'}hideMenus`;
48
71
  await page.goto(`/#${route}${hideMenusParam}`);
49
72
  await page.waitForLoadState('networkidle');
@@ -62,29 +85,16 @@ ROUTES.forEach((options, route) => {
62
85
  await page.waitForTimeout(options?.snapshot?.waitForTimeout);
63
86
  }
64
87
 
65
- await injectAxe(page);
66
- await checkA11y(
67
- page,
68
- undefined,
69
- {
70
- axeOptions: {
71
- runOnly: {
72
- type: 'tag',
73
- values: ['best-practices', 'wcag2a', 'wcag2aa', 'wcag21aa'],
74
- },
75
- },
76
- detailedReport: true,
77
- detailedReportOptions: {
78
- html: true,
79
- },
80
- },
81
- true, // options?.axe?.skipFailures ?? false,
82
- 'html',
83
- {
84
- outputDirPath: outputPath.replace(/\/[^/]+$/, ''),
85
- outputDir: `axe-${themeName}`,
86
- reportFileName: `${route.replace(/[/?]/g, '-')}.html`,
87
- },
88
- );
88
+ const builder = new AxeBuilder({ page }).withTags(AXE_TAGS);
89
+ const results = await builder.analyze();
90
+ await testInfo.attach('axe-results', {
91
+ body: JSON.stringify(results, null, 2),
92
+ contentType: 'application/json',
93
+ });
94
+ createHtmlReport({
95
+ results,
96
+ options: buildReportOptions(testInfo, route),
97
+ });
98
+ logViolations(route, results.violations);
89
99
  });
90
100
  });
@@ -119,57 +119,49 @@ ROUTES.set('button-link/image', {
119
119
  },
120
120
  },
121
121
  });
122
- ROUTES.set('button/basic', {
122
+ ROUTES.set('button/variants', {
123
123
  snapshot: {
124
124
  zoom: {
125
125
  skip: true,
126
126
  },
127
127
  },
128
128
  });
129
- ROUTES.set('button/icons', {
129
+ ROUTES.set('button/disabled', {
130
130
  snapshot: {
131
- skip: true,
132
131
  zoom: {
133
132
  skip: true,
134
133
  },
135
134
  },
136
135
  });
137
- ROUTES.set('button/width', {
136
+ ROUTES.set('button/hide-label', {
138
137
  snapshot: {
139
- skip: true,
140
138
  zoom: {
141
139
  skip: true,
142
140
  },
143
141
  },
144
142
  });
145
- ROUTES.set('button/access-key', {
143
+ ROUTES.set('button/icons', {
146
144
  snapshot: {
147
- skip: true,
148
145
  zoom: {
149
146
  skip: true,
150
147
  },
151
148
  },
152
149
  });
153
- ROUTES.set('button/baselined', {
150
+ ROUTES.set('button/short-key', {
154
151
  snapshot: {
155
- skip: true,
156
152
  zoom: {
157
153
  skip: true,
158
154
  },
159
155
  },
160
156
  });
161
- ROUTES.set('button/short-key', {
157
+ ROUTES.set('card/basic', {
162
158
  snapshot: {
163
- viewportSize: {
164
- width: 800,
165
- height: 434,
166
- },
167
159
  zoom: {
168
160
  skip: true,
169
161
  },
170
162
  },
171
163
  });
172
- ROUTES.set('card/basic', {
164
+ ROUTES.set('card/headlines', {
173
165
  snapshot: {
174
166
  zoom: {
175
167
  skip: true,
@@ -297,7 +289,18 @@ ROUTES.set('icon/basic', {
297
289
  snapshot: {
298
290
  viewportSize: {
299
291
  width: 60,
300
- height: 80,
292
+ height: 200,
293
+ },
294
+ zoom: {
295
+ skip: true,
296
+ },
297
+ },
298
+ });
299
+ ROUTES.set('icon/font-awesome', {
300
+ snapshot: {
301
+ viewportSize: {
302
+ width: 250,
303
+ height: 345,
301
304
  },
302
305
  zoom: {
303
306
  skip: true,
@@ -572,6 +575,17 @@ ROUTES.set('pagination/basic', {
572
575
  },
573
576
  },
574
577
  });
578
+ ROUTES.set('popover-button/basic', {
579
+ snapshot: {
580
+ zoom: {
581
+ skip: true,
582
+ },
583
+ viewportSize: {
584
+ width: 200,
585
+ height: 220,
586
+ }
587
+ },
588
+ });
575
589
  ROUTES.set('progress/basic', {
576
590
  snapshot: {
577
591
  zoom: {
@@ -604,7 +618,6 @@ ROUTES.set('select/basic?noColumns', {
604
618
  });
605
619
  ROUTES.set('skip-nav/basic', {
606
620
  snapshot: {
607
- skip: true,
608
621
  zoom: {
609
622
  skip: true,
610
623
  },
@@ -1,4 +1,4 @@
1
- import { test, expect } from '@playwright/test';
1
+ import { expect, test } from '@playwright/test';
2
2
  import { ROUTES } from './sample-app.routes.js';
3
3
 
4
4
  // https://playwright.dev/docs/emulation