@vanrot/testing 0.1.0 → 0.2.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/accessibility.d.ts +19 -0
- package/dist/accessibility.d.ts.map +1 -0
- package/dist/accessibility.js +133 -0
- package/dist/accessibility.js.map +1 -0
- package/dist/async.d.ts +31 -0
- package/dist/async.d.ts.map +1 -0
- package/dist/async.js +95 -0
- package/dist/async.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/page-test.d.ts +19 -0
- package/dist/page-test.d.ts.map +1 -0
- package/dist/page-test.js +59 -0
- package/dist/page-test.js.map +1 -0
- package/dist/router.d.ts +21 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +82 -0
- package/dist/router.js.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface AccessibilityAssertionsOptions {
|
|
2
|
+
source?: string;
|
|
3
|
+
}
|
|
4
|
+
export interface RoleAssertionOptions {
|
|
5
|
+
name?: string | RegExp;
|
|
6
|
+
}
|
|
7
|
+
export interface AccessibilityAssertions {
|
|
8
|
+
readonly expect: {
|
|
9
|
+
role(role: string, options?: RoleAssertionOptions): HTMLElement;
|
|
10
|
+
enabled(role: string, options?: RoleAssertionOptions): void;
|
|
11
|
+
disabled(role: string, options?: RoleAssertionOptions): void;
|
|
12
|
+
focusOn(element: Element): void;
|
|
13
|
+
focusMoves(from: HTMLElement, to: HTMLElement, action: () => void | Promise<void>): Promise<void>;
|
|
14
|
+
semanticButton(element: Element): void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function createAccessibilityAssertions(root: Element, options?: AccessibilityAssertionsOptions): AccessibilityAssertions;
|
|
18
|
+
export declare function findByRole(root: Element, role: string, options?: RoleAssertionOptions): HTMLElement | null;
|
|
19
|
+
//# sourceMappingURL=accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../src/accessibility.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,8BAA8B;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE;QACf,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,WAAW,CAAC;QAChE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;QAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;QAC7D,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;QAChC,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAClG,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;KACxC,CAAC;CACH;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,8BAAmC,GAC3C,uBAAuB,CAuEzB;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,oBAAyB,GACjC,WAAW,GAAG,IAAI,CAmBpB"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { expect } from 'vitest';
|
|
2
|
+
export function createAccessibilityAssertions(root, options = {}) {
|
|
3
|
+
return {
|
|
4
|
+
expect: {
|
|
5
|
+
role(role, roleOptions = {}) {
|
|
6
|
+
const element = findByRole(root, role, roleOptions);
|
|
7
|
+
if (element === null) {
|
|
8
|
+
throw new Error(message(options, `Expected role "${role}"${formatName(roleOptions)}.`));
|
|
9
|
+
}
|
|
10
|
+
return element;
|
|
11
|
+
},
|
|
12
|
+
enabled(role, roleOptions = {}) {
|
|
13
|
+
const element = findByRole(root, role, roleOptions);
|
|
14
|
+
if (element === null) {
|
|
15
|
+
throw new Error(message(options, `Expected enabled role "${role}"${formatName(roleOptions)}.`));
|
|
16
|
+
}
|
|
17
|
+
expect(isDisabled(element), message(options, `Expected role "${role}" to be enabled.`)).toBe(false);
|
|
18
|
+
},
|
|
19
|
+
disabled(role, roleOptions = {}) {
|
|
20
|
+
const element = findByRole(root, role, roleOptions);
|
|
21
|
+
if (element === null) {
|
|
22
|
+
throw new Error(message(options, `Expected disabled role "${role}"${formatName(roleOptions)}.`));
|
|
23
|
+
}
|
|
24
|
+
expect(isDisabled(element), message(options, `Expected role "${role}" to be disabled.`)).toBe(true);
|
|
25
|
+
},
|
|
26
|
+
focusOn(element) {
|
|
27
|
+
expect(document.activeElement, message(options, `Expected focus on <${element.tagName.toLowerCase()}>.`)).toBe(element);
|
|
28
|
+
},
|
|
29
|
+
async focusMoves(from, to, action) {
|
|
30
|
+
from.focus();
|
|
31
|
+
await action();
|
|
32
|
+
await Promise.resolve();
|
|
33
|
+
expect(document.activeElement, message(options, `Expected focus to move from <${from.tagName.toLowerCase()}> to <${to.tagName.toLowerCase()}>.`)).toBe(to);
|
|
34
|
+
},
|
|
35
|
+
semanticButton(element) {
|
|
36
|
+
if (element.tagName.toLowerCase() === 'button') {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(message(options, `Expected a semantic <button> for role "button", received <${element.tagName.toLowerCase()}>.`));
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function findByRole(root, role, options = {}) {
|
|
45
|
+
const elements = root instanceof HTMLElement
|
|
46
|
+
? [root, ...Array.from(root.querySelectorAll('*'))]
|
|
47
|
+
: Array.from(root.querySelectorAll('*'));
|
|
48
|
+
for (const element of elements) {
|
|
49
|
+
if (readRole(element) !== role) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (options.name !== undefined && !matchesName(readAccessibleName(element), options.name)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
return element;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function readRole(element) {
|
|
60
|
+
const explicitRole = element.getAttribute('role');
|
|
61
|
+
if (explicitRole !== null && explicitRole.length > 0) {
|
|
62
|
+
return explicitRole;
|
|
63
|
+
}
|
|
64
|
+
const tagName = element.tagName.toLowerCase();
|
|
65
|
+
if (tagName === 'button') {
|
|
66
|
+
return 'button';
|
|
67
|
+
}
|
|
68
|
+
if (tagName === 'a' && element.hasAttribute('href')) {
|
|
69
|
+
return 'link';
|
|
70
|
+
}
|
|
71
|
+
if (tagName === 'input') {
|
|
72
|
+
return readInputRole(element);
|
|
73
|
+
}
|
|
74
|
+
if (/^h[1-6]$/.test(tagName)) {
|
|
75
|
+
return 'heading';
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function readInputRole(input) {
|
|
80
|
+
if (input.type === 'checkbox') {
|
|
81
|
+
return 'checkbox';
|
|
82
|
+
}
|
|
83
|
+
if (input.type === 'radio') {
|
|
84
|
+
return 'radio';
|
|
85
|
+
}
|
|
86
|
+
if (input.type === 'button' || input.type === 'submit' || input.type === 'reset') {
|
|
87
|
+
return 'button';
|
|
88
|
+
}
|
|
89
|
+
return 'textbox';
|
|
90
|
+
}
|
|
91
|
+
function readAccessibleName(element) {
|
|
92
|
+
const ariaLabel = element.getAttribute('aria-label');
|
|
93
|
+
if (ariaLabel !== null) {
|
|
94
|
+
return ariaLabel.trim();
|
|
95
|
+
}
|
|
96
|
+
const labelledBy = element.getAttribute('aria-labelledby');
|
|
97
|
+
if (labelledBy !== null) {
|
|
98
|
+
return labelledBy
|
|
99
|
+
.split(/\s+/)
|
|
100
|
+
.map((id) => document.getElementById(id)?.textContent?.trim() ?? '')
|
|
101
|
+
.filter((value) => value.length > 0)
|
|
102
|
+
.join(' ');
|
|
103
|
+
}
|
|
104
|
+
if (element instanceof HTMLInputElement && element.value.length > 0) {
|
|
105
|
+
return element.value;
|
|
106
|
+
}
|
|
107
|
+
return (element.textContent ?? '').replace(/\s+/g, ' ').trim();
|
|
108
|
+
}
|
|
109
|
+
function matchesName(actual, expected) {
|
|
110
|
+
if (typeof expected === 'string') {
|
|
111
|
+
return actual === expected;
|
|
112
|
+
}
|
|
113
|
+
return expected.test(actual);
|
|
114
|
+
}
|
|
115
|
+
function isDisabled(element) {
|
|
116
|
+
if (element.getAttribute('aria-disabled') === 'true') {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return 'disabled' in element && Boolean(element.disabled);
|
|
120
|
+
}
|
|
121
|
+
function formatName(options) {
|
|
122
|
+
if (options.name === undefined) {
|
|
123
|
+
return '';
|
|
124
|
+
}
|
|
125
|
+
return ` named "${String(options.name)}"`;
|
|
126
|
+
}
|
|
127
|
+
function message(options, text) {
|
|
128
|
+
if (options.source === undefined) {
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
return `${options.source}: ${text}`;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=accessibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility.js","sourceRoot":"","sources":["../src/accessibility.ts"],"names":[],"mappings":"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"}
|
package/dist/async.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface FlushTestingTasksOptions {
|
|
2
|
+
turns?: number;
|
|
3
|
+
}
|
|
4
|
+
export interface WaitForDomUpdateOptions {
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
intervalMs?: number;
|
|
7
|
+
message?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface VitestTimerLike {
|
|
10
|
+
advanceTimersByTimeAsync?: (ms: number) => Promise<unknown>;
|
|
11
|
+
advanceTimersByTime?: (ms: number) => unknown;
|
|
12
|
+
runOnlyPendingTimersAsync?: () => Promise<unknown>;
|
|
13
|
+
runOnlyPendingTimers?: () => unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface FakeTimerBridge {
|
|
16
|
+
advanceBy(ms: number): Promise<void>;
|
|
17
|
+
runPending(): Promise<void>;
|
|
18
|
+
flush(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface AsyncTestScope {
|
|
21
|
+
addAbortController(controller: AbortController): void;
|
|
22
|
+
addCleanup(cleanup: () => void | Promise<void>): void;
|
|
23
|
+
track<T>(promise: Promise<T>): Promise<T>;
|
|
24
|
+
pendingCount(): number;
|
|
25
|
+
cleanup(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export declare function flushTestingTasks(options?: FlushTestingTasksOptions): Promise<void>;
|
|
28
|
+
export declare function waitForDomUpdate(assertion: () => void | Promise<void>, options?: WaitForDomUpdateOptions): Promise<void>;
|
|
29
|
+
export declare function createFakeTimerBridge(timers: VitestTimerLike): FakeTimerBridge;
|
|
30
|
+
export declare function createAsyncTestScope(): AsyncTestScope;
|
|
31
|
+
//# sourceMappingURL=async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../src/async.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,yBAAyB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACtD,UAAU,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1C,YAAY,IAAI,MAAM,CAAC;IACvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7F;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACrC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAwB9E;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CA4CrD"}
|
package/dist/async.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export async function flushTestingTasks(options = {}) {
|
|
2
|
+
const turns = options.turns ?? 1;
|
|
3
|
+
for (let turn = 0; turn < turns; turn += 1) {
|
|
4
|
+
await Promise.resolve();
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export async function waitForDomUpdate(assertion, options = {}) {
|
|
8
|
+
const timeoutMs = options.timeoutMs ?? 1000;
|
|
9
|
+
const intervalMs = options.intervalMs ?? 5;
|
|
10
|
+
const startedAt = Date.now();
|
|
11
|
+
let lastError;
|
|
12
|
+
while (Date.now() - startedAt <= timeoutMs) {
|
|
13
|
+
try {
|
|
14
|
+
await assertion();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
lastError = error;
|
|
19
|
+
await sleep(intervalMs);
|
|
20
|
+
await flushTestingTasks();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const detail = lastError instanceof Error ? lastError.message : String(lastError);
|
|
24
|
+
throw new Error(options.message ?? `Timed out waiting for DOM update. Last error: ${detail}`);
|
|
25
|
+
}
|
|
26
|
+
export function createFakeTimerBridge(timers) {
|
|
27
|
+
return {
|
|
28
|
+
async advanceBy(ms) {
|
|
29
|
+
if (timers.advanceTimersByTimeAsync !== undefined) {
|
|
30
|
+
await timers.advanceTimersByTimeAsync(ms);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
timers.advanceTimersByTime?.(ms);
|
|
34
|
+
}
|
|
35
|
+
await flushTestingTasks();
|
|
36
|
+
},
|
|
37
|
+
async runPending() {
|
|
38
|
+
if (timers.runOnlyPendingTimersAsync !== undefined) {
|
|
39
|
+
await timers.runOnlyPendingTimersAsync();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
timers.runOnlyPendingTimers?.();
|
|
43
|
+
}
|
|
44
|
+
await flushTestingTasks();
|
|
45
|
+
},
|
|
46
|
+
async flush() {
|
|
47
|
+
await this.runPending();
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function createAsyncTestScope() {
|
|
52
|
+
const pending = new Set();
|
|
53
|
+
const cleanups = [];
|
|
54
|
+
const abortControllers = [];
|
|
55
|
+
let cleaned = false;
|
|
56
|
+
return {
|
|
57
|
+
addAbortController(controller) {
|
|
58
|
+
abortControllers.push(controller);
|
|
59
|
+
},
|
|
60
|
+
addCleanup(cleanup) {
|
|
61
|
+
cleanups.push(cleanup);
|
|
62
|
+
},
|
|
63
|
+
track(promise) {
|
|
64
|
+
let tracked;
|
|
65
|
+
tracked = promise.finally(() => {
|
|
66
|
+
pending.delete(tracked);
|
|
67
|
+
});
|
|
68
|
+
pending.add(tracked);
|
|
69
|
+
return tracked;
|
|
70
|
+
},
|
|
71
|
+
pendingCount() {
|
|
72
|
+
return pending.size;
|
|
73
|
+
},
|
|
74
|
+
async cleanup() {
|
|
75
|
+
if (cleaned) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
cleaned = true;
|
|
79
|
+
for (const controller of abortControllers) {
|
|
80
|
+
controller.abort();
|
|
81
|
+
}
|
|
82
|
+
await Promise.allSettled([...pending]);
|
|
83
|
+
for (const cleanup of cleanups.reverse()) {
|
|
84
|
+
await cleanup();
|
|
85
|
+
}
|
|
86
|
+
await flushTestingTasks();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function sleep(ms) {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
setTimeout(resolve, ms);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../src/async.ts"],"names":[],"mappings":"AA+BA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,UAAoC,EAAE;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;IAEjC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,KAAK,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAAqC,EACrC,UAAmC,EAAE;IAErC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,SAAkB,CAAC;IAEvB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,SAAS,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,CAAC;YAClB,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;YACxB,MAAM,iBAAiB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClF,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,IAAI,iDAAiD,MAAM,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAuB;IAC3D,OAAO;QACL,KAAK,CAAC,SAAS,CAAC,EAAU;YACxB,IAAI,MAAM,CAAC,wBAAwB,KAAK,SAAS,EAAE,CAAC;gBAClD,MAAM,MAAM,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC;YACnC,CAAC;YAED,MAAM,iBAAiB,EAAE,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,UAAU;YACd,IAAI,MAAM,CAAC,yBAAyB,KAAK,SAAS,EAAE,CAAC;gBACnD,MAAM,MAAM,CAAC,yBAAyB,EAAE,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAClC,CAAC;YAED,MAAM,iBAAiB,EAAE,CAAC;QAC5B,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,MAAM,QAAQ,GAAsC,EAAE,CAAC;IACvD,MAAM,gBAAgB,GAAsB,EAAE,CAAC;IAC/C,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO;QACL,kBAAkB,CAAC,UAA2B;YAC5C,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;QACD,UAAU,CAAC,OAAmC;YAC5C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QACD,KAAK,CAAI,OAAmB;YAC1B,IAAI,OAAmB,CAAC;YACxB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC7B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrB,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,YAAY;YACV,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;QACD,KAAK,CAAC,OAAO;YACX,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YAED,OAAO,GAAG,IAAI,CAAC;YAEf,KAAK,MAAM,UAAU,IAAI,gBAAgB,EAAE,CAAC;gBAC1C,UAAU,CAAC,KAAK,EAAE,CAAC;YACrB,CAAC;YAED,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAEvC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;gBACzC,MAAM,OAAO,EAAE,CAAC;YAClB,CAAC;YAED,MAAM,iBAAiB,EAAE,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export type { ComponentTestBody, ComponentTestBuilder, } from './component-test.js';
|
|
2
2
|
export { testComponent } from './component-test.js';
|
|
3
3
|
export type { Screen } from './screen.js';
|
|
4
|
+
export type { PageTestBody, PageTestBuilder, PageTestCleanup, PageTestHarness } from './page-test.js';
|
|
5
|
+
export { runPageTest, testPage } from './page-test.js';
|
|
6
|
+
export type { AccessibilityAssertions, AccessibilityAssertionsOptions, RoleAssertionOptions, } from './accessibility.js';
|
|
7
|
+
export { createAccessibilityAssertions, findByRole } from './accessibility.js';
|
|
8
|
+
export type { AsyncTestScope, FakeTimerBridge, FlushTestingTasksOptions, VitestTimerLike, WaitForDomUpdateOptions, } from './async.js';
|
|
9
|
+
export { createAsyncTestScope, createFakeTimerBridge, flushTestingTasks, waitForDomUpdate, } from './async.js';
|
|
10
|
+
export type { RouterTestHarness, RouterTestOptions } from './router.js';
|
|
11
|
+
export { setupRouterTest } from './router.js';
|
|
4
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,6BAA6B,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/E,YAAY,EACV,cAAc,EACd,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export { testComponent } from './component-test.js';
|
|
2
|
+
export { runPageTest, testPage } from './page-test.js';
|
|
3
|
+
export { createAccessibilityAssertions, findByRole } from './accessibility.js';
|
|
4
|
+
export { createAsyncTestScope, createFakeTimerBridge, flushTestingTasks, waitForDomUpdate, } from './async.js';
|
|
5
|
+
export { setupRouterTest } from './router.js';
|
|
2
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAMvD,OAAO,EAAE,6BAA6B,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAQ/E,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ComponentType } from '@vanrot/runtime';
|
|
2
|
+
import { type Screen } from './screen.js';
|
|
3
|
+
export type PageTestCleanup = () => void | Promise<void>;
|
|
4
|
+
export type PageTestBody = (page: PageTestHarness) => void | Promise<void>;
|
|
5
|
+
export interface PageTestBuilder {
|
|
6
|
+
can(description: string, body: PageTestBody): void;
|
|
7
|
+
}
|
|
8
|
+
export interface PageTestHarness {
|
|
9
|
+
readonly root: HTMLElement;
|
|
10
|
+
readonly target: HTMLElement;
|
|
11
|
+
readonly screen: Screen;
|
|
12
|
+
readonly click: Screen['click'];
|
|
13
|
+
addCleanup(cleanup: PageTestCleanup): void;
|
|
14
|
+
rerender(Component: ComponentType): Promise<void>;
|
|
15
|
+
cleanup(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function testPage(Page: ComponentType): PageTestBuilder;
|
|
18
|
+
export declare function runPageTest(Page: ComponentType, body: PageTestBody): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=page-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-test.d.ts","sourceRoot":"","sources":["../src/page-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE5E,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AACzD,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,CAQ7D;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDxF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mount } from '@vanrot/runtime';
|
|
2
|
+
import { test } from 'vitest';
|
|
3
|
+
import { createScreen } from './screen.js';
|
|
4
|
+
export function testPage(Page) {
|
|
5
|
+
return {
|
|
6
|
+
can(description, body) {
|
|
7
|
+
test(description, async function () {
|
|
8
|
+
await runPageTest(Page, body);
|
|
9
|
+
});
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function runPageTest(Page, body) {
|
|
14
|
+
const target = document.createElement('main');
|
|
15
|
+
target.setAttribute('data-vanrot-test-page-root', '');
|
|
16
|
+
document.body.append(target);
|
|
17
|
+
let app = null;
|
|
18
|
+
let cleaned = false;
|
|
19
|
+
const cleanups = [];
|
|
20
|
+
const screen = createScreen(target);
|
|
21
|
+
const mountPage = (Component) => {
|
|
22
|
+
app?.destroy();
|
|
23
|
+
target.replaceChildren();
|
|
24
|
+
app = mount(Component, target);
|
|
25
|
+
};
|
|
26
|
+
const cleanup = async () => {
|
|
27
|
+
if (cleaned) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
cleaned = true;
|
|
31
|
+
app?.destroy();
|
|
32
|
+
app = null;
|
|
33
|
+
target.remove();
|
|
34
|
+
for (const registeredCleanup of cleanups.reverse()) {
|
|
35
|
+
await registeredCleanup();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const harness = {
|
|
39
|
+
root: target,
|
|
40
|
+
target,
|
|
41
|
+
screen,
|
|
42
|
+
click: screen.click,
|
|
43
|
+
addCleanup(registeredCleanup) {
|
|
44
|
+
cleanups.push(registeredCleanup);
|
|
45
|
+
},
|
|
46
|
+
async rerender(Component) {
|
|
47
|
+
mountPage(Component);
|
|
48
|
+
},
|
|
49
|
+
cleanup,
|
|
50
|
+
};
|
|
51
|
+
mountPage(Page);
|
|
52
|
+
try {
|
|
53
|
+
await body(harness);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await cleanup();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=page-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-test.js","sourceRoot":"","sources":["../src/page-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAsC,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAmBxD,MAAM,UAAU,QAAQ,CAAC,IAAmB;IAC1C,OAAO;QACL,GAAG,CAAC,WAAmB,EAAE,IAAkB;YACzC,IAAI,CAAC,WAAW,EAAE,KAAK;gBACrB,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAmB,EAAE,IAAkB;IACvE,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,YAAY,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAE7B,IAAI,GAAG,GAAqB,IAAI,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,MAAM,SAAS,GAAG,CAAC,SAAwB,EAAQ,EAAE;QACnD,GAAG,EAAE,OAAO,EAAE,CAAC;QACf,MAAM,CAAC,eAAe,EAAE,CAAC;QACzB,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QAED,OAAO,GAAG,IAAI,CAAC;QACf,GAAG,EAAE,OAAO,EAAE,CAAC;QACf,GAAG,GAAG,IAAI,CAAC;QACX,MAAM,CAAC,MAAM,EAAE,CAAC;QAEhB,KAAK,MAAM,iBAAiB,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YACnD,MAAM,iBAAiB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAAoB;QAC/B,IAAI,EAAE,MAAM;QACZ,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,CAAC,iBAAkC;YAC3C,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnC,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,SAAwB;YACrC,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;QACD,OAAO;KACR,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type DefinedRoute, type DefinedRouteTable, type RouteParams, type RouteQuery, type RouteUrlInput } from '@vanrot/router';
|
|
2
|
+
export interface RouterTestOptions {
|
|
3
|
+
initialRoute?: DefinedRoute;
|
|
4
|
+
initialPath?: string;
|
|
5
|
+
params?: RouteParams;
|
|
6
|
+
query?: RouteQuery;
|
|
7
|
+
}
|
|
8
|
+
export interface RouterTestHarness {
|
|
9
|
+
readonly routes: DefinedRouteTable;
|
|
10
|
+
routeUrl(route: DefinedRoute, input?: RouteUrlInput): string;
|
|
11
|
+
navigate(route: DefinedRoute, input?: RouteUrlInput): Promise<boolean>;
|
|
12
|
+
cleanup(): Promise<void>;
|
|
13
|
+
readonly expect: {
|
|
14
|
+
currentRoute(route: DefinedRoute): void;
|
|
15
|
+
currentPath(path: string): void;
|
|
16
|
+
params(params: RouteParams): void;
|
|
17
|
+
query(query: RouteQuery): void;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare function setupRouterTest(routes: DefinedRouteTable, options?: RouterTestOptions): Promise<RouterTestHarness>;
|
|
21
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7D,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE;QACf,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;QACxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;QAClC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;KAChC,CAAC;CACH;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CAkE5B"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { buildRouteUrl, getCurrentRouteChain, navigate as navigatePath, provideRouter, resolveRoutePage, } from '@vanrot/router';
|
|
2
|
+
import { resetRouterForTests } from '@vanrot/router/internal';
|
|
3
|
+
import { expect } from 'vitest';
|
|
4
|
+
export async function setupRouterTest(routes, options = {}) {
|
|
5
|
+
let cleaned = false;
|
|
6
|
+
resetRouterForTests();
|
|
7
|
+
const initialPath = options.initialPath ??
|
|
8
|
+
(options.initialRoute !== undefined
|
|
9
|
+
? buildRouteUrl(options.initialRoute, routeUrlInputFromOptions(options))
|
|
10
|
+
: '/');
|
|
11
|
+
window.history.replaceState(null, '', initialPath);
|
|
12
|
+
await provideRouter(routes);
|
|
13
|
+
await resolveCurrentPage();
|
|
14
|
+
const assertActive = () => {
|
|
15
|
+
if (cleaned) {
|
|
16
|
+
throw new Error('Router test has already been cleaned up.');
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const harness = {
|
|
20
|
+
routes,
|
|
21
|
+
routeUrl(route, input = {}) {
|
|
22
|
+
assertActive();
|
|
23
|
+
return buildRouteUrl(route, input);
|
|
24
|
+
},
|
|
25
|
+
async navigate(route, input = {}) {
|
|
26
|
+
assertActive();
|
|
27
|
+
const didNavigate = await navigatePath(buildRouteUrl(route, input));
|
|
28
|
+
if (didNavigate) {
|
|
29
|
+
await resolveCurrentPage();
|
|
30
|
+
}
|
|
31
|
+
return didNavigate;
|
|
32
|
+
},
|
|
33
|
+
async cleanup() {
|
|
34
|
+
if (cleaned) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
cleaned = true;
|
|
38
|
+
resetRouterForTests();
|
|
39
|
+
window.history.replaceState(null, '', '/');
|
|
40
|
+
},
|
|
41
|
+
expect: {
|
|
42
|
+
currentRoute(route) {
|
|
43
|
+
assertActive();
|
|
44
|
+
expect(readCurrentRoute()?.route).toBe(route);
|
|
45
|
+
},
|
|
46
|
+
currentPath(path) {
|
|
47
|
+
assertActive();
|
|
48
|
+
expect(`${window.location.pathname}${window.location.search}`).toBe(path);
|
|
49
|
+
},
|
|
50
|
+
params(params) {
|
|
51
|
+
assertActive();
|
|
52
|
+
expect(readCurrentRoute()?.params).toEqual(params);
|
|
53
|
+
},
|
|
54
|
+
query(query) {
|
|
55
|
+
assertActive();
|
|
56
|
+
expect(readCurrentRoute()?.query).toEqual(query);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
return harness;
|
|
61
|
+
}
|
|
62
|
+
function readCurrentRoute() {
|
|
63
|
+
const chain = getCurrentRouteChain();
|
|
64
|
+
if (chain === null) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return chain.chain[chain.chain.length - 1] ?? null;
|
|
68
|
+
}
|
|
69
|
+
function routeUrlInputFromOptions(options) {
|
|
70
|
+
return {
|
|
71
|
+
...(options.params !== undefined ? { params: options.params } : {}),
|
|
72
|
+
...(options.query !== undefined ? { query: options.query } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function resolveCurrentPage() {
|
|
76
|
+
const match = readCurrentRoute();
|
|
77
|
+
if (match === null) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
await resolveRoutePage(match.route);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanrot/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.14.0"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@vanrot/router": "0.2.0",
|
|
21
22
|
"@vanrot/runtime": "0.1.0"
|
|
22
23
|
},
|
|
23
24
|
"peerDependencies": {
|
|
@@ -27,11 +28,11 @@
|
|
|
27
28
|
"vitest": "^4.0.14"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
|
-
"prebuild": "pnpm --filter @vanrot/
|
|
31
|
+
"prebuild": "pnpm --filter @vanrot/router build",
|
|
31
32
|
"build": "tsc -p tsconfig.json",
|
|
32
|
-
"pretypecheck": "pnpm --filter @vanrot/
|
|
33
|
+
"pretypecheck": "pnpm --filter @vanrot/router build",
|
|
33
34
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
|
-
"pretest": "pnpm --filter @vanrot/
|
|
35
|
+
"pretest": "pnpm --filter @vanrot/router build",
|
|
35
36
|
"test": "vitest run",
|
|
36
37
|
"clean": "node -e \"import('node:fs').then(({ rmSync }) => rmSync('dist', { recursive: true, force: true }))\""
|
|
37
38
|
},
|