@zeus-js/testing 0.0.0-stage → 0.1.1-beta.14
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 +21 -2
- package/dist/testing.cjs +149 -0
- package/dist/testing.d.ts +34 -0
- package/dist/testing.esm-bundler.js +137 -0
- package/dist/testing.prod.cjs +149 -0
- package/index.cjs +7 -0
- package/package.json +54 -3
package/README.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zeus-js/testing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Zeus 的官方测试工具,面向组件和应用测试提供稳定的公共入口,不要求测试代码导入 runtime-dom 或 signal 的 private subpath。
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import {
|
|
7
|
+
fireEvent,
|
|
8
|
+
flushEffects,
|
|
9
|
+
queryByRole,
|
|
10
|
+
render,
|
|
11
|
+
} from '@zeus-js/testing'
|
|
12
|
+
|
|
13
|
+
const view = render(() => <button onClick={() => {}}>Save</button>)
|
|
14
|
+
|
|
15
|
+
fireEvent.click(queryByRole(view.container, 'button', { name: 'Save' })!)
|
|
16
|
+
await flushEffects()
|
|
17
|
+
view.dispose()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`render` 返回当前容器、`dispose`/`unmount` 和 `rerender`。`fireEvent` 创建并派发 DOM 事件;`queryByRole` 支持显式 role 以及常用原生元素的隐式 role,并可按 accessible name 过滤。
|
|
21
|
+
|
|
22
|
+
该包只依赖 `@zeus-js/zeus` 公共 API。它不提供网络、浏览器启动或快照功能;浏览器级测试继续使用项目的 Playwright/Puppeteer 配置。
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testing v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
let _zeus_js_zeus = require("@zeus-js/zeus");
|
|
11
|
+
//#region packages/core/testing/src/index.ts
|
|
12
|
+
/** Mount a Zeus view into an isolated test container. */
|
|
13
|
+
function render(value, options = {}) {
|
|
14
|
+
const document = getDocument();
|
|
15
|
+
const container = options.container ?? document.createElement("div");
|
|
16
|
+
const baseElement = container.ownerDocument?.body ?? container;
|
|
17
|
+
let dispose = (0, _zeus_js_zeus.render)(value, container);
|
|
18
|
+
const unmount = () => {
|
|
19
|
+
dispose();
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
container,
|
|
23
|
+
baseElement,
|
|
24
|
+
dispose: unmount,
|
|
25
|
+
unmount,
|
|
26
|
+
rerender(nextValue) {
|
|
27
|
+
dispose();
|
|
28
|
+
dispose = (0, _zeus_js_zeus.render)(nextValue, container);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function getDocument() {
|
|
33
|
+
if (typeof globalThis.document === "undefined") throw new Error("@zeus-js/testing requires a DOM environment. Configure JSDOM or a browser test environment before rendering.");
|
|
34
|
+
return globalThis.document;
|
|
35
|
+
}
|
|
36
|
+
function fireEvent(element, eventOrType, init = {}) {
|
|
37
|
+
const event = typeof eventOrType === "string" ? createEvent(element.ownerDocument, eventOrType, init) : eventOrType;
|
|
38
|
+
return element.dispatchEvent(event);
|
|
39
|
+
}
|
|
40
|
+
(function(_fireEvent) {
|
|
41
|
+
function click(element, init) {
|
|
42
|
+
return fireEvent(element, "click", init);
|
|
43
|
+
}
|
|
44
|
+
_fireEvent.click = click;
|
|
45
|
+
function input(element, init) {
|
|
46
|
+
return fireEvent(element, "input", init);
|
|
47
|
+
}
|
|
48
|
+
_fireEvent.input = input;
|
|
49
|
+
function change(element, init) {
|
|
50
|
+
return fireEvent(element, "change", init);
|
|
51
|
+
}
|
|
52
|
+
_fireEvent.change = change;
|
|
53
|
+
function submit(element, init) {
|
|
54
|
+
return fireEvent(element, "submit", init);
|
|
55
|
+
}
|
|
56
|
+
_fireEvent.submit = submit;
|
|
57
|
+
function focus(element, init) {
|
|
58
|
+
return fireEvent(element, "focus", init);
|
|
59
|
+
}
|
|
60
|
+
_fireEvent.focus = focus;
|
|
61
|
+
function blur(element, init) {
|
|
62
|
+
return fireEvent(element, "blur", init);
|
|
63
|
+
}
|
|
64
|
+
_fireEvent.blur = blur;
|
|
65
|
+
})(fireEvent || (fireEvent = {}));
|
|
66
|
+
function createEvent(document, type, init) {
|
|
67
|
+
const EventConstructor = eventConstructor(type, document.defaultView);
|
|
68
|
+
const { bubbles = true, cancelable = true, composed = true, ...extra } = init;
|
|
69
|
+
const event = new EventConstructor(type, {
|
|
70
|
+
bubbles,
|
|
71
|
+
cancelable,
|
|
72
|
+
composed
|
|
73
|
+
});
|
|
74
|
+
for (const [key, value] of Object.entries(extra)) try {
|
|
75
|
+
Object.defineProperty(event, key, {
|
|
76
|
+
configurable: true,
|
|
77
|
+
enumerable: true,
|
|
78
|
+
value
|
|
79
|
+
});
|
|
80
|
+
} catch {}
|
|
81
|
+
return event;
|
|
82
|
+
}
|
|
83
|
+
function eventConstructor(type, window) {
|
|
84
|
+
if (!window) return globalThis.Event;
|
|
85
|
+
if (/^(click|dblclick|mousedown|mouseup|mousemove|mouseover|mouseout)$/.test(type)) return window.MouseEvent;
|
|
86
|
+
if (/^(input|change|beforeinput)$/.test(type)) return window.InputEvent;
|
|
87
|
+
if (/^(submit|reset)$/.test(type)) return window.Event;
|
|
88
|
+
if (/^(focus|blur|focusin|focusout)$/.test(type)) return window.FocusEvent;
|
|
89
|
+
if (type.startsWith("key")) return window.KeyboardEvent;
|
|
90
|
+
return window.Event;
|
|
91
|
+
}
|
|
92
|
+
/** Let queued microtasks and framework effects settle before assertions. */
|
|
93
|
+
async function flushEffects() {
|
|
94
|
+
await Promise.resolve();
|
|
95
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
96
|
+
}
|
|
97
|
+
/** Find the first element with an explicit or implicit ARIA role. */
|
|
98
|
+
function queryByRole(container, role, options = {}) {
|
|
99
|
+
return [...isElement(container) && hasRole(container, role) ? [container] : [], ...Array.from(container.querySelectorAll("*")).filter((element) => hasRole(element, role))].find((element) => {
|
|
100
|
+
const name = accessibleName(element);
|
|
101
|
+
return options.name === void 0 ? true : typeof options.name === "string" ? name === options.name : options.name.test(name);
|
|
102
|
+
}) ?? null;
|
|
103
|
+
}
|
|
104
|
+
function isElement(value) {
|
|
105
|
+
return "getAttribute" in value && typeof value.getAttribute === "function";
|
|
106
|
+
}
|
|
107
|
+
function hasRole(element, requestedRole) {
|
|
108
|
+
const explicitRole = element.getAttribute("role");
|
|
109
|
+
if (explicitRole) return explicitRole.split(/\s+/).includes(requestedRole);
|
|
110
|
+
const tag = element.tagName.toLowerCase();
|
|
111
|
+
if (requestedRole === "button") return tag === "button";
|
|
112
|
+
if (requestedRole === "link") return tag === "a" && element.hasAttribute("href");
|
|
113
|
+
if (requestedRole === "heading") return /^h[1-6]$/.test(tag);
|
|
114
|
+
if (requestedRole === "checkbox") return tag === "input" && element.getAttribute("type") === "checkbox";
|
|
115
|
+
if (requestedRole === "radio") return tag === "input" && element.getAttribute("type") === "radio";
|
|
116
|
+
if (requestedRole === "textbox") return tag === "textarea" || tag === "input" && ![
|
|
117
|
+
"button",
|
|
118
|
+
"checkbox",
|
|
119
|
+
"hidden",
|
|
120
|
+
"image",
|
|
121
|
+
"radio",
|
|
122
|
+
"range",
|
|
123
|
+
"reset",
|
|
124
|
+
"submit"
|
|
125
|
+
].includes(element.getAttribute("type") ?? "text");
|
|
126
|
+
if (requestedRole === "img") return tag === "img" && element.hasAttribute("alt");
|
|
127
|
+
if (requestedRole === "list") return tag === "ul" || tag === "ol";
|
|
128
|
+
if (requestedRole === "listitem") return tag === "li";
|
|
129
|
+
if (requestedRole === "option") return tag === "option";
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function accessibleName(element) {
|
|
133
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
134
|
+
if (labelledBy) {
|
|
135
|
+
const document = element.ownerDocument;
|
|
136
|
+
return labelledBy.split(/\s+/).map((id) => document.getElementById(id)?.textContent ?? "").join(" ").trim();
|
|
137
|
+
}
|
|
138
|
+
return (element.getAttribute("aria-label") ?? element.getAttribute("alt") ?? element.getAttribute("title") ?? element.textContent ?? "").trim();
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
Object.defineProperty(exports, "fireEvent", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
get: function() {
|
|
144
|
+
return fireEvent;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
exports.flushEffects = flushEffects;
|
|
148
|
+
exports.queryByRole = queryByRole;
|
|
149
|
+
exports.render = render;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JSXValue } from '@zeus-js/zeus';
|
|
2
|
+
|
|
3
|
+
export interface RenderResult {
|
|
4
|
+
container: HTMLElement;
|
|
5
|
+
baseElement: HTMLElement;
|
|
6
|
+
dispose: () => void;
|
|
7
|
+
unmount: () => void;
|
|
8
|
+
rerender: (value: JSXValue | (() => JSXValue)) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface RenderTestOptions {
|
|
11
|
+
container?: HTMLElement;
|
|
12
|
+
}
|
|
13
|
+
/** Mount a Zeus view into an isolated test container. */
|
|
14
|
+
export declare function render(value: JSXValue | (() => JSXValue), options?: RenderTestOptions): RenderResult;
|
|
15
|
+
export type FireEventInit = EventInit & Record<string, unknown>;
|
|
16
|
+
export declare function fireEvent(element: Element, event: Event): boolean;
|
|
17
|
+
export declare function fireEvent(element: Element, type: string, init?: FireEventInit): boolean;
|
|
18
|
+
declare namespace fireEvent {
|
|
19
|
+
function click(element: Element, init?: FireEventInit): boolean;
|
|
20
|
+
function input(element: Element, init?: FireEventInit): boolean;
|
|
21
|
+
function change(element: Element, init?: FireEventInit): boolean;
|
|
22
|
+
function submit(element: Element, init?: FireEventInit): boolean;
|
|
23
|
+
function focus(element: Element, init?: FireEventInit): boolean;
|
|
24
|
+
function blur(element: Element, init?: FireEventInit): boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Let queued microtasks and framework effects settle before assertions. */
|
|
27
|
+
export declare function flushEffects(): Promise<void>;
|
|
28
|
+
export interface RoleQueryOptions {
|
|
29
|
+
name?: string | RegExp;
|
|
30
|
+
}
|
|
31
|
+
/** Find the first element with an explicit or implicit ARIA role. */
|
|
32
|
+
export declare function queryByRole(container: ParentNode, role: string, options?: RoleQueryOptions): HTMLElement | null;
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testing v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
import { render as render$1 } from "@zeus-js/zeus";
|
|
7
|
+
//#region packages/core/testing/src/index.ts
|
|
8
|
+
/** Mount a Zeus view into an isolated test container. */
|
|
9
|
+
function render(value, options = {}) {
|
|
10
|
+
const document = getDocument();
|
|
11
|
+
const container = options.container ?? document.createElement("div");
|
|
12
|
+
const baseElement = container.ownerDocument?.body ?? container;
|
|
13
|
+
let dispose = render$1(value, container);
|
|
14
|
+
const unmount = () => {
|
|
15
|
+
dispose();
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
container,
|
|
19
|
+
baseElement,
|
|
20
|
+
dispose: unmount,
|
|
21
|
+
unmount,
|
|
22
|
+
rerender(nextValue) {
|
|
23
|
+
dispose();
|
|
24
|
+
dispose = render$1(nextValue, container);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function getDocument() {
|
|
29
|
+
if (typeof globalThis.document === "undefined") throw new Error("@zeus-js/testing requires a DOM environment. Configure JSDOM or a browser test environment before rendering.");
|
|
30
|
+
return globalThis.document;
|
|
31
|
+
}
|
|
32
|
+
function fireEvent(element, eventOrType, init = {}) {
|
|
33
|
+
const event = typeof eventOrType === "string" ? createEvent(element.ownerDocument, eventOrType, init) : eventOrType;
|
|
34
|
+
return element.dispatchEvent(event);
|
|
35
|
+
}
|
|
36
|
+
(function(_fireEvent) {
|
|
37
|
+
function click(element, init) {
|
|
38
|
+
return fireEvent(element, "click", init);
|
|
39
|
+
}
|
|
40
|
+
_fireEvent.click = click;
|
|
41
|
+
function input(element, init) {
|
|
42
|
+
return fireEvent(element, "input", init);
|
|
43
|
+
}
|
|
44
|
+
_fireEvent.input = input;
|
|
45
|
+
function change(element, init) {
|
|
46
|
+
return fireEvent(element, "change", init);
|
|
47
|
+
}
|
|
48
|
+
_fireEvent.change = change;
|
|
49
|
+
function submit(element, init) {
|
|
50
|
+
return fireEvent(element, "submit", init);
|
|
51
|
+
}
|
|
52
|
+
_fireEvent.submit = submit;
|
|
53
|
+
function focus(element, init) {
|
|
54
|
+
return fireEvent(element, "focus", init);
|
|
55
|
+
}
|
|
56
|
+
_fireEvent.focus = focus;
|
|
57
|
+
function blur(element, init) {
|
|
58
|
+
return fireEvent(element, "blur", init);
|
|
59
|
+
}
|
|
60
|
+
_fireEvent.blur = blur;
|
|
61
|
+
})(fireEvent || (fireEvent = {}));
|
|
62
|
+
function createEvent(document, type, init) {
|
|
63
|
+
const EventConstructor = eventConstructor(type, document.defaultView);
|
|
64
|
+
const { bubbles = true, cancelable = true, composed = true, ...extra } = init;
|
|
65
|
+
const event = new EventConstructor(type, {
|
|
66
|
+
bubbles,
|
|
67
|
+
cancelable,
|
|
68
|
+
composed
|
|
69
|
+
});
|
|
70
|
+
for (const [key, value] of Object.entries(extra)) try {
|
|
71
|
+
Object.defineProperty(event, key, {
|
|
72
|
+
configurable: true,
|
|
73
|
+
enumerable: true,
|
|
74
|
+
value
|
|
75
|
+
});
|
|
76
|
+
} catch {}
|
|
77
|
+
return event;
|
|
78
|
+
}
|
|
79
|
+
function eventConstructor(type, window) {
|
|
80
|
+
if (!window) return globalThis.Event;
|
|
81
|
+
if (/^(click|dblclick|mousedown|mouseup|mousemove|mouseover|mouseout)$/.test(type)) return window.MouseEvent;
|
|
82
|
+
if (/^(input|change|beforeinput)$/.test(type)) return window.InputEvent;
|
|
83
|
+
if (/^(submit|reset)$/.test(type)) return window.Event;
|
|
84
|
+
if (/^(focus|blur|focusin|focusout)$/.test(type)) return window.FocusEvent;
|
|
85
|
+
if (type.startsWith("key")) return window.KeyboardEvent;
|
|
86
|
+
return window.Event;
|
|
87
|
+
}
|
|
88
|
+
/** Let queued microtasks and framework effects settle before assertions. */
|
|
89
|
+
async function flushEffects() {
|
|
90
|
+
await Promise.resolve();
|
|
91
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
92
|
+
}
|
|
93
|
+
/** Find the first element with an explicit or implicit ARIA role. */
|
|
94
|
+
function queryByRole(container, role, options = {}) {
|
|
95
|
+
return [...isElement(container) && hasRole(container, role) ? [container] : [], ...Array.from(container.querySelectorAll("*")).filter((element) => hasRole(element, role))].find((element) => {
|
|
96
|
+
const name = accessibleName(element);
|
|
97
|
+
return options.name === void 0 ? true : typeof options.name === "string" ? name === options.name : options.name.test(name);
|
|
98
|
+
}) ?? null;
|
|
99
|
+
}
|
|
100
|
+
function isElement(value) {
|
|
101
|
+
return "getAttribute" in value && typeof value.getAttribute === "function";
|
|
102
|
+
}
|
|
103
|
+
function hasRole(element, requestedRole) {
|
|
104
|
+
const explicitRole = element.getAttribute("role");
|
|
105
|
+
if (explicitRole) return explicitRole.split(/\s+/).includes(requestedRole);
|
|
106
|
+
const tag = element.tagName.toLowerCase();
|
|
107
|
+
if (requestedRole === "button") return tag === "button";
|
|
108
|
+
if (requestedRole === "link") return tag === "a" && element.hasAttribute("href");
|
|
109
|
+
if (requestedRole === "heading") return /^h[1-6]$/.test(tag);
|
|
110
|
+
if (requestedRole === "checkbox") return tag === "input" && element.getAttribute("type") === "checkbox";
|
|
111
|
+
if (requestedRole === "radio") return tag === "input" && element.getAttribute("type") === "radio";
|
|
112
|
+
if (requestedRole === "textbox") return tag === "textarea" || tag === "input" && ![
|
|
113
|
+
"button",
|
|
114
|
+
"checkbox",
|
|
115
|
+
"hidden",
|
|
116
|
+
"image",
|
|
117
|
+
"radio",
|
|
118
|
+
"range",
|
|
119
|
+
"reset",
|
|
120
|
+
"submit"
|
|
121
|
+
].includes(element.getAttribute("type") ?? "text");
|
|
122
|
+
if (requestedRole === "img") return tag === "img" && element.hasAttribute("alt");
|
|
123
|
+
if (requestedRole === "list") return tag === "ul" || tag === "ol";
|
|
124
|
+
if (requestedRole === "listitem") return tag === "li";
|
|
125
|
+
if (requestedRole === "option") return tag === "option";
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
function accessibleName(element) {
|
|
129
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
130
|
+
if (labelledBy) {
|
|
131
|
+
const document = element.ownerDocument;
|
|
132
|
+
return labelledBy.split(/\s+/).map((id) => document.getElementById(id)?.textContent ?? "").join(" ").trim();
|
|
133
|
+
}
|
|
134
|
+
return (element.getAttribute("aria-label") ?? element.getAttribute("alt") ?? element.getAttribute("title") ?? element.textContent ?? "").trim();
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { fireEvent, flushEffects, queryByRole, render };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* testing v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
let _zeus_js_zeus = require("@zeus-js/zeus");
|
|
11
|
+
//#region packages/core/testing/src/index.ts
|
|
12
|
+
/** Mount a Zeus view into an isolated test container. */
|
|
13
|
+
function render(value, options = {}) {
|
|
14
|
+
const document = getDocument();
|
|
15
|
+
const container = options.container ?? document.createElement("div");
|
|
16
|
+
const baseElement = container.ownerDocument?.body ?? container;
|
|
17
|
+
let dispose = (0, _zeus_js_zeus.render)(value, container);
|
|
18
|
+
const unmount = () => {
|
|
19
|
+
dispose();
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
container,
|
|
23
|
+
baseElement,
|
|
24
|
+
dispose: unmount,
|
|
25
|
+
unmount,
|
|
26
|
+
rerender(nextValue) {
|
|
27
|
+
dispose();
|
|
28
|
+
dispose = (0, _zeus_js_zeus.render)(nextValue, container);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function getDocument() {
|
|
33
|
+
if (typeof globalThis.document === "undefined") throw new Error("@zeus-js/testing requires a DOM environment. Configure JSDOM or a browser test environment before rendering.");
|
|
34
|
+
return globalThis.document;
|
|
35
|
+
}
|
|
36
|
+
function fireEvent(element, eventOrType, init = {}) {
|
|
37
|
+
const event = typeof eventOrType === "string" ? createEvent(element.ownerDocument, eventOrType, init) : eventOrType;
|
|
38
|
+
return element.dispatchEvent(event);
|
|
39
|
+
}
|
|
40
|
+
(function(_fireEvent) {
|
|
41
|
+
function click(element, init) {
|
|
42
|
+
return fireEvent(element, "click", init);
|
|
43
|
+
}
|
|
44
|
+
_fireEvent.click = click;
|
|
45
|
+
function input(element, init) {
|
|
46
|
+
return fireEvent(element, "input", init);
|
|
47
|
+
}
|
|
48
|
+
_fireEvent.input = input;
|
|
49
|
+
function change(element, init) {
|
|
50
|
+
return fireEvent(element, "change", init);
|
|
51
|
+
}
|
|
52
|
+
_fireEvent.change = change;
|
|
53
|
+
function submit(element, init) {
|
|
54
|
+
return fireEvent(element, "submit", init);
|
|
55
|
+
}
|
|
56
|
+
_fireEvent.submit = submit;
|
|
57
|
+
function focus(element, init) {
|
|
58
|
+
return fireEvent(element, "focus", init);
|
|
59
|
+
}
|
|
60
|
+
_fireEvent.focus = focus;
|
|
61
|
+
function blur(element, init) {
|
|
62
|
+
return fireEvent(element, "blur", init);
|
|
63
|
+
}
|
|
64
|
+
_fireEvent.blur = blur;
|
|
65
|
+
})(fireEvent || (fireEvent = {}));
|
|
66
|
+
function createEvent(document, type, init) {
|
|
67
|
+
const EventConstructor = eventConstructor(type, document.defaultView);
|
|
68
|
+
const { bubbles = true, cancelable = true, composed = true, ...extra } = init;
|
|
69
|
+
const event = new EventConstructor(type, {
|
|
70
|
+
bubbles,
|
|
71
|
+
cancelable,
|
|
72
|
+
composed
|
|
73
|
+
});
|
|
74
|
+
for (const [key, value] of Object.entries(extra)) try {
|
|
75
|
+
Object.defineProperty(event, key, {
|
|
76
|
+
configurable: true,
|
|
77
|
+
enumerable: true,
|
|
78
|
+
value
|
|
79
|
+
});
|
|
80
|
+
} catch {}
|
|
81
|
+
return event;
|
|
82
|
+
}
|
|
83
|
+
function eventConstructor(type, window) {
|
|
84
|
+
if (!window) return globalThis.Event;
|
|
85
|
+
if (/^(click|dblclick|mousedown|mouseup|mousemove|mouseover|mouseout)$/.test(type)) return window.MouseEvent;
|
|
86
|
+
if (/^(input|change|beforeinput)$/.test(type)) return window.InputEvent;
|
|
87
|
+
if (/^(submit|reset)$/.test(type)) return window.Event;
|
|
88
|
+
if (/^(focus|blur|focusin|focusout)$/.test(type)) return window.FocusEvent;
|
|
89
|
+
if (type.startsWith("key")) return window.KeyboardEvent;
|
|
90
|
+
return window.Event;
|
|
91
|
+
}
|
|
92
|
+
/** Let queued microtasks and framework effects settle before assertions. */
|
|
93
|
+
async function flushEffects() {
|
|
94
|
+
await Promise.resolve();
|
|
95
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
96
|
+
}
|
|
97
|
+
/** Find the first element with an explicit or implicit ARIA role. */
|
|
98
|
+
function queryByRole(container, role, options = {}) {
|
|
99
|
+
return [...isElement(container) && hasRole(container, role) ? [container] : [], ...Array.from(container.querySelectorAll("*")).filter((element) => hasRole(element, role))].find((element) => {
|
|
100
|
+
const name = accessibleName(element);
|
|
101
|
+
return options.name === void 0 ? true : typeof options.name === "string" ? name === options.name : options.name.test(name);
|
|
102
|
+
}) ?? null;
|
|
103
|
+
}
|
|
104
|
+
function isElement(value) {
|
|
105
|
+
return "getAttribute" in value && typeof value.getAttribute === "function";
|
|
106
|
+
}
|
|
107
|
+
function hasRole(element, requestedRole) {
|
|
108
|
+
const explicitRole = element.getAttribute("role");
|
|
109
|
+
if (explicitRole) return explicitRole.split(/\s+/).includes(requestedRole);
|
|
110
|
+
const tag = element.tagName.toLowerCase();
|
|
111
|
+
if (requestedRole === "button") return tag === "button";
|
|
112
|
+
if (requestedRole === "link") return tag === "a" && element.hasAttribute("href");
|
|
113
|
+
if (requestedRole === "heading") return /^h[1-6]$/.test(tag);
|
|
114
|
+
if (requestedRole === "checkbox") return tag === "input" && element.getAttribute("type") === "checkbox";
|
|
115
|
+
if (requestedRole === "radio") return tag === "input" && element.getAttribute("type") === "radio";
|
|
116
|
+
if (requestedRole === "textbox") return tag === "textarea" || tag === "input" && ![
|
|
117
|
+
"button",
|
|
118
|
+
"checkbox",
|
|
119
|
+
"hidden",
|
|
120
|
+
"image",
|
|
121
|
+
"radio",
|
|
122
|
+
"range",
|
|
123
|
+
"reset",
|
|
124
|
+
"submit"
|
|
125
|
+
].includes(element.getAttribute("type") ?? "text");
|
|
126
|
+
if (requestedRole === "img") return tag === "img" && element.hasAttribute("alt");
|
|
127
|
+
if (requestedRole === "list") return tag === "ul" || tag === "ol";
|
|
128
|
+
if (requestedRole === "listitem") return tag === "li";
|
|
129
|
+
if (requestedRole === "option") return tag === "option";
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function accessibleName(element) {
|
|
133
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
134
|
+
if (labelledBy) {
|
|
135
|
+
const document = element.ownerDocument;
|
|
136
|
+
return labelledBy.split(/\s+/).map((id) => document.getElementById(id)?.textContent ?? "").join(" ").trim();
|
|
137
|
+
}
|
|
138
|
+
return (element.getAttribute("aria-label") ?? element.getAttribute("alt") ?? element.getAttribute("title") ?? element.textContent ?? "").trim();
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
Object.defineProperty(exports, "fireEvent", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
get: function() {
|
|
144
|
+
return fireEvent;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
exports.flushEffects = flushEffects;
|
|
148
|
+
exports.queryByRole = queryByRole;
|
|
149
|
+
exports.render = render;
|
package/index.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeus-js/testing",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.1-beta.14",
|
|
5
|
+
"description": "Official testing utilities for Zeus applications and components",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/baicie/zeus",
|
|
10
|
+
"directory": "packages/core/testing"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"provenance": true
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/testing.d.ts",
|
|
20
|
+
"module": "./dist/testing.esm-bundler.js",
|
|
21
|
+
"import": "./dist/testing.esm-bundler.js",
|
|
22
|
+
"require": {
|
|
23
|
+
"production": "./dist/testing.prod.cjs",
|
|
24
|
+
"development": "./dist/testing.cjs",
|
|
25
|
+
"default": "./index.cjs"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"index.cjs",
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@zeus-js/zeus": "0.1.1-beta.14"
|
|
35
|
+
},
|
|
36
|
+
"author": "Baicie",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/baicie/zeus/issues"
|
|
39
|
+
},
|
|
40
|
+
"buildOptions": {
|
|
41
|
+
"name": "ZeusTesting",
|
|
42
|
+
"formats": [
|
|
43
|
+
"esm-bundler",
|
|
44
|
+
"cjs"
|
|
45
|
+
],
|
|
46
|
+
"target": "es2022"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/baicie/zeus/tree/main/packages/core/testing#readme",
|
|
49
|
+
"keywords": [
|
|
50
|
+
"zeus",
|
|
51
|
+
"testing",
|
|
52
|
+
"vitest"
|
|
53
|
+
],
|
|
54
|
+
"main": "index.cjs",
|
|
55
|
+
"module": "dist/testing.esm-bundler.js",
|
|
56
|
+
"types": "dist/testing.d.ts"
|
|
6
57
|
}
|