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 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
+ [![npm version](https://img.shields.io/npm/v/vi-axe.svg)](http://npm.im/vi-axe)
8
+ ![node](https://img.shields.io/node/v/vi-axe)
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.
@@ -1,4 +1,4 @@
1
- import { n as e } from "./src-hfrDQhV4.js";
1
+ import { n as e } from "./src-CypcCU5u.js";
2
2
  //#region src/extend-expect.ts
3
3
  expect.extend(e);
4
4
  //#endregion
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as e, r as t, t as n } from "./src-hfrDQhV4.js";
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, t;
2538
- if (typeof e != "function") return !1;
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], s = o === void 0 ? "" : o;
13045
- t[decodeURIComponent(a)] = decodeURIComponent(s);
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.asin(a), s = Math.cos(o);
34518
- return x(Math.acos(parseFloat(t[0]) / s));
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(e * (180 / Math.PI));
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], a = i === void 0 ? "" : i;
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: a.toLowerCase()
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" ? TypeError(`html parameter ("${e}") has no elements`) : TypeError("html parameter should be an HTML string or an HTML element");
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, a = m.map(({ ruleId: e }) => ({
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: [...a, ...r],
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 && t(n), e(r);
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 = { toHaveNoViolations(t) {
42337
- if (t.violations === void 0) throw TypeError("Unexpected aXe results object. No violations property found.\nDid you change the `reporter` in your aXe configuration?");
42338
- let { toolOptions: n } = t, r = [];
42339
- n && "impactLevels" in n && n.impactLevels && ({impactLevels: r} = n);
42340
- let i = x(t.violations, r), a = ((t) => t.length === 0 ? "" : t.map((t) => t.nodes.map((n) => {
42341
- let r = `Expected the HTML found at $('${n.target.join(", ")}') to have no violations:
42342
-
42343
- `, i = "";
42344
- return t.helpUrl && (i = `You can find more information on this issue here: \n${e("blue", t.helpUrl)}`), `${r}${e("gray", n.html)}
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: i,
42340
+ actual: n,
42356
42341
  message: () => {
42357
- if (!o) return `${y(".toHaveNoViolations")}\n\n${a}`;
42342
+ if (!r) return `${y(".toHaveNoViolations")}\n\n${w(n)}`;
42358
42343
  },
42359
- pass: o
42344
+ pass: r
42360
42345
  };
42361
- } }, C = v();
42346
+ } }, E = v();
42362
42347
  //#endregion
42363
- export { S as n, v as r, C as t };
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.4",
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": "^16.0.0",
37
- "@testing-library/vue": "^8.0.0",
38
- "@types/lodash.merge": "^4.6.9",
39
- "@types/node": "^25.4.0",
40
- "@types/react": "^19.0.0",
41
- "@types/react-dom": "^19.0.0",
42
- "@vue/test-utils": "^2.2.1",
43
- "jsdom": "^28.1.0",
44
- "react": "^19.0.0",
45
- "react-dom": "^19.0.0",
46
- "typescript": "^5.9.3",
47
- "unplugin-isolated-decl": "^0.15.7",
48
- "vite": "^8.0.0",
49
- "vitest": "^4.0.18",
50
- "vue": "^3.2.41"
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"