@vitest/browser 4.0.15 → 4.0.17
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/client/.vite/manifest.json +2 -2
- package/dist/client/__vitest__/assets/index-BUCFJtth.js +57 -0
- package/dist/client/__vitest__/index.html +1 -1
- package/dist/client/__vitest_browser__/{orchestrator-8U3FyXSU.js → orchestrator-S_3e_uzt.js} +40 -8
- package/dist/client/__vitest_browser__/{tester-CrGChK3u.js → tester-k74mgIRa.js} +63 -36
- package/dist/client/error-catcher.js +5 -1
- package/dist/client/esm-client-injector.js +2 -1
- package/dist/client/orchestrator.html +1 -1
- package/dist/client/tester/tester.html +1 -1
- package/dist/client.js +197 -211
- package/dist/expect-element.js +3 -3
- package/dist/{index-CEutxZap.js → index-D6m36C6U.js} +1 -1
- package/dist/index.js +403 -307
- package/dist/locators.js +1 -1
- package/dist/shared/screenshotMatcher/types.d.ts +1 -1
- package/package.json +9 -8
- package/dist/client/__vitest__/assets/index-CfDzoXo3.js +0 -57
package/dist/client.js
CHANGED
|
@@ -1,220 +1,206 @@
|
|
|
1
|
+
//#region src/messages.ts
|
|
1
2
|
const TYPE_REQUEST = "q";
|
|
2
3
|
const TYPE_RESPONSE = "s";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
const defaultDeserialize = defaultSerialize;
|
|
8
|
-
const { clearTimeout: clearTimeout$1, setTimeout: setTimeout$1 } = globalThis;
|
|
9
|
-
const random = Math.random.bind(Math);
|
|
10
|
-
function createBirpc($functions, options) {
|
|
11
|
-
const {
|
|
12
|
-
post,
|
|
13
|
-
on,
|
|
14
|
-
off = () => {
|
|
15
|
-
},
|
|
16
|
-
eventNames = [],
|
|
17
|
-
serialize = defaultSerialize,
|
|
18
|
-
deserialize = defaultDeserialize,
|
|
19
|
-
resolver,
|
|
20
|
-
bind = "rpc",
|
|
21
|
-
timeout = DEFAULT_TIMEOUT
|
|
22
|
-
} = options;
|
|
23
|
-
let $closed = false;
|
|
24
|
-
const _rpcPromiseMap = /* @__PURE__ */ new Map();
|
|
25
|
-
let _promiseInit;
|
|
26
|
-
async function _call(method, args, event, optional) {
|
|
27
|
-
if ($closed)
|
|
28
|
-
throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
29
|
-
const req = { m: method, a: args, t: TYPE_REQUEST };
|
|
30
|
-
if (optional)
|
|
31
|
-
req.o = true;
|
|
32
|
-
const send = async (_req) => post(serialize(_req));
|
|
33
|
-
if (event) {
|
|
34
|
-
await send(req);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (_promiseInit) {
|
|
38
|
-
try {
|
|
39
|
-
await _promiseInit;
|
|
40
|
-
} finally {
|
|
41
|
-
_promiseInit = void 0;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
let { promise, resolve, reject } = createPromiseWithResolvers();
|
|
45
|
-
const id = nanoid();
|
|
46
|
-
req.i = id;
|
|
47
|
-
let timeoutId;
|
|
48
|
-
async function handler(newReq = req) {
|
|
49
|
-
if (timeout >= 0) {
|
|
50
|
-
timeoutId = setTimeout$1(() => {
|
|
51
|
-
try {
|
|
52
|
-
const handleResult = options.onTimeoutError?.(method, args);
|
|
53
|
-
if (handleResult !== true)
|
|
54
|
-
throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
reject(e);
|
|
57
|
-
}
|
|
58
|
-
_rpcPromiseMap.delete(id);
|
|
59
|
-
}, timeout);
|
|
60
|
-
if (typeof timeoutId === "object")
|
|
61
|
-
timeoutId = timeoutId.unref?.();
|
|
62
|
-
}
|
|
63
|
-
_rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
|
|
64
|
-
await send(newReq);
|
|
65
|
-
return promise;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
if (options.onRequest)
|
|
69
|
-
await options.onRequest(req, handler, resolve);
|
|
70
|
-
else
|
|
71
|
-
await handler();
|
|
72
|
-
} catch (e) {
|
|
73
|
-
if (options.onGeneralError?.(e) !== true)
|
|
74
|
-
throw e;
|
|
75
|
-
return;
|
|
76
|
-
} finally {
|
|
77
|
-
clearTimeout$1(timeoutId);
|
|
78
|
-
_rpcPromiseMap.delete(id);
|
|
79
|
-
}
|
|
80
|
-
return promise;
|
|
81
|
-
}
|
|
82
|
-
const $call = (method, ...args) => _call(method, args, false);
|
|
83
|
-
const $callOptional = (method, ...args) => _call(method, args, false, true);
|
|
84
|
-
const $callEvent = (method, ...args) => _call(method, args, true);
|
|
85
|
-
const $callRaw = (options2) => _call(options2.method, options2.args, options2.event, options2.optional);
|
|
86
|
-
const builtinMethods = {
|
|
87
|
-
$call,
|
|
88
|
-
$callOptional,
|
|
89
|
-
$callEvent,
|
|
90
|
-
$callRaw,
|
|
91
|
-
$rejectPendingCalls,
|
|
92
|
-
get $closed() {
|
|
93
|
-
return $closed;
|
|
94
|
-
},
|
|
95
|
-
$close,
|
|
96
|
-
$functions
|
|
97
|
-
};
|
|
98
|
-
const rpc = new Proxy({}, {
|
|
99
|
-
get(_, method) {
|
|
100
|
-
if (Object.prototype.hasOwnProperty.call(builtinMethods, method))
|
|
101
|
-
return builtinMethods[method];
|
|
102
|
-
if (method === "then" && !eventNames.includes("then") && !("then" in $functions))
|
|
103
|
-
return void 0;
|
|
104
|
-
const sendEvent = (...args) => _call(method, args, true);
|
|
105
|
-
if (eventNames.includes(method)) {
|
|
106
|
-
sendEvent.asEvent = sendEvent;
|
|
107
|
-
return sendEvent;
|
|
108
|
-
}
|
|
109
|
-
const sendCall = (...args) => _call(method, args, false);
|
|
110
|
-
sendCall.asEvent = sendEvent;
|
|
111
|
-
return sendCall;
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
function $close(customError) {
|
|
115
|
-
$closed = true;
|
|
116
|
-
_rpcPromiseMap.forEach(({ reject, method }) => {
|
|
117
|
-
const error = new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
118
|
-
if (customError) {
|
|
119
|
-
customError.cause ??= error;
|
|
120
|
-
return reject(customError);
|
|
121
|
-
}
|
|
122
|
-
reject(error);
|
|
123
|
-
});
|
|
124
|
-
_rpcPromiseMap.clear();
|
|
125
|
-
off(onMessage);
|
|
126
|
-
}
|
|
127
|
-
function $rejectPendingCalls(handler) {
|
|
128
|
-
const entries = Array.from(_rpcPromiseMap.values());
|
|
129
|
-
const handlerResults = entries.map(({ method, reject }) => {
|
|
130
|
-
if (!handler) {
|
|
131
|
-
return reject(new Error(`[birpc]: rejected pending call "${method}".`));
|
|
132
|
-
}
|
|
133
|
-
return handler({ method, reject });
|
|
134
|
-
});
|
|
135
|
-
_rpcPromiseMap.clear();
|
|
136
|
-
return handlerResults;
|
|
137
|
-
}
|
|
138
|
-
async function onMessage(data, ...extra) {
|
|
139
|
-
let msg;
|
|
140
|
-
try {
|
|
141
|
-
msg = deserialize(data);
|
|
142
|
-
} catch (e) {
|
|
143
|
-
if (options.onGeneralError?.(e) !== true)
|
|
144
|
-
throw e;
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (msg.t === TYPE_REQUEST) {
|
|
148
|
-
const { m: method, a: args, o: optional } = msg;
|
|
149
|
-
let result, error;
|
|
150
|
-
let fn = await (resolver ? resolver(method, $functions[method]) : $functions[method]);
|
|
151
|
-
if (optional)
|
|
152
|
-
fn ||= () => void 0;
|
|
153
|
-
if (!fn) {
|
|
154
|
-
error = new Error(`[birpc] function "${method}" not found`);
|
|
155
|
-
} else {
|
|
156
|
-
try {
|
|
157
|
-
result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
|
|
158
|
-
} catch (e) {
|
|
159
|
-
error = e;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (msg.i) {
|
|
163
|
-
if (error && options.onError)
|
|
164
|
-
options.onError(error, method, args);
|
|
165
|
-
if (error && options.onFunctionError) {
|
|
166
|
-
if (options.onFunctionError(error, method, args) === true)
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
if (!error) {
|
|
170
|
-
try {
|
|
171
|
-
await post(serialize({ t: TYPE_RESPONSE, i: msg.i, r: result }), ...extra);
|
|
172
|
-
return;
|
|
173
|
-
} catch (e) {
|
|
174
|
-
error = e;
|
|
175
|
-
if (options.onGeneralError?.(e, method, args) !== true)
|
|
176
|
-
throw e;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
try {
|
|
180
|
-
await post(serialize({ t: TYPE_RESPONSE, i: msg.i, e: error }), ...extra);
|
|
181
|
-
} catch (e) {
|
|
182
|
-
if (options.onGeneralError?.(e, method, args) !== true)
|
|
183
|
-
throw e;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
const { i: ack, r: result, e: error } = msg;
|
|
188
|
-
const promise = _rpcPromiseMap.get(ack);
|
|
189
|
-
if (promise) {
|
|
190
|
-
clearTimeout$1(promise.timeoutId);
|
|
191
|
-
if (error)
|
|
192
|
-
promise.reject(error);
|
|
193
|
-
else
|
|
194
|
-
promise.resolve(result);
|
|
195
|
-
}
|
|
196
|
-
_rpcPromiseMap.delete(ack);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
_promiseInit = on(onMessage);
|
|
200
|
-
return rpc;
|
|
201
|
-
}
|
|
4
|
+
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/utils.ts
|
|
202
7
|
function createPromiseWithResolvers() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
8
|
+
let resolve;
|
|
9
|
+
let reject;
|
|
10
|
+
return {
|
|
11
|
+
promise: new Promise((res, rej) => {
|
|
12
|
+
resolve = res;
|
|
13
|
+
reject = rej;
|
|
14
|
+
}),
|
|
15
|
+
resolve,
|
|
16
|
+
reject
|
|
17
|
+
};
|
|
210
18
|
}
|
|
19
|
+
const random = Math.random.bind(Math);
|
|
211
20
|
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
212
21
|
function nanoid(size = 21) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
22
|
+
let id = "";
|
|
23
|
+
let i = size;
|
|
24
|
+
while (i--) id += urlAlphabet[random() * 64 | 0];
|
|
25
|
+
return id;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/main.ts
|
|
30
|
+
const DEFAULT_TIMEOUT = 6e4;
|
|
31
|
+
const defaultSerialize = (i) => i;
|
|
32
|
+
const defaultDeserialize = defaultSerialize;
|
|
33
|
+
const { clearTimeout: clearTimeout$1, setTimeout: setTimeout$1 } = globalThis;
|
|
34
|
+
function createBirpc($functions, options) {
|
|
35
|
+
const { post, on, off = () => {}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT, proxify = true } = options;
|
|
36
|
+
let $closed = false;
|
|
37
|
+
const _rpcPromiseMap = /* @__PURE__ */ new Map();
|
|
38
|
+
let _promiseInit;
|
|
39
|
+
let rpc;
|
|
40
|
+
async function _call(method, args, event, optional) {
|
|
41
|
+
if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
42
|
+
const req = {
|
|
43
|
+
m: method,
|
|
44
|
+
a: args,
|
|
45
|
+
t: TYPE_REQUEST
|
|
46
|
+
};
|
|
47
|
+
if (optional) req.o = true;
|
|
48
|
+
const send = async (_req) => post(serialize(_req));
|
|
49
|
+
if (event) {
|
|
50
|
+
await send(req);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (_promiseInit) try {
|
|
54
|
+
await _promiseInit;
|
|
55
|
+
} finally {
|
|
56
|
+
_promiseInit = void 0;
|
|
57
|
+
}
|
|
58
|
+
let { promise, resolve, reject } = createPromiseWithResolvers();
|
|
59
|
+
const id = nanoid();
|
|
60
|
+
req.i = id;
|
|
61
|
+
let timeoutId;
|
|
62
|
+
async function handler(newReq = req) {
|
|
63
|
+
if (timeout >= 0) {
|
|
64
|
+
timeoutId = setTimeout$1(() => {
|
|
65
|
+
try {
|
|
66
|
+
if (options.onTimeoutError?.call(rpc, method, args) !== true) throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
reject(e);
|
|
69
|
+
}
|
|
70
|
+
_rpcPromiseMap.delete(id);
|
|
71
|
+
}, timeout);
|
|
72
|
+
if (typeof timeoutId === "object") timeoutId = timeoutId.unref?.();
|
|
73
|
+
}
|
|
74
|
+
_rpcPromiseMap.set(id, {
|
|
75
|
+
resolve,
|
|
76
|
+
reject,
|
|
77
|
+
timeoutId,
|
|
78
|
+
method
|
|
79
|
+
});
|
|
80
|
+
await send(newReq);
|
|
81
|
+
return promise;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
|
|
85
|
+
else await handler();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
|
|
88
|
+
return;
|
|
89
|
+
} finally {
|
|
90
|
+
clearTimeout$1(timeoutId);
|
|
91
|
+
_rpcPromiseMap.delete(id);
|
|
92
|
+
}
|
|
93
|
+
return promise;
|
|
94
|
+
}
|
|
95
|
+
const builtinMethods = {
|
|
96
|
+
$call: (method, ...args) => _call(method, args, false),
|
|
97
|
+
$callOptional: (method, ...args) => _call(method, args, false, true),
|
|
98
|
+
$callEvent: (method, ...args) => _call(method, args, true),
|
|
99
|
+
$callRaw: (options$1) => _call(options$1.method, options$1.args, options$1.event, options$1.optional),
|
|
100
|
+
$rejectPendingCalls,
|
|
101
|
+
get $closed() {
|
|
102
|
+
return $closed;
|
|
103
|
+
},
|
|
104
|
+
get $meta() {
|
|
105
|
+
return options.meta;
|
|
106
|
+
},
|
|
107
|
+
$close,
|
|
108
|
+
$functions
|
|
109
|
+
};
|
|
110
|
+
if (proxify) rpc = new Proxy({}, { get(_, method) {
|
|
111
|
+
if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
|
|
112
|
+
if (method === "then" && !eventNames.includes("then") && !("then" in $functions)) return void 0;
|
|
113
|
+
const sendEvent = (...args) => _call(method, args, true);
|
|
114
|
+
if (eventNames.includes(method)) {
|
|
115
|
+
sendEvent.asEvent = sendEvent;
|
|
116
|
+
return sendEvent;
|
|
117
|
+
}
|
|
118
|
+
const sendCall = (...args) => _call(method, args, false);
|
|
119
|
+
sendCall.asEvent = sendEvent;
|
|
120
|
+
return sendCall;
|
|
121
|
+
} });
|
|
122
|
+
else rpc = builtinMethods;
|
|
123
|
+
function $close(customError) {
|
|
124
|
+
$closed = true;
|
|
125
|
+
_rpcPromiseMap.forEach(({ reject, method }) => {
|
|
126
|
+
const error = /* @__PURE__ */ new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
127
|
+
if (customError) {
|
|
128
|
+
customError.cause ??= error;
|
|
129
|
+
return reject(customError);
|
|
130
|
+
}
|
|
131
|
+
reject(error);
|
|
132
|
+
});
|
|
133
|
+
_rpcPromiseMap.clear();
|
|
134
|
+
off(onMessage);
|
|
135
|
+
}
|
|
136
|
+
function $rejectPendingCalls(handler) {
|
|
137
|
+
const handlerResults = Array.from(_rpcPromiseMap.values()).map(({ method, reject }) => {
|
|
138
|
+
if (!handler) return reject(/* @__PURE__ */ new Error(`[birpc]: rejected pending call "${method}".`));
|
|
139
|
+
return handler({
|
|
140
|
+
method,
|
|
141
|
+
reject
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
_rpcPromiseMap.clear();
|
|
145
|
+
return handlerResults;
|
|
146
|
+
}
|
|
147
|
+
async function onMessage(data, ...extra) {
|
|
148
|
+
let msg;
|
|
149
|
+
try {
|
|
150
|
+
msg = deserialize(data);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (msg.t === TYPE_REQUEST) {
|
|
156
|
+
const { m: method, a: args, o: optional } = msg;
|
|
157
|
+
let result, error;
|
|
158
|
+
let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
|
|
159
|
+
if (optional) fn ||= () => void 0;
|
|
160
|
+
if (!fn) error = /* @__PURE__ */ new Error(`[birpc] function "${method}" not found`);
|
|
161
|
+
else try {
|
|
162
|
+
result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
error = e;
|
|
165
|
+
}
|
|
166
|
+
if (msg.i) {
|
|
167
|
+
if (error && options.onFunctionError) {
|
|
168
|
+
if (options.onFunctionError.call(rpc, error, method, args) === true) return;
|
|
169
|
+
}
|
|
170
|
+
if (!error) try {
|
|
171
|
+
await post(serialize({
|
|
172
|
+
t: TYPE_RESPONSE,
|
|
173
|
+
i: msg.i,
|
|
174
|
+
r: result
|
|
175
|
+
}), ...extra);
|
|
176
|
+
return;
|
|
177
|
+
} catch (e) {
|
|
178
|
+
error = e;
|
|
179
|
+
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
await post(serialize({
|
|
183
|
+
t: TYPE_RESPONSE,
|
|
184
|
+
i: msg.i,
|
|
185
|
+
e: error
|
|
186
|
+
}), ...extra);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
const { i: ack, r: result, e: error } = msg;
|
|
193
|
+
const promise = _rpcPromiseMap.get(ack);
|
|
194
|
+
if (promise) {
|
|
195
|
+
clearTimeout$1(promise.timeoutId);
|
|
196
|
+
if (error) promise.reject(error);
|
|
197
|
+
else promise.resolve(result);
|
|
198
|
+
}
|
|
199
|
+
_rpcPromiseMap.delete(ack);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
_promiseInit = on(onMessage);
|
|
203
|
+
return rpc;
|
|
218
204
|
}
|
|
219
205
|
|
|
220
206
|
/// <reference types="../types/index.d.ts" />
|
package/dist/expect-element.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{expect,chai}from"vitest";import{getType}from"vitest/internal/browser";import{k as kAriaCheckedRoles,L as Locator,g as getAriaChecked,a as getAriaRole,b as getAriaDisabled,c as beginAriaCaches,e as endAriaCaches,i as isElementVisible$1,d as getElementAccessibleDescription,f as getElementAccessibleErrorMessage,h as getElementAccessibleName,j as cssEscape,l as convertToSelector,m as getBrowserState,p as processTimeoutOptions}from"./index-
|
|
1
|
+
import{recordArtifact,expect,chai}from"vitest";import{getType}from"vitest/internal/browser";import{k as kAriaCheckedRoles,L as Locator,g as getAriaChecked,a as getAriaRole,b as getAriaDisabled,c as beginAriaCaches,e as endAriaCaches,i as isElementVisible$1,d as getElementAccessibleDescription,f as getElementAccessibleErrorMessage,h as getElementAccessibleName,j as cssEscape,l as convertToSelector,m as getBrowserState,p as processTimeoutOptions}from"./index-D6m36C6U.js";import{server}from"vitest/browser";function getAriaCheckedRoles(){return[...kAriaCheckedRoles]}function queryElementFromUserInput(_,K,q){return _ instanceof Locator&&(_=_.query()),_==null?null:getElementFromUserInput(_,K,q)}function getElementFromUserInput(_,K,q){_ instanceof Locator&&(_=_.element());let J=_?.ownerDocument?.defaultView||window;if(_ instanceof J.HTMLElement||_ instanceof J.SVGElement)return _;throw new UserInputElementTypeError(_,K,q)}function getNodeFromUserInput(_,K,q){_ instanceof Locator&&(_=_.element());let J=_.ownerDocument?.defaultView||window;if(_ instanceof J.Node)return _;throw new UserInputNodeTypeError(_,K,q)}function getMessage(_,K,q,J,Y,X){return[`${K}\n`,`${q}:\n${_.utils.EXPECTED_COLOR(redent(display(_,J),2))}`,`${Y}:\n${_.utils.RECEIVED_COLOR(redent(display(_,X),2))}`].join(`
|
|
2
2
|
`)}function redent(_,K){return indentString(stripIndent(_),K)}function indentString(_,K){return _.replace(/^(?!\s*$)/gm,` `.repeat(K))}function minIndent(_){let K=_.match(/^[ \t]*(?=\S)/gm);return K?K.reduce((_,K)=>Math.min(_,K.length),1/0):0}function stripIndent(_){let K=minIndent(_);if(K===0)return _;let q=RegExp(`^[ \\t]{${K}}`,`gm`);return _.replace(q,``)}function display(_,K){return typeof K==`string`?K:_.utils.stringify(K)}function toSentence(_,{wordConnector:K=`, `,lastWordConnector:q=` and `}={}){return[_.slice(0,-1).join(K),_.at(-1)].join(_.length>1?q:``)}class GenericTypeError extends Error{constructor(_,K,q,J){super(),Error.captureStackTrace&&Error.captureStackTrace(this,q);let Y=``;try{Y=J.utils.printWithType(`Received`,K,J.utils.printReceived)}catch{}this.message=[J.utils.matcherHint(`${J.isNot?`.not`:``}.${q.name}`,`received`,``),``,`${J.utils.RECEIVED_COLOR(`received`)} value must ${_} or a Locator that returns ${_}.`,Y].join(`
|
|
3
3
|
`)}}class UserInputElementTypeError extends GenericTypeError{constructor(_,K,q){super(`an HTMLElement or an SVGElement`,_,K,q)}}class UserInputNodeTypeError extends GenericTypeError{constructor(_,K,q){super(`a Node`,_,K,q)}}function getTag(_){return _ instanceof HTMLFormElement?`FORM`:_.tagName.toUpperCase()}function isInputElement(_){return getTag(_)===`INPUT`}function getSingleElementValue(_){if(_)switch(getTag(_)){case`INPUT`:return getInputValue(_);case`SELECT`:return getSelectValue(_);default:return _.value??getAccessibleValue(_)}}function getSelectValue({multiple:_,options:K}){let q=[...K].filter(_=>_.selected);if(_)return[...q].map(_=>_.value);if(q.length!==0)return q[0].value}function getInputValue(_){switch(_.type){case`number`:return _.value===``?null:Number(_.value);case`checkbox`:return _.checked;default:return _.value}}const rolesSupportingValues=[`meter`,`progressbar`,`slider`,`spinbutton`];function getAccessibleValue(_){if(rolesSupportingValues.includes(_.getAttribute(`role`)||``))return Number(_.getAttribute(`aria-valuenow`))}function normalize(_){return _.replace(/\s+/g,` `).trim()}function matches(_,K){return K instanceof RegExp?K.test(_):_.includes(String(K))}function arrayAsSetComparison(_,K){if(Array.isArray(_)&&Array.isArray(K)){let q=new Set(K);for(let K of new Set(_))if(!q.has(K))return!1;return!0}}const supportedRoles=getAriaCheckedRoles();function toBeChecked(_){let K=getElementFromUserInput(_,toBeChecked,this);if(!(()=>isInputElement(K)&&[`checkbox`,`radio`].includes(K.type))()&&!(()=>supportedRoles.includes(getAriaRole(K)||``)&&[`true`,`false`].includes(K.getAttribute(`aria-checked`)||``))())return{pass:!1,message:()=>`only inputs with type="checkbox" or type="radio" or elements with ${supportedRolesSentence()} and a valid aria-checked attribute can be used with .toBeChecked(). Use .toHaveValue() instead`};let q=getAriaChecked(K)===!0;return{pass:q,message:()=>{let _=q?`is`:`is not`;return[this.utils.matcherHint(`${this.isNot?`.not`:``}.toBeChecked`,`element`,``),``,`Received element ${_} checked:`,` ${this.utils.printReceived(K.cloneNode(!1))}`].join(`
|
|
4
4
|
`)}}}function supportedRolesSentence(){return toSentence(supportedRoles.map(_=>`role="${_}"`),{lastWordConnector:` or `})}function toBeEmptyDOMElement(_){let K=getElementFromUserInput(_,toBeEmptyDOMElement,this);return{pass:isEmptyElement(K),message:()=>[this.utils.matcherHint(`${this.isNot?`.not`:``}.toBeEmptyDOMElement`,`element`,``),``,`Received:`,` ${this.utils.printReceived(K.innerHTML)}`].join(`
|
|
@@ -23,5 +23,5 @@ import{expect,chai}from"vitest";import{getType}from"vitest/internal/browser";imp
|
|
|
23
23
|
`)}}}function getMultiElementValue(_){let K=``;for(let q of _){if(K&&K!==q.type)throw Error(`Multiple form elements with the same name must be of the same type`);K=q.type}switch(K){case`radio`:{let K=_.find(_=>_.checked);return K?K.value:void 0}case`checkbox`:return _.filter(_=>_.checked).map(_=>_.value);default:return _.map(_=>_.value)}}function getFormValue(_,K){let q=[..._.querySelectorAll(`[name="${cssEscape(K)}"]`)];if(q.length!==0)switch(q.length){case 1:return getSingleElementValue(q[0]);default:return getMultiElementValue(q)}}function getPureName(_){return/\[\]$/.test(_)?_.slice(0,-2):_}function getAllFormValues(_){let K={};for(let q of _.elements){if(!(`name`in q))continue;let J=q.name;K[getPureName(J)]=getFormValue(_,J)}return K}function toHaveRole(_,K){let q=getElementFromUserInput(_,toHaveRole,this);beginAriaCaches();let J=getAriaRole(q);return endAriaCaches(),{pass:J===K,message:()=>{let _=this.isNot?`not to`:`to`;return getMessage(this,this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveRole`,`element`,``),`Expected element ${_} have role`,K,`Received`,J)}}}function toHaveSelection(_,K){let q=getElementFromUserInput(_,toHaveSelection,this),J=K!==void 0;if(J&&typeof K!=`string`)throw Error(`expected selection must be a string or undefined`);let Y=getSelection(q);return{pass:J?this.equals(Y,K,[arrayAsSetComparison,...this.customTesters]):!!Y,message:()=>{let _=this.isNot?`not to`:`to`,q=this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveSelection`,`element`,K);return getMessage(this,q,`Expected the element ${_} have selection`,J?K:`(any)`,`Received`,Y)}}}function getSelection(_){let K=_.ownerDocument.getSelection();if(!K)return``;if([`INPUT`,`TEXTAREA`].includes(getTag(_))){let K=_;return[`radio`,`checkbox`].includes(K.type)||K.selectionStart==null||K.selectionEnd==null?``:K.value.toString().substring(K.selectionStart,K.selectionEnd)}if(K.anchorNode===null||K.focusNode===null)return``;let q=K.getRangeAt(0),J=_.ownerDocument.createRange();if(K.containsNode(_,!1))J.selectNodeContents(_),K.removeAllRanges(),K.addRange(J);else if(!(_.contains(K.anchorNode)&&_.contains(K.focusNode))){let Y=_===q.startContainer||_.contains(q.startContainer),X=_===q.endContainer||_.contains(q.endContainer);K.removeAllRanges(),(Y||X)&&(J.selectNodeContents(_),Y&&J.setStart(q.startContainer,q.startOffset),X&&J.setEnd(q.endContainer,q.endOffset),K.addRange(J))}let Y=K.toString();return K.removeAllRanges(),K.addRange(q),Y}const browser=server.config.browser.name,usedValuesProps=new Set(`backgroundPosition.background-position.bottom.left.right.top.height.width.margin-bottom.marginBottom.margin-left.marginLeft.margin-right.marginRight.margin-top.marginTop.min-height.minHeight.min-width.minWidth.padding-bottom.padding-left.padding-right.padding-top.text-indent.paddingBottom.paddingLeft.paddingRight.paddingTop.textIndent`.split(`.`));function toHaveStyle(_,K){let q=getElementFromUserInput(_,toHaveStyle,this),{getComputedStyle:J}=q.ownerDocument.defaultView,Y=typeof K==`object`?getStyleFromObjectCSS(K):computeCSSStyleDeclaration(K),X=J(q),Z=new Set(Array.from(q.style));return{pass:isSubset(Y,q,X,Z),message:()=>{let _=`${this.isNot?`.not`:``}.toHaveStyle`,K=new Set(Object.keys(Y)),J=printoutObjectStyles(Array.from(X).filter(_=>K.has(_)).reduce((_,K)=>(_[K]=(Z.has(K)&&usedValuesProps.has(K)?q.style:X)[K],_),{})),Q=J===``?`Expected styles could not be parsed by the browser. Did you make a typo?`:this.utils.diff(printoutObjectStyles(Y),J);return[this.utils.matcherHint(_,`element`,``),Q].join(`
|
|
24
24
|
|
|
25
25
|
`)}}}function getStyleFromObjectCSS(_){let K=browser===`chrome`||browser===`chromium`?document:document.implementation.createHTMLDocument(``),q=K.createElement(`div`);K.body.appendChild(q);let J=Object.keys(_);J.forEach(K=>{q.style[K]=_[K]});let Y={},X=window.getComputedStyle(q);return J.forEach(_=>{let K=(usedValuesProps.has(_)?q.style:X)[_];K!=null&&(Y[_]=K)}),q.remove(),Y}function computeCSSStyleDeclaration(_){let K=browser===`chrome`||browser===`chromium`||browser===`webkit`?document:document.implementation.createHTMLDocument(``),q=K.createElement(`div`);q.setAttribute(`style`,_.replace(/\n/g,``)),K.body.appendChild(q);let J=window.getComputedStyle(q),Y=Array.from(q.style).reduce((_,K)=>(_[K]=usedValuesProps.has(K)?q.style.getPropertyValue(K):J.getPropertyValue(K),_),{});return q.remove(),Y}function printoutObjectStyles(_){return Object.keys(_).sort().map(K=>`${K}: ${_[K]};`).join(`
|
|
26
|
-
`)}function isSubset(_,K,q,J){let Y=Object.keys(_);return Y.length?Y.every(Y=>{let X=_[Y],Z=Y.startsWith(`--`),Q=[Y];return Z||Q.push(Y.toLowerCase()),Q.some(_=>{let Z=J.has(Y)&&usedValuesProps.has(Y)?K.style:q;return Z[_]===X||Z.getPropertyValue(_)===X})}):!1}function toHaveTextContent(_,K,q={normalizeWhitespace:!0}){let J=getNodeFromUserInput(_,toHaveTextContent,this),Y=q.normalizeWhitespace?normalize(J.textContent||``):(J.textContent||``).replace(/\u00A0/g,` `),X=Y!==``&&K===``;return{pass:!X&&matches(Y,K),message:()=>{let _=this.isNot?`not to`:`to`;return getMessage(this,this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveTextContent`,`element`,``),X?`Checking with empty string will always match, use .toBeEmptyDOMElement() instead`:`Expected element ${_} have text content`,K,`Received`,Y)}}}function toHaveValue(_,K){let q=getElementFromUserInput(_,toHaveValue,this);if(isInputElement(q)&&[`checkbox`,`radio`].includes(q.type))throw Error(`input with type=checkbox or type=radio cannot be used with .toHaveValue(). Use .toBeChecked() for type=checkbox or .toHaveFormValues() instead`);let J=getSingleElementValue(q),Y=K!==void 0,X=K,Z=J;return K==J&&K!==J&&(X=`${K} (${typeof K})`,Z=`${J} (${typeof J})`),{pass:Y?this.equals(J,K,[arrayAsSetComparison,...this.customTesters]):!!J,message:()=>{let _=this.isNot?`not to`:`to`,q=this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveValue`,`element`,K);return getMessage(this,q,`Expected the element ${_} have value`,Y?X:`(any)`,`Received`,Z)}}}const counters=new Map([]);async function toMatchScreenshot(
|
|
27
|
-
`)}}const matchers={toBeDisabled,toBeEnabled,toBeEmptyDOMElement,toBeInTheDocument,toBeInViewport,toBeInvalid,toBeRequired,toBeValid,toBeVisible,toContainElement,toContainHTML,toHaveAccessibleDescription,toHaveAccessibleErrorMessage,toHaveAccessibleName,toHaveAttribute,toHaveClass,toHaveFocus,toHaveFormValues,toHaveStyle,toHaveTextContent,toHaveValue,toHaveDisplayValue,toBeChecked,toBePartiallyChecked,toHaveRole,toHaveSelection,toMatchScreenshot},kLocator=Symbol.for(`$$vitest:locator`);function element(
|
|
26
|
+
`)}function isSubset(_,K,q,J){let Y=Object.keys(_);return Y.length?Y.every(Y=>{let X=_[Y],Z=Y.startsWith(`--`),Q=[Y];return Z||Q.push(Y.toLowerCase()),Q.some(_=>{let Z=J.has(Y)&&usedValuesProps.has(Y)?K.style:q;return Z[_]===X||Z.getPropertyValue(_)===X})}):!1}function toHaveTextContent(_,K,q={normalizeWhitespace:!0}){let J=getNodeFromUserInput(_,toHaveTextContent,this),Y=q.normalizeWhitespace?normalize(J.textContent||``):(J.textContent||``).replace(/\u00A0/g,` `),X=Y!==``&&K===``;return{pass:!X&&matches(Y,K),message:()=>{let _=this.isNot?`not to`:`to`;return getMessage(this,this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveTextContent`,`element`,``),X?`Checking with empty string will always match, use .toBeEmptyDOMElement() instead`:`Expected element ${_} have text content`,K,`Received`,Y)}}}function toHaveValue(_,K){let q=getElementFromUserInput(_,toHaveValue,this);if(isInputElement(q)&&[`checkbox`,`radio`].includes(q.type))throw Error(`input with type=checkbox or type=radio cannot be used with .toHaveValue(). Use .toBeChecked() for type=checkbox or .toHaveFormValues() instead`);let J=getSingleElementValue(q),Y=K!==void 0,X=K,Z=J;return K==J&&K!==J&&(X=`${K} (${typeof K})`,Z=`${J} (${typeof J})`),{pass:Y?this.equals(J,K,[arrayAsSetComparison,...this.customTesters]):!!J,message:()=>{let _=this.isNot?`not to`:`to`,q=this.utils.matcherHint(`${this.isNot?`.not`:``}.toHaveValue`,`element`,K);return getMessage(this,q,`Expected the element ${_} have value`,Y?X:`(any)`,`Received`,Z)}}}const counters=new Map([]);async function toMatchScreenshot(K,q,J=typeof q==`object`?q:{}){if(this.isNot)throw Error(`'toMatchScreenshot' cannot be used with "not"`);if(this.task===void 0||this.currentTestName===void 0)throw Error(`'toMatchScreenshot' cannot be used without test context`);let Y=`${this.task.result?.repeatCount??0}${this.testPath}${this.currentTestName}`,X=counters.get(Y);X===void 0&&(X={current:0},counters.set(Y,X)),X.current+=1;let Z=typeof q==`string`?q:`${this.currentTestName} ${X.current}`,Q=J.screenshotOptions&&`mask`in J.screenshotOptions?{...J,screenshotOptions:{...J.screenshotOptions,mask:J.screenshotOptions.mask.map(convertToSelector)}}:J,$=await getBrowserState().commands.triggerCommand(`__vitest_screenshotMatcher`,[Z,this.currentTestName,{element:convertToSelector(K),...Q}]);if($.pass===!1){let K=[];$.reference&&K.push({name:`reference`,...$.reference}),$.actual&&K.push({name:`actual`,...$.actual}),$.diff&&K.push({name:`diff`,...$.diff}),K.length>0&&await recordArtifact(this.task,{type:`internal:toMatchScreenshot`,kind:`visual-regression`,message:$.message,attachments:K})}return{pass:$.pass,message:()=>$.pass?``:[this.utils.matcherHint(`toMatchScreenshot`,`element`,``),``,$.message,$.reference?`\nReference screenshot:\n ${this.utils.EXPECTED_COLOR($.reference.path)}`:null,$.actual?`\nActual screenshot:\n ${this.utils.RECEIVED_COLOR($.actual.path)}`:null,$.diff?this.utils.DIM_COLOR(`\nDiff image:\n ${$.diff.path}`):null,``].filter(_=>_!==null).join(`
|
|
27
|
+
`)}}const matchers={toBeDisabled,toBeEnabled,toBeEmptyDOMElement,toBeInTheDocument,toBeInViewport,toBeInvalid,toBeRequired,toBeValid,toBeVisible,toContainElement,toContainHTML,toHaveAccessibleDescription,toHaveAccessibleErrorMessage,toHaveAccessibleName,toHaveAttribute,toHaveClass,toHaveFocus,toHaveFormValues,toHaveStyle,toHaveTextContent,toHaveValue,toHaveDisplayValue,toBeChecked,toBePartiallyChecked,toHaveRole,toHaveSelection,toMatchScreenshot},kLocator=Symbol.for(`$$vitest:locator`);function element(_,Y){if(_!=null&&!(_ instanceof HTMLElement)&&!(_ instanceof SVGElement)&&!(kLocator in _))throw Error(`Invalid element or locator: ${_}. Expected an instance of HTMLElement, SVGElement or Locator, received ${getType(_)}`);let X=expect.poll(function(){if(_ instanceof Element||_==null)return _;let K=chai.util.flag(this,`negate`),J=chai.util.flag(this,`_name`);if(K&&J===`toBeInTheDocument`)return _.query();if(J===`toHaveLength`)return _.elements();if(J===`toMatchScreenshot`&&!chai.util.flag(this,`_poll.assert_once`)&&chai.util.flag(this,`_poll.assert_once`,!0),chai.util.flag(this,`_isLastPollAttempt`))return _.element();let Y=_.query();if(!Y)throw Error(`Cannot find element with locator: ${JSON.stringify(_)}`);return Y},processTimeoutOptions(Y));return chai.util.flag(X,`_poll.element`,!0),X}expect.extend(matchers),expect.element=element;
|