vi-axe 0.0.4 → 0.0.6
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 +133 -0
- package/dist/extend-expect.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-hfrDQhV4.js → src-CypcCU5u.js} +32 -47
- package/package.json +16 -16
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# vi-axe
|
|
2
|
+
|
|
3
|
+
### A modern fork / rewrite of jest-axe for vitest.
|
|
4
|
+
|
|
5
|
+
I'm aware that vitest-axe also exists, however it seems to be unmaintained.
|
|
6
|
+
|
|
7
|
+
[](http://npm.im/vi-axe)
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
Custom Vitest matcher for [aXe](https://github.com/dequelabs/axe-core) to test accessibility in your components and pages.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add -D vi-axe
|
|
16
|
+
# or
|
|
17
|
+
npm install -D vi-axe
|
|
18
|
+
# or
|
|
19
|
+
yarn add -D vi-axe
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
Configure Vitest so the `toHaveNoViolations` matcher is available in all tests. In your Vitest config (e.g. `vitest.config.ts`):
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { defineConfig } from "vitest/config";
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
test: {
|
|
31
|
+
environment: "jsdom", // required for axe
|
|
32
|
+
setupFiles: ["vi-axe/extend-expect"],
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Alternatively, in a single test file:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import "vi-axe/extend-expect";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Basic
|
|
46
|
+
|
|
47
|
+
Run axe on an HTML string or DOM element, then assert there are no violations:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { axe } from "vi-axe";
|
|
51
|
+
|
|
52
|
+
test("has no a11y violations", async () => {
|
|
53
|
+
const html = `<main><a href="https://example.com">Example</a></main>`;
|
|
54
|
+
const results = await axe(html);
|
|
55
|
+
expect(results).toHaveNoViolations();
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### With React Testing Library
|
|
60
|
+
|
|
61
|
+
Pass the rendered container (or any element) to `axe`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { render } from "@testing-library/react";
|
|
65
|
+
import { axe } from "vi-axe";
|
|
66
|
+
|
|
67
|
+
test("component has no a11y violations", async () => {
|
|
68
|
+
const { container } = render(<MyComponent />);
|
|
69
|
+
const results = await axe(container);
|
|
70
|
+
expect(results).toHaveNoViolations();
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With Vue Test Utils
|
|
75
|
+
|
|
76
|
+
Pass the wrapper element:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { mount } from "@vue/test-utils";
|
|
80
|
+
import { axe } from "vi-axe";
|
|
81
|
+
|
|
82
|
+
test("component has no a11y violations", async () => {
|
|
83
|
+
const wrapper = mount(MyComponent);
|
|
84
|
+
const results = await axe(wrapper.element);
|
|
85
|
+
expect(results).toHaveNoViolations();
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Custom configuration
|
|
90
|
+
|
|
91
|
+
Use `configureAxe` to create an axe runner with default options. You can pass [axe-core run options](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md) and vi-axe-specific options:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { configureAxe } from "vi-axe";
|
|
95
|
+
|
|
96
|
+
const axe = configureAxe({
|
|
97
|
+
globalOptions: {
|
|
98
|
+
rules: [{ id: "link-name", enabled: false }],
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("custom axe run", async () => {
|
|
103
|
+
const results = await axe("<a href='#'></a>");
|
|
104
|
+
expect(results).toHaveNoViolations();
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Per-run options can be passed as the second argument:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const results = await axe(html, {
|
|
112
|
+
rules: { "link-name": { enabled: false } },
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Filtering by impact level
|
|
117
|
+
|
|
118
|
+
To fail only for certain impact levels (e.g. critical/serious), use `impactLevels` in `configureAxe`. The matcher will then consider only violations with those impacts:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { configureAxe } from "vi-axe";
|
|
122
|
+
|
|
123
|
+
const axe = configureAxe({
|
|
124
|
+
impactLevels: ["critical", "serious"],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Requirements
|
|
129
|
+
|
|
130
|
+
- **Node**: >= 20
|
|
131
|
+
- **Vitest** with **jsdom** (or another DOM environment); axe needs a DOM to run.
|
|
132
|
+
|
|
133
|
+
Color contrast rules are disabled by default in vi-axe because they do not work reliably in jsdom.
|
package/dist/extend-expect.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./src-
|
|
1
|
+
import { n as e, r as t, t as n } from "./src-CypcCU5u.js";
|
|
2
2
|
export { n as axe, t as configureAxe, e as toHaveNoViolations };
|
|
@@ -2534,15 +2534,8 @@ var t = Object.create, n = Object.defineProperty, r = Object.getOwnPropertyDescr
|
|
|
2534
2534
|
symbol: !0
|
|
2535
2535
|
};
|
|
2536
2536
|
t.exports = function() {
|
|
2537
|
-
var e = n.Symbol
|
|
2538
|
-
|
|
2539
|
-
t = e("test symbol");
|
|
2540
|
-
try {
|
|
2541
|
-
String(t);
|
|
2542
|
-
} catch {
|
|
2543
|
-
return !1;
|
|
2544
|
-
}
|
|
2545
|
-
return !(!r[a(e.iterator)] || !r[a(e.toPrimitive)] || !r[a(e.toStringTag)]);
|
|
2537
|
+
var e = n.Symbol;
|
|
2538
|
+
return !(typeof e != "function" || (e("test symbol"), !r[a(e.iterator)]) || !r[a(e.toPrimitive)] || !r[a(e.toStringTag)]);
|
|
2546
2539
|
};
|
|
2547
2540
|
}), br = B(function(e, t) {
|
|
2548
2541
|
t.exports = function(e) {
|
|
@@ -13041,8 +13034,8 @@ var t = Object.create, n = Object.defineProperty, r = Object.getOwnPropertyDescr
|
|
|
13041
13034
|
var n = e.substring(1).split("&");
|
|
13042
13035
|
if (!n || !n.length) return t;
|
|
13043
13036
|
for (var r = 0; r < n.length; r++) {
|
|
13044
|
-
var i = L(n[r].split("="), 2), a = i[0], o = i[1]
|
|
13045
|
-
t[decodeURIComponent(a)] = decodeURIComponent(
|
|
13037
|
+
var i = L(n[r].split("="), 2), a = i[0], o = i[1];
|
|
13038
|
+
t[decodeURIComponent(a)] = decodeURIComponent(o === void 0 ? "" : o);
|
|
13046
13039
|
}
|
|
13047
13040
|
return t;
|
|
13048
13041
|
}
|
|
@@ -34514,11 +34507,11 @@ See: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`);
|
|
|
34514
34507
|
var n = L(t, 2), r = n[0], i = n[1];
|
|
34515
34508
|
return x(Math.atan2(parseFloat(i), parseFloat(r)));
|
|
34516
34509
|
}
|
|
34517
|
-
var a = parseFloat(t[8]), o = Math.
|
|
34518
|
-
return x(Math.acos(parseFloat(t[0]) /
|
|
34510
|
+
var a = parseFloat(t[8]), o = Math.cos(Math.asin(a));
|
|
34511
|
+
return x(Math.acos(parseFloat(t[0]) / o));
|
|
34519
34512
|
}
|
|
34520
34513
|
function x(e) {
|
|
34521
|
-
return Math.round(
|
|
34514
|
+
return Math.round(180 / Math.PI * e);
|
|
34522
34515
|
}
|
|
34523
34516
|
function S(e) {
|
|
34524
34517
|
return e %= 400, e < 0 && (e += 400), Math.round(e / 400 * 360);
|
|
@@ -35337,10 +35330,10 @@ See: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`);
|
|
|
35337
35330
|
return n && r;
|
|
35338
35331
|
}
|
|
35339
35332
|
function QE(e) {
|
|
35340
|
-
var t = L(e.match(/^([0-9.]+)([a-z]+)$/i) || [], 3), n = t[1], r = n === void 0 ? "" : n, i = t[2]
|
|
35333
|
+
var t = L(e.match(/^([0-9.]+)([a-z]+)$/i) || [], 3), n = t[1], r = n === void 0 ? "" : n, i = t[2];
|
|
35341
35334
|
return {
|
|
35342
35335
|
value: parseFloat(r),
|
|
35343
|
-
unit:
|
|
35336
|
+
unit: (i === void 0 ? "" : i).toLowerCase()
|
|
35344
35337
|
};
|
|
35345
35338
|
}
|
|
35346
35339
|
var $E = {
|
|
@@ -42285,7 +42278,10 @@ See: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`);
|
|
|
42285
42278
|
return !1;
|
|
42286
42279
|
}
|
|
42287
42280
|
t.exports = cn;
|
|
42288
|
-
})), f = /* @__PURE__ */ l(u(), 1), p = /* @__PURE__ */ l(d(), 1), m = f.default.getRules(["cat.color"])
|
|
42281
|
+
})), f = /* @__PURE__ */ l(u(), 1), p = /* @__PURE__ */ l(d(), 1), m = f.default.getRules(["cat.color"]).map(({ ruleId: e }) => ({
|
|
42282
|
+
enabled: !1,
|
|
42283
|
+
id: e
|
|
42284
|
+
}));
|
|
42289
42285
|
function h(e) {
|
|
42290
42286
|
return !!e && typeof e == "object" && typeof e.tagName == "string";
|
|
42291
42287
|
}
|
|
@@ -42303,21 +42299,18 @@ function _(e) {
|
|
|
42303
42299
|
};
|
|
42304
42300
|
return document.body.innerHTML = e, [document.body, n];
|
|
42305
42301
|
}
|
|
42306
|
-
throw typeof e == "string" ?
|
|
42302
|
+
throw TypeError(typeof e == "string" ? `html parameter ("${e}") has no elements` : "html parameter should be an HTML string or an HTML element");
|
|
42307
42303
|
}
|
|
42308
42304
|
function v(e = {}) {
|
|
42309
|
-
let { globalOptions: t = {}, ...n } = e, { rules: r = [], ...i } = t
|
|
42310
|
-
enabled: !1,
|
|
42311
|
-
id: e
|
|
42312
|
-
}));
|
|
42305
|
+
let { globalOptions: t = {}, ...n } = e, { rules: r = [], ...i } = t;
|
|
42313
42306
|
return f.default.configure({
|
|
42314
|
-
rules: [...
|
|
42307
|
+
rules: [...m, ...r],
|
|
42315
42308
|
...i
|
|
42316
42309
|
}), function(e, t = {}) {
|
|
42317
42310
|
let [r, i] = _(e), a = (0, p.default)({}, n, t);
|
|
42318
42311
|
return new Promise((e, t) => {
|
|
42319
42312
|
f.default.run(r, a, (n, r) => {
|
|
42320
|
-
i(), n
|
|
42313
|
+
i(), n ? t(n) : e(r);
|
|
42321
42314
|
});
|
|
42322
42315
|
});
|
|
42323
42316
|
};
|
|
@@ -42333,31 +42326,23 @@ function b(e) {
|
|
|
42333
42326
|
function x(e, t) {
|
|
42334
42327
|
return t && t.length > 0 ? e.filter((e) => e.impact !== void 0 && e.impact !== null && t.includes(e.impact)) : e;
|
|
42335
42328
|
}
|
|
42336
|
-
var S =
|
|
42337
|
-
|
|
42338
|
-
|
|
42339
|
-
|
|
42340
|
-
|
|
42341
|
-
|
|
42342
|
-
|
|
42343
|
-
|
|
42344
|
-
|
|
42345
|
-
|
|
42346
|
-
Received:
|
|
42347
|
-
|
|
42348
|
-
${b(`${t.help} (${t.id})`)}
|
|
42349
|
-
|
|
42350
|
-
${e("yellow", n.failureSummary ?? "")}
|
|
42351
|
-
|
|
42352
|
-
${i}`;
|
|
42353
|
-
}).join("\n\n")).join("\n\n────────\n\n"))(i), o = a.length === 0;
|
|
42329
|
+
var S = "\n\n", C = "────────";
|
|
42330
|
+
function w(t) {
|
|
42331
|
+
return t.map((t) => t.nodes.map((n) => {
|
|
42332
|
+
let r = `Expected the HTML found at $('${n.target.join(", ")}') to have no violations:${S}`, i = t.helpUrl ? `You can find more information on this issue here: \n${e("blue", t.helpUrl)}` : "";
|
|
42333
|
+
return `${r}${e("gray", n.html)}${S}Received:${S}${b(`${t.help} (${t.id})`)}${S}${e("yellow", n.failureSummary ?? "")}${S}${i}`;
|
|
42334
|
+
}).join(S)).join(S + C + S);
|
|
42335
|
+
}
|
|
42336
|
+
var T = { toHaveNoViolations(e) {
|
|
42337
|
+
if (e.violations === void 0) throw TypeError("Unexpected aXe results object. No violations property found.\nDid you change the `reporter` in your aXe configuration?");
|
|
42338
|
+
let t = e.toolOptions?.impactLevels, n = x(e.violations, t), r = n.length === 0;
|
|
42354
42339
|
return {
|
|
42355
|
-
actual:
|
|
42340
|
+
actual: n,
|
|
42356
42341
|
message: () => {
|
|
42357
|
-
if (!
|
|
42342
|
+
if (!r) return `${y(".toHaveNoViolations")}\n\n${w(n)}`;
|
|
42358
42343
|
},
|
|
42359
|
-
pass:
|
|
42344
|
+
pass: r
|
|
42360
42345
|
};
|
|
42361
|
-
} },
|
|
42346
|
+
} }, E = v();
|
|
42362
42347
|
//#endregion
|
|
42363
|
-
export {
|
|
42348
|
+
export { T as n, v as r, E as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vi-axe",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Modern Vitest matcher for aXe for testing accessibility using axe-core",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a11y",
|
|
@@ -33,21 +33,21 @@
|
|
|
33
33
|
"lodash.merge": "4.6.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@testing-library/react": "
|
|
37
|
-
"@testing-library/vue": "
|
|
38
|
-
"@types/lodash.merge": "
|
|
39
|
-
"@types/node": "
|
|
40
|
-
"@types/react": "
|
|
41
|
-
"@types/react-dom": "
|
|
42
|
-
"@vue/test-utils": "
|
|
43
|
-
"jsdom": "
|
|
44
|
-
"react": "
|
|
45
|
-
"react-dom": "
|
|
46
|
-
"typescript": "
|
|
47
|
-
"unplugin-isolated-decl": "
|
|
48
|
-
"vite": "
|
|
49
|
-
"vitest": "
|
|
50
|
-
"vue": "
|
|
36
|
+
"@testing-library/react": "16.3.2",
|
|
37
|
+
"@testing-library/vue": "8.1.0",
|
|
38
|
+
"@types/lodash.merge": "4.6.9",
|
|
39
|
+
"@types/node": "25.5.0",
|
|
40
|
+
"@types/react": "19.2.14",
|
|
41
|
+
"@types/react-dom": "19.2.3",
|
|
42
|
+
"@vue/test-utils": "2.4.6",
|
|
43
|
+
"jsdom": "29.0.1",
|
|
44
|
+
"react": "19.2.4",
|
|
45
|
+
"react-dom": "19.2.4",
|
|
46
|
+
"typescript": "6.0.2",
|
|
47
|
+
"unplugin-isolated-decl": "0.15.7",
|
|
48
|
+
"vite": "8.0.3",
|
|
49
|
+
"vitest": "4.1.2",
|
|
50
|
+
"vue": "3.5.31"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">= 20.0.0"
|