@d-zero/a11y-check-core 0.2.0 → 0.4.0
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/dist/clean-results.d.ts +4 -0
- package/dist/clean-results.js +4 -0
- package/dist/color-contrast.d.ts +4 -0
- package/dist/color-contrast.js +4 -0
- package/dist/color.d.ts +4 -0
- package/dist/color.js +4 -0
- package/dist/create-scenario.d.ts +4 -0
- package/dist/create-scenario.js +4 -0
- package/dist/import-scenarios.d.ts +6 -0
- package/dist/import-scenarios.js +12 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/sc-number-comparator.d.ts +5 -0
- package/dist/sc-number-comparator.js +5 -0
- package/dist/scenario-child-process.d.ts +4 -0
- package/dist/scenario-child-process.js +64 -0
- package/dist/scenario-main-process.d.ts +11 -0
- package/dist/scenario-main-process.js +48 -0
- package/dist/types.d.ts +5 -5
- package/package.json +8 -8
- package/dist/color-contrast.spec.d.ts +0 -1
- package/dist/color-contrast.spec.js +0 -41
- package/dist/sc-number-comparator.spec.d.ts +0 -1
- package/dist/sc-number-comparator.spec.js +0 -21
- package/dist/scenario-runner.d.ts +0 -5
- package/dist/scenario-runner.js +0 -91
package/dist/clean-results.d.ts
CHANGED
package/dist/clean-results.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { hash } from '@d-zero/shared/hash';
|
|
2
2
|
import { pathComparator } from '@d-zero/shared/sort/path';
|
|
3
3
|
import { scNumberComparator } from './sc-number-comparator.js';
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param results
|
|
7
|
+
*/
|
|
4
8
|
export function cleanResults(results) {
|
|
5
9
|
const hashMap = new Map();
|
|
6
10
|
for (const result of results) {
|
package/dist/color-contrast.d.ts
CHANGED
package/dist/color-contrast.js
CHANGED
|
@@ -9,6 +9,10 @@ export var ColorContrastError;
|
|
|
9
9
|
ColorContrastError[ColorContrastError["FOREGROUND_COLOR_HAS_ALPHA"] = 2] = "FOREGROUND_COLOR_HAS_ALPHA";
|
|
10
10
|
ColorContrastError[ColorContrastError["BACKGROUND_COLOR_HAS_ALPHA"] = 3] = "BACKGROUND_COLOR_HAS_ALPHA";
|
|
11
11
|
})(ColorContrastError || (ColorContrastError = {}));
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param style
|
|
15
|
+
*/
|
|
12
16
|
export function colorContrastCheck(style) {
|
|
13
17
|
const foreground = colorFnToHex(style.color);
|
|
14
18
|
if (!foreground) {
|
package/dist/color.d.ts
CHANGED
package/dist/color.js
CHANGED
package/dist/create-scenario.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param scenarios
|
|
4
|
+
*/
|
|
5
|
+
export async function importScenarios(scenarios) {
|
|
6
|
+
return await Promise.all(scenarios.map(async ([modulePath, options]) => {
|
|
7
|
+
const mod = await import(modulePath);
|
|
8
|
+
const creator = mod.default;
|
|
9
|
+
const optionsValue = options ? JSON.parse(options) : {};
|
|
10
|
+
return creator(optionsValue);
|
|
11
|
+
}));
|
|
12
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ export { colorContrastCheck, ColorContrastError } from './color-contrast.js';
|
|
|
2
2
|
export { colorFnToHex } from './color.js';
|
|
3
3
|
export { createScenario } from './create-scenario.js';
|
|
4
4
|
export { scNumberComparator } from './sc-number-comparator.js';
|
|
5
|
-
export { scenarioRunner } from './scenario-
|
|
5
|
+
export { scenarioRunner } from './scenario-main-process.js';
|
|
6
|
+
export { importScenarios } from './import-scenarios.js';
|
|
6
7
|
export * from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,6 @@ export { colorContrastCheck, ColorContrastError } from './color-contrast.js';
|
|
|
2
2
|
export { colorFnToHex } from './color.js';
|
|
3
3
|
export { createScenario } from './create-scenario.js';
|
|
4
4
|
export { scNumberComparator } from './sc-number-comparator.js';
|
|
5
|
-
export { scenarioRunner } from './scenario-
|
|
5
|
+
export { scenarioRunner } from './scenario-main-process.js';
|
|
6
|
+
export { importScenarios } from './import-scenarios.js';
|
|
6
7
|
export * from './types.js';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { importScenarios } from '@d-zero/a11y-check-core';
|
|
2
|
+
import { createChildProcess } from '@d-zero/puppeteer-dealer';
|
|
3
|
+
import { beforePageScan, defaultSizes, pageScanListener, } from '@d-zero/puppeteer-page-scan';
|
|
4
|
+
import { Cache } from '@d-zero/shared/cache';
|
|
5
|
+
import c from 'ansi-colors';
|
|
6
|
+
createChildProcess(async (param) => {
|
|
7
|
+
const { cacheDir } = param;
|
|
8
|
+
const cache = new Cache('a11y-check/run-puppeteer', cacheDir);
|
|
9
|
+
const sizes = {
|
|
10
|
+
desktop: defaultSizes.desktop,
|
|
11
|
+
mobile: defaultSizes.mobile,
|
|
12
|
+
};
|
|
13
|
+
const scenarios = await importScenarios(param.scenarios);
|
|
14
|
+
return {
|
|
15
|
+
// async beforeOpenPage(_, url, logger) {
|
|
16
|
+
// if (options?.cache === false) {
|
|
17
|
+
// logger('Clearing cache');
|
|
18
|
+
// await cache.clear();
|
|
19
|
+
// }
|
|
20
|
+
// logger('Restoring cache');
|
|
21
|
+
// const cached = await cache.load(url, (key, value) => {
|
|
22
|
+
// if (key === 'timestamp') {
|
|
23
|
+
// return new Date(Date.parse(value));
|
|
24
|
+
// }
|
|
25
|
+
// return value;
|
|
26
|
+
// });
|
|
27
|
+
// if (cached) {
|
|
28
|
+
// logger('Hit cache, skipping page scan');
|
|
29
|
+
// await delay(600);
|
|
30
|
+
// needAnalysis.push(...cached.needAnalysis);
|
|
31
|
+
// passed.push(...cached.passed);
|
|
32
|
+
// violations.push(...cached.violations);
|
|
33
|
+
// return false;
|
|
34
|
+
// }
|
|
35
|
+
// return true;
|
|
36
|
+
// },
|
|
37
|
+
async eachPage({ page, url }, logger) {
|
|
38
|
+
const urlNeedAnalysis = [];
|
|
39
|
+
const urlPassed = [];
|
|
40
|
+
const urlViolations = [];
|
|
41
|
+
for (const [name, size] of Object.entries(sizes)) {
|
|
42
|
+
const sizeLabel = c.bgMagenta(` ${name} `);
|
|
43
|
+
for (const scenario of scenarios) {
|
|
44
|
+
await beforePageScan(page, url, {
|
|
45
|
+
name,
|
|
46
|
+
...size,
|
|
47
|
+
listener: pageScanListener(logger),
|
|
48
|
+
});
|
|
49
|
+
const scenarioResult = await scenario.exec(page, name, (log) => logger(`${sizeLabel} ${log}`));
|
|
50
|
+
urlNeedAnalysis.push(...(scenarioResult.needAnalysis ?? []));
|
|
51
|
+
urlPassed.push(...(scenarioResult.passed ?? []));
|
|
52
|
+
urlViolations.push(...(scenarioResult.violations ?? []));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const urlResults = {
|
|
56
|
+
needAnalysis: urlNeedAnalysis,
|
|
57
|
+
passed: urlPassed,
|
|
58
|
+
violations: urlViolations,
|
|
59
|
+
};
|
|
60
|
+
await cache.store(url, urlResults);
|
|
61
|
+
return urlResults;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CoreOptions, Result, ScenarioRunnerOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param urlList
|
|
5
|
+
* @param scenarios
|
|
6
|
+
* @param options
|
|
7
|
+
*/
|
|
8
|
+
export declare function scenarioRunner<O>(urlList: readonly (string | {
|
|
9
|
+
id: string | null;
|
|
10
|
+
url: string;
|
|
11
|
+
})[], scenarios: readonly [modulePath: string, options?: string][], options?: O & CoreOptions & ScenarioRunnerOptions): Promise<Result>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { importScenarios } from '@d-zero/a11y-check-core';
|
|
3
|
+
import { createProcess, deal } from '@d-zero/puppeteer-dealer';
|
|
4
|
+
import c from 'ansi-colors';
|
|
5
|
+
import { cleanResults } from './clean-results.js';
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param urlList
|
|
9
|
+
* @param scenarios
|
|
10
|
+
* @param options
|
|
11
|
+
*/
|
|
12
|
+
export async function scenarioRunner(urlList, scenarios, options) {
|
|
13
|
+
const needAnalysis = [];
|
|
14
|
+
const passed = [];
|
|
15
|
+
const violations = [];
|
|
16
|
+
await deal(urlList.map((url) => {
|
|
17
|
+
if (typeof url === 'string') {
|
|
18
|
+
return { id: null, url };
|
|
19
|
+
}
|
|
20
|
+
return url;
|
|
21
|
+
}), (_, done, total) => {
|
|
22
|
+
return `${c.bold.magenta('🧿 A11y checking%dots%')} ${done}/${total}`;
|
|
23
|
+
}, () => {
|
|
24
|
+
return createProcess(path.resolve(import.meta.dirname, 'scenario-child-process.js'), {
|
|
25
|
+
scenarios,
|
|
26
|
+
cacheDir: options?.cacheDir ?? '.a11y-check-core',
|
|
27
|
+
}, options);
|
|
28
|
+
}, (result) => {
|
|
29
|
+
needAnalysis.push(...result.needAnalysis);
|
|
30
|
+
passed.push(...result.passed);
|
|
31
|
+
violations.push(...result.violations);
|
|
32
|
+
});
|
|
33
|
+
const cleanedViolations = cleanResults(violations);
|
|
34
|
+
process.stdout.write(`📊 Found ${cleanedViolations.length} violations\n`);
|
|
35
|
+
const scenarioModules = await importScenarios(scenarios);
|
|
36
|
+
for (const scenario of scenarioModules) {
|
|
37
|
+
const targets = needAnalysis.filter((result) => result.scenarioId === scenario.id);
|
|
38
|
+
if (targets.length === 0) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
await scenario.analyze?.(targets, (log) => process.stdout.write(`${log}\n`));
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
needAnalysis: needAnalysis,
|
|
45
|
+
passed: passed,
|
|
46
|
+
violations: cleanedViolations,
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DealOptions } from '@d-zero/dealer';
|
|
2
|
-
import type { Page } from '@d-zero/puppeteer-page';
|
|
3
2
|
import type { PageHook } from '@d-zero/puppeteer-page-scan';
|
|
3
|
+
import type { Page } from 'puppeteer';
|
|
4
4
|
export type CoreOptions = {
|
|
5
5
|
readonly screenshot?: boolean;
|
|
6
6
|
readonly cache?: boolean;
|
|
@@ -12,10 +12,10 @@ export type ScenarioRunnerOptions = DealOptions & {
|
|
|
12
12
|
};
|
|
13
13
|
export type ScenarioCreator<O> = (options?: O) => Scenario;
|
|
14
14
|
export type Scenario = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
exec: ScenarioExecutor;
|
|
18
|
-
analyze?: ScenarioAnalyzer;
|
|
15
|
+
readonly modulePath: string;
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly exec: ScenarioExecutor;
|
|
18
|
+
readonly analyze?: ScenarioAnalyzer;
|
|
19
19
|
};
|
|
20
20
|
export type ScenarioExecutor = (page: Page, sizeName: string, log: (log: string) => void) => Promise<Partial<Result>>;
|
|
21
21
|
export type ScenarioAnalyzer = (results: NeedAnalysis[], log: (log: string) => void) => Promise<void | Partial<Result>> | void | Partial<Result>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d-zero/a11y-check-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Accessibility Checker (Core Module)",
|
|
5
5
|
"author": "D-ZERO",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
"clean": "tsc --build --clean"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@d-zero/puppeteer-dealer": "0.
|
|
28
|
-
"@d-zero/shared": "0.
|
|
27
|
+
"@d-zero/puppeteer-dealer": "0.4.0",
|
|
28
|
+
"@d-zero/shared": "0.8.0",
|
|
29
29
|
"ansi-colors": "4.1.3",
|
|
30
|
-
"color-contrast-checker": "2.1.0"
|
|
30
|
+
"color-contrast-checker": "2.1.0",
|
|
31
|
+
"puppeteer": "24.9.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"@d-zero/dealer": "1.
|
|
34
|
-
"@d-zero/puppeteer-page": "0.
|
|
35
|
-
"@d-zero/puppeteer-page-scan": "2.0.0"
|
|
34
|
+
"@d-zero/dealer": "1.3.1",
|
|
35
|
+
"@d-zero/puppeteer-page-scan": "4.0.0"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "4e9cc7b87e0fef91b6f2d4edfb66ca9134b2491b"
|
|
38
38
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
2
|
-
import { colorContrastCheck, ColorContrastError } from './color-contrast.js';
|
|
3
|
-
test('#000 vs #FFF', () => {
|
|
4
|
-
expect(colorContrastCheck({
|
|
5
|
-
color: 'rgb(0, 0, 0)',
|
|
6
|
-
backgroundColor: 'rgb(255, 255, 255)',
|
|
7
|
-
backgroundImage: '',
|
|
8
|
-
closestBackgroundColor: null,
|
|
9
|
-
closestBackgroundImage: null,
|
|
10
|
-
})).toStrictEqual({
|
|
11
|
-
foreground: {
|
|
12
|
-
a: 1,
|
|
13
|
-
b: 0,
|
|
14
|
-
g: 0,
|
|
15
|
-
hex: '#000000',
|
|
16
|
-
hexA: '#000000FF',
|
|
17
|
-
r: 0,
|
|
18
|
-
},
|
|
19
|
-
background: {
|
|
20
|
-
a: 1,
|
|
21
|
-
b: 255,
|
|
22
|
-
g: 255,
|
|
23
|
-
hex: '#FFFFFF',
|
|
24
|
-
hexA: '#FFFFFFFF',
|
|
25
|
-
r: 255,
|
|
26
|
-
},
|
|
27
|
-
ratio: 21,
|
|
28
|
-
ratioText: '21:1',
|
|
29
|
-
AA: true,
|
|
30
|
-
AAA: true,
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
test('has alpha channel', () => {
|
|
34
|
-
expect(colorContrastCheck({
|
|
35
|
-
color: 'rgba(0, 0, 0, 0.5)',
|
|
36
|
-
backgroundColor: 'rgb(255, 255, 255)',
|
|
37
|
-
backgroundImage: '',
|
|
38
|
-
closestBackgroundColor: null,
|
|
39
|
-
closestBackgroundImage: null,
|
|
40
|
-
})).toBe(ColorContrastError.FOREGROUND_COLOR_HAS_ALPHA);
|
|
41
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
2
|
-
import { scNumberComparator } from './sc-number-comparator.js';
|
|
3
|
-
test('scNumberComparator', () => {
|
|
4
|
-
expect([
|
|
5
|
-
//
|
|
6
|
-
'1.2.3',
|
|
7
|
-
'1.1.1',
|
|
8
|
-
'1.2.1',
|
|
9
|
-
'1.1.2',
|
|
10
|
-
null,
|
|
11
|
-
'1.1.3',
|
|
12
|
-
].toSorted(scNumberComparator)).toStrictEqual([
|
|
13
|
-
//
|
|
14
|
-
'1.1.1',
|
|
15
|
-
'1.1.2',
|
|
16
|
-
'1.1.3',
|
|
17
|
-
'1.2.1',
|
|
18
|
-
'1.2.3',
|
|
19
|
-
null,
|
|
20
|
-
]);
|
|
21
|
-
});
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { CoreOptions, Result, Scenario, ScenarioRunnerOptions } from './types.js';
|
|
2
|
-
export declare function scenarioRunner<O>(urlList: readonly (string | {
|
|
3
|
-
id: string | null;
|
|
4
|
-
url: string;
|
|
5
|
-
})[], scenarios: readonly Scenario[], options?: O & CoreOptions & ScenarioRunnerOptions): Promise<Result>;
|
package/dist/scenario-runner.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { deal } from '@d-zero/puppeteer-dealer';
|
|
2
|
-
import { beforePageScan, defaultSizes, pageScanListener, } from '@d-zero/puppeteer-page-scan';
|
|
3
|
-
import { Cache } from '@d-zero/shared/cache';
|
|
4
|
-
import { delay } from '@d-zero/shared/delay';
|
|
5
|
-
import c from 'ansi-colors';
|
|
6
|
-
import { cleanResults } from './clean-results.js';
|
|
7
|
-
export async function scenarioRunner(urlList, scenarios, options) {
|
|
8
|
-
const cache = new Cache('a11y-check/run-puppeteer', options?.cacheDir);
|
|
9
|
-
const hooks = options?.hooks;
|
|
10
|
-
const sizes = {
|
|
11
|
-
desktop: defaultSizes.desktop,
|
|
12
|
-
mobile: defaultSizes.mobile,
|
|
13
|
-
};
|
|
14
|
-
const needAnalysis = [];
|
|
15
|
-
const passed = [];
|
|
16
|
-
const violations = [];
|
|
17
|
-
await deal(urlList.map((url) => {
|
|
18
|
-
if (typeof url === 'string') {
|
|
19
|
-
return { id: null, url };
|
|
20
|
-
}
|
|
21
|
-
return url;
|
|
22
|
-
}), (_, done, total) => {
|
|
23
|
-
return `${c.bold.magenta('🧿 A11y checking%dots%')} ${done}/${total}`;
|
|
24
|
-
}, {
|
|
25
|
-
async beforeOpenPage(_, url, logger) {
|
|
26
|
-
if (options?.cache === false) {
|
|
27
|
-
logger('Clearing cache');
|
|
28
|
-
await cache.clear();
|
|
29
|
-
}
|
|
30
|
-
logger('Restoring cache');
|
|
31
|
-
const cached = await cache.load(url, (key, value) => {
|
|
32
|
-
if (key === 'timestamp') {
|
|
33
|
-
return new Date(Date.parse(value));
|
|
34
|
-
}
|
|
35
|
-
return value;
|
|
36
|
-
});
|
|
37
|
-
if (cached) {
|
|
38
|
-
logger('Hit cache, skipping page scan');
|
|
39
|
-
await delay(600);
|
|
40
|
-
needAnalysis.push(...cached.needAnalysis);
|
|
41
|
-
passed.push(...cached.passed);
|
|
42
|
-
violations.push(...cached.violations);
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
46
|
-
},
|
|
47
|
-
async deal(page, _, url, logger) {
|
|
48
|
-
const urlNeedAnalysis = [];
|
|
49
|
-
const urlPassed = [];
|
|
50
|
-
const urlViolations = [];
|
|
51
|
-
for (const [name, size] of Object.entries(sizes)) {
|
|
52
|
-
const sizeLabel = c.bgMagenta(` ${name} `);
|
|
53
|
-
for (const scenario of scenarios) {
|
|
54
|
-
await beforePageScan(page, url, {
|
|
55
|
-
name,
|
|
56
|
-
...size,
|
|
57
|
-
hooks,
|
|
58
|
-
listener: pageScanListener(logger),
|
|
59
|
-
});
|
|
60
|
-
const scenarioResult = await scenario.exec(page, name, (log) => logger(`${sizeLabel} ${log}`));
|
|
61
|
-
urlNeedAnalysis.push(...(scenarioResult.needAnalysis ?? []));
|
|
62
|
-
urlPassed.push(...(scenarioResult.passed ?? []));
|
|
63
|
-
urlViolations.push(...(scenarioResult.violations ?? []));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const urlResults = {
|
|
67
|
-
needAnalysis: urlNeedAnalysis,
|
|
68
|
-
passed: urlPassed,
|
|
69
|
-
violations: urlViolations,
|
|
70
|
-
};
|
|
71
|
-
await cache.store(url, urlResults);
|
|
72
|
-
needAnalysis.push(...urlNeedAnalysis);
|
|
73
|
-
passed.push(...urlPassed);
|
|
74
|
-
violations.push(...urlViolations);
|
|
75
|
-
},
|
|
76
|
-
}, options);
|
|
77
|
-
const cleanedViolations = cleanResults(violations);
|
|
78
|
-
process.stdout.write(`📊 Found ${cleanedViolations.length} violations\n`);
|
|
79
|
-
for (const scenario of scenarios) {
|
|
80
|
-
const targets = needAnalysis.filter((result) => result.scenarioId === scenario.id);
|
|
81
|
-
if (targets.length === 0) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
await scenario.analyze?.(targets, (log) => process.stdout.write(`${log}\n`));
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
needAnalysis: needAnalysis,
|
|
88
|
-
passed: passed,
|
|
89
|
-
violations: cleanedViolations,
|
|
90
|
-
};
|
|
91
|
-
}
|