@vitest/browser 2.1.3 → 2.1.5
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 +7 -21
- package/context.d.ts +6 -0
- package/dist/client/.vite/manifest.json +7 -7
- package/dist/client/__vitest__/assets/index-CGSAdLIf.css +1 -0
- package/dist/client/__vitest__/assets/index-DWjHzG4O.js +52 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/orchestrator-DnP17K36.js +238 -0
- package/dist/client/__vitest_browser__/{tester-DZCtFstH.js → tester-BaiNqOPw.js} +7749 -6109
- package/dist/client/__vitest_browser__/utils-Owv5OOOf.js +195 -0
- package/dist/client/error-catcher.js +3 -1
- package/dist/client/esm-client-injector.js +50 -48
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/tester.html +3 -19
- package/dist/client.js +28 -5
- package/dist/context.js +96 -47
- package/dist/{index-Cgg35wOd.js → index-CKtADM3n.js} +35 -3
- package/dist/index.d.ts +10 -6
- package/dist/index.js +2016 -1825
- package/dist/locators/index.js +1 -1
- package/dist/locators/playwright.js +1 -1
- package/dist/locators/preview.js +11 -11
- package/dist/locators/webdriverio.js +1 -1
- package/dist/providers.js +8 -7
- package/dist/state.js +1 -1
- package/dist/utils.js +1 -1
- package/package.json +18 -18
- package/dist/client/__vitest__/assets/index-CADIw8eX.js +0 -52
- package/dist/client/__vitest__/assets/index-D5rK8X7L.css +0 -1
- package/dist/client/__vitest_browser__/orchestrator-BCPid0xo.js +0 -1099
- package/dist/client/__vitest_browser__/preload-helper-D-WYp1PK.js +0 -317
package/dist/index.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { createDebugger, isFileServingAllowed, getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir, createViteLogger, createViteServer } from 'vitest/node';
|
|
2
1
|
import c from 'tinyrainbow';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { ServerMockResolver, dynamicImportPlugin } from '@vitest/mocker/node';
|
|
6
|
-
import { readFile as readFile$1, mkdir } from 'node:fs/promises';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { createDefer, slash, toArray } from '@vitest/utils';
|
|
9
|
-
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
2
|
+
import { isFileServingAllowed, getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
|
|
3
|
+
import fs, { promises, readFileSync, lstatSync, existsSync } from 'node:fs';
|
|
10
4
|
import { createRequire } from 'node:module';
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
5
|
+
import { dynamicImportPlugin, ServerMockResolver } from '@vitest/mocker/node';
|
|
6
|
+
import { slash, toArray, createDefer } from '@vitest/utils';
|
|
13
7
|
import MagicString from 'magic-string';
|
|
8
|
+
import sirv from 'sirv';
|
|
9
|
+
import { coverageConfigDefaults } from 'vitest/config';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
14
11
|
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-Cv9wga63.js';
|
|
15
12
|
import { resolve as resolve$1, dirname as dirname$1, basename as basename$1, normalize as normalize$1 } from 'node:path';
|
|
13
|
+
import { mkdir, readFile as readFile$1 } from 'node:fs/promises';
|
|
16
14
|
import crypto from 'node:crypto';
|
|
15
|
+
import { WebSocketServer } from 'ws';
|
|
16
|
+
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
17
17
|
import * as nodeos from 'node:os';
|
|
18
18
|
|
|
19
|
-
var version = "2.1.
|
|
19
|
+
var version = "2.1.5";
|
|
20
20
|
|
|
21
21
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
22
22
|
function normalizeWindowsPath(input = "") {
|
|
@@ -197,850 +197,367 @@ const basename = function(p, extension) {
|
|
|
197
197
|
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
return i;
|
|
203
|
-
}
|
|
204
|
-
const defaultDeserialize = defaultSerialize;
|
|
205
|
-
const { clearTimeout, setTimeout } = globalThis;
|
|
206
|
-
const random = Math.random.bind(Math);
|
|
207
|
-
function createBirpc(functions, options) {
|
|
208
|
-
const {
|
|
209
|
-
post,
|
|
210
|
-
on,
|
|
211
|
-
eventNames = [],
|
|
212
|
-
serialize = defaultSerialize,
|
|
213
|
-
deserialize = defaultDeserialize,
|
|
214
|
-
resolver,
|
|
215
|
-
timeout = DEFAULT_TIMEOUT
|
|
216
|
-
} = options;
|
|
217
|
-
const rpcPromiseMap = /* @__PURE__ */ new Map();
|
|
218
|
-
let _promise;
|
|
219
|
-
const rpc = new Proxy({}, {
|
|
220
|
-
get(_, method) {
|
|
221
|
-
if (method === "$functions")
|
|
222
|
-
return functions;
|
|
223
|
-
if (method === "then" && !eventNames.includes("then") && !("then" in functions))
|
|
224
|
-
return void 0;
|
|
225
|
-
const sendEvent = (...args) => {
|
|
226
|
-
post(serialize({ m: method, a: args, t: "q" }));
|
|
227
|
-
};
|
|
228
|
-
if (eventNames.includes(method)) {
|
|
229
|
-
sendEvent.asEvent = sendEvent;
|
|
230
|
-
return sendEvent;
|
|
231
|
-
}
|
|
232
|
-
const sendCall = async (...args) => {
|
|
233
|
-
await _promise;
|
|
234
|
-
return new Promise((resolve, reject) => {
|
|
235
|
-
const id = nanoid();
|
|
236
|
-
let timeoutId;
|
|
237
|
-
if (timeout >= 0) {
|
|
238
|
-
timeoutId = setTimeout(() => {
|
|
239
|
-
try {
|
|
240
|
-
options.onTimeoutError?.(method, args);
|
|
241
|
-
throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
242
|
-
} catch (e) {
|
|
243
|
-
reject(e);
|
|
244
|
-
}
|
|
245
|
-
rpcPromiseMap.delete(id);
|
|
246
|
-
}, timeout);
|
|
247
|
-
if (typeof timeoutId === "object")
|
|
248
|
-
timeoutId = timeoutId.unref?.();
|
|
249
|
-
}
|
|
250
|
-
rpcPromiseMap.set(id, { resolve, reject, timeoutId });
|
|
251
|
-
post(serialize({ m: method, a: args, i: id, t: "q" }));
|
|
252
|
-
});
|
|
253
|
-
};
|
|
254
|
-
sendCall.asEvent = sendEvent;
|
|
255
|
-
return sendCall;
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
_promise = on(async (data, ...extra) => {
|
|
259
|
-
const msg = deserialize(data);
|
|
260
|
-
if (msg.t === "q") {
|
|
261
|
-
const { m: method, a: args } = msg;
|
|
262
|
-
let result, error;
|
|
263
|
-
const fn = resolver ? resolver(method, functions[method]) : functions[method];
|
|
264
|
-
if (!fn) {
|
|
265
|
-
error = new Error(`[birpc] function "${method}" not found`);
|
|
266
|
-
} else {
|
|
267
|
-
try {
|
|
268
|
-
result = await fn.apply(rpc, args);
|
|
269
|
-
} catch (e) {
|
|
270
|
-
error = e;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (msg.i) {
|
|
274
|
-
if (error && options.onError)
|
|
275
|
-
options.onError(error, method, args);
|
|
276
|
-
post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
const { i: ack, r: result, e: error } = msg;
|
|
280
|
-
const promise = rpcPromiseMap.get(ack);
|
|
281
|
-
if (promise) {
|
|
282
|
-
clearTimeout(promise.timeoutId);
|
|
283
|
-
if (error)
|
|
284
|
-
promise.reject(error);
|
|
285
|
-
else
|
|
286
|
-
promise.resolve(result);
|
|
287
|
-
}
|
|
288
|
-
rpcPromiseMap.delete(ack);
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
return rpc;
|
|
292
|
-
}
|
|
293
|
-
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
294
|
-
function nanoid(size = 21) {
|
|
295
|
-
let id = "";
|
|
296
|
-
let i = size;
|
|
297
|
-
while (i--)
|
|
298
|
-
id += urlAlphabet[random() * 64 | 0];
|
|
299
|
-
return id;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/// <reference types="../types/index.d.ts" />
|
|
303
|
-
|
|
304
|
-
// (c) 2020-present Andrea Giammarchi
|
|
305
|
-
|
|
306
|
-
const {parse: $parse, stringify: $stringify} = JSON;
|
|
307
|
-
const {keys} = Object;
|
|
308
|
-
|
|
309
|
-
const Primitive = String; // it could be Number
|
|
310
|
-
const primitive = 'string'; // it could be 'number'
|
|
311
|
-
|
|
312
|
-
const ignore = {};
|
|
313
|
-
const object = 'object';
|
|
314
|
-
|
|
315
|
-
const noop = (_, value) => value;
|
|
316
|
-
|
|
317
|
-
const primitives = value => (
|
|
318
|
-
value instanceof Primitive ? Primitive(value) : value
|
|
319
|
-
);
|
|
200
|
+
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
201
|
+
const distRoot = resolve(pkgRoot, "dist");
|
|
320
202
|
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
203
|
+
const clear = async (context, selector) => {
|
|
204
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
205
|
+
const { iframe } = context;
|
|
206
|
+
const element = iframe.locator(selector);
|
|
207
|
+
await element.clear({
|
|
208
|
+
timeout: 1e3
|
|
209
|
+
});
|
|
210
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
211
|
+
const browser = context.browser;
|
|
212
|
+
const element = await browser.$(selector);
|
|
213
|
+
await element.clearValue();
|
|
214
|
+
} else {
|
|
215
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
324
218
|
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
output[k] = $.call(output, k, tmp);
|
|
339
|
-
}
|
|
340
|
-
else if (output[k] !== ignore)
|
|
341
|
-
output[k] = $.call(output, k, value);
|
|
219
|
+
const click = async (context, selector, options = {}) => {
|
|
220
|
+
const provider = context.provider;
|
|
221
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
222
|
+
const tester = context.iframe;
|
|
223
|
+
await tester.locator(selector).click({
|
|
224
|
+
timeout: 1e3,
|
|
225
|
+
...options
|
|
226
|
+
});
|
|
227
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
228
|
+
const browser = context.browser;
|
|
229
|
+
await browser.$(selector).click(options);
|
|
230
|
+
} else {
|
|
231
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
|
|
342
232
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
233
|
+
};
|
|
234
|
+
const dblClick = async (context, selector, options = {}) => {
|
|
235
|
+
const provider = context.provider;
|
|
236
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
237
|
+
const tester = context.iframe;
|
|
238
|
+
await tester.locator(selector).dblclick(options);
|
|
239
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
240
|
+
const browser = context.browser;
|
|
241
|
+
await browser.$(selector).doubleClick();
|
|
242
|
+
} else {
|
|
243
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const tripleClick = async (context, selector, options = {}) => {
|
|
247
|
+
const provider = context.provider;
|
|
248
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
249
|
+
const tester = context.iframe;
|
|
250
|
+
await tester.locator(selector).click({
|
|
251
|
+
timeout: 1e3,
|
|
252
|
+
...options,
|
|
253
|
+
clickCount: 3
|
|
254
|
+
});
|
|
255
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
256
|
+
const browser = context.browser;
|
|
257
|
+
await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: await browser.$(selector) }).down().up().pause(50).down().up().pause(50).down().up().pause(50).perform();
|
|
258
|
+
} else {
|
|
259
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
|
|
346
260
|
}
|
|
347
|
-
return output;
|
|
348
261
|
};
|
|
349
262
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
263
|
+
const dragAndDrop = async (context, source, target, options_) => {
|
|
264
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
265
|
+
const frame = await context.frame();
|
|
266
|
+
await frame.dragAndDrop(
|
|
267
|
+
source,
|
|
268
|
+
target,
|
|
269
|
+
{
|
|
270
|
+
timeout: 1e3,
|
|
271
|
+
...options_
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
275
|
+
const $source = context.browser.$(source);
|
|
276
|
+
const $target = context.browser.$(target);
|
|
277
|
+
const options = options_ || {};
|
|
278
|
+
const duration = options.duration ?? 10;
|
|
279
|
+
await context.browser.action("pointer").move({ duration: 0, origin: $source, x: options.sourceX ?? 0, y: options.sourceY ?? 0 }).down({ button: 0 }).move({ duration: 0, origin: "pointer", x: 0, y: 0 }).pause(duration).move({ duration: 0, origin: $target, x: options.targetX ?? 0, y: options.targetY ?? 0 }).move({ duration: 0, origin: "pointer", x: 1, y: 0 }).move({ duration: 0, origin: "pointer", x: -1, y: 0 }).up({ button: 0 }).perform();
|
|
280
|
+
} else {
|
|
281
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
|
|
282
|
+
}
|
|
354
283
|
};
|
|
355
284
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
revive(input, new Set, value, $) :
|
|
368
|
-
value;
|
|
369
|
-
return $.call({'': tmp}, '', tmp);
|
|
285
|
+
const fill = async (context, selector, text, options = {}) => {
|
|
286
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
287
|
+
const { iframe } = context;
|
|
288
|
+
const element = iframe.locator(selector);
|
|
289
|
+
await element.fill(text, { timeout: 1e3, ...options });
|
|
290
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
291
|
+
const browser = context.browser;
|
|
292
|
+
await browser.$(selector).setValue(text);
|
|
293
|
+
} else {
|
|
294
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
295
|
+
}
|
|
370
296
|
};
|
|
371
297
|
|
|
372
|
-
/**
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
firstRun = true;
|
|
390
|
-
output[i] = $stringify(input[i++], replace, space);
|
|
391
|
-
}
|
|
392
|
-
return '[' + output.join(',') + ']';
|
|
393
|
-
function replace(key, value) {
|
|
394
|
-
if (firstRun) {
|
|
395
|
-
firstRun = !firstRun;
|
|
396
|
-
return value;
|
|
298
|
+
const types = { "application/andrew-inset": ["ez"], "application/appinstaller": ["appinstaller"], "application/applixware": ["aw"], "application/appx": ["appx"], "application/appxbundle": ["appxbundle"], "application/atom+xml": ["atom"], "application/atomcat+xml": ["atomcat"], "application/atomdeleted+xml": ["atomdeleted"], "application/atomsvc+xml": ["atomsvc"], "application/atsc-dwd+xml": ["dwd"], "application/atsc-held+xml": ["held"], "application/atsc-rsat+xml": ["rsat"], "application/automationml-aml+xml": ["aml"], "application/automationml-amlx+zip": ["amlx"], "application/bdoc": ["bdoc"], "application/calendar+xml": ["xcs"], "application/ccxml+xml": ["ccxml"], "application/cdfx+xml": ["cdfx"], "application/cdmi-capability": ["cdmia"], "application/cdmi-container": ["cdmic"], "application/cdmi-domain": ["cdmid"], "application/cdmi-object": ["cdmio"], "application/cdmi-queue": ["cdmiq"], "application/cpl+xml": ["cpl"], "application/cu-seeme": ["cu"], "application/cwl": ["cwl"], "application/dash+xml": ["mpd"], "application/dash-patch+xml": ["mpp"], "application/davmount+xml": ["davmount"], "application/docbook+xml": ["dbk"], "application/dssc+der": ["dssc"], "application/dssc+xml": ["xdssc"], "application/ecmascript": ["ecma"], "application/emma+xml": ["emma"], "application/emotionml+xml": ["emotionml"], "application/epub+zip": ["epub"], "application/exi": ["exi"], "application/express": ["exp"], "application/fdf": ["fdf"], "application/fdt+xml": ["fdt"], "application/font-tdpfr": ["pfr"], "application/geo+json": ["geojson"], "application/gml+xml": ["gml"], "application/gpx+xml": ["gpx"], "application/gxf": ["gxf"], "application/gzip": ["gz"], "application/hjson": ["hjson"], "application/hyperstudio": ["stk"], "application/inkml+xml": ["ink", "inkml"], "application/ipfix": ["ipfix"], "application/its+xml": ["its"], "application/java-archive": ["jar", "war", "ear"], "application/java-serialized-object": ["ser"], "application/java-vm": ["class"], "application/javascript": ["*js"], "application/json": ["json", "map"], "application/json5": ["json5"], "application/jsonml+json": ["jsonml"], "application/ld+json": ["jsonld"], "application/lgr+xml": ["lgr"], "application/lost+xml": ["lostxml"], "application/mac-binhex40": ["hqx"], "application/mac-compactpro": ["cpt"], "application/mads+xml": ["mads"], "application/manifest+json": ["webmanifest"], "application/marc": ["mrc"], "application/marcxml+xml": ["mrcx"], "application/mathematica": ["ma", "nb", "mb"], "application/mathml+xml": ["mathml"], "application/mbox": ["mbox"], "application/media-policy-dataset+xml": ["mpf"], "application/mediaservercontrol+xml": ["mscml"], "application/metalink+xml": ["metalink"], "application/metalink4+xml": ["meta4"], "application/mets+xml": ["mets"], "application/mmt-aei+xml": ["maei"], "application/mmt-usd+xml": ["musd"], "application/mods+xml": ["mods"], "application/mp21": ["m21", "mp21"], "application/mp4": ["*mp4", "*mpg4", "mp4s", "m4p"], "application/msix": ["msix"], "application/msixbundle": ["msixbundle"], "application/msword": ["doc", "dot"], "application/mxf": ["mxf"], "application/n-quads": ["nq"], "application/n-triples": ["nt"], "application/node": ["cjs"], "application/octet-stream": ["bin", "dms", "lrf", "mar", "so", "dist", "distz", "pkg", "bpk", "dump", "elc", "deploy", "exe", "dll", "deb", "dmg", "iso", "img", "msi", "msp", "msm", "buffer"], "application/oda": ["oda"], "application/oebps-package+xml": ["opf"], "application/ogg": ["ogx"], "application/omdoc+xml": ["omdoc"], "application/onenote": ["onetoc", "onetoc2", "onetmp", "onepkg"], "application/oxps": ["oxps"], "application/p2p-overlay+xml": ["relo"], "application/patch-ops-error+xml": ["xer"], "application/pdf": ["pdf"], "application/pgp-encrypted": ["pgp"], "application/pgp-keys": ["asc"], "application/pgp-signature": ["sig", "*asc"], "application/pics-rules": ["prf"], "application/pkcs10": ["p10"], "application/pkcs7-mime": ["p7m", "p7c"], "application/pkcs7-signature": ["p7s"], "application/pkcs8": ["p8"], "application/pkix-attr-cert": ["ac"], "application/pkix-cert": ["cer"], "application/pkix-crl": ["crl"], "application/pkix-pkipath": ["pkipath"], "application/pkixcmp": ["pki"], "application/pls+xml": ["pls"], "application/postscript": ["ai", "eps", "ps"], "application/provenance+xml": ["provx"], "application/pskc+xml": ["pskcxml"], "application/raml+yaml": ["raml"], "application/rdf+xml": ["rdf", "owl"], "application/reginfo+xml": ["rif"], "application/relax-ng-compact-syntax": ["rnc"], "application/resource-lists+xml": ["rl"], "application/resource-lists-diff+xml": ["rld"], "application/rls-services+xml": ["rs"], "application/route-apd+xml": ["rapd"], "application/route-s-tsid+xml": ["sls"], "application/route-usd+xml": ["rusd"], "application/rpki-ghostbusters": ["gbr"], "application/rpki-manifest": ["mft"], "application/rpki-roa": ["roa"], "application/rsd+xml": ["rsd"], "application/rss+xml": ["rss"], "application/rtf": ["rtf"], "application/sbml+xml": ["sbml"], "application/scvp-cv-request": ["scq"], "application/scvp-cv-response": ["scs"], "application/scvp-vp-request": ["spq"], "application/scvp-vp-response": ["spp"], "application/sdp": ["sdp"], "application/senml+xml": ["senmlx"], "application/sensml+xml": ["sensmlx"], "application/set-payment-initiation": ["setpay"], "application/set-registration-initiation": ["setreg"], "application/shf+xml": ["shf"], "application/sieve": ["siv", "sieve"], "application/smil+xml": ["smi", "smil"], "application/sparql-query": ["rq"], "application/sparql-results+xml": ["srx"], "application/sql": ["sql"], "application/srgs": ["gram"], "application/srgs+xml": ["grxml"], "application/sru+xml": ["sru"], "application/ssdl+xml": ["ssdl"], "application/ssml+xml": ["ssml"], "application/swid+xml": ["swidtag"], "application/tei+xml": ["tei", "teicorpus"], "application/thraud+xml": ["tfi"], "application/timestamped-data": ["tsd"], "application/toml": ["toml"], "application/trig": ["trig"], "application/ttml+xml": ["ttml"], "application/ubjson": ["ubj"], "application/urc-ressheet+xml": ["rsheet"], "application/urc-targetdesc+xml": ["td"], "application/voicexml+xml": ["vxml"], "application/wasm": ["wasm"], "application/watcherinfo+xml": ["wif"], "application/widget": ["wgt"], "application/winhlp": ["hlp"], "application/wsdl+xml": ["wsdl"], "application/wspolicy+xml": ["wspolicy"], "application/xaml+xml": ["xaml"], "application/xcap-att+xml": ["xav"], "application/xcap-caps+xml": ["xca"], "application/xcap-diff+xml": ["xdf"], "application/xcap-el+xml": ["xel"], "application/xcap-ns+xml": ["xns"], "application/xenc+xml": ["xenc"], "application/xfdf": ["xfdf"], "application/xhtml+xml": ["xhtml", "xht"], "application/xliff+xml": ["xlf"], "application/xml": ["xml", "xsl", "xsd", "rng"], "application/xml-dtd": ["dtd"], "application/xop+xml": ["xop"], "application/xproc+xml": ["xpl"], "application/xslt+xml": ["*xsl", "xslt"], "application/xspf+xml": ["xspf"], "application/xv+xml": ["mxml", "xhvml", "xvml", "xvm"], "application/yang": ["yang"], "application/yin+xml": ["yin"], "application/zip": ["zip"], "audio/3gpp": ["*3gpp"], "audio/aac": ["adts", "aac"], "audio/adpcm": ["adp"], "audio/amr": ["amr"], "audio/basic": ["au", "snd"], "audio/midi": ["mid", "midi", "kar", "rmi"], "audio/mobile-xmf": ["mxmf"], "audio/mp3": ["*mp3"], "audio/mp4": ["m4a", "mp4a"], "audio/mpeg": ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"], "audio/ogg": ["oga", "ogg", "spx", "opus"], "audio/s3m": ["s3m"], "audio/silk": ["sil"], "audio/wav": ["wav"], "audio/wave": ["*wav"], "audio/webm": ["weba"], "audio/xm": ["xm"], "font/collection": ["ttc"], "font/otf": ["otf"], "font/ttf": ["ttf"], "font/woff": ["woff"], "font/woff2": ["woff2"], "image/aces": ["exr"], "image/apng": ["apng"], "image/avci": ["avci"], "image/avcs": ["avcs"], "image/avif": ["avif"], "image/bmp": ["bmp", "dib"], "image/cgm": ["cgm"], "image/dicom-rle": ["drle"], "image/dpx": ["dpx"], "image/emf": ["emf"], "image/fits": ["fits"], "image/g3fax": ["g3"], "image/gif": ["gif"], "image/heic": ["heic"], "image/heic-sequence": ["heics"], "image/heif": ["heif"], "image/heif-sequence": ["heifs"], "image/hej2k": ["hej2"], "image/hsj2": ["hsj2"], "image/ief": ["ief"], "image/jls": ["jls"], "image/jp2": ["jp2", "jpg2"], "image/jpeg": ["jpeg", "jpg", "jpe"], "image/jph": ["jph"], "image/jphc": ["jhc"], "image/jpm": ["jpm", "jpgm"], "image/jpx": ["jpx", "jpf"], "image/jxr": ["jxr"], "image/jxra": ["jxra"], "image/jxrs": ["jxrs"], "image/jxs": ["jxs"], "image/jxsc": ["jxsc"], "image/jxsi": ["jxsi"], "image/jxss": ["jxss"], "image/ktx": ["ktx"], "image/ktx2": ["ktx2"], "image/png": ["png"], "image/sgi": ["sgi"], "image/svg+xml": ["svg", "svgz"], "image/t38": ["t38"], "image/tiff": ["tif", "tiff"], "image/tiff-fx": ["tfx"], "image/webp": ["webp"], "image/wmf": ["wmf"], "message/disposition-notification": ["disposition-notification"], "message/global": ["u8msg"], "message/global-delivery-status": ["u8dsn"], "message/global-disposition-notification": ["u8mdn"], "message/global-headers": ["u8hdr"], "message/rfc822": ["eml", "mime"], "model/3mf": ["3mf"], "model/gltf+json": ["gltf"], "model/gltf-binary": ["glb"], "model/iges": ["igs", "iges"], "model/jt": ["jt"], "model/mesh": ["msh", "mesh", "silo"], "model/mtl": ["mtl"], "model/obj": ["obj"], "model/prc": ["prc"], "model/step+xml": ["stpx"], "model/step+zip": ["stpz"], "model/step-xml+zip": ["stpxz"], "model/stl": ["stl"], "model/u3d": ["u3d"], "model/vrml": ["wrl", "vrml"], "model/x3d+binary": ["*x3db", "x3dbz"], "model/x3d+fastinfoset": ["x3db"], "model/x3d+vrml": ["*x3dv", "x3dvz"], "model/x3d+xml": ["x3d", "x3dz"], "model/x3d-vrml": ["x3dv"], "text/cache-manifest": ["appcache", "manifest"], "text/calendar": ["ics", "ifb"], "text/coffeescript": ["coffee", "litcoffee"], "text/css": ["css"], "text/csv": ["csv"], "text/html": ["html", "htm", "shtml"], "text/jade": ["jade"], "text/javascript": ["js", "mjs"], "text/jsx": ["jsx"], "text/less": ["less"], "text/markdown": ["md", "markdown"], "text/mathml": ["mml"], "text/mdx": ["mdx"], "text/n3": ["n3"], "text/plain": ["txt", "text", "conf", "def", "list", "log", "in", "ini"], "text/richtext": ["rtx"], "text/rtf": ["*rtf"], "text/sgml": ["sgml", "sgm"], "text/shex": ["shex"], "text/slim": ["slim", "slm"], "text/spdx": ["spdx"], "text/stylus": ["stylus", "styl"], "text/tab-separated-values": ["tsv"], "text/troff": ["t", "tr", "roff", "man", "me", "ms"], "text/turtle": ["ttl"], "text/uri-list": ["uri", "uris", "urls"], "text/vcard": ["vcard"], "text/vtt": ["vtt"], "text/wgsl": ["wgsl"], "text/xml": ["*xml"], "text/yaml": ["yaml", "yml"], "video/3gpp": ["3gp", "3gpp"], "video/3gpp2": ["3g2"], "video/h261": ["h261"], "video/h263": ["h263"], "video/h264": ["h264"], "video/iso.segment": ["m4s"], "video/jpeg": ["jpgv"], "video/jpm": ["*jpm", "*jpgm"], "video/mj2": ["mj2", "mjp2"], "video/mp2t": ["ts"], "video/mp4": ["mp4", "mp4v", "mpg4"], "video/mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v"], "video/ogg": ["ogv"], "video/quicktime": ["qt", "mov"], "video/webm": ["webm"] };
|
|
299
|
+
Object.freeze(types);
|
|
300
|
+
|
|
301
|
+
var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
302
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
303
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
304
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
305
|
+
};
|
|
306
|
+
var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
|
|
307
|
+
class Mime {
|
|
308
|
+
constructor(...args) {
|
|
309
|
+
_Mime_extensionToType.set(this, new Map());
|
|
310
|
+
_Mime_typeToExtension.set(this, new Map());
|
|
311
|
+
_Mime_typeToExtensions.set(this, new Map());
|
|
312
|
+
for (const arg of args) {
|
|
313
|
+
this.define(arg);
|
|
314
|
+
}
|
|
397
315
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
316
|
+
define(typeMap, force = false) {
|
|
317
|
+
for (let [type, extensions] of Object.entries(typeMap)) {
|
|
318
|
+
type = type.toLowerCase();
|
|
319
|
+
extensions = extensions.map((ext) => ext.toLowerCase());
|
|
320
|
+
if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
|
|
321
|
+
__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
|
|
322
|
+
}
|
|
323
|
+
const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
|
|
324
|
+
let first = true;
|
|
325
|
+
for (let extension of extensions) {
|
|
326
|
+
const starred = extension.startsWith('*');
|
|
327
|
+
extension = starred ? extension.slice(1) : extension;
|
|
328
|
+
allExtensions?.add(extension);
|
|
329
|
+
if (first) {
|
|
330
|
+
__classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
|
|
331
|
+
}
|
|
332
|
+
first = false;
|
|
333
|
+
if (starred)
|
|
334
|
+
continue;
|
|
335
|
+
const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
|
|
336
|
+
if (currentType && currentType != type && !force) {
|
|
337
|
+
throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
|
|
338
|
+
}
|
|
339
|
+
__classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return this;
|
|
404
343
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const wss = new WebSocketServer({ noServer: true });
|
|
416
|
-
vite.httpServer?.on("upgrade", (request, socket, head) => {
|
|
417
|
-
if (!request.url) {
|
|
418
|
-
return;
|
|
344
|
+
getType(path) {
|
|
345
|
+
if (typeof path !== 'string')
|
|
346
|
+
return null;
|
|
347
|
+
const last = path.replace(/^.*[/\\]/, '').toLowerCase();
|
|
348
|
+
const ext = last.replace(/^.*\./, '').toLowerCase();
|
|
349
|
+
const hasPath = last.length < path.length;
|
|
350
|
+
const hasDot = ext.length < last.length - 1;
|
|
351
|
+
if (!hasDot && hasPath)
|
|
352
|
+
return null;
|
|
353
|
+
return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
|
|
419
354
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
355
|
+
getExtension(type) {
|
|
356
|
+
if (typeof type !== 'string')
|
|
357
|
+
return null;
|
|
358
|
+
type = type?.split?.(';')[0];
|
|
359
|
+
return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
|
|
423
360
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const rpc = setupClient(sessionId, ws);
|
|
429
|
-
const state = server.state;
|
|
430
|
-
const clients = type === "tester" ? state.testers : state.orchestrators;
|
|
431
|
-
clients.set(sessionId, rpc);
|
|
432
|
-
debug$1?.("[%s] Browser API connected to %s", sessionId, type);
|
|
433
|
-
ws.on("close", () => {
|
|
434
|
-
debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
|
|
435
|
-
clients.delete(sessionId);
|
|
436
|
-
server.state.removeCDPHandler(sessionId);
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
});
|
|
440
|
-
function checkFileAccess(path) {
|
|
441
|
-
if (!isFileServingAllowed(path, vite)) {
|
|
442
|
-
throw new Error(
|
|
443
|
-
`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
|
|
444
|
-
);
|
|
361
|
+
getAllExtensions(type) {
|
|
362
|
+
if (typeof type !== 'string')
|
|
363
|
+
return null;
|
|
364
|
+
return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
|
|
445
365
|
}
|
|
366
|
+
_freeze() {
|
|
367
|
+
this.define = () => {
|
|
368
|
+
throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
|
|
369
|
+
};
|
|
370
|
+
Object.freeze(this);
|
|
371
|
+
for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
|
|
372
|
+
Object.freeze(extensions);
|
|
373
|
+
}
|
|
374
|
+
return this;
|
|
375
|
+
}
|
|
376
|
+
_getTestState() {
|
|
377
|
+
return {
|
|
378
|
+
types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
|
|
379
|
+
extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
_Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
|
|
384
|
+
|
|
385
|
+
var mime = new Mime(types)._freeze();
|
|
386
|
+
|
|
387
|
+
function assertFileAccess(path, project) {
|
|
388
|
+
if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server)) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
|
|
391
|
+
);
|
|
446
392
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
393
|
+
}
|
|
394
|
+
const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
|
|
395
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
396
|
+
assertFileAccess(filepath, project);
|
|
397
|
+
if (typeof options === "object" && !options.encoding) {
|
|
398
|
+
options.encoding = "utf-8";
|
|
399
|
+
}
|
|
400
|
+
return promises.readFile(filepath, options);
|
|
401
|
+
};
|
|
402
|
+
const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
|
|
403
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
404
|
+
assertFileAccess(filepath, project);
|
|
405
|
+
const dir = dirname$1(filepath);
|
|
406
|
+
if (!fs.existsSync(dir)) {
|
|
407
|
+
await promises.mkdir(dir, { recursive: true });
|
|
408
|
+
}
|
|
409
|
+
await promises.writeFile(filepath, data, options);
|
|
410
|
+
};
|
|
411
|
+
const removeFile = async ({ project, testPath = process.cwd() }, path) => {
|
|
412
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
413
|
+
assertFileAccess(filepath, project);
|
|
414
|
+
await promises.rm(filepath);
|
|
415
|
+
};
|
|
416
|
+
const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
|
|
417
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
418
|
+
assertFileAccess(filepath, project);
|
|
419
|
+
const content = await promises.readFile(filepath, encoding || "base64");
|
|
420
|
+
return {
|
|
421
|
+
content,
|
|
422
|
+
basename: basename$1(filepath),
|
|
423
|
+
mime: mime.getType(filepath)
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const hover = async (context, selector, options = {}) => {
|
|
428
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
429
|
+
await context.iframe.locator(selector).hover({
|
|
430
|
+
timeout: 1e3,
|
|
431
|
+
...options
|
|
450
432
|
});
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
_error.stacks = server.parseErrorStacktrace(_error);
|
|
457
|
-
}
|
|
458
|
-
ctx.state.catchError(error, type);
|
|
459
|
-
},
|
|
460
|
-
async onCollected(files) {
|
|
461
|
-
ctx.state.collectFiles(project, files);
|
|
462
|
-
await ctx.report("onCollected", files);
|
|
463
|
-
},
|
|
464
|
-
async onTaskUpdate(packs) {
|
|
465
|
-
ctx.state.updateTasks(packs);
|
|
466
|
-
await ctx.report("onTaskUpdate", packs);
|
|
467
|
-
},
|
|
468
|
-
onAfterSuiteRun(meta) {
|
|
469
|
-
ctx.coverageProvider?.onAfterSuiteRun(meta);
|
|
470
|
-
},
|
|
471
|
-
sendLog(log) {
|
|
472
|
-
return ctx.report("onUserConsoleLog", log);
|
|
473
|
-
},
|
|
474
|
-
resolveSnapshotPath(testPath) {
|
|
475
|
-
return ctx.snapshot.resolvePath(testPath);
|
|
476
|
-
},
|
|
477
|
-
resolveSnapshotRawPath(testPath, rawPath) {
|
|
478
|
-
return ctx.snapshot.resolveRawPath(testPath, rawPath);
|
|
479
|
-
},
|
|
480
|
-
snapshotSaved(snapshot) {
|
|
481
|
-
ctx.snapshot.add(snapshot);
|
|
482
|
-
},
|
|
483
|
-
async readSnapshotFile(snapshotPath) {
|
|
484
|
-
checkFileAccess(snapshotPath);
|
|
485
|
-
if (!existsSync(snapshotPath)) {
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
return promises.readFile(snapshotPath, "utf-8");
|
|
489
|
-
},
|
|
490
|
-
async saveSnapshotFile(id, content) {
|
|
491
|
-
checkFileAccess(id);
|
|
492
|
-
await promises.mkdir(dirname(id), { recursive: true });
|
|
493
|
-
return promises.writeFile(id, content, "utf-8");
|
|
494
|
-
},
|
|
495
|
-
async removeSnapshotFile(id) {
|
|
496
|
-
checkFileAccess(id);
|
|
497
|
-
if (!existsSync(id)) {
|
|
498
|
-
throw new Error(`Snapshot file "${id}" does not exist.`);
|
|
499
|
-
}
|
|
500
|
-
return promises.unlink(id);
|
|
501
|
-
},
|
|
502
|
-
getBrowserFileSourceMap(id) {
|
|
503
|
-
const mod = server.vite.moduleGraph.getModuleById(id);
|
|
504
|
-
return mod?.transformResult?.map;
|
|
505
|
-
},
|
|
506
|
-
onCancel(reason) {
|
|
507
|
-
ctx.cancelCurrentRun(reason);
|
|
508
|
-
},
|
|
509
|
-
async resolveId(id, importer) {
|
|
510
|
-
return mockResolver.resolveId(id, importer);
|
|
511
|
-
},
|
|
512
|
-
debug(...args) {
|
|
513
|
-
ctx.logger.console.debug(...args);
|
|
514
|
-
},
|
|
515
|
-
getCountOfFailedTests() {
|
|
516
|
-
return ctx.state.getCountOfFailedTests();
|
|
517
|
-
},
|
|
518
|
-
async triggerCommand(contextId, command, testPath, payload) {
|
|
519
|
-
debug$1?.('[%s] Triggering command "%s"', contextId, command);
|
|
520
|
-
const provider = server.provider;
|
|
521
|
-
if (!provider) {
|
|
522
|
-
throw new Error("Commands are only available for browser tests.");
|
|
523
|
-
}
|
|
524
|
-
const commands = project.config.browser?.commands;
|
|
525
|
-
if (!commands || !commands[command]) {
|
|
526
|
-
throw new Error(`Unknown command "${command}".`);
|
|
527
|
-
}
|
|
528
|
-
if (provider.beforeCommand) {
|
|
529
|
-
await provider.beforeCommand(command, payload);
|
|
530
|
-
}
|
|
531
|
-
const context = Object.assign(
|
|
532
|
-
{
|
|
533
|
-
testPath,
|
|
534
|
-
project,
|
|
535
|
-
provider,
|
|
536
|
-
contextId
|
|
537
|
-
},
|
|
538
|
-
provider.getCommandsContext(contextId)
|
|
539
|
-
);
|
|
540
|
-
let result;
|
|
541
|
-
try {
|
|
542
|
-
result = await commands[command](context, ...payload);
|
|
543
|
-
} finally {
|
|
544
|
-
if (provider.afterCommand) {
|
|
545
|
-
await provider.afterCommand(command, payload);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return result;
|
|
549
|
-
},
|
|
550
|
-
finishBrowserTests(contextId) {
|
|
551
|
-
debug$1?.("[%s] Finishing browser tests for context", contextId);
|
|
552
|
-
return server.state.getContext(contextId)?.resolve();
|
|
553
|
-
},
|
|
554
|
-
resolveMock(rawId, importer, options) {
|
|
555
|
-
return mockResolver.resolveMock(rawId, importer, options);
|
|
556
|
-
},
|
|
557
|
-
invalidate(ids) {
|
|
558
|
-
return mockResolver.invalidate(ids);
|
|
559
|
-
},
|
|
560
|
-
// CDP
|
|
561
|
-
async sendCdpEvent(contextId, event, payload) {
|
|
562
|
-
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
563
|
-
return cdp.send(event, payload);
|
|
564
|
-
},
|
|
565
|
-
async trackCdpEvent(contextId, type, event, listenerId) {
|
|
566
|
-
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
567
|
-
cdp[type](event, listenerId);
|
|
568
|
-
}
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
post: (msg) => ws.send(msg),
|
|
572
|
-
on: (fn) => ws.on("message", fn),
|
|
573
|
-
eventNames: ["onCancel", "cdpEvent"],
|
|
574
|
-
serialize: (data) => stringify(data, stringifyReplace),
|
|
575
|
-
deserialize: parse,
|
|
576
|
-
onTimeoutError(functionName) {
|
|
577
|
-
throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
);
|
|
581
|
-
ctx.onCancel((reason) => rpc.onCancel(reason));
|
|
582
|
-
return rpc;
|
|
433
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
434
|
+
const browser = context.browser;
|
|
435
|
+
await browser.$(selector).moveTo(options);
|
|
436
|
+
} else {
|
|
437
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
|
|
583
438
|
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
var clickableInputTypes;
|
|
442
|
+
(function(clickableInputTypes) {
|
|
443
|
+
clickableInputTypes["button"] = "button";
|
|
444
|
+
clickableInputTypes["color"] = "color";
|
|
445
|
+
clickableInputTypes["file"] = "file";
|
|
446
|
+
clickableInputTypes["image"] = "image";
|
|
447
|
+
clickableInputTypes["reset"] = "reset";
|
|
448
|
+
clickableInputTypes["submit"] = "submit";
|
|
449
|
+
clickableInputTypes["checkbox"] = "checkbox";
|
|
450
|
+
clickableInputTypes["radio"] = "radio";
|
|
451
|
+
})(clickableInputTypes || (clickableInputTypes = {}));
|
|
452
|
+
|
|
453
|
+
var editableInputTypes;
|
|
454
|
+
(function(editableInputTypes) {
|
|
455
|
+
editableInputTypes["text"] = "text";
|
|
456
|
+
editableInputTypes["date"] = "date";
|
|
457
|
+
editableInputTypes["datetime-local"] = "datetime-local";
|
|
458
|
+
editableInputTypes["email"] = "email";
|
|
459
|
+
editableInputTypes["month"] = "month";
|
|
460
|
+
editableInputTypes["number"] = "number";
|
|
461
|
+
editableInputTypes["password"] = "password";
|
|
462
|
+
editableInputTypes["search"] = "search";
|
|
463
|
+
editableInputTypes["tel"] = "tel";
|
|
464
|
+
editableInputTypes["time"] = "time";
|
|
465
|
+
editableInputTypes["url"] = "url";
|
|
466
|
+
editableInputTypes["week"] = "week";
|
|
467
|
+
})(editableInputTypes || (editableInputTypes = {}));
|
|
468
|
+
|
|
469
|
+
var maxLengthSupportedTypes;
|
|
470
|
+
(function(maxLengthSupportedTypes) {
|
|
471
|
+
maxLengthSupportedTypes["email"] = "email";
|
|
472
|
+
maxLengthSupportedTypes["password"] = "password";
|
|
473
|
+
maxLengthSupportedTypes["search"] = "search";
|
|
474
|
+
maxLengthSupportedTypes["telephone"] = "telephone";
|
|
475
|
+
maxLengthSupportedTypes["text"] = "text";
|
|
476
|
+
maxLengthSupportedTypes["url"] = "url";
|
|
477
|
+
})(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
|
|
478
|
+
|
|
479
|
+
var bracketDict;
|
|
480
|
+
(function(bracketDict) {
|
|
481
|
+
bracketDict["{"] = "}";
|
|
482
|
+
bracketDict["["] = "]";
|
|
483
|
+
})(bracketDict || (bracketDict = {}));
|
|
484
|
+
/**
|
|
485
|
+
* Read the next key definition from user input
|
|
486
|
+
*
|
|
487
|
+
* Describe key per `{descriptor}` or `[descriptor]`.
|
|
488
|
+
* Everything else will be interpreted as a single character as descriptor - e.g. `a`.
|
|
489
|
+
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
|
|
490
|
+
* A previously pressed key can be released per `{/descriptor}`.
|
|
491
|
+
* Keeping the key pressed can be written as `{descriptor>}`.
|
|
492
|
+
* When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
|
|
493
|
+
* You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
|
|
494
|
+
*/ function readNextDescriptor(text, context) {
|
|
495
|
+
let pos = 0;
|
|
496
|
+
const startBracket = text[pos] in bracketDict ? text[pos] : '';
|
|
497
|
+
pos += startBracket.length;
|
|
498
|
+
const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
|
|
499
|
+
const type = isEscapedChar ? '' : startBracket;
|
|
500
|
+
return {
|
|
501
|
+
type,
|
|
502
|
+
...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
|
|
503
|
+
};
|
|
584
504
|
}
|
|
585
|
-
function
|
|
586
|
-
|
|
587
|
-
(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
505
|
+
function readPrintableChar(text, pos, context) {
|
|
506
|
+
const descriptor = text[pos];
|
|
507
|
+
assertDescriptor(descriptor, text, pos);
|
|
508
|
+
pos += descriptor.length;
|
|
509
|
+
return {
|
|
510
|
+
consumedLength: pos,
|
|
511
|
+
descriptor,
|
|
512
|
+
releasePrevious: false,
|
|
513
|
+
releaseSelf: true,
|
|
514
|
+
repeat: 1
|
|
515
|
+
};
|
|
593
516
|
}
|
|
594
|
-
function
|
|
595
|
-
|
|
596
|
-
const
|
|
517
|
+
function readTag(text, pos, startBracket, context) {
|
|
518
|
+
var _text_slice_match, _text_slice_match1;
|
|
519
|
+
const releasePreviousModifier = text[pos] === '/' ? '/' : '';
|
|
520
|
+
pos += releasePreviousModifier.length;
|
|
521
|
+
const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
|
|
522
|
+
pos += Number(escapedDescriptor);
|
|
523
|
+
const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === void 0 ? void 0 : _text_slice_match[0];
|
|
524
|
+
assertDescriptor(descriptor, text, pos);
|
|
525
|
+
pos += descriptor.length;
|
|
526
|
+
var _text_slice_match_;
|
|
527
|
+
const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === void 0 ? void 0 : _text_slice_match1[0]) !== null && _text_slice_match_ !== void 0 ? _text_slice_match_ : '';
|
|
528
|
+
pos += repeatModifier.length;
|
|
529
|
+
const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
|
|
530
|
+
pos += releaseSelfModifier.length;
|
|
531
|
+
const expectedEndBracket = bracketDict[startBracket];
|
|
532
|
+
const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
|
|
533
|
+
if (!endBracket) {
|
|
534
|
+
throw new Error(getErrorMessage([
|
|
535
|
+
!repeatModifier && 'repeat modifier',
|
|
536
|
+
!releaseSelfModifier && 'release modifier',
|
|
537
|
+
`"${expectedEndBracket}"`
|
|
538
|
+
].filter(Boolean).join(' or '), text[pos], text));
|
|
539
|
+
}
|
|
540
|
+
pos += endBracket.length;
|
|
597
541
|
return {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
542
|
+
consumedLength: pos,
|
|
543
|
+
descriptor,
|
|
544
|
+
releasePrevious: !!releasePreviousModifier,
|
|
545
|
+
repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
|
|
546
|
+
releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
|
|
602
547
|
};
|
|
603
|
-
} else {
|
|
604
|
-
return value;
|
|
605
|
-
}
|
|
606
548
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
cdps = /* @__PURE__ */ new Map();
|
|
612
|
-
contexts = /* @__PURE__ */ new Map();
|
|
613
|
-
getContext(contextId) {
|
|
614
|
-
return this.contexts.get(contextId);
|
|
615
|
-
}
|
|
616
|
-
createAsyncContext(method, contextId, files) {
|
|
617
|
-
const defer = createDefer();
|
|
618
|
-
this.contexts.set(contextId, {
|
|
619
|
-
files,
|
|
620
|
-
method,
|
|
621
|
-
resolve: () => {
|
|
622
|
-
defer.resolve();
|
|
623
|
-
this.contexts.delete(contextId);
|
|
624
|
-
},
|
|
625
|
-
reject: defer.reject
|
|
626
|
-
});
|
|
627
|
-
return defer;
|
|
628
|
-
}
|
|
629
|
-
async removeCDPHandler(sessionId) {
|
|
630
|
-
this.cdps.delete(sessionId);
|
|
631
|
-
}
|
|
549
|
+
function assertDescriptor(descriptor, text, pos, context) {
|
|
550
|
+
if (!descriptor) {
|
|
551
|
+
throw new Error(getErrorMessage('key descriptor', text[pos], text));
|
|
552
|
+
}
|
|
632
553
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const providers = await import('./providers.js');
|
|
641
|
-
const provider = options.provider || "preview";
|
|
642
|
-
return providers[provider];
|
|
643
|
-
}
|
|
644
|
-
let customProviderModule;
|
|
645
|
-
try {
|
|
646
|
-
customProviderModule = await project.runner.executeId(
|
|
647
|
-
options.provider
|
|
648
|
-
);
|
|
649
|
-
} catch (error) {
|
|
650
|
-
throw new Error(
|
|
651
|
-
`Failed to load custom BrowserProvider from ${options.provider}`,
|
|
652
|
-
{ cause: error }
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
if (customProviderModule.default == null) {
|
|
656
|
-
throw new Error(
|
|
657
|
-
`Custom BrowserProvider loaded from ${options.provider} was not the default export`
|
|
658
|
-
);
|
|
659
|
-
}
|
|
660
|
-
return customProviderModule.default;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
class BrowserServerCDPHandler {
|
|
664
|
-
constructor(session, tester) {
|
|
665
|
-
this.session = session;
|
|
666
|
-
this.tester = tester;
|
|
667
|
-
}
|
|
668
|
-
listenerIds = {};
|
|
669
|
-
listeners = {};
|
|
670
|
-
send(method, params) {
|
|
671
|
-
return this.session.send(method, params);
|
|
672
|
-
}
|
|
673
|
-
on(event, id, once = false) {
|
|
674
|
-
if (!this.listenerIds[event]) {
|
|
675
|
-
this.listenerIds[event] = [];
|
|
676
|
-
}
|
|
677
|
-
this.listenerIds[event].push(id);
|
|
678
|
-
if (!this.listeners[event]) {
|
|
679
|
-
this.listeners[event] = (payload) => {
|
|
680
|
-
this.tester.cdpEvent(
|
|
681
|
-
event,
|
|
682
|
-
payload
|
|
683
|
-
);
|
|
684
|
-
if (once) {
|
|
685
|
-
this.off(event, id);
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
this.session.on(event, this.listeners[event]);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
off(event, id) {
|
|
692
|
-
if (!this.listenerIds[event]) {
|
|
693
|
-
this.listenerIds[event] = [];
|
|
694
|
-
}
|
|
695
|
-
this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
|
|
696
|
-
if (!this.listenerIds[event].length) {
|
|
697
|
-
this.session.off(event, this.listeners[event]);
|
|
698
|
-
delete this.listeners[event];
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
once(event, listener) {
|
|
702
|
-
this.on(event, listener, true);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
class BrowserServer {
|
|
707
|
-
constructor(project, base) {
|
|
708
|
-
this.project = project;
|
|
709
|
-
this.base = base;
|
|
710
|
-
this.stackTraceOptions = {
|
|
711
|
-
frameFilter: project.config.onStackTrace,
|
|
712
|
-
getSourceMap: (id) => {
|
|
713
|
-
const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
|
|
714
|
-
return result?.map;
|
|
715
|
-
},
|
|
716
|
-
getFileName: (id) => {
|
|
717
|
-
const mod = this.vite.moduleGraph.getModuleById(id);
|
|
718
|
-
if (mod?.file) {
|
|
719
|
-
return mod.file;
|
|
720
|
-
}
|
|
721
|
-
const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
|
|
722
|
-
if (modUrl?.file) {
|
|
723
|
-
return modUrl.file;
|
|
724
|
-
}
|
|
725
|
-
return id;
|
|
726
|
-
}
|
|
727
|
-
};
|
|
728
|
-
this.state = new BrowserServerState();
|
|
729
|
-
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
730
|
-
const distRoot = resolve(pkgRoot, "dist");
|
|
731
|
-
this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
|
|
732
|
-
this.faviconUrl = `${base}__vitest__/favicon.svg`;
|
|
733
|
-
this.manifest = (async () => {
|
|
734
|
-
return JSON.parse(
|
|
735
|
-
await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
|
|
736
|
-
);
|
|
737
|
-
})().then((manifest) => this.manifest = manifest);
|
|
738
|
-
this.testerHtml = readFile$1(
|
|
739
|
-
resolve(distRoot, "client/tester/tester.html"),
|
|
740
|
-
"utf8"
|
|
741
|
-
).then((html) => this.testerHtml = html);
|
|
742
|
-
this.orchestratorHtml = (project.config.browser.ui ? readFile$1(resolve(distRoot, "client/__vitest__/index.html"), "utf8") : readFile$1(resolve(distRoot, "client/orchestrator.html"), "utf8")).then((html) => this.orchestratorHtml = html);
|
|
743
|
-
this.injectorJs = readFile$1(
|
|
744
|
-
resolve(distRoot, "client/esm-client-injector.js"),
|
|
745
|
-
"utf8"
|
|
746
|
-
).then((js) => this.injectorJs = js);
|
|
747
|
-
this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
|
|
748
|
-
const builtinProviders = ["playwright", "webdriverio", "preview"];
|
|
749
|
-
const providerName = project.config.browser.provider || "preview";
|
|
750
|
-
if (builtinProviders.includes(providerName)) {
|
|
751
|
-
this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
|
|
752
|
-
}
|
|
753
|
-
this.stateJs = readFile$1(
|
|
754
|
-
resolve(distRoot, "state.js"),
|
|
755
|
-
"utf-8"
|
|
756
|
-
).then((js) => this.stateJs = js);
|
|
757
|
-
}
|
|
758
|
-
faviconUrl;
|
|
759
|
-
prefixTesterUrl;
|
|
760
|
-
orchestratorScripts;
|
|
761
|
-
testerScripts;
|
|
762
|
-
manifest;
|
|
763
|
-
testerHtml;
|
|
764
|
-
orchestratorHtml;
|
|
765
|
-
injectorJs;
|
|
766
|
-
errorCatcherUrl;
|
|
767
|
-
locatorsUrl;
|
|
768
|
-
stateJs;
|
|
769
|
-
state;
|
|
770
|
-
provider;
|
|
771
|
-
vite;
|
|
772
|
-
stackTraceOptions;
|
|
773
|
-
setServer(server) {
|
|
774
|
-
this.vite = server;
|
|
775
|
-
}
|
|
776
|
-
getSerializableConfig() {
|
|
777
|
-
const config = wrapConfig(this.project.getSerializableConfig());
|
|
778
|
-
config.env ??= {};
|
|
779
|
-
config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
|
|
780
|
-
return config;
|
|
781
|
-
}
|
|
782
|
-
resolveTesterUrl(pathname) {
|
|
783
|
-
const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
|
|
784
|
-
const decodedTestFile = decodeURIComponent(testFile);
|
|
785
|
-
return { contextId, testFile: decodedTestFile };
|
|
786
|
-
}
|
|
787
|
-
async formatScripts(scripts) {
|
|
788
|
-
if (!scripts?.length) {
|
|
789
|
-
return "";
|
|
790
|
-
}
|
|
791
|
-
const server = this.vite;
|
|
792
|
-
const promises = scripts.map(
|
|
793
|
-
async ({ content, src, async, id, type = "module" }, index) => {
|
|
794
|
-
const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
|
|
795
|
-
const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
|
|
796
|
-
await server.moduleGraph.ensureEntryFromUrl(transformId);
|
|
797
|
-
const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
|
|
798
|
-
return `<script type="${type}"${async ? " async" : ""}${srcLink ? ` src="${slash(`/@fs/${srcLink}`)}"` : ""}>${contentProcessed || ""}<\/script>`;
|
|
799
|
-
}
|
|
800
|
-
);
|
|
801
|
-
return (await Promise.all(promises)).join("\n");
|
|
802
|
-
}
|
|
803
|
-
async initBrowserProvider() {
|
|
804
|
-
if (this.provider) {
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
const Provider = await getBrowserProvider(this.project.config.browser, this.project);
|
|
808
|
-
this.provider = new Provider();
|
|
809
|
-
const browser = this.project.config.browser.name;
|
|
810
|
-
if (!browser) {
|
|
811
|
-
throw new Error(
|
|
812
|
-
`[${this.project.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
const supportedBrowsers = this.provider.getSupportedBrowsers();
|
|
816
|
-
if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
|
|
817
|
-
throw new Error(
|
|
818
|
-
`[${this.project.getName()}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
const providerOptions = this.project.config.browser.providerOptions;
|
|
822
|
-
await this.provider.initialize(this.project, {
|
|
823
|
-
browser,
|
|
824
|
-
options: providerOptions
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
parseErrorStacktrace(e, options = {}) {
|
|
828
|
-
return parseErrorStacktrace(e, {
|
|
829
|
-
...this.stackTraceOptions,
|
|
830
|
-
...options
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
parseStacktrace(trace, options = {}) {
|
|
834
|
-
return parseStacktrace(trace, {
|
|
835
|
-
...this.stackTraceOptions,
|
|
836
|
-
...options
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
cdpSessionsPromises = /* @__PURE__ */ new Map();
|
|
840
|
-
async ensureCDPHandler(contextId, sessionId) {
|
|
841
|
-
const cachedHandler = this.state.cdps.get(sessionId);
|
|
842
|
-
if (cachedHandler) {
|
|
843
|
-
return cachedHandler;
|
|
844
|
-
}
|
|
845
|
-
const provider = this.provider;
|
|
846
|
-
if (!provider.getCDPSession) {
|
|
847
|
-
throw new Error(`CDP is not supported by the provider "${provider.name}".`);
|
|
848
|
-
}
|
|
849
|
-
const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
|
|
850
|
-
const promise2 = provider.getCDPSession(contextId).finally(() => {
|
|
851
|
-
this.cdpSessionsPromises.delete(sessionId);
|
|
852
|
-
});
|
|
853
|
-
this.cdpSessionsPromises.set(sessionId, promise2);
|
|
854
|
-
return promise2;
|
|
855
|
-
})();
|
|
856
|
-
const session = await promise;
|
|
857
|
-
const rpc = this.state.testers.get(sessionId);
|
|
858
|
-
if (!rpc) {
|
|
859
|
-
throw new Error(`Tester RPC "${sessionId}" was not established.`);
|
|
860
|
-
}
|
|
861
|
-
const handler = new BrowserServerCDPHandler(session, rpc);
|
|
862
|
-
this.state.cdps.set(
|
|
863
|
-
sessionId,
|
|
864
|
-
handler
|
|
865
|
-
);
|
|
866
|
-
return handler;
|
|
867
|
-
}
|
|
868
|
-
async close() {
|
|
869
|
-
await this.vite.close();
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
function wrapConfig(config) {
|
|
873
|
-
return {
|
|
874
|
-
...config,
|
|
875
|
-
// workaround RegExp serialization
|
|
876
|
-
testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
const click = async (context, selector, options = {}) => {
|
|
881
|
-
const provider = context.provider;
|
|
882
|
-
if (provider instanceof PlaywrightBrowserProvider) {
|
|
883
|
-
const tester = context.iframe;
|
|
884
|
-
await tester.locator(selector).click({
|
|
885
|
-
timeout: 1e3,
|
|
886
|
-
...options
|
|
887
|
-
});
|
|
888
|
-
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
889
|
-
const browser = context.browser;
|
|
890
|
-
await browser.$(selector).click(options);
|
|
891
|
-
} else {
|
|
892
|
-
throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
const dblClick = async (context, selector, options = {}) => {
|
|
896
|
-
const provider = context.provider;
|
|
897
|
-
if (provider instanceof PlaywrightBrowserProvider) {
|
|
898
|
-
const tester = context.iframe;
|
|
899
|
-
await tester.locator(selector).dblclick(options);
|
|
900
|
-
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
901
|
-
const browser = context.browser;
|
|
902
|
-
await browser.$(selector).doubleClick();
|
|
903
|
-
} else {
|
|
904
|
-
throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
const tripleClick = async (context, selector, options = {}) => {
|
|
908
|
-
const provider = context.provider;
|
|
909
|
-
if (provider instanceof PlaywrightBrowserProvider) {
|
|
910
|
-
const tester = context.iframe;
|
|
911
|
-
await tester.locator(selector).click({
|
|
912
|
-
timeout: 1e3,
|
|
913
|
-
...options,
|
|
914
|
-
clickCount: 3
|
|
915
|
-
});
|
|
916
|
-
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
917
|
-
const browser = context.browser;
|
|
918
|
-
await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: await browser.$(selector) }).down().up().pause(50).down().up().pause(50).down().up().pause(50).perform();
|
|
919
|
-
} else {
|
|
920
|
-
throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
var clickableInputTypes;
|
|
925
|
-
(function(clickableInputTypes) {
|
|
926
|
-
clickableInputTypes["button"] = "button";
|
|
927
|
-
clickableInputTypes["color"] = "color";
|
|
928
|
-
clickableInputTypes["file"] = "file";
|
|
929
|
-
clickableInputTypes["image"] = "image";
|
|
930
|
-
clickableInputTypes["reset"] = "reset";
|
|
931
|
-
clickableInputTypes["submit"] = "submit";
|
|
932
|
-
clickableInputTypes["checkbox"] = "checkbox";
|
|
933
|
-
clickableInputTypes["radio"] = "radio";
|
|
934
|
-
})(clickableInputTypes || (clickableInputTypes = {}));
|
|
935
|
-
|
|
936
|
-
var editableInputTypes;
|
|
937
|
-
(function(editableInputTypes) {
|
|
938
|
-
editableInputTypes["text"] = "text";
|
|
939
|
-
editableInputTypes["date"] = "date";
|
|
940
|
-
editableInputTypes["datetime-local"] = "datetime-local";
|
|
941
|
-
editableInputTypes["email"] = "email";
|
|
942
|
-
editableInputTypes["month"] = "month";
|
|
943
|
-
editableInputTypes["number"] = "number";
|
|
944
|
-
editableInputTypes["password"] = "password";
|
|
945
|
-
editableInputTypes["search"] = "search";
|
|
946
|
-
editableInputTypes["tel"] = "tel";
|
|
947
|
-
editableInputTypes["time"] = "time";
|
|
948
|
-
editableInputTypes["url"] = "url";
|
|
949
|
-
editableInputTypes["week"] = "week";
|
|
950
|
-
})(editableInputTypes || (editableInputTypes = {}));
|
|
951
|
-
|
|
952
|
-
var maxLengthSupportedTypes;
|
|
953
|
-
(function(maxLengthSupportedTypes) {
|
|
954
|
-
maxLengthSupportedTypes["email"] = "email";
|
|
955
|
-
maxLengthSupportedTypes["password"] = "password";
|
|
956
|
-
maxLengthSupportedTypes["search"] = "search";
|
|
957
|
-
maxLengthSupportedTypes["telephone"] = "telephone";
|
|
958
|
-
maxLengthSupportedTypes["text"] = "text";
|
|
959
|
-
maxLengthSupportedTypes["url"] = "url";
|
|
960
|
-
})(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
|
|
961
|
-
|
|
962
|
-
var bracketDict;
|
|
963
|
-
(function(bracketDict) {
|
|
964
|
-
bracketDict["{"] = "}";
|
|
965
|
-
bracketDict["["] = "]";
|
|
966
|
-
})(bracketDict || (bracketDict = {}));
|
|
967
|
-
/**
|
|
968
|
-
* Read the next key definition from user input
|
|
969
|
-
*
|
|
970
|
-
* Describe key per `{descriptor}` or `[descriptor]`.
|
|
971
|
-
* Everything else will be interpreted as a single character as descriptor - e.g. `a`.
|
|
972
|
-
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
|
|
973
|
-
* A previously pressed key can be released per `{/descriptor}`.
|
|
974
|
-
* Keeping the key pressed can be written as `{descriptor>}`.
|
|
975
|
-
* When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
|
|
976
|
-
* You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
|
|
977
|
-
*/ function readNextDescriptor(text, context) {
|
|
978
|
-
let pos = 0;
|
|
979
|
-
const startBracket = text[pos] in bracketDict ? text[pos] : '';
|
|
980
|
-
pos += startBracket.length;
|
|
981
|
-
const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
|
|
982
|
-
const type = isEscapedChar ? '' : startBracket;
|
|
983
|
-
return {
|
|
984
|
-
type,
|
|
985
|
-
...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
function readPrintableChar(text, pos, context) {
|
|
989
|
-
const descriptor = text[pos];
|
|
990
|
-
assertDescriptor(descriptor, text, pos);
|
|
991
|
-
pos += descriptor.length;
|
|
992
|
-
return {
|
|
993
|
-
consumedLength: pos,
|
|
994
|
-
descriptor,
|
|
995
|
-
releasePrevious: false,
|
|
996
|
-
releaseSelf: true,
|
|
997
|
-
repeat: 1
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
function readTag(text, pos, startBracket, context) {
|
|
1001
|
-
var _text_slice_match, _text_slice_match1;
|
|
1002
|
-
const releasePreviousModifier = text[pos] === '/' ? '/' : '';
|
|
1003
|
-
pos += releasePreviousModifier.length;
|
|
1004
|
-
const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
|
|
1005
|
-
pos += Number(escapedDescriptor);
|
|
1006
|
-
const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === void 0 ? void 0 : _text_slice_match[0];
|
|
1007
|
-
assertDescriptor(descriptor, text, pos);
|
|
1008
|
-
pos += descriptor.length;
|
|
1009
|
-
var _text_slice_match_;
|
|
1010
|
-
const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === void 0 ? void 0 : _text_slice_match1[0]) !== null && _text_slice_match_ !== void 0 ? _text_slice_match_ : '';
|
|
1011
|
-
pos += repeatModifier.length;
|
|
1012
|
-
const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
|
|
1013
|
-
pos += releaseSelfModifier.length;
|
|
1014
|
-
const expectedEndBracket = bracketDict[startBracket];
|
|
1015
|
-
const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
|
|
1016
|
-
if (!endBracket) {
|
|
1017
|
-
throw new Error(getErrorMessage([
|
|
1018
|
-
!repeatModifier && 'repeat modifier',
|
|
1019
|
-
!releaseSelfModifier && 'release modifier',
|
|
1020
|
-
`"${expectedEndBracket}"`
|
|
1021
|
-
].filter(Boolean).join(' or '), text[pos], text));
|
|
1022
|
-
}
|
|
1023
|
-
pos += endBracket.length;
|
|
1024
|
-
return {
|
|
1025
|
-
consumedLength: pos,
|
|
1026
|
-
descriptor,
|
|
1027
|
-
releasePrevious: !!releasePreviousModifier,
|
|
1028
|
-
repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
|
|
1029
|
-
releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
|
|
1030
|
-
};
|
|
1031
|
-
}
|
|
1032
|
-
function assertDescriptor(descriptor, text, pos, context) {
|
|
1033
|
-
if (!descriptor) {
|
|
1034
|
-
throw new Error(getErrorMessage('key descriptor', text[pos], text));
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
|
|
1038
|
-
if (releaseSelfModifier) {
|
|
1039
|
-
return releaseSelfModifier === '/';
|
|
1040
|
-
}
|
|
1041
|
-
if (repeatModifier) {
|
|
1042
|
-
return false;
|
|
1043
|
-
}
|
|
554
|
+
function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
|
|
555
|
+
if (releaseSelfModifier) {
|
|
556
|
+
return releaseSelfModifier === '/';
|
|
557
|
+
}
|
|
558
|
+
if (repeatModifier) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
1044
561
|
}
|
|
1045
562
|
function getErrorMessage(expected, found, text, context) {
|
|
1046
563
|
return `Expected ${expected} but found "${found !== null && found !== void 0 ? found : ''}" in "${text}"
|
|
@@ -1065,45 +582,6 @@ var PointerEventsCheckLevel;
|
|
|
1065
582
|
/** No pointer events check */ PointerEventsCheckLevel[PointerEventsCheckLevel["Never"] = 0] = "Never";
|
|
1066
583
|
})(PointerEventsCheckLevel || (PointerEventsCheckLevel = {}));
|
|
1067
584
|
|
|
1068
|
-
/**
|
|
1069
|
-
* Parse key defintions per `keyboardMap`
|
|
1070
|
-
*
|
|
1071
|
-
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
|
|
1072
|
-
* Everything else will be interpreted as a typed character - e.g. `a`.
|
|
1073
|
-
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
|
|
1074
|
-
* Keeping the key pressed can be written as `{key>}`.
|
|
1075
|
-
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
|
|
1076
|
-
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
|
|
1077
|
-
*/ function parseKeyDef(keyboardMap, text) {
|
|
1078
|
-
const defs = [];
|
|
1079
|
-
do {
|
|
1080
|
-
const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
|
|
1081
|
-
var _keyboardMap_find;
|
|
1082
|
-
const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
|
|
1083
|
-
if (type === '[') {
|
|
1084
|
-
var _def_code;
|
|
1085
|
-
return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
|
|
1086
|
-
} else if (type === '{') {
|
|
1087
|
-
var _def_key;
|
|
1088
|
-
return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
|
|
1089
|
-
}
|
|
1090
|
-
return def.key === descriptor;
|
|
1091
|
-
})) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
|
|
1092
|
-
key: 'Unknown',
|
|
1093
|
-
code: 'Unknown',
|
|
1094
|
-
[type === '[' ? 'code' : 'key']: descriptor
|
|
1095
|
-
};
|
|
1096
|
-
defs.push({
|
|
1097
|
-
keyDef,
|
|
1098
|
-
releasePrevious,
|
|
1099
|
-
releaseSelf,
|
|
1100
|
-
repeat
|
|
1101
|
-
});
|
|
1102
|
-
text = text.slice(consumedLength);
|
|
1103
|
-
}while (text)
|
|
1104
|
-
return defs;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
585
|
var DOM_KEY_LOCATION;
|
|
1108
586
|
(function(DOM_KEY_LOCATION) {
|
|
1109
587
|
DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
|
|
@@ -1263,6 +741,45 @@ var DOM_KEY_LOCATION;
|
|
|
1263
741
|
}
|
|
1264
742
|
];
|
|
1265
743
|
|
|
744
|
+
/**
|
|
745
|
+
* Parse key defintions per `keyboardMap`
|
|
746
|
+
*
|
|
747
|
+
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
|
|
748
|
+
* Everything else will be interpreted as a typed character - e.g. `a`.
|
|
749
|
+
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
|
|
750
|
+
* Keeping the key pressed can be written as `{key>}`.
|
|
751
|
+
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
|
|
752
|
+
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
|
|
753
|
+
*/ function parseKeyDef(keyboardMap, text) {
|
|
754
|
+
const defs = [];
|
|
755
|
+
do {
|
|
756
|
+
const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
|
|
757
|
+
var _keyboardMap_find;
|
|
758
|
+
const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
|
|
759
|
+
if (type === '[') {
|
|
760
|
+
var _def_code;
|
|
761
|
+
return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
|
|
762
|
+
} else if (type === '{') {
|
|
763
|
+
var _def_key;
|
|
764
|
+
return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
|
|
765
|
+
}
|
|
766
|
+
return def.key === descriptor;
|
|
767
|
+
})) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
|
|
768
|
+
key: 'Unknown',
|
|
769
|
+
code: 'Unknown',
|
|
770
|
+
[type === '[' ? 'code' : 'key']: descriptor
|
|
771
|
+
};
|
|
772
|
+
defs.push({
|
|
773
|
+
keyDef,
|
|
774
|
+
releasePrevious,
|
|
775
|
+
releaseSelf,
|
|
776
|
+
repeat
|
|
777
|
+
});
|
|
778
|
+
text = text.slice(consumedLength);
|
|
779
|
+
}while (text)
|
|
780
|
+
return defs;
|
|
781
|
+
}
|
|
782
|
+
|
|
1266
783
|
const keyboard = async (context, text, state) => {
|
|
1267
784
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1268
785
|
const frame = await context.frame();
|
|
@@ -1292,6 +809,24 @@ const keyboard = async (context, text, state) => {
|
|
|
1292
809
|
unreleased: Array.from(pressed)
|
|
1293
810
|
};
|
|
1294
811
|
};
|
|
812
|
+
const keyboardCleanup = async (context, state) => {
|
|
813
|
+
const { provider, contextId } = context;
|
|
814
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
815
|
+
const page = provider.getPage(contextId);
|
|
816
|
+
for (const key of state.unreleased) {
|
|
817
|
+
await page.keyboard.up(key);
|
|
818
|
+
}
|
|
819
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
820
|
+
const keyboard2 = provider.browser.action("key");
|
|
821
|
+
for (const key of state.unreleased) {
|
|
822
|
+
keyboard2.up(key);
|
|
823
|
+
}
|
|
824
|
+
await keyboard2.perform();
|
|
825
|
+
} else {
|
|
826
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support keyboard api`);
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
const VALID_KEYS = /* @__PURE__ */ new Set(["Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backquote", "`", "~", "Digit1", "1", "!", "Digit2", "2", "@", "Digit3", "3", "#", "Digit4", "4", "$", "Digit5", "5", "%", "Digit6", "6", "^", "Digit7", "7", "&", "Digit8", "8", "*", "Digit9", "9", "(", "Digit0", "0", ")", "Minus", "-", "_", "Equal", "=", "+", "Backslash", "\\", "|", "Backspace", "Tab", "KeyQ", "q", "Q", "KeyW", "w", "W", "KeyE", "e", "E", "KeyR", "r", "R", "KeyT", "t", "T", "KeyY", "y", "Y", "KeyU", "u", "U", "KeyI", "i", "I", "KeyO", "o", "O", "KeyP", "p", "P", "BracketLeft", "[", "{", "BracketRight", "]", "}", "CapsLock", "KeyA", "a", "A", "KeyS", "s", "S", "KeyD", "d", "D", "KeyF", "f", "F", "KeyG", "g", "G", "KeyH", "h", "H", "KeyJ", "j", "J", "KeyK", "k", "K", "KeyL", "l", "L", "Semicolon", ";", ":", "Quote", "'", '"', "Enter", "\n", "\r", "ShiftLeft", "Shift", "KeyZ", "z", "Z", "KeyX", "x", "X", "KeyC", "c", "C", "KeyV", "v", "V", "KeyB", "b", "B", "KeyN", "n", "N", "KeyM", "m", "M", "Comma", ",", "<", "Period", ".", ">", "Slash", "/", "?", "ShiftRight", "ControlLeft", "Control", "MetaLeft", "Meta", "AltLeft", "Alt", "Space", " ", "AltRight", "AltGraph", "MetaRight", "ContextMenu", "ControlRight", "PrintScreen", "ScrollLock", "Pause", "PageUp", "PageDown", "Insert", "Delete", "Home", "End", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "NumLock", "NumpadDivide", "NumpadMultiply", "NumpadSubtract", "Numpad7", "Numpad8", "Numpad9", "Numpad4", "Numpad5", "Numpad6", "NumpadAdd", "Numpad1", "Numpad2", "Numpad3", "Numpad0", "NumpadDecimal", "NumpadEnter"]);
|
|
1295
830
|
async function keyboardImplementation(pressed, provider, contextId, text, selectAll2, skipRelease) {
|
|
1296
831
|
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1297
832
|
const page = provider.getPage(contextId);
|
|
@@ -1299,7 +834,9 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
|
|
|
1299
834
|
for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
|
|
1300
835
|
const key = keyDef.key;
|
|
1301
836
|
if (pressed.has(key)) {
|
|
1302
|
-
|
|
837
|
+
if (VALID_KEYS.has(key)) {
|
|
838
|
+
await page.keyboard.up(key);
|
|
839
|
+
}
|
|
1303
840
|
pressed.delete(key);
|
|
1304
841
|
}
|
|
1305
842
|
if (!releasePrevious) {
|
|
@@ -1308,10 +845,16 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
|
|
|
1308
845
|
continue;
|
|
1309
846
|
}
|
|
1310
847
|
for (let i = 1; i <= repeat; i++) {
|
|
1311
|
-
|
|
848
|
+
if (VALID_KEYS.has(key)) {
|
|
849
|
+
await page.keyboard.down(key);
|
|
850
|
+
} else {
|
|
851
|
+
await page.keyboard.insertText(key);
|
|
852
|
+
}
|
|
1312
853
|
}
|
|
1313
854
|
if (releaseSelf) {
|
|
1314
|
-
|
|
855
|
+
if (VALID_KEYS.has(key)) {
|
|
856
|
+
await page.keyboard.up(key);
|
|
857
|
+
}
|
|
1315
858
|
} else {
|
|
1316
859
|
pressed.add(key);
|
|
1317
860
|
}
|
|
@@ -1319,7 +862,9 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
|
|
|
1319
862
|
}
|
|
1320
863
|
if (!skipRelease && pressed.size) {
|
|
1321
864
|
for (const key of pressed) {
|
|
1322
|
-
|
|
865
|
+
if (VALID_KEYS.has(key)) {
|
|
866
|
+
await page.keyboard.up(key);
|
|
867
|
+
}
|
|
1323
868
|
}
|
|
1324
869
|
}
|
|
1325
870
|
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
@@ -1355,1030 +900,1663 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
|
|
|
1355
900
|
}
|
|
1356
901
|
}
|
|
1357
902
|
}
|
|
1358
|
-
const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
|
|
1359
|
-
await keyboard2.perform(allRelease ? false : skipRelease);
|
|
903
|
+
const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
|
|
904
|
+
await keyboard2.perform(allRelease ? false : skipRelease);
|
|
905
|
+
}
|
|
906
|
+
return {
|
|
907
|
+
pressed
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function focusIframe() {
|
|
911
|
+
if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
|
|
912
|
+
window.focus();
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function selectAll() {
|
|
916
|
+
const element = document.activeElement;
|
|
917
|
+
if (element && element.select) {
|
|
918
|
+
element.select();
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const screenshot = async (context, name, options = {}) => {
|
|
923
|
+
if (!context.testPath) {
|
|
924
|
+
throw new Error(`Cannot take a screenshot without a test path`);
|
|
925
|
+
}
|
|
926
|
+
const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
|
|
927
|
+
context.testPath,
|
|
928
|
+
name,
|
|
929
|
+
context.project.config
|
|
930
|
+
);
|
|
931
|
+
const savePath = normalize$1(path);
|
|
932
|
+
await mkdir(dirname(path), { recursive: true });
|
|
933
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
934
|
+
if (options.element) {
|
|
935
|
+
const { element: selector, ...config } = options;
|
|
936
|
+
const element = context.iframe.locator(`${selector}`);
|
|
937
|
+
const buffer2 = await element.screenshot({
|
|
938
|
+
timeout: 1e3,
|
|
939
|
+
...config,
|
|
940
|
+
path: savePath
|
|
941
|
+
});
|
|
942
|
+
return returnResult(options, path, buffer2);
|
|
943
|
+
}
|
|
944
|
+
const buffer = await context.iframe.locator("body").screenshot({
|
|
945
|
+
...options,
|
|
946
|
+
path: savePath
|
|
947
|
+
});
|
|
948
|
+
return returnResult(options, path, buffer);
|
|
949
|
+
}
|
|
950
|
+
if (context.provider instanceof WebdriverBrowserProvider) {
|
|
951
|
+
const page = context.provider.browser;
|
|
952
|
+
if (!options.element) {
|
|
953
|
+
const body = await page.$("body");
|
|
954
|
+
const buffer2 = await body.saveScreenshot(savePath);
|
|
955
|
+
return returnResult(options, path, buffer2);
|
|
956
|
+
}
|
|
957
|
+
const element = await page.$(`${options.element}`);
|
|
958
|
+
const buffer = await element.saveScreenshot(savePath);
|
|
959
|
+
return returnResult(options, path, buffer);
|
|
960
|
+
}
|
|
961
|
+
throw new Error(
|
|
962
|
+
`Provider "${context.provider.name}" does not support screenshots`
|
|
963
|
+
);
|
|
964
|
+
};
|
|
965
|
+
function resolveScreenshotPath(testPath, name, config) {
|
|
966
|
+
const dir = dirname(testPath);
|
|
967
|
+
const base = basename(testPath);
|
|
968
|
+
if (config.browser.screenshotDirectory) {
|
|
969
|
+
return resolve(
|
|
970
|
+
config.browser.screenshotDirectory,
|
|
971
|
+
relative(config.root, dir),
|
|
972
|
+
base,
|
|
973
|
+
name
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
return resolve(dir, "__screenshots__", base, name);
|
|
977
|
+
}
|
|
978
|
+
function returnResult(options, path, buffer) {
|
|
979
|
+
if (options.base64) {
|
|
980
|
+
return { path, base64: buffer.toString("base64") };
|
|
981
|
+
}
|
|
982
|
+
return path;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const selectOptions = async (context, selector, userValues, options = {}) => {
|
|
986
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
987
|
+
const value = userValues;
|
|
988
|
+
const { iframe } = context;
|
|
989
|
+
const selectElement = iframe.locator(selector);
|
|
990
|
+
const values = await Promise.all(value.map(async (v) => {
|
|
991
|
+
if (typeof v === "string") {
|
|
992
|
+
return v;
|
|
993
|
+
}
|
|
994
|
+
const elementHandler = await iframe.locator(v.element).elementHandle();
|
|
995
|
+
if (!elementHandler) {
|
|
996
|
+
throw new Error(`Element not found: ${v.element}`);
|
|
997
|
+
}
|
|
998
|
+
return elementHandler;
|
|
999
|
+
}));
|
|
1000
|
+
await selectElement.selectOption(values, {
|
|
1001
|
+
timeout: 1e3,
|
|
1002
|
+
...options
|
|
1003
|
+
});
|
|
1004
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1005
|
+
const values = userValues;
|
|
1006
|
+
if (!values.length) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const browser = context.browser;
|
|
1010
|
+
if (values.length === 1 && "index" in values[0]) {
|
|
1011
|
+
const selectElement = browser.$(selector);
|
|
1012
|
+
await selectElement.selectByIndex(values[0].index);
|
|
1013
|
+
} else {
|
|
1014
|
+
throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
const tab = async (context, options = {}) => {
|
|
1022
|
+
const provider = context.provider;
|
|
1023
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1024
|
+
const page = context.page;
|
|
1025
|
+
await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (provider instanceof WebdriverBrowserProvider) {
|
|
1029
|
+
const { Key } = await import('webdriverio');
|
|
1030
|
+
const browser = context.browser;
|
|
1031
|
+
await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
throw new Error(`Provider "${provider.name}" doesn't support tab command`);
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
const type = async (context, selector, text, options = {}) => {
|
|
1038
|
+
const { skipClick = false, skipAutoClose = false } = options;
|
|
1039
|
+
const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
|
|
1040
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1041
|
+
const { iframe } = context;
|
|
1042
|
+
const element = iframe.locator(selector);
|
|
1043
|
+
if (!skipClick) {
|
|
1044
|
+
await element.focus();
|
|
1045
|
+
}
|
|
1046
|
+
await keyboardImplementation(
|
|
1047
|
+
unreleased,
|
|
1048
|
+
context.provider,
|
|
1049
|
+
context.contextId,
|
|
1050
|
+
text,
|
|
1051
|
+
() => element.selectText(),
|
|
1052
|
+
skipAutoClose
|
|
1053
|
+
);
|
|
1054
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1055
|
+
const browser = context.browser;
|
|
1056
|
+
const element = browser.$(selector);
|
|
1057
|
+
if (!skipClick && !await element.isFocused()) {
|
|
1058
|
+
await element.click();
|
|
1059
|
+
}
|
|
1060
|
+
await keyboardImplementation(
|
|
1061
|
+
unreleased,
|
|
1062
|
+
context.provider,
|
|
1063
|
+
context.contextId,
|
|
1064
|
+
text,
|
|
1065
|
+
() => browser.execute(() => {
|
|
1066
|
+
const element2 = document.activeElement;
|
|
1067
|
+
if (element2) {
|
|
1068
|
+
element2.select();
|
|
1069
|
+
}
|
|
1070
|
+
}),
|
|
1071
|
+
skipAutoClose
|
|
1072
|
+
);
|
|
1073
|
+
} else {
|
|
1074
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
|
|
1075
|
+
}
|
|
1076
|
+
return {
|
|
1077
|
+
unreleased: Array.from(unreleased)
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const upload = async (context, selector, files) => {
|
|
1082
|
+
const testPath = context.testPath;
|
|
1083
|
+
if (!testPath) {
|
|
1084
|
+
throw new Error(`Cannot upload files outside of a test`);
|
|
1085
|
+
}
|
|
1086
|
+
const testDir = dirname(testPath);
|
|
1087
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1088
|
+
const { iframe } = context;
|
|
1089
|
+
const playwrightFiles = files.map((file) => {
|
|
1090
|
+
if (typeof file === "string") {
|
|
1091
|
+
return resolve(testDir, file);
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
name: file.name,
|
|
1095
|
+
mimeType: file.mimeType,
|
|
1096
|
+
buffer: Buffer.from(file.base64, "base64")
|
|
1097
|
+
};
|
|
1098
|
+
});
|
|
1099
|
+
await iframe.locator(selector).setInputFiles(playwrightFiles);
|
|
1100
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1101
|
+
for (const file of files) {
|
|
1102
|
+
if (typeof file !== "string") {
|
|
1103
|
+
throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
const element = context.browser.$(selector);
|
|
1107
|
+
for (const file of files) {
|
|
1108
|
+
const filepath = resolve(testDir, file);
|
|
1109
|
+
const remoteFilePath = await context.browser.uploadFile(filepath);
|
|
1110
|
+
await element.addValue(remoteFilePath);
|
|
1111
|
+
}
|
|
1112
|
+
} else {
|
|
1113
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
var builtinCommands = {
|
|
1118
|
+
readFile,
|
|
1119
|
+
removeFile,
|
|
1120
|
+
writeFile,
|
|
1121
|
+
__vitest_fileInfo: _fileInfo,
|
|
1122
|
+
__vitest_upload: upload,
|
|
1123
|
+
__vitest_click: click,
|
|
1124
|
+
__vitest_dblClick: dblClick,
|
|
1125
|
+
__vitest_tripleClick: tripleClick,
|
|
1126
|
+
__vitest_screenshot: screenshot,
|
|
1127
|
+
__vitest_type: type,
|
|
1128
|
+
__vitest_clear: clear,
|
|
1129
|
+
__vitest_fill: fill,
|
|
1130
|
+
__vitest_tab: tab,
|
|
1131
|
+
__vitest_keyboard: keyboard,
|
|
1132
|
+
__vitest_selectOptions: selectOptions,
|
|
1133
|
+
__vitest_dragAndDrop: dragAndDrop,
|
|
1134
|
+
__vitest_hover: hover,
|
|
1135
|
+
__vitest_cleanup: keyboardCleanup
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
|
|
1139
|
+
const ID_CONTEXT = "@vitest/browser/context";
|
|
1140
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1141
|
+
function BrowserContext(server) {
|
|
1142
|
+
const project = server.project;
|
|
1143
|
+
project.config.browser.commands ??= {};
|
|
1144
|
+
for (const [name, command] of Object.entries(builtinCommands)) {
|
|
1145
|
+
project.config.browser.commands[name] ??= command;
|
|
1146
|
+
}
|
|
1147
|
+
for (const command in project.config.browser.commands) {
|
|
1148
|
+
if (!/^[a-z_$][\w$]*$/i.test(command)) {
|
|
1149
|
+
throw new Error(
|
|
1150
|
+
`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1360
1153
|
}
|
|
1361
1154
|
return {
|
|
1362
|
-
|
|
1155
|
+
name: "vitest:browser:virtual-module:context",
|
|
1156
|
+
enforce: "pre",
|
|
1157
|
+
resolveId(id) {
|
|
1158
|
+
if (id === ID_CONTEXT) {
|
|
1159
|
+
return VIRTUAL_ID_CONTEXT;
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
load(id) {
|
|
1163
|
+
if (id === VIRTUAL_ID_CONTEXT) {
|
|
1164
|
+
return generateContextFile.call(this, server);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1363
1167
|
};
|
|
1364
1168
|
}
|
|
1365
|
-
function
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1169
|
+
async function generateContextFile(server) {
|
|
1170
|
+
const commands = Object.keys(server.project.config.browser.commands ?? {});
|
|
1171
|
+
const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
|
|
1172
|
+
const provider = server.provider;
|
|
1173
|
+
const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
|
|
1174
|
+
return ` ["${command}"]: (...args) => rpc().triggerCommand(contextId, "${command}", filepath(), args),`;
|
|
1175
|
+
}).join("\n");
|
|
1176
|
+
const userEventNonProviderImport = await getUserEventImport(
|
|
1177
|
+
provider,
|
|
1178
|
+
this.resolve.bind(this)
|
|
1179
|
+
);
|
|
1180
|
+
const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
|
|
1181
|
+
return `
|
|
1182
|
+
import { page, createUserEvent, cdp } from '${distContextPath}'
|
|
1183
|
+
${userEventNonProviderImport}
|
|
1184
|
+
const filepath = () => ${filepathCode}
|
|
1185
|
+
const rpc = () => __vitest_worker__.rpc
|
|
1186
|
+
const contextId = __vitest_browser_runner__.contextId
|
|
1187
|
+
|
|
1188
|
+
export const server = {
|
|
1189
|
+
platform: ${JSON.stringify(process.platform)},
|
|
1190
|
+
version: ${JSON.stringify(process.version)},
|
|
1191
|
+
provider: ${JSON.stringify(provider.name)},
|
|
1192
|
+
browser: ${JSON.stringify(server.project.config.browser.name)},
|
|
1193
|
+
commands: {
|
|
1194
|
+
${commandsCode}
|
|
1195
|
+
},
|
|
1196
|
+
config: __vitest_browser_runner__.config,
|
|
1369
1197
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1198
|
+
export const commands = server.commands
|
|
1199
|
+
export const userEvent = createUserEvent(_userEventSetup)
|
|
1200
|
+
export { page, cdp }
|
|
1201
|
+
`;
|
|
1202
|
+
}
|
|
1203
|
+
async function getUserEventImport(provider, resolve2) {
|
|
1204
|
+
if (provider.name !== "preview") {
|
|
1205
|
+
return "const _userEventSetup = undefined";
|
|
1206
|
+
}
|
|
1207
|
+
const resolved = await resolve2("@testing-library/user-event", __dirname);
|
|
1208
|
+
if (!resolved) {
|
|
1209
|
+
throw new Error(`Failed to resolve user-event package from ${__dirname}`);
|
|
1374
1210
|
}
|
|
1211
|
+
return `import { userEvent as __vitest_user_event__ } from '${slash(`/@fs/${resolved.id}`)}'
|
|
1212
|
+
const _userEventSetup = __vitest_user_event__
|
|
1213
|
+
`;
|
|
1375
1214
|
}
|
|
1376
1215
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
() => element.selectText(),
|
|
1392
|
-
skipAutoClose
|
|
1216
|
+
function replacer(code, values) {
|
|
1217
|
+
return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
|
|
1218
|
+
}
|
|
1219
|
+
const builtinProviders = ["webdriverio", "playwright", "preview"];
|
|
1220
|
+
async function getBrowserProvider(options, project) {
|
|
1221
|
+
if (options.provider == null || builtinProviders.includes(options.provider)) {
|
|
1222
|
+
const providers = await import('./providers.js');
|
|
1223
|
+
const provider = options.provider || "preview";
|
|
1224
|
+
return providers[provider];
|
|
1225
|
+
}
|
|
1226
|
+
let customProviderModule;
|
|
1227
|
+
try {
|
|
1228
|
+
customProviderModule = await project.runner.executeId(
|
|
1229
|
+
options.provider
|
|
1393
1230
|
);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
await element.click();
|
|
1399
|
-
}
|
|
1400
|
-
await keyboardImplementation(
|
|
1401
|
-
unreleased,
|
|
1402
|
-
context.provider,
|
|
1403
|
-
context.contextId,
|
|
1404
|
-
text,
|
|
1405
|
-
() => browser.execute(() => {
|
|
1406
|
-
const element2 = document.activeElement;
|
|
1407
|
-
if (element2) {
|
|
1408
|
-
element2.select();
|
|
1409
|
-
}
|
|
1410
|
-
}),
|
|
1411
|
-
skipAutoClose
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
throw new Error(
|
|
1233
|
+
`Failed to load custom BrowserProvider from ${options.provider}`,
|
|
1234
|
+
{ cause: error }
|
|
1412
1235
|
);
|
|
1413
|
-
} else {
|
|
1414
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
|
|
1415
1236
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
const clear = async (context, selector) => {
|
|
1422
|
-
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1423
|
-
const { iframe } = context;
|
|
1424
|
-
const element = iframe.locator(selector);
|
|
1425
|
-
await element.clear({
|
|
1426
|
-
timeout: 1e3
|
|
1427
|
-
});
|
|
1428
|
-
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1429
|
-
const browser = context.browser;
|
|
1430
|
-
const element = await browser.$(selector);
|
|
1431
|
-
await element.clearValue();
|
|
1432
|
-
} else {
|
|
1433
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
1237
|
+
if (customProviderModule.default == null) {
|
|
1238
|
+
throw new Error(
|
|
1239
|
+
`Custom BrowserProvider loaded from ${options.provider} was not the default export`
|
|
1240
|
+
);
|
|
1434
1241
|
}
|
|
1435
|
-
|
|
1242
|
+
return customProviderModule.default;
|
|
1243
|
+
}
|
|
1436
1244
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
const browser = context.browser;
|
|
1444
|
-
await browser.$(selector).setValue(text);
|
|
1445
|
-
} else {
|
|
1446
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
1245
|
+
async function resolveOrchestrator(server, url, res) {
|
|
1246
|
+
const project = server.project;
|
|
1247
|
+
let contextId = url.searchParams.get("contextId");
|
|
1248
|
+
if (!contextId) {
|
|
1249
|
+
const contexts = [...server.state.orchestrators.keys()];
|
|
1250
|
+
contextId = contexts[contexts.length - 1] ?? "none";
|
|
1447
1251
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1252
|
+
const files = server.state.getContext(contextId)?.files ?? [];
|
|
1253
|
+
const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
|
|
1254
|
+
const injector = replacer(injectorJs, {
|
|
1255
|
+
__VITEST_PROVIDER__: JSON.stringify(server.provider.name),
|
|
1256
|
+
__VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
|
|
1257
|
+
__VITEST_VITE_CONFIG__: JSON.stringify({
|
|
1258
|
+
root: server.vite.config.root
|
|
1259
|
+
}),
|
|
1260
|
+
__VITEST_FILES__: JSON.stringify(files),
|
|
1261
|
+
__VITEST_TYPE__: '"orchestrator"',
|
|
1262
|
+
__VITEST_CONTEXT_ID__: JSON.stringify(contextId),
|
|
1263
|
+
__VITEST_TESTER_ID__: '"none"',
|
|
1264
|
+
__VITEST_PROVIDED_CONTEXT__: "{}"
|
|
1265
|
+
});
|
|
1266
|
+
res.removeHeader("Content-Security-Policy");
|
|
1267
|
+
if (!server.orchestratorScripts) {
|
|
1268
|
+
server.orchestratorScripts = (await server.formatScripts(
|
|
1269
|
+
project.config.browser.orchestratorScripts
|
|
1270
|
+
)).map((script) => {
|
|
1271
|
+
let html = "<script ";
|
|
1272
|
+
for (const attr in script.attrs || {}) {
|
|
1273
|
+
html += `${attr}="${script.attrs[attr]}" `;
|
|
1462
1274
|
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1275
|
+
html += `>${script.children}<\/script>`;
|
|
1276
|
+
return html;
|
|
1277
|
+
}).join("\n");
|
|
1278
|
+
}
|
|
1279
|
+
let baseHtml = typeof server.orchestratorHtml === "string" ? server.orchestratorHtml : await server.orchestratorHtml;
|
|
1280
|
+
if (project.config.browser.ui) {
|
|
1281
|
+
const manifestContent = server.manifest instanceof Promise ? await server.manifest : server.manifest;
|
|
1282
|
+
const jsEntry = manifestContent["orchestrator.html"].file;
|
|
1283
|
+
const base = server.vite.config.base || "/";
|
|
1284
|
+
baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
|
|
1285
|
+
"<!-- !LOAD_METADATA! -->",
|
|
1286
|
+
[
|
|
1287
|
+
"{__VITEST_INJECTOR__}",
|
|
1288
|
+
"{__VITEST_ERROR_CATCHER__}",
|
|
1289
|
+
"{__VITEST_SCRIPTS__}",
|
|
1290
|
+
`<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
|
|
1291
|
+
].join("\n")
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
return replacer(baseHtml, {
|
|
1295
|
+
__VITEST_FAVICON__: server.faviconUrl,
|
|
1296
|
+
__VITEST_TITLE__: "Vitest Browser Runner",
|
|
1297
|
+
__VITEST_SCRIPTS__: server.orchestratorScripts,
|
|
1298
|
+
__VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
|
|
1299
|
+
__VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
|
|
1300
|
+
__VITEST_CONTEXT_ID__: JSON.stringify(contextId)
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/// <reference types="../types/index.d.ts" />
|
|
1305
|
+
|
|
1306
|
+
// (c) 2020-present Andrea Giammarchi
|
|
1307
|
+
|
|
1308
|
+
const {parse: $parse, stringify: $stringify} = JSON;
|
|
1309
|
+
const {keys} = Object;
|
|
1310
|
+
|
|
1311
|
+
const Primitive = String; // it could be Number
|
|
1312
|
+
const primitive = 'string'; // it could be 'number'
|
|
1313
|
+
|
|
1314
|
+
const ignore = {};
|
|
1315
|
+
const object = 'object';
|
|
1316
|
+
|
|
1317
|
+
const noop = (_, value) => value;
|
|
1318
|
+
|
|
1319
|
+
const primitives = value => (
|
|
1320
|
+
value instanceof Primitive ? Primitive(value) : value
|
|
1321
|
+
);
|
|
1322
|
+
|
|
1323
|
+
const Primitives = (_, value) => (
|
|
1324
|
+
typeof value === primitive ? new Primitive(value) : value
|
|
1325
|
+
);
|
|
1326
|
+
|
|
1327
|
+
const revive = (input, parsed, output, $) => {
|
|
1328
|
+
const lazy = [];
|
|
1329
|
+
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
|
|
1330
|
+
const k = ke[y];
|
|
1331
|
+
const value = output[k];
|
|
1332
|
+
if (value instanceof Primitive) {
|
|
1333
|
+
const tmp = input[value];
|
|
1334
|
+
if (typeof tmp === object && !parsed.has(tmp)) {
|
|
1335
|
+
parsed.add(tmp);
|
|
1336
|
+
output[k] = ignore;
|
|
1337
|
+
lazy.push({k, a: [input, parsed, tmp, $]});
|
|
1338
|
+
}
|
|
1339
|
+
else
|
|
1340
|
+
output[k] = $.call(output, k, tmp);
|
|
1480
1341
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1342
|
+
else if (output[k] !== ignore)
|
|
1343
|
+
output[k] = $.call(output, k, value);
|
|
1344
|
+
}
|
|
1345
|
+
for (let {length} = lazy, i = 0; i < length; i++) {
|
|
1346
|
+
const {k, a} = lazy[i];
|
|
1347
|
+
output[k] = $.call(output, k, revive.apply(null, a));
|
|
1483
1348
|
}
|
|
1349
|
+
return output;
|
|
1484
1350
|
};
|
|
1485
1351
|
|
|
1486
|
-
const
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1352
|
+
const set = (known, input, value) => {
|
|
1353
|
+
const index = Primitive(input.push(value) - 1);
|
|
1354
|
+
known.set(value, index);
|
|
1355
|
+
return index;
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Converts a specialized flatted string into a JS value.
|
|
1360
|
+
* @param {string} text
|
|
1361
|
+
* @param {(this: any, key: string, value: any) => any} [reviver]
|
|
1362
|
+
* @returns {any}
|
|
1363
|
+
*/
|
|
1364
|
+
const parse = (text, reviver) => {
|
|
1365
|
+
const input = $parse(text, Primitives).map(primitives);
|
|
1366
|
+
const value = input[0];
|
|
1367
|
+
const $ = reviver || noop;
|
|
1368
|
+
const tmp = typeof value === object && value ?
|
|
1369
|
+
revive(input, new Set, value, $) :
|
|
1370
|
+
value;
|
|
1371
|
+
return $.call({'': tmp}, '', tmp);
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* Converts a JS value into a specialized flatted string.
|
|
1376
|
+
* @param {any} value
|
|
1377
|
+
* @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
|
|
1378
|
+
* @param {string | number | undefined} [space]
|
|
1379
|
+
* @returns {string}
|
|
1380
|
+
*/
|
|
1381
|
+
const stringify = (value, replacer, space) => {
|
|
1382
|
+
const $ = replacer && typeof replacer === object ?
|
|
1383
|
+
(k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
|
|
1384
|
+
(replacer || noop);
|
|
1385
|
+
const known = new Map;
|
|
1386
|
+
const input = [];
|
|
1387
|
+
const output = [];
|
|
1388
|
+
let i = +set(known, input, $.call({'': value}, '', value));
|
|
1389
|
+
let firstRun = !i;
|
|
1390
|
+
while (i < input.length) {
|
|
1391
|
+
firstRun = true;
|
|
1392
|
+
output[i] = $stringify(input[i++], replace, space);
|
|
1492
1393
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1394
|
+
return '[' + output.join(',') + ']';
|
|
1395
|
+
function replace(key, value) {
|
|
1396
|
+
if (firstRun) {
|
|
1397
|
+
firstRun = !firstRun;
|
|
1398
|
+
return value;
|
|
1399
|
+
}
|
|
1400
|
+
const after = $.call(this, key, value);
|
|
1401
|
+
switch (typeof after) {
|
|
1402
|
+
case object:
|
|
1403
|
+
if (after === null) return after;
|
|
1404
|
+
case primitive:
|
|
1405
|
+
return known.get(after) || set(known, input, after);
|
|
1406
|
+
}
|
|
1407
|
+
return after;
|
|
1498
1408
|
}
|
|
1499
|
-
throw new Error(`Provider "${provider.name}" doesn't support tab command`);
|
|
1500
1409
|
};
|
|
1501
1410
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
{
|
|
1509
|
-
timeout: 1e3,
|
|
1510
|
-
...options_
|
|
1511
|
-
}
|
|
1411
|
+
async function resolveTester(server, url, res, next) {
|
|
1412
|
+
const csp = res.getHeader("Content-Security-Policy");
|
|
1413
|
+
if (typeof csp === "string") {
|
|
1414
|
+
res.setHeader(
|
|
1415
|
+
"Content-Security-Policy",
|
|
1416
|
+
csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
|
|
1512
1417
|
);
|
|
1513
|
-
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1514
|
-
const $source = context.browser.$(source);
|
|
1515
|
-
const $target = context.browser.$(target);
|
|
1516
|
-
const options = options_ || {};
|
|
1517
|
-
const duration = options.duration ?? 10;
|
|
1518
|
-
await context.browser.action("pointer").move({ duration: 0, origin: $source, x: options.sourceX ?? 0, y: options.sourceY ?? 0 }).down({ button: 0 }).move({ duration: 0, origin: "pointer", x: 0, y: 0 }).pause(duration).move({ duration: 0, origin: $target, x: options.targetX ?? 0, y: options.targetY ?? 0 }).move({ duration: 0, origin: "pointer", x: 1, y: 0 }).move({ duration: 0, origin: "pointer", x: -1, y: 0 }).up({ button: 0 }).perform();
|
|
1519
|
-
} else {
|
|
1520
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
|
|
1521
1418
|
}
|
|
1522
|
-
};
|
|
1523
|
-
|
|
1524
|
-
const
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1419
|
+
const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
|
|
1420
|
+
const project = server.project;
|
|
1421
|
+
const state = server.state;
|
|
1422
|
+
const { testFiles } = await project.globTestFiles();
|
|
1423
|
+
const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
|
|
1424
|
+
const iframeId = JSON.stringify(testFile);
|
|
1425
|
+
const context = state.getContext(contextId);
|
|
1426
|
+
const files = context?.files ?? [];
|
|
1427
|
+
const method = context?.method ?? "run";
|
|
1428
|
+
const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
|
|
1429
|
+
const injector = replacer(injectorJs, {
|
|
1430
|
+
__VITEST_PROVIDER__: JSON.stringify(server.provider.name),
|
|
1431
|
+
__VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
|
|
1432
|
+
__VITEST_FILES__: JSON.stringify(files),
|
|
1433
|
+
__VITEST_VITE_CONFIG__: JSON.stringify({
|
|
1434
|
+
root: server.vite.config.root
|
|
1435
|
+
}),
|
|
1436
|
+
__VITEST_TYPE__: '"tester"',
|
|
1437
|
+
__VITEST_CONTEXT_ID__: JSON.stringify(contextId),
|
|
1438
|
+
__VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
|
|
1439
|
+
__VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
|
|
1440
|
+
});
|
|
1441
|
+
const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
|
|
1442
|
+
try {
|
|
1443
|
+
const url2 = join("/@fs/", server.testerFilepath);
|
|
1444
|
+
const indexhtml = await server.vite.transformIndexHtml(url2, testerHtml);
|
|
1445
|
+
return replacer(indexhtml, {
|
|
1446
|
+
__VITEST_FAVICON__: server.faviconUrl,
|
|
1447
|
+
__VITEST_INJECTOR__: injector,
|
|
1448
|
+
__VITEST_APPEND__: `
|
|
1449
|
+
__vitest_browser_runner__.runningFiles = ${tests}
|
|
1450
|
+
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
1451
|
+
__vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
|
|
1452
|
+
document.querySelector('script[data-vitest-append]').remove()
|
|
1453
|
+
`
|
|
1529
1454
|
});
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
} else {
|
|
1534
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
|
|
1455
|
+
} catch (err) {
|
|
1456
|
+
context?.reject(err);
|
|
1457
|
+
next(err);
|
|
1535
1458
|
}
|
|
1536
|
-
}
|
|
1459
|
+
}
|
|
1537
1460
|
|
|
1538
|
-
const
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1545
|
-
const { iframe } = context;
|
|
1546
|
-
const playwrightFiles = files.map((file) => {
|
|
1547
|
-
if (typeof file === "string") {
|
|
1548
|
-
return resolve(testDir, file);
|
|
1549
|
-
}
|
|
1550
|
-
return {
|
|
1551
|
-
name: file.name,
|
|
1552
|
-
mimeType: file.mimeType,
|
|
1553
|
-
buffer: Buffer.from(file.base64, "base64")
|
|
1554
|
-
};
|
|
1461
|
+
const versionRegexp = /(?:\?|&)v=\w{8}/;
|
|
1462
|
+
var BrowserPlugin = (browserServer, base = "/") => {
|
|
1463
|
+
const project = browserServer.project;
|
|
1464
|
+
function isPackageExists(pkg, root) {
|
|
1465
|
+
return browserServer.project.ctx.packageInstaller.isPackageExists?.(pkg, {
|
|
1466
|
+
paths: [root]
|
|
1555
1467
|
});
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1468
|
+
}
|
|
1469
|
+
return [
|
|
1470
|
+
{
|
|
1471
|
+
enforce: "pre",
|
|
1472
|
+
name: "vitest:browser",
|
|
1473
|
+
async configureServer(server) {
|
|
1474
|
+
browserServer.setServer(server);
|
|
1475
|
+
server.middlewares.use(function vitestHeaders(_req, res, next) {
|
|
1476
|
+
const headers = server.config.server.headers;
|
|
1477
|
+
if (headers) {
|
|
1478
|
+
for (const name in headers) {
|
|
1479
|
+
res.setHeader(name, headers[name]);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
next();
|
|
1483
|
+
});
|
|
1484
|
+
server.middlewares.use(async function vitestBrowserMode(req, res, next) {
|
|
1485
|
+
if (!req.url || !browserServer.provider) {
|
|
1486
|
+
return next();
|
|
1487
|
+
}
|
|
1488
|
+
const url = new URL(req.url, "http://localhost");
|
|
1489
|
+
if (!url.pathname.startsWith(browserServer.prefixTesterUrl) && url.pathname !== base) {
|
|
1490
|
+
return next();
|
|
1491
|
+
}
|
|
1492
|
+
res.setHeader(
|
|
1493
|
+
"Cache-Control",
|
|
1494
|
+
"no-cache, max-age=0, must-revalidate"
|
|
1495
|
+
);
|
|
1496
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1497
|
+
res.removeHeader("X-Frame-Options");
|
|
1498
|
+
if (url.pathname === base) {
|
|
1499
|
+
const html2 = await resolveOrchestrator(browserServer, url, res);
|
|
1500
|
+
res.write(html2, "utf-8");
|
|
1501
|
+
res.end();
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const html = await resolveTester(browserServer, url, res, next);
|
|
1505
|
+
if (html) {
|
|
1506
|
+
res.write(html, "utf-8");
|
|
1507
|
+
res.end();
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
server.middlewares.use(
|
|
1511
|
+
`${base}favicon.svg`,
|
|
1512
|
+
(_, res) => {
|
|
1513
|
+
const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
|
|
1514
|
+
res.write(content, "utf-8");
|
|
1515
|
+
res.end();
|
|
1516
|
+
}
|
|
1517
|
+
);
|
|
1518
|
+
const coverageFolder = resolveCoverageFolder(project);
|
|
1519
|
+
const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
|
|
1520
|
+
if (coveragePath && base === coveragePath) {
|
|
1521
|
+
throw new Error(
|
|
1522
|
+
`The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
if (coverageFolder) {
|
|
1526
|
+
server.middlewares.use(
|
|
1527
|
+
coveragePath,
|
|
1528
|
+
sirv(coverageFolder[0], {
|
|
1529
|
+
single: true,
|
|
1530
|
+
dev: true,
|
|
1531
|
+
setHeaders: (res) => {
|
|
1532
|
+
res.setHeader(
|
|
1533
|
+
"Cache-Control",
|
|
1534
|
+
"public,max-age=0,must-revalidate"
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
})
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
|
|
1541
|
+
if (screenshotFailures) {
|
|
1542
|
+
server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
|
|
1543
|
+
if (!req.url || !browserServer.provider) {
|
|
1544
|
+
res.statusCode = 404;
|
|
1545
|
+
res.end();
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
const url = new URL(req.url, "http://localhost");
|
|
1549
|
+
const file = url.searchParams.get("file");
|
|
1550
|
+
if (!file) {
|
|
1551
|
+
res.statusCode = 404;
|
|
1552
|
+
res.end();
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
let stat;
|
|
1556
|
+
try {
|
|
1557
|
+
stat = lstatSync(file);
|
|
1558
|
+
} catch {
|
|
1559
|
+
}
|
|
1560
|
+
if (!stat?.isFile()) {
|
|
1561
|
+
res.statusCode = 404;
|
|
1562
|
+
res.end();
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const ext = extname(file);
|
|
1566
|
+
const buffer = readFileSync(file);
|
|
1567
|
+
res.setHeader(
|
|
1568
|
+
"Cache-Control",
|
|
1569
|
+
"public,max-age=0,must-revalidate"
|
|
1570
|
+
);
|
|
1571
|
+
res.setHeader("Content-Length", buffer.length);
|
|
1572
|
+
res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
|
|
1573
|
+
res.end(buffer);
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
server.middlewares.use((req, res, next) => {
|
|
1577
|
+
if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
|
|
1578
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
1579
|
+
const setHeader = res.setHeader.bind(res);
|
|
1580
|
+
res.setHeader = function(name, value) {
|
|
1581
|
+
if (name === "Cache-Control") {
|
|
1582
|
+
return res;
|
|
1583
|
+
}
|
|
1584
|
+
return setHeader(name, value);
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
next();
|
|
1588
|
+
});
|
|
1561
1589
|
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
};
|
|
1573
|
-
|
|
1574
|
-
const types = { "application/andrew-inset": ["ez"], "application/appinstaller": ["appinstaller"], "application/applixware": ["aw"], "application/appx": ["appx"], "application/appxbundle": ["appxbundle"], "application/atom+xml": ["atom"], "application/atomcat+xml": ["atomcat"], "application/atomdeleted+xml": ["atomdeleted"], "application/atomsvc+xml": ["atomsvc"], "application/atsc-dwd+xml": ["dwd"], "application/atsc-held+xml": ["held"], "application/atsc-rsat+xml": ["rsat"], "application/automationml-aml+xml": ["aml"], "application/automationml-amlx+zip": ["amlx"], "application/bdoc": ["bdoc"], "application/calendar+xml": ["xcs"], "application/ccxml+xml": ["ccxml"], "application/cdfx+xml": ["cdfx"], "application/cdmi-capability": ["cdmia"], "application/cdmi-container": ["cdmic"], "application/cdmi-domain": ["cdmid"], "application/cdmi-object": ["cdmio"], "application/cdmi-queue": ["cdmiq"], "application/cpl+xml": ["cpl"], "application/cu-seeme": ["cu"], "application/cwl": ["cwl"], "application/dash+xml": ["mpd"], "application/dash-patch+xml": ["mpp"], "application/davmount+xml": ["davmount"], "application/docbook+xml": ["dbk"], "application/dssc+der": ["dssc"], "application/dssc+xml": ["xdssc"], "application/ecmascript": ["ecma"], "application/emma+xml": ["emma"], "application/emotionml+xml": ["emotionml"], "application/epub+zip": ["epub"], "application/exi": ["exi"], "application/express": ["exp"], "application/fdf": ["fdf"], "application/fdt+xml": ["fdt"], "application/font-tdpfr": ["pfr"], "application/geo+json": ["geojson"], "application/gml+xml": ["gml"], "application/gpx+xml": ["gpx"], "application/gxf": ["gxf"], "application/gzip": ["gz"], "application/hjson": ["hjson"], "application/hyperstudio": ["stk"], "application/inkml+xml": ["ink", "inkml"], "application/ipfix": ["ipfix"], "application/its+xml": ["its"], "application/java-archive": ["jar", "war", "ear"], "application/java-serialized-object": ["ser"], "application/java-vm": ["class"], "application/javascript": ["*js"], "application/json": ["json", "map"], "application/json5": ["json5"], "application/jsonml+json": ["jsonml"], "application/ld+json": ["jsonld"], "application/lgr+xml": ["lgr"], "application/lost+xml": ["lostxml"], "application/mac-binhex40": ["hqx"], "application/mac-compactpro": ["cpt"], "application/mads+xml": ["mads"], "application/manifest+json": ["webmanifest"], "application/marc": ["mrc"], "application/marcxml+xml": ["mrcx"], "application/mathematica": ["ma", "nb", "mb"], "application/mathml+xml": ["mathml"], "application/mbox": ["mbox"], "application/media-policy-dataset+xml": ["mpf"], "application/mediaservercontrol+xml": ["mscml"], "application/metalink+xml": ["metalink"], "application/metalink4+xml": ["meta4"], "application/mets+xml": ["mets"], "application/mmt-aei+xml": ["maei"], "application/mmt-usd+xml": ["musd"], "application/mods+xml": ["mods"], "application/mp21": ["m21", "mp21"], "application/mp4": ["*mp4", "*mpg4", "mp4s", "m4p"], "application/msix": ["msix"], "application/msixbundle": ["msixbundle"], "application/msword": ["doc", "dot"], "application/mxf": ["mxf"], "application/n-quads": ["nq"], "application/n-triples": ["nt"], "application/node": ["cjs"], "application/octet-stream": ["bin", "dms", "lrf", "mar", "so", "dist", "distz", "pkg", "bpk", "dump", "elc", "deploy", "exe", "dll", "deb", "dmg", "iso", "img", "msi", "msp", "msm", "buffer"], "application/oda": ["oda"], "application/oebps-package+xml": ["opf"], "application/ogg": ["ogx"], "application/omdoc+xml": ["omdoc"], "application/onenote": ["onetoc", "onetoc2", "onetmp", "onepkg"], "application/oxps": ["oxps"], "application/p2p-overlay+xml": ["relo"], "application/patch-ops-error+xml": ["xer"], "application/pdf": ["pdf"], "application/pgp-encrypted": ["pgp"], "application/pgp-keys": ["asc"], "application/pgp-signature": ["sig", "*asc"], "application/pics-rules": ["prf"], "application/pkcs10": ["p10"], "application/pkcs7-mime": ["p7m", "p7c"], "application/pkcs7-signature": ["p7s"], "application/pkcs8": ["p8"], "application/pkix-attr-cert": ["ac"], "application/pkix-cert": ["cer"], "application/pkix-crl": ["crl"], "application/pkix-pkipath": ["pkipath"], "application/pkixcmp": ["pki"], "application/pls+xml": ["pls"], "application/postscript": ["ai", "eps", "ps"], "application/provenance+xml": ["provx"], "application/pskc+xml": ["pskcxml"], "application/raml+yaml": ["raml"], "application/rdf+xml": ["rdf", "owl"], "application/reginfo+xml": ["rif"], "application/relax-ng-compact-syntax": ["rnc"], "application/resource-lists+xml": ["rl"], "application/resource-lists-diff+xml": ["rld"], "application/rls-services+xml": ["rs"], "application/route-apd+xml": ["rapd"], "application/route-s-tsid+xml": ["sls"], "application/route-usd+xml": ["rusd"], "application/rpki-ghostbusters": ["gbr"], "application/rpki-manifest": ["mft"], "application/rpki-roa": ["roa"], "application/rsd+xml": ["rsd"], "application/rss+xml": ["rss"], "application/rtf": ["rtf"], "application/sbml+xml": ["sbml"], "application/scvp-cv-request": ["scq"], "application/scvp-cv-response": ["scs"], "application/scvp-vp-request": ["spq"], "application/scvp-vp-response": ["spp"], "application/sdp": ["sdp"], "application/senml+xml": ["senmlx"], "application/sensml+xml": ["sensmlx"], "application/set-payment-initiation": ["setpay"], "application/set-registration-initiation": ["setreg"], "application/shf+xml": ["shf"], "application/sieve": ["siv", "sieve"], "application/smil+xml": ["smi", "smil"], "application/sparql-query": ["rq"], "application/sparql-results+xml": ["srx"], "application/sql": ["sql"], "application/srgs": ["gram"], "application/srgs+xml": ["grxml"], "application/sru+xml": ["sru"], "application/ssdl+xml": ["ssdl"], "application/ssml+xml": ["ssml"], "application/swid+xml": ["swidtag"], "application/tei+xml": ["tei", "teicorpus"], "application/thraud+xml": ["tfi"], "application/timestamped-data": ["tsd"], "application/toml": ["toml"], "application/trig": ["trig"], "application/ttml+xml": ["ttml"], "application/ubjson": ["ubj"], "application/urc-ressheet+xml": ["rsheet"], "application/urc-targetdesc+xml": ["td"], "application/voicexml+xml": ["vxml"], "application/wasm": ["wasm"], "application/watcherinfo+xml": ["wif"], "application/widget": ["wgt"], "application/winhlp": ["hlp"], "application/wsdl+xml": ["wsdl"], "application/wspolicy+xml": ["wspolicy"], "application/xaml+xml": ["xaml"], "application/xcap-att+xml": ["xav"], "application/xcap-caps+xml": ["xca"], "application/xcap-diff+xml": ["xdf"], "application/xcap-el+xml": ["xel"], "application/xcap-ns+xml": ["xns"], "application/xenc+xml": ["xenc"], "application/xfdf": ["xfdf"], "application/xhtml+xml": ["xhtml", "xht"], "application/xliff+xml": ["xlf"], "application/xml": ["xml", "xsl", "xsd", "rng"], "application/xml-dtd": ["dtd"], "application/xop+xml": ["xop"], "application/xproc+xml": ["xpl"], "application/xslt+xml": ["*xsl", "xslt"], "application/xspf+xml": ["xspf"], "application/xv+xml": ["mxml", "xhvml", "xvml", "xvm"], "application/yang": ["yang"], "application/yin+xml": ["yin"], "application/zip": ["zip"], "audio/3gpp": ["*3gpp"], "audio/aac": ["adts", "aac"], "audio/adpcm": ["adp"], "audio/amr": ["amr"], "audio/basic": ["au", "snd"], "audio/midi": ["mid", "midi", "kar", "rmi"], "audio/mobile-xmf": ["mxmf"], "audio/mp3": ["*mp3"], "audio/mp4": ["m4a", "mp4a"], "audio/mpeg": ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"], "audio/ogg": ["oga", "ogg", "spx", "opus"], "audio/s3m": ["s3m"], "audio/silk": ["sil"], "audio/wav": ["wav"], "audio/wave": ["*wav"], "audio/webm": ["weba"], "audio/xm": ["xm"], "font/collection": ["ttc"], "font/otf": ["otf"], "font/ttf": ["ttf"], "font/woff": ["woff"], "font/woff2": ["woff2"], "image/aces": ["exr"], "image/apng": ["apng"], "image/avci": ["avci"], "image/avcs": ["avcs"], "image/avif": ["avif"], "image/bmp": ["bmp", "dib"], "image/cgm": ["cgm"], "image/dicom-rle": ["drle"], "image/dpx": ["dpx"], "image/emf": ["emf"], "image/fits": ["fits"], "image/g3fax": ["g3"], "image/gif": ["gif"], "image/heic": ["heic"], "image/heic-sequence": ["heics"], "image/heif": ["heif"], "image/heif-sequence": ["heifs"], "image/hej2k": ["hej2"], "image/hsj2": ["hsj2"], "image/ief": ["ief"], "image/jls": ["jls"], "image/jp2": ["jp2", "jpg2"], "image/jpeg": ["jpeg", "jpg", "jpe"], "image/jph": ["jph"], "image/jphc": ["jhc"], "image/jpm": ["jpm", "jpgm"], "image/jpx": ["jpx", "jpf"], "image/jxr": ["jxr"], "image/jxra": ["jxra"], "image/jxrs": ["jxrs"], "image/jxs": ["jxs"], "image/jxsc": ["jxsc"], "image/jxsi": ["jxsi"], "image/jxss": ["jxss"], "image/ktx": ["ktx"], "image/ktx2": ["ktx2"], "image/png": ["png"], "image/sgi": ["sgi"], "image/svg+xml": ["svg", "svgz"], "image/t38": ["t38"], "image/tiff": ["tif", "tiff"], "image/tiff-fx": ["tfx"], "image/webp": ["webp"], "image/wmf": ["wmf"], "message/disposition-notification": ["disposition-notification"], "message/global": ["u8msg"], "message/global-delivery-status": ["u8dsn"], "message/global-disposition-notification": ["u8mdn"], "message/global-headers": ["u8hdr"], "message/rfc822": ["eml", "mime"], "model/3mf": ["3mf"], "model/gltf+json": ["gltf"], "model/gltf-binary": ["glb"], "model/iges": ["igs", "iges"], "model/jt": ["jt"], "model/mesh": ["msh", "mesh", "silo"], "model/mtl": ["mtl"], "model/obj": ["obj"], "model/prc": ["prc"], "model/step+xml": ["stpx"], "model/step+zip": ["stpz"], "model/step-xml+zip": ["stpxz"], "model/stl": ["stl"], "model/u3d": ["u3d"], "model/vrml": ["wrl", "vrml"], "model/x3d+binary": ["*x3db", "x3dbz"], "model/x3d+fastinfoset": ["x3db"], "model/x3d+vrml": ["*x3dv", "x3dvz"], "model/x3d+xml": ["x3d", "x3dz"], "model/x3d-vrml": ["x3dv"], "text/cache-manifest": ["appcache", "manifest"], "text/calendar": ["ics", "ifb"], "text/coffeescript": ["coffee", "litcoffee"], "text/css": ["css"], "text/csv": ["csv"], "text/html": ["html", "htm", "shtml"], "text/jade": ["jade"], "text/javascript": ["js", "mjs"], "text/jsx": ["jsx"], "text/less": ["less"], "text/markdown": ["md", "markdown"], "text/mathml": ["mml"], "text/mdx": ["mdx"], "text/n3": ["n3"], "text/plain": ["txt", "text", "conf", "def", "list", "log", "in", "ini"], "text/richtext": ["rtx"], "text/rtf": ["*rtf"], "text/sgml": ["sgml", "sgm"], "text/shex": ["shex"], "text/slim": ["slim", "slm"], "text/spdx": ["spdx"], "text/stylus": ["stylus", "styl"], "text/tab-separated-values": ["tsv"], "text/troff": ["t", "tr", "roff", "man", "me", "ms"], "text/turtle": ["ttl"], "text/uri-list": ["uri", "uris", "urls"], "text/vcard": ["vcard"], "text/vtt": ["vtt"], "text/wgsl": ["wgsl"], "text/xml": ["*xml"], "text/yaml": ["yaml", "yml"], "video/3gpp": ["3gp", "3gpp"], "video/3gpp2": ["3g2"], "video/h261": ["h261"], "video/h263": ["h263"], "video/h264": ["h264"], "video/iso.segment": ["m4s"], "video/jpeg": ["jpgv"], "video/jpm": ["*jpm", "*jpgm"], "video/mj2": ["mj2", "mjp2"], "video/mp2t": ["ts"], "video/mp4": ["mp4", "mp4v", "mpg4"], "video/mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v"], "video/ogg": ["ogv"], "video/quicktime": ["qt", "mov"], "video/webm": ["webm"] };
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
1578
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
1579
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
1580
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
1581
|
-
};
|
|
1582
|
-
var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
|
|
1583
|
-
class Mime {
|
|
1584
|
-
constructor(...args) {
|
|
1585
|
-
_Mime_extensionToType.set(this, new Map());
|
|
1586
|
-
_Mime_typeToExtension.set(this, new Map());
|
|
1587
|
-
_Mime_typeToExtensions.set(this, new Map());
|
|
1588
|
-
for (const arg of args) {
|
|
1589
|
-
this.define(arg);
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
name: "vitest:browser:tests",
|
|
1593
|
+
enforce: "pre",
|
|
1594
|
+
async config() {
|
|
1595
|
+
const { testFiles: allTestFiles } = await project.globTestFiles();
|
|
1596
|
+
const browserTestFiles = allTestFiles.filter(
|
|
1597
|
+
(file) => getFilePoolName(project, file) === "browser"
|
|
1598
|
+
);
|
|
1599
|
+
const setupFiles = toArray(project.config.setupFiles);
|
|
1600
|
+
const define = {};
|
|
1601
|
+
for (const env in project.config.env || {}) {
|
|
1602
|
+
const stringValue = JSON.stringify(project.config.env[env]);
|
|
1603
|
+
define[`import.meta.env.${env}`] = stringValue;
|
|
1590
1604
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1605
|
+
const entries = [
|
|
1606
|
+
...browserTestFiles,
|
|
1607
|
+
...setupFiles,
|
|
1608
|
+
resolve(distDir, "index.js"),
|
|
1609
|
+
resolve(distDir, "browser.js"),
|
|
1610
|
+
resolve(distDir, "runners.js"),
|
|
1611
|
+
resolve(distDir, "utils.js"),
|
|
1612
|
+
...project.config.snapshotSerializers || []
|
|
1613
|
+
];
|
|
1614
|
+
const exclude = [
|
|
1615
|
+
"vitest",
|
|
1616
|
+
"vitest/utils",
|
|
1617
|
+
"vitest/browser",
|
|
1618
|
+
"vitest/runners",
|
|
1619
|
+
"@vitest/browser",
|
|
1620
|
+
"@vitest/browser/client",
|
|
1621
|
+
"@vitest/utils",
|
|
1622
|
+
"@vitest/utils/source-map",
|
|
1623
|
+
"@vitest/runner",
|
|
1624
|
+
"@vitest/spy",
|
|
1625
|
+
"@vitest/utils/error",
|
|
1626
|
+
"@vitest/snapshot",
|
|
1627
|
+
"@vitest/expect",
|
|
1628
|
+
"std-env",
|
|
1629
|
+
"tinybench",
|
|
1630
|
+
"tinyspy",
|
|
1631
|
+
"tinyrainbow",
|
|
1632
|
+
"pathe",
|
|
1633
|
+
"msw",
|
|
1634
|
+
"msw/browser"
|
|
1635
|
+
];
|
|
1636
|
+
if (project.config.diff) {
|
|
1637
|
+
entries.push(project.config.diff);
|
|
1638
|
+
}
|
|
1639
|
+
if (project.ctx.coverageProvider) {
|
|
1640
|
+
const coverage = project.ctx.config.coverage;
|
|
1641
|
+
const provider = coverage.provider;
|
|
1642
|
+
if (provider === "v8") {
|
|
1643
|
+
const path = tryResolve("@vitest/coverage-v8", [project.config.root]);
|
|
1644
|
+
if (path) {
|
|
1645
|
+
entries.push(path);
|
|
1646
|
+
exclude.push("@vitest/coverage-v8/browser");
|
|
1598
1647
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
allExtensions?.add(extension);
|
|
1605
|
-
if (first) {
|
|
1606
|
-
__classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
|
|
1607
|
-
}
|
|
1608
|
-
first = false;
|
|
1609
|
-
if (starred)
|
|
1610
|
-
continue;
|
|
1611
|
-
const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
|
|
1612
|
-
if (currentType && currentType != type && !force) {
|
|
1613
|
-
throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
|
|
1614
|
-
}
|
|
1615
|
-
__classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
|
|
1648
|
+
} else if (provider === "istanbul") {
|
|
1649
|
+
const path = tryResolve("@vitest/coverage-istanbul", [project.config.root]);
|
|
1650
|
+
if (path) {
|
|
1651
|
+
entries.push(path);
|
|
1652
|
+
exclude.push("@vitest/coverage-istanbul");
|
|
1616
1653
|
}
|
|
1654
|
+
} else if (provider === "custom" && coverage.customProviderModule) {
|
|
1655
|
+
entries.push(coverage.customProviderModule);
|
|
1656
|
+
}
|
|
1617
1657
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1658
|
+
const include = [
|
|
1659
|
+
"vitest > expect-type",
|
|
1660
|
+
"vitest > @vitest/snapshot > magic-string",
|
|
1661
|
+
"vitest > chai",
|
|
1662
|
+
"vitest > chai > loupe",
|
|
1663
|
+
"vitest > @vitest/utils > loupe",
|
|
1664
|
+
"@vitest/browser > @testing-library/user-event",
|
|
1665
|
+
"@vitest/browser > @testing-library/dom"
|
|
1666
|
+
];
|
|
1667
|
+
const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
|
|
1668
|
+
const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
|
|
1669
|
+
if (svelte) {
|
|
1670
|
+
exclude.push("vitest-browser-svelte");
|
|
1671
|
+
}
|
|
1672
|
+
const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
|
|
1673
|
+
if (vueTestUtils) {
|
|
1674
|
+
include.push("@vue/test-utils");
|
|
1675
|
+
}
|
|
1676
|
+
return {
|
|
1677
|
+
define,
|
|
1678
|
+
resolve: {
|
|
1679
|
+
dedupe: ["vitest"]
|
|
1680
|
+
},
|
|
1681
|
+
optimizeDeps: {
|
|
1682
|
+
entries,
|
|
1683
|
+
exclude,
|
|
1684
|
+
include
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
},
|
|
1688
|
+
async resolveId(id) {
|
|
1689
|
+
if (!/\?browserv=\w+$/.test(id)) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
let useId = id.slice(0, id.lastIndexOf("?"));
|
|
1693
|
+
if (useId.startsWith("/@fs/")) {
|
|
1694
|
+
useId = useId.slice(5);
|
|
1695
|
+
}
|
|
1696
|
+
if (/^\w:/.test(useId)) {
|
|
1697
|
+
useId = useId.replace(/\\/g, "/");
|
|
1698
|
+
}
|
|
1699
|
+
return useId;
|
|
1700
|
+
}
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
name: "vitest:browser:resolve-virtual",
|
|
1704
|
+
async resolveId(rawId) {
|
|
1705
|
+
if (rawId === "/mockServiceWorker.js") {
|
|
1706
|
+
return this.resolve("msw/mockServiceWorker.js", distRoot, {
|
|
1707
|
+
skipSelf: true
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
name: "vitest:browser:assets",
|
|
1714
|
+
configureServer(server) {
|
|
1715
|
+
server.middlewares.use(
|
|
1716
|
+
"/__vitest__",
|
|
1717
|
+
sirv(resolve(distRoot, "client/__vitest__"))
|
|
1718
|
+
);
|
|
1719
|
+
},
|
|
1720
|
+
resolveId(id) {
|
|
1721
|
+
if (id.startsWith("/__vitest_browser__/")) {
|
|
1722
|
+
return resolve(distRoot, "client", id.slice(1));
|
|
1723
|
+
}
|
|
1724
|
+
},
|
|
1725
|
+
transform(code, id) {
|
|
1726
|
+
if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
|
|
1727
|
+
const utilRequire = "nodeUtil = require_util();";
|
|
1728
|
+
return code.replace(utilRequire, " ".repeat(utilRequire.length));
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
},
|
|
1732
|
+
BrowserContext(browserServer),
|
|
1733
|
+
dynamicImportPlugin({
|
|
1734
|
+
globalThisAccessor: '"__vitest_browser_runner__"',
|
|
1735
|
+
filter(id) {
|
|
1736
|
+
if (id.includes(distRoot)) {
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
}),
|
|
1742
|
+
{
|
|
1743
|
+
name: "vitest:browser:config",
|
|
1744
|
+
enforce: "post",
|
|
1745
|
+
async config(viteConfig) {
|
|
1746
|
+
// Enables using ignore hint for coverage providers with @preserve keyword
|
|
1747
|
+
if (viteConfig.esbuild !== false) {
|
|
1748
|
+
viteConfig.esbuild ||= {};
|
|
1749
|
+
viteConfig.esbuild.legalComments = "inline";
|
|
1750
|
+
}
|
|
1751
|
+
const defaultPort = project.ctx._browserLastPort++;
|
|
1752
|
+
const api = resolveApiServerConfig(
|
|
1753
|
+
viteConfig.test?.browser || {},
|
|
1754
|
+
defaultPort
|
|
1755
|
+
);
|
|
1756
|
+
viteConfig.server = {
|
|
1757
|
+
...viteConfig.server,
|
|
1758
|
+
port: defaultPort,
|
|
1759
|
+
...api,
|
|
1760
|
+
middlewareMode: false,
|
|
1761
|
+
open: false
|
|
1645
1762
|
};
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1763
|
+
viteConfig.server.fs ??= {};
|
|
1764
|
+
viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
|
|
1765
|
+
viteConfig.server.fs.allow.push(
|
|
1766
|
+
...resolveFsAllow(
|
|
1767
|
+
project.ctx.config.root,
|
|
1768
|
+
project.ctx.server.config.configFile
|
|
1769
|
+
),
|
|
1770
|
+
distRoot
|
|
1771
|
+
);
|
|
1772
|
+
return {
|
|
1773
|
+
resolve: {
|
|
1774
|
+
alias: viteConfig.test?.alias
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
name: "vitest:browser:in-source-tests",
|
|
1781
|
+
transform(code, id) {
|
|
1782
|
+
if (!project.isTestFile(id) || !code.includes("import.meta.vitest")) {
|
|
1783
|
+
return;
|
|
1649
1784
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1785
|
+
const s = new MagicString(code, { filename: cleanUrl(id) });
|
|
1786
|
+
s.prepend(
|
|
1787
|
+
`import.meta.vitest = __vitest_index__;
|
|
1788
|
+
`
|
|
1789
|
+
);
|
|
1653
1790
|
return {
|
|
1654
|
-
|
|
1655
|
-
|
|
1791
|
+
code: s.toString(),
|
|
1792
|
+
map: s.generateMap({ hires: true })
|
|
1656
1793
|
};
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
_Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
|
|
1660
|
-
|
|
1661
|
-
var mime = new Mime(types)._freeze();
|
|
1662
|
-
|
|
1663
|
-
function assertFileAccess(path, project) {
|
|
1664
|
-
if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server)) {
|
|
1665
|
-
throw new Error(
|
|
1666
|
-
`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
|
|
1667
|
-
);
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
|
|
1671
|
-
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1672
|
-
assertFileAccess(filepath, project);
|
|
1673
|
-
if (typeof options === "object" && !options.encoding) {
|
|
1674
|
-
options.encoding = "utf-8";
|
|
1675
|
-
}
|
|
1676
|
-
return promises.readFile(filepath, options);
|
|
1677
|
-
};
|
|
1678
|
-
const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
|
|
1679
|
-
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1680
|
-
assertFileAccess(filepath, project);
|
|
1681
|
-
const dir = dirname$1(filepath);
|
|
1682
|
-
if (!fs.existsSync(dir)) {
|
|
1683
|
-
await promises.mkdir(dir, { recursive: true });
|
|
1684
|
-
}
|
|
1685
|
-
await promises.writeFile(filepath, data, options);
|
|
1686
|
-
};
|
|
1687
|
-
const removeFile = async ({ project, testPath = process.cwd() }, path) => {
|
|
1688
|
-
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1689
|
-
assertFileAccess(filepath, project);
|
|
1690
|
-
await promises.rm(filepath);
|
|
1691
|
-
};
|
|
1692
|
-
const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
|
|
1693
|
-
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1694
|
-
assertFileAccess(filepath, project);
|
|
1695
|
-
const content = await promises.readFile(filepath, encoding || "base64");
|
|
1696
|
-
return {
|
|
1697
|
-
content,
|
|
1698
|
-
basename: basename$1(filepath),
|
|
1699
|
-
mime: mime.getType(filepath)
|
|
1700
|
-
};
|
|
1701
|
-
};
|
|
1702
|
-
|
|
1703
|
-
const screenshot = async (context, name, options = {}) => {
|
|
1704
|
-
if (!context.testPath) {
|
|
1705
|
-
throw new Error(`Cannot take a screenshot without a test path`);
|
|
1706
|
-
}
|
|
1707
|
-
const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
|
|
1708
|
-
context.testPath,
|
|
1709
|
-
name,
|
|
1710
|
-
context.project.config
|
|
1711
|
-
);
|
|
1712
|
-
const savePath = normalize$1(path);
|
|
1713
|
-
await mkdir(dirname(path), { recursive: true });
|
|
1714
|
-
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1715
|
-
if (options.element) {
|
|
1716
|
-
const { element: selector, ...config } = options;
|
|
1717
|
-
const element = context.iframe.locator(`${selector}`);
|
|
1718
|
-
const buffer2 = await element.screenshot({
|
|
1719
|
-
timeout: 1e3,
|
|
1720
|
-
...config,
|
|
1721
|
-
path: savePath
|
|
1722
|
-
});
|
|
1723
|
-
return returnResult(options, path, buffer2);
|
|
1724
|
-
}
|
|
1725
|
-
const buffer = await context.iframe.locator("body").screenshot({
|
|
1726
|
-
...options,
|
|
1727
|
-
path: savePath
|
|
1728
|
-
});
|
|
1729
|
-
return returnResult(options, path, buffer);
|
|
1730
|
-
}
|
|
1731
|
-
if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1732
|
-
const page = context.provider.browser;
|
|
1733
|
-
if (!options.element) {
|
|
1734
|
-
const body = await page.$("body");
|
|
1735
|
-
const buffer2 = await body.saveScreenshot(savePath);
|
|
1736
|
-
return returnResult(options, path, buffer2);
|
|
1737
|
-
}
|
|
1738
|
-
const element = await page.$(`${options.element}`);
|
|
1739
|
-
const buffer = await element.saveScreenshot(savePath);
|
|
1740
|
-
return returnResult(options, path, buffer);
|
|
1741
|
-
}
|
|
1742
|
-
throw new Error(
|
|
1743
|
-
`Provider "${context.provider.name}" does not support screenshots`
|
|
1744
|
-
);
|
|
1745
|
-
};
|
|
1746
|
-
function resolveScreenshotPath(testPath, name, config) {
|
|
1747
|
-
const dir = dirname(testPath);
|
|
1748
|
-
const base = basename(testPath);
|
|
1749
|
-
if (config.browser.screenshotDirectory) {
|
|
1750
|
-
return resolve(
|
|
1751
|
-
config.browser.screenshotDirectory,
|
|
1752
|
-
relative(config.root, dir),
|
|
1753
|
-
base,
|
|
1754
|
-
name
|
|
1755
|
-
);
|
|
1756
|
-
}
|
|
1757
|
-
return resolve(dir, "__screenshots__", base, name);
|
|
1758
|
-
}
|
|
1759
|
-
function returnResult(options, path, buffer) {
|
|
1760
|
-
if (options.base64) {
|
|
1761
|
-
return { path, base64: buffer.toString("base64") };
|
|
1762
|
-
}
|
|
1763
|
-
return path;
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
var builtinCommands = {
|
|
1767
|
-
readFile,
|
|
1768
|
-
removeFile,
|
|
1769
|
-
writeFile,
|
|
1770
|
-
__vitest_fileInfo: _fileInfo,
|
|
1771
|
-
__vitest_upload: upload,
|
|
1772
|
-
__vitest_click: click,
|
|
1773
|
-
__vitest_dblClick: dblClick,
|
|
1774
|
-
__vitest_tripleClick: tripleClick,
|
|
1775
|
-
__vitest_screenshot: screenshot,
|
|
1776
|
-
__vitest_type: type,
|
|
1777
|
-
__vitest_clear: clear,
|
|
1778
|
-
__vitest_fill: fill,
|
|
1779
|
-
__vitest_tab: tab,
|
|
1780
|
-
__vitest_keyboard: keyboard,
|
|
1781
|
-
__vitest_selectOptions: selectOptions,
|
|
1782
|
-
__vitest_dragAndDrop: dragAndDrop,
|
|
1783
|
-
__vitest_hover: hover
|
|
1784
|
-
};
|
|
1785
|
-
|
|
1786
|
-
const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
|
|
1787
|
-
const ID_CONTEXT = "@vitest/browser/context";
|
|
1788
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1789
|
-
function BrowserContext(server) {
|
|
1790
|
-
const project = server.project;
|
|
1791
|
-
project.config.browser.commands ??= {};
|
|
1792
|
-
for (const [name, command] of Object.entries(builtinCommands)) {
|
|
1793
|
-
project.config.browser.commands[name] ??= command;
|
|
1794
|
-
}
|
|
1795
|
-
for (const command in project.config.browser.commands) {
|
|
1796
|
-
if (!/^[a-z_$][\w$]*$/i.test(command)) {
|
|
1797
|
-
throw new Error(
|
|
1798
|
-
`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
|
|
1799
|
-
);
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
return {
|
|
1803
|
-
name: "vitest:browser:virtual-module:context",
|
|
1804
|
-
enforce: "pre",
|
|
1805
|
-
resolveId(id) {
|
|
1806
|
-
if (id === ID_CONTEXT) {
|
|
1807
|
-
return VIRTUAL_ID_CONTEXT;
|
|
1808
1794
|
}
|
|
1809
1795
|
},
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1796
|
+
{
|
|
1797
|
+
name: "vitest:browser:worker",
|
|
1798
|
+
transform(code, id, _options) {
|
|
1799
|
+
if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
|
|
1800
|
+
const s = new MagicString(code);
|
|
1801
|
+
s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
|
|
1802
|
+
return {
|
|
1803
|
+
code: s.toString(),
|
|
1804
|
+
map: s.generateMap({ hires: "boundary" })
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1813
1807
|
}
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
name: "vitest:browser:transform-tester-html",
|
|
1811
|
+
enforce: "pre",
|
|
1812
|
+
async transformIndexHtml(html, ctx) {
|
|
1813
|
+
if (ctx.filename !== browserServer.testerFilepath) {
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
if (!browserServer.testerScripts) {
|
|
1817
|
+
const testerScripts = await browserServer.formatScripts(
|
|
1818
|
+
project.config.browser.testerScripts
|
|
1819
|
+
);
|
|
1820
|
+
browserServer.testerScripts = testerScripts;
|
|
1821
|
+
}
|
|
1822
|
+
const stateJs = typeof browserServer.stateJs === "string" ? browserServer.stateJs : await browserServer.stateJs;
|
|
1823
|
+
const testerTags = [];
|
|
1824
|
+
const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === browserServer.testerFilepath;
|
|
1825
|
+
if (!isDefaultTemplate) {
|
|
1826
|
+
const manifestContent = browserServer.manifest instanceof Promise ? await browserServer.manifest : browserServer.manifest;
|
|
1827
|
+
const testerEntry = manifestContent["tester/tester.html"];
|
|
1828
|
+
testerTags.push({
|
|
1829
|
+
tag: "script",
|
|
1830
|
+
attrs: {
|
|
1831
|
+
type: "module",
|
|
1832
|
+
crossorigin: "",
|
|
1833
|
+
src: `${browserServer.base}${testerEntry.file}`
|
|
1834
|
+
},
|
|
1835
|
+
injectTo: "head"
|
|
1836
|
+
});
|
|
1837
|
+
for (const importName of testerEntry.imports || []) {
|
|
1838
|
+
const entryManifest = manifestContent[importName];
|
|
1839
|
+
if (entryManifest) {
|
|
1840
|
+
testerTags.push(
|
|
1841
|
+
{
|
|
1842
|
+
tag: "link",
|
|
1843
|
+
attrs: {
|
|
1844
|
+
href: `${browserServer.base}${entryManifest.file}`,
|
|
1845
|
+
rel: "modulepreload",
|
|
1846
|
+
crossorigin: ""
|
|
1847
|
+
},
|
|
1848
|
+
injectTo: "head"
|
|
1849
|
+
}
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
} else {
|
|
1854
|
+
testerTags.push({
|
|
1855
|
+
tag: "style",
|
|
1856
|
+
children: `
|
|
1857
|
+
html {
|
|
1858
|
+
padding: 0;
|
|
1859
|
+
margin: 0;
|
|
1816
1860
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1861
|
+
body {
|
|
1862
|
+
padding: 0;
|
|
1863
|
+
margin: 0;
|
|
1864
|
+
min-height: 100vh;
|
|
1865
|
+
}`,
|
|
1866
|
+
injectTo: "head"
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
return [
|
|
1870
|
+
{
|
|
1871
|
+
tag: "script",
|
|
1872
|
+
children: "{__VITEST_INJECTOR__}",
|
|
1873
|
+
injectTo: "head-prepend"
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
tag: "script",
|
|
1877
|
+
children: stateJs,
|
|
1878
|
+
injectTo: "head-prepend"
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
tag: "script",
|
|
1882
|
+
attrs: {
|
|
1883
|
+
type: "module",
|
|
1884
|
+
src: browserServer.errorCatcherUrl
|
|
1885
|
+
},
|
|
1886
|
+
injectTo: "head"
|
|
1887
|
+
},
|
|
1888
|
+
browserServer.locatorsUrl ? {
|
|
1889
|
+
tag: "script",
|
|
1890
|
+
attrs: {
|
|
1891
|
+
type: "module",
|
|
1892
|
+
src: browserServer.locatorsUrl
|
|
1893
|
+
},
|
|
1894
|
+
injectTo: "head"
|
|
1895
|
+
} : null,
|
|
1896
|
+
...browserServer.testerScripts,
|
|
1897
|
+
...testerTags,
|
|
1898
|
+
{
|
|
1899
|
+
tag: "script",
|
|
1900
|
+
attrs: {
|
|
1901
|
+
"type": "module",
|
|
1902
|
+
"data-vitest-append": ""
|
|
1903
|
+
},
|
|
1904
|
+
children: "{__VITEST_APPEND__}",
|
|
1905
|
+
injectTo: "body"
|
|
1906
|
+
}
|
|
1907
|
+
].filter((s) => s != null);
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
name: "vitest:browser:support-testing-library",
|
|
1912
|
+
config() {
|
|
1913
|
+
return {
|
|
1914
|
+
optimizeDeps: {
|
|
1915
|
+
esbuildOptions: {
|
|
1916
|
+
plugins: [
|
|
1917
|
+
{
|
|
1918
|
+
name: "test-utils-rewrite",
|
|
1919
|
+
setup(build) {
|
|
1920
|
+
build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
|
|
1921
|
+
const _require2 = getRequire();
|
|
1922
|
+
const resolved = _require2.resolve(args.path, {
|
|
1923
|
+
paths: [args.importer]
|
|
1924
|
+
});
|
|
1925
|
+
return { path: resolved };
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
]
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
];
|
|
1936
|
+
};
|
|
1937
|
+
function tryResolve(path, paths) {
|
|
1938
|
+
try {
|
|
1939
|
+
const _require2 = getRequire();
|
|
1940
|
+
return _require2.resolve(path, { paths });
|
|
1941
|
+
} catch {
|
|
1942
|
+
return void 0;
|
|
1943
|
+
}
|
|
1845
1944
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1945
|
+
let _require;
|
|
1946
|
+
function getRequire() {
|
|
1947
|
+
if (!_require) {
|
|
1948
|
+
_require = createRequire(import.meta.url);
|
|
1949
|
+
}
|
|
1950
|
+
return _require;
|
|
1850
1951
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1952
|
+
function resolveCoverageFolder(project) {
|
|
1953
|
+
const options = project.ctx.config;
|
|
1954
|
+
const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
|
|
1955
|
+
if (typeof reporter === "string") {
|
|
1956
|
+
return reporter === "html";
|
|
1957
|
+
}
|
|
1958
|
+
return reporter[0] === "html";
|
|
1959
|
+
}) : void 0;
|
|
1960
|
+
if (!htmlReporter) {
|
|
1961
|
+
return void 0;
|
|
1854
1962
|
}
|
|
1855
|
-
const
|
|
1856
|
-
|
|
1857
|
-
|
|
1963
|
+
const root = resolve(
|
|
1964
|
+
options.root || process.cwd(),
|
|
1965
|
+
options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
|
|
1966
|
+
);
|
|
1967
|
+
const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
|
|
1968
|
+
if (!subdir || typeof subdir !== "string") {
|
|
1969
|
+
return [root, `/${basename(root)}/`];
|
|
1858
1970
|
}
|
|
1859
|
-
return
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1971
|
+
return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
|
|
1972
|
+
}
|
|
1973
|
+
const postfixRE = /[?#].*$/;
|
|
1974
|
+
function cleanUrl(url) {
|
|
1975
|
+
return url.replace(postfixRE, "");
|
|
1864
1976
|
}
|
|
1865
1977
|
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1978
|
+
const DEFAULT_TIMEOUT = 6e4;
|
|
1979
|
+
function defaultSerialize(i) {
|
|
1980
|
+
return i;
|
|
1981
|
+
}
|
|
1982
|
+
const defaultDeserialize = defaultSerialize;
|
|
1983
|
+
const { clearTimeout, setTimeout } = globalThis;
|
|
1984
|
+
const random = Math.random.bind(Math);
|
|
1985
|
+
function createBirpc(functions, options) {
|
|
1986
|
+
const {
|
|
1987
|
+
post,
|
|
1988
|
+
on,
|
|
1989
|
+
off = () => {
|
|
1990
|
+
},
|
|
1991
|
+
eventNames = [],
|
|
1992
|
+
serialize = defaultSerialize,
|
|
1993
|
+
deserialize = defaultDeserialize,
|
|
1994
|
+
resolver,
|
|
1995
|
+
bind = "rpc",
|
|
1996
|
+
timeout = DEFAULT_TIMEOUT
|
|
1997
|
+
} = options;
|
|
1998
|
+
const rpcPromiseMap = /* @__PURE__ */ new Map();
|
|
1999
|
+
let _promise;
|
|
2000
|
+
let closed = false;
|
|
2001
|
+
const rpc = new Proxy({}, {
|
|
2002
|
+
get(_, method) {
|
|
2003
|
+
if (method === "$functions")
|
|
2004
|
+
return functions;
|
|
2005
|
+
if (method === "$close")
|
|
2006
|
+
return close;
|
|
2007
|
+
if (method === "then" && !eventNames.includes("then") && !("then" in functions))
|
|
2008
|
+
return void 0;
|
|
2009
|
+
const sendEvent = (...args) => {
|
|
2010
|
+
post(serialize({ m: method, a: args, t: "q" }));
|
|
2011
|
+
};
|
|
2012
|
+
if (eventNames.includes(method)) {
|
|
2013
|
+
sendEvent.asEvent = sendEvent;
|
|
2014
|
+
return sendEvent;
|
|
2015
|
+
}
|
|
2016
|
+
const sendCall = async (...args) => {
|
|
2017
|
+
if (closed)
|
|
2018
|
+
throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
|
|
2019
|
+
if (_promise) {
|
|
2020
|
+
try {
|
|
2021
|
+
await _promise;
|
|
2022
|
+
} finally {
|
|
2023
|
+
_promise = void 0;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
return new Promise((resolve, reject) => {
|
|
2027
|
+
const id = nanoid();
|
|
2028
|
+
let timeoutId;
|
|
2029
|
+
if (timeout >= 0) {
|
|
2030
|
+
timeoutId = setTimeout(() => {
|
|
2031
|
+
try {
|
|
2032
|
+
options.onTimeoutError?.(method, args);
|
|
2033
|
+
throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
2034
|
+
} catch (e) {
|
|
2035
|
+
reject(e);
|
|
2036
|
+
}
|
|
2037
|
+
rpcPromiseMap.delete(id);
|
|
2038
|
+
}, timeout);
|
|
2039
|
+
if (typeof timeoutId === "object")
|
|
2040
|
+
timeoutId = timeoutId.unref?.();
|
|
2041
|
+
}
|
|
2042
|
+
rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
|
|
2043
|
+
post(serialize({ m: method, a: args, i: id, t: "q" }));
|
|
2044
|
+
});
|
|
2045
|
+
};
|
|
2046
|
+
sendCall.asEvent = sendEvent;
|
|
2047
|
+
return sendCall;
|
|
2048
|
+
}
|
|
1886
2049
|
});
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
);
|
|
2050
|
+
function close() {
|
|
2051
|
+
closed = true;
|
|
2052
|
+
rpcPromiseMap.forEach(({ reject, method }) => {
|
|
2053
|
+
reject(new Error(`[birpc] rpc is closed, cannot call "${method}"`));
|
|
2054
|
+
});
|
|
2055
|
+
rpcPromiseMap.clear();
|
|
2056
|
+
off(onMessage);
|
|
1892
2057
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2058
|
+
async function onMessage(data, ...extra) {
|
|
2059
|
+
const msg = deserialize(data);
|
|
2060
|
+
if (msg.t === "q") {
|
|
2061
|
+
const { m: method, a: args } = msg;
|
|
2062
|
+
let result, error;
|
|
2063
|
+
const fn = resolver ? resolver(method, functions[method]) : functions[method];
|
|
2064
|
+
if (!fn) {
|
|
2065
|
+
error = new Error(`[birpc] function "${method}" not found`);
|
|
2066
|
+
} else {
|
|
2067
|
+
try {
|
|
2068
|
+
result = await fn.apply(bind === "rpc" ? rpc : functions, args);
|
|
2069
|
+
} catch (e) {
|
|
2070
|
+
error = e;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
if (msg.i) {
|
|
2074
|
+
if (error && options.onError)
|
|
2075
|
+
options.onError(error, method, args);
|
|
2076
|
+
post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
|
|
2077
|
+
}
|
|
2078
|
+
} else {
|
|
2079
|
+
const { i: ack, r: result, e: error } = msg;
|
|
2080
|
+
const promise = rpcPromiseMap.get(ack);
|
|
2081
|
+
if (promise) {
|
|
2082
|
+
clearTimeout(promise.timeoutId);
|
|
2083
|
+
if (error)
|
|
2084
|
+
promise.reject(error);
|
|
2085
|
+
else
|
|
2086
|
+
promise.resolve(result);
|
|
2087
|
+
}
|
|
2088
|
+
rpcPromiseMap.delete(ack);
|
|
2089
|
+
}
|
|
1907
2090
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
__VITEST_TITLE__: "Vitest Browser Runner",
|
|
1911
|
-
__VITEST_SCRIPTS__: server.orchestratorScripts,
|
|
1912
|
-
__VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
|
|
1913
|
-
__VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
|
|
1914
|
-
__VITEST_CONTEXT_ID__: JSON.stringify(contextId)
|
|
1915
|
-
});
|
|
2091
|
+
_promise = on(onMessage);
|
|
2092
|
+
return rpc;
|
|
1916
2093
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
);
|
|
1925
|
-
}
|
|
1926
|
-
const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
|
|
1927
|
-
const project = server.project;
|
|
1928
|
-
const state = server.state;
|
|
1929
|
-
const { testFiles } = await project.globTestFiles();
|
|
1930
|
-
const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
|
|
1931
|
-
const iframeId = JSON.stringify(testFile);
|
|
1932
|
-
const context = state.getContext(contextId);
|
|
1933
|
-
const files = context?.files ?? [];
|
|
1934
|
-
const method = context?.method ?? "run";
|
|
1935
|
-
const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
|
|
1936
|
-
const injector = replacer(injectorJs, {
|
|
1937
|
-
__VITEST_PROVIDER__: JSON.stringify(server.provider.name),
|
|
1938
|
-
__VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
|
|
1939
|
-
__VITEST_FILES__: JSON.stringify(files),
|
|
1940
|
-
__VITEST_VITE_CONFIG__: JSON.stringify({
|
|
1941
|
-
root: server.vite.config.root
|
|
1942
|
-
}),
|
|
1943
|
-
__VITEST_TYPE__: '"tester"',
|
|
1944
|
-
__VITEST_CONTEXT_ID__: JSON.stringify(contextId),
|
|
1945
|
-
__VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
|
|
1946
|
-
__VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
|
|
1947
|
-
});
|
|
1948
|
-
if (!server.testerScripts) {
|
|
1949
|
-
const testerScripts = await server.formatScripts(
|
|
1950
|
-
project.config.browser.testerScripts
|
|
1951
|
-
);
|
|
1952
|
-
const clientScript = `<script type="module" src="${server.base}@vite/client"><\/script>`;
|
|
1953
|
-
const stateJs = typeof server.stateJs === "string" ? server.stateJs : await server.stateJs;
|
|
1954
|
-
const stateScript = `<script type="module">${stateJs}<\/script>`;
|
|
1955
|
-
server.testerScripts = `${stateScript}${clientScript}${testerScripts}`;
|
|
1956
|
-
}
|
|
1957
|
-
const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
|
|
1958
|
-
return replacer(testerHtml, {
|
|
1959
|
-
__VITEST_FAVICON__: server.faviconUrl,
|
|
1960
|
-
__VITEST_TITLE__: "Vitest Browser Tester",
|
|
1961
|
-
__VITEST_SCRIPTS__: server.testerScripts,
|
|
1962
|
-
__VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
|
|
1963
|
-
__VITEST_INTERNAL_SCRIPTS__: [
|
|
1964
|
-
`<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
|
|
1965
|
-
server.locatorsUrl ? `<script type="module" src="${server.locatorsUrl}"><\/script>` : ""
|
|
1966
|
-
].join("\n"),
|
|
1967
|
-
__VITEST_APPEND__: `<script data-vitest-append type="module">
|
|
1968
|
-
__vitest_browser_runner__.runningFiles = ${tests}
|
|
1969
|
-
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
1970
|
-
__vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
|
|
1971
|
-
document.querySelector('script[data-vitest-append]').remove()
|
|
1972
|
-
<\/script>`
|
|
1973
|
-
});
|
|
2094
|
+
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
2095
|
+
function nanoid(size = 21) {
|
|
2096
|
+
let id = "";
|
|
2097
|
+
let i = size;
|
|
2098
|
+
while (i--)
|
|
2099
|
+
id += urlAlphabet[random() * 64 | 0];
|
|
2100
|
+
return id;
|
|
1974
2101
|
}
|
|
1975
2102
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
const project =
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2103
|
+
const debug$1 = createDebugger("vitest:browser:api");
|
|
2104
|
+
const BROWSER_API_PATH = "/__vitest_browser_api__";
|
|
2105
|
+
function setupBrowserRpc(server) {
|
|
2106
|
+
const project = server.project;
|
|
2107
|
+
const vite = server.vite;
|
|
2108
|
+
const ctx = project.ctx;
|
|
2109
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
2110
|
+
vite.httpServer?.on("upgrade", (request, socket, head) => {
|
|
2111
|
+
if (!request.url) {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
const { pathname, searchParams } = new URL(request.url, "http://localhost");
|
|
2115
|
+
if (pathname !== BROWSER_API_PATH) {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
const type = searchParams.get("type") ?? "tester";
|
|
2119
|
+
const sessionId = searchParams.get("sessionId") ?? "0";
|
|
2120
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
2121
|
+
wss.emit("connection", ws, request);
|
|
2122
|
+
const rpc = setupClient(sessionId, ws);
|
|
2123
|
+
const state = server.state;
|
|
2124
|
+
const clients = type === "tester" ? state.testers : state.orchestrators;
|
|
2125
|
+
clients.set(sessionId, rpc);
|
|
2126
|
+
debug$1?.("[%s] Browser API connected to %s", sessionId, type);
|
|
2127
|
+
ws.on("close", () => {
|
|
2128
|
+
debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
|
|
2129
|
+
clients.delete(sessionId);
|
|
2130
|
+
server.state.removeCDPHandler(sessionId);
|
|
2131
|
+
});
|
|
2132
|
+
});
|
|
2133
|
+
});
|
|
2134
|
+
function checkFileAccess(path) {
|
|
2135
|
+
if (!isFileServingAllowed(path, vite)) {
|
|
2136
|
+
throw new Error(
|
|
2137
|
+
`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
|
|
2138
|
+
);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
function setupClient(sessionId, ws) {
|
|
2142
|
+
const mockResolver = new ServerMockResolver(server.vite, {
|
|
2143
|
+
moduleDirectories: project.config.server?.deps?.moduleDirectories
|
|
2144
|
+
});
|
|
2145
|
+
const rpc = createBirpc(
|
|
2146
|
+
{
|
|
2147
|
+
async onUnhandledError(error, type) {
|
|
2148
|
+
if (error && typeof error === "object") {
|
|
2149
|
+
const _error = error;
|
|
2150
|
+
_error.stacks = server.parseErrorStacktrace(_error);
|
|
2002
2151
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
);
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2152
|
+
ctx.state.catchError(error, type);
|
|
2153
|
+
},
|
|
2154
|
+
async onCollected(files) {
|
|
2155
|
+
ctx.state.collectFiles(project, files);
|
|
2156
|
+
await ctx.report("onCollected", files);
|
|
2157
|
+
},
|
|
2158
|
+
async onTaskUpdate(packs) {
|
|
2159
|
+
ctx.state.updateTasks(packs);
|
|
2160
|
+
await ctx.report("onTaskUpdate", packs);
|
|
2161
|
+
},
|
|
2162
|
+
onAfterSuiteRun(meta) {
|
|
2163
|
+
ctx.coverageProvider?.onAfterSuiteRun(meta);
|
|
2164
|
+
},
|
|
2165
|
+
sendLog(log) {
|
|
2166
|
+
return ctx.report("onUserConsoleLog", log);
|
|
2167
|
+
},
|
|
2168
|
+
resolveSnapshotPath(testPath) {
|
|
2169
|
+
return ctx.snapshot.resolvePath(testPath);
|
|
2170
|
+
},
|
|
2171
|
+
resolveSnapshotRawPath(testPath, rawPath) {
|
|
2172
|
+
return ctx.snapshot.resolveRawPath(testPath, rawPath);
|
|
2173
|
+
},
|
|
2174
|
+
snapshotSaved(snapshot) {
|
|
2175
|
+
ctx.snapshot.add(snapshot);
|
|
2176
|
+
},
|
|
2177
|
+
async readSnapshotFile(snapshotPath) {
|
|
2178
|
+
checkFileAccess(snapshotPath);
|
|
2179
|
+
if (!existsSync(snapshotPath)) {
|
|
2180
|
+
return null;
|
|
2014
2181
|
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2182
|
+
return promises.readFile(snapshotPath, "utf-8");
|
|
2183
|
+
},
|
|
2184
|
+
async saveSnapshotFile(id, content) {
|
|
2185
|
+
checkFileAccess(id);
|
|
2186
|
+
await promises.mkdir(dirname(id), { recursive: true });
|
|
2187
|
+
return promises.writeFile(id, content, "utf-8");
|
|
2188
|
+
},
|
|
2189
|
+
async removeSnapshotFile(id) {
|
|
2190
|
+
checkFileAccess(id);
|
|
2191
|
+
if (!existsSync(id)) {
|
|
2192
|
+
throw new Error(`Snapshot file "${id}" does not exist.`);
|
|
2025
2193
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
)
|
|
2048
|
-
|
|
2049
|
-
const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
|
|
2050
|
-
if (screenshotFailures) {
|
|
2051
|
-
server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
|
|
2052
|
-
if (!req.url || !browserServer.provider) {
|
|
2053
|
-
res.statusCode = 404;
|
|
2054
|
-
res.end();
|
|
2055
|
-
return;
|
|
2056
|
-
}
|
|
2057
|
-
const url = new URL(req.url, "http://localhost");
|
|
2058
|
-
const file = url.searchParams.get("file");
|
|
2059
|
-
if (!file) {
|
|
2060
|
-
res.statusCode = 404;
|
|
2061
|
-
res.end();
|
|
2062
|
-
return;
|
|
2063
|
-
}
|
|
2064
|
-
let stat;
|
|
2065
|
-
try {
|
|
2066
|
-
stat = lstatSync(file);
|
|
2067
|
-
} catch {
|
|
2068
|
-
}
|
|
2069
|
-
if (!stat?.isFile()) {
|
|
2070
|
-
res.statusCode = 404;
|
|
2071
|
-
res.end();
|
|
2072
|
-
return;
|
|
2073
|
-
}
|
|
2074
|
-
const ext = extname(file);
|
|
2075
|
-
const buffer = readFileSync(file);
|
|
2076
|
-
res.setHeader(
|
|
2077
|
-
"Cache-Control",
|
|
2078
|
-
"public,max-age=0,must-revalidate"
|
|
2079
|
-
);
|
|
2080
|
-
res.setHeader("Content-Length", buffer.length);
|
|
2081
|
-
res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
|
|
2082
|
-
res.end(buffer);
|
|
2083
|
-
});
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
},
|
|
2087
|
-
{
|
|
2088
|
-
name: "vitest:browser:tests",
|
|
2089
|
-
enforce: "pre",
|
|
2090
|
-
async config() {
|
|
2091
|
-
const { testFiles: allTestFiles } = await project.globTestFiles();
|
|
2092
|
-
const browserTestFiles = allTestFiles.filter(
|
|
2093
|
-
(file) => getFilePoolName(project, file) === "browser"
|
|
2094
|
-
);
|
|
2095
|
-
const setupFiles = toArray(project.config.setupFiles);
|
|
2096
|
-
const define = {};
|
|
2097
|
-
for (const env in project.config.env || {}) {
|
|
2098
|
-
const stringValue = JSON.stringify(project.config.env[env]);
|
|
2099
|
-
define[`process.env.${env}`] = stringValue;
|
|
2100
|
-
define[`import.meta.env.${env}`] = stringValue;
|
|
2101
|
-
}
|
|
2102
|
-
const entries = [
|
|
2103
|
-
...browserTestFiles,
|
|
2104
|
-
...setupFiles,
|
|
2105
|
-
resolve(distDir, "index.js"),
|
|
2106
|
-
resolve(distDir, "browser.js"),
|
|
2107
|
-
resolve(distDir, "runners.js"),
|
|
2108
|
-
resolve(distDir, "utils.js"),
|
|
2109
|
-
...project.config.snapshotSerializers || []
|
|
2110
|
-
];
|
|
2111
|
-
const exclude = [
|
|
2112
|
-
"vitest",
|
|
2113
|
-
"vitest/utils",
|
|
2114
|
-
"vitest/browser",
|
|
2115
|
-
"vitest/runners",
|
|
2116
|
-
"@vitest/browser",
|
|
2117
|
-
"@vitest/browser/client",
|
|
2118
|
-
"@vitest/utils",
|
|
2119
|
-
"@vitest/utils/source-map",
|
|
2120
|
-
"@vitest/runner",
|
|
2121
|
-
"@vitest/spy",
|
|
2122
|
-
"@vitest/utils/error",
|
|
2123
|
-
"@vitest/snapshot",
|
|
2124
|
-
"@vitest/expect",
|
|
2125
|
-
"std-env",
|
|
2126
|
-
"tinybench",
|
|
2127
|
-
"tinyspy",
|
|
2128
|
-
"tinyrainbow",
|
|
2129
|
-
"pathe",
|
|
2130
|
-
"msw",
|
|
2131
|
-
"msw/browser"
|
|
2132
|
-
];
|
|
2133
|
-
if (project.config.diff) {
|
|
2134
|
-
entries.push(project.config.diff);
|
|
2135
|
-
}
|
|
2136
|
-
if (project.ctx.coverageProvider) {
|
|
2137
|
-
const coverage = project.ctx.config.coverage;
|
|
2138
|
-
const provider = coverage.provider;
|
|
2139
|
-
if (provider === "v8") {
|
|
2140
|
-
const path = tryResolve("@vitest/coverage-v8", [project.ctx.config.root]);
|
|
2141
|
-
if (path) {
|
|
2142
|
-
entries.push(path);
|
|
2143
|
-
exclude.push("@vitest/coverage-v8/browser");
|
|
2144
|
-
}
|
|
2145
|
-
} else if (provider === "istanbul") {
|
|
2146
|
-
const path = tryResolve("@vitest/coverage-istanbul", [project.ctx.config.root]);
|
|
2147
|
-
if (path) {
|
|
2148
|
-
entries.push(path);
|
|
2149
|
-
exclude.push("@vitest/coverage-istanbul");
|
|
2150
|
-
}
|
|
2151
|
-
} else if (provider === "custom" && coverage.customProviderModule) {
|
|
2152
|
-
entries.push(coverage.customProviderModule);
|
|
2194
|
+
return promises.unlink(id);
|
|
2195
|
+
},
|
|
2196
|
+
getBrowserFileSourceMap(id) {
|
|
2197
|
+
const mod = server.vite.moduleGraph.getModuleById(id);
|
|
2198
|
+
return mod?.transformResult?.map;
|
|
2199
|
+
},
|
|
2200
|
+
onCancel(reason) {
|
|
2201
|
+
ctx.cancelCurrentRun(reason);
|
|
2202
|
+
},
|
|
2203
|
+
async resolveId(id, importer) {
|
|
2204
|
+
return mockResolver.resolveId(id, importer);
|
|
2205
|
+
},
|
|
2206
|
+
debug(...args) {
|
|
2207
|
+
ctx.logger.console.debug(...args);
|
|
2208
|
+
},
|
|
2209
|
+
getCountOfFailedTests() {
|
|
2210
|
+
return ctx.state.getCountOfFailedTests();
|
|
2211
|
+
},
|
|
2212
|
+
async triggerCommand(contextId, command, testPath, payload) {
|
|
2213
|
+
debug$1?.('[%s] Triggering command "%s"', contextId, command);
|
|
2214
|
+
const provider = server.provider;
|
|
2215
|
+
if (!provider) {
|
|
2216
|
+
throw new Error("Commands are only available for browser tests.");
|
|
2153
2217
|
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
"vitest > chai",
|
|
2158
|
-
"vitest > chai > loupe",
|
|
2159
|
-
"vitest > @vitest/utils > loupe",
|
|
2160
|
-
"@vitest/browser > @testing-library/user-event",
|
|
2161
|
-
"@vitest/browser > @testing-library/dom"
|
|
2162
|
-
];
|
|
2163
|
-
const react = tryResolve("vitest-browser-react", [project.ctx.config.root]);
|
|
2164
|
-
if (react) {
|
|
2165
|
-
include.push(react);
|
|
2166
|
-
}
|
|
2167
|
-
const vue = tryResolve("vitest-browser-vue", [project.ctx.config.root]);
|
|
2168
|
-
if (vue) {
|
|
2169
|
-
include.push(vue);
|
|
2170
|
-
}
|
|
2171
|
-
const svelte = tryResolve("vitest-browser-svelte", [project.ctx.config.root]);
|
|
2172
|
-
if (svelte) {
|
|
2173
|
-
exclude.push(svelte);
|
|
2174
|
-
}
|
|
2175
|
-
return {
|
|
2176
|
-
define,
|
|
2177
|
-
resolve: {
|
|
2178
|
-
dedupe: ["vitest"]
|
|
2179
|
-
},
|
|
2180
|
-
optimizeDeps: {
|
|
2181
|
-
entries,
|
|
2182
|
-
exclude,
|
|
2183
|
-
include
|
|
2218
|
+
const commands = project.config.browser?.commands;
|
|
2219
|
+
if (!commands || !commands[command]) {
|
|
2220
|
+
throw new Error(`Unknown command "${command}".`);
|
|
2184
2221
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
return
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
+
if (provider.beforeCommand) {
|
|
2223
|
+
await provider.beforeCommand(command, payload);
|
|
2224
|
+
}
|
|
2225
|
+
const context = Object.assign(
|
|
2226
|
+
{
|
|
2227
|
+
testPath,
|
|
2228
|
+
project,
|
|
2229
|
+
provider,
|
|
2230
|
+
contextId
|
|
2231
|
+
},
|
|
2232
|
+
provider.getCommandsContext(contextId)
|
|
2233
|
+
);
|
|
2234
|
+
let result;
|
|
2235
|
+
try {
|
|
2236
|
+
result = await commands[command](context, ...payload);
|
|
2237
|
+
} finally {
|
|
2238
|
+
if (provider.afterCommand) {
|
|
2239
|
+
await provider.afterCommand(command, payload);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
return result;
|
|
2243
|
+
},
|
|
2244
|
+
finishBrowserTests(contextId) {
|
|
2245
|
+
debug$1?.("[%s] Finishing browser tests for context", contextId);
|
|
2246
|
+
return server.state.getContext(contextId)?.resolve();
|
|
2247
|
+
},
|
|
2248
|
+
resolveMock(rawId, importer, options) {
|
|
2249
|
+
return mockResolver.resolveMock(rawId, importer, options);
|
|
2250
|
+
},
|
|
2251
|
+
invalidate(ids) {
|
|
2252
|
+
return mockResolver.invalidate(ids);
|
|
2253
|
+
},
|
|
2254
|
+
// CDP
|
|
2255
|
+
async sendCdpEvent(contextId, event, payload) {
|
|
2256
|
+
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
2257
|
+
return cdp.send(event, payload);
|
|
2258
|
+
},
|
|
2259
|
+
async trackCdpEvent(contextId, type, event, listenerId) {
|
|
2260
|
+
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
2261
|
+
cdp[type](event, listenerId);
|
|
2222
2262
|
}
|
|
2223
2263
|
},
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2264
|
+
{
|
|
2265
|
+
post: (msg) => ws.send(msg),
|
|
2266
|
+
on: (fn) => ws.on("message", fn),
|
|
2267
|
+
eventNames: ["onCancel", "cdpEvent"],
|
|
2268
|
+
serialize: (data) => stringify(data, stringifyReplace),
|
|
2269
|
+
deserialize: parse,
|
|
2270
|
+
onTimeoutError(functionName) {
|
|
2271
|
+
throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
|
|
2228
2272
|
}
|
|
2229
2273
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2274
|
+
);
|
|
2275
|
+
ctx.onCancel((reason) => rpc.onCancel(reason));
|
|
2276
|
+
return rpc;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
function cloneByOwnProperties(value) {
|
|
2280
|
+
return Object.getOwnPropertyNames(value).reduce(
|
|
2281
|
+
(clone, prop) => ({
|
|
2282
|
+
...clone,
|
|
2283
|
+
[prop]: value[prop]
|
|
2234
2284
|
}),
|
|
2235
|
-
{
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2285
|
+
{}
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
function stringifyReplace(key, value) {
|
|
2289
|
+
if (value instanceof Error) {
|
|
2290
|
+
const cloned = cloneByOwnProperties(value);
|
|
2291
|
+
return {
|
|
2292
|
+
name: value.name,
|
|
2293
|
+
message: value.message,
|
|
2294
|
+
stack: value.stack,
|
|
2295
|
+
...cloned
|
|
2296
|
+
};
|
|
2297
|
+
} else {
|
|
2298
|
+
return value;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
class BrowserServerCDPHandler {
|
|
2303
|
+
constructor(session, tester) {
|
|
2304
|
+
this.session = session;
|
|
2305
|
+
this.tester = tester;
|
|
2306
|
+
}
|
|
2307
|
+
listenerIds = {};
|
|
2308
|
+
listeners = {};
|
|
2309
|
+
send(method, params) {
|
|
2310
|
+
return this.session.send(method, params);
|
|
2311
|
+
}
|
|
2312
|
+
on(event, id, once = false) {
|
|
2313
|
+
if (!this.listenerIds[event]) {
|
|
2314
|
+
this.listenerIds[event] = [];
|
|
2315
|
+
}
|
|
2316
|
+
this.listenerIds[event].push(id);
|
|
2317
|
+
if (!this.listeners[event]) {
|
|
2318
|
+
this.listeners[event] = (payload) => {
|
|
2319
|
+
this.tester.cdpEvent(
|
|
2320
|
+
event,
|
|
2321
|
+
payload
|
|
2264
2322
|
);
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2323
|
+
if (once) {
|
|
2324
|
+
this.off(event, id);
|
|
2325
|
+
}
|
|
2326
|
+
};
|
|
2327
|
+
this.session.on(event, this.listeners[event]);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
off(event, id) {
|
|
2331
|
+
if (!this.listenerIds[event]) {
|
|
2332
|
+
this.listenerIds[event] = [];
|
|
2333
|
+
}
|
|
2334
|
+
this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
|
|
2335
|
+
if (!this.listenerIds[event].length) {
|
|
2336
|
+
this.session.off(event, this.listeners[event]);
|
|
2337
|
+
delete this.listeners[event];
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
once(event, listener) {
|
|
2341
|
+
this.on(event, listener, true);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
class BrowserServerState {
|
|
2346
|
+
orchestrators = /* @__PURE__ */ new Map();
|
|
2347
|
+
testers = /* @__PURE__ */ new Map();
|
|
2348
|
+
cdps = /* @__PURE__ */ new Map();
|
|
2349
|
+
contexts = /* @__PURE__ */ new Map();
|
|
2350
|
+
getContext(contextId) {
|
|
2351
|
+
return this.contexts.get(contextId);
|
|
2352
|
+
}
|
|
2353
|
+
createAsyncContext(method, contextId, files) {
|
|
2354
|
+
const defer = createDefer();
|
|
2355
|
+
this.contexts.set(contextId, {
|
|
2356
|
+
files,
|
|
2357
|
+
method,
|
|
2358
|
+
resolve: () => {
|
|
2359
|
+
defer.resolve();
|
|
2360
|
+
this.contexts.delete(contextId);
|
|
2361
|
+
},
|
|
2362
|
+
reject: defer.reject
|
|
2363
|
+
});
|
|
2364
|
+
return defer;
|
|
2365
|
+
}
|
|
2366
|
+
async removeCDPHandler(sessionId) {
|
|
2367
|
+
this.cdps.delete(sessionId);
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
class BrowserServer {
|
|
2372
|
+
constructor(project, base) {
|
|
2373
|
+
this.project = project;
|
|
2374
|
+
this.base = base;
|
|
2375
|
+
this.stackTraceOptions = {
|
|
2376
|
+
frameFilter: project.config.onStackTrace,
|
|
2377
|
+
getSourceMap: (id) => {
|
|
2378
|
+
const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
|
|
2379
|
+
return result?.map;
|
|
2380
|
+
},
|
|
2381
|
+
getFileName: (id) => {
|
|
2382
|
+
const mod = this.vite.moduleGraph.getModuleById(id);
|
|
2383
|
+
if (mod?.file) {
|
|
2384
|
+
return mod.file;
|
|
2277
2385
|
}
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
`
|
|
2282
|
-
);
|
|
2283
|
-
return {
|
|
2284
|
-
code: s.toString(),
|
|
2285
|
-
map: s.generateMap({ hires: true })
|
|
2286
|
-
};
|
|
2287
|
-
}
|
|
2288
|
-
},
|
|
2289
|
-
{
|
|
2290
|
-
name: "vitest:browser:worker",
|
|
2291
|
-
transform(code, id, _options) {
|
|
2292
|
-
if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
|
|
2293
|
-
const s = new MagicString(code);
|
|
2294
|
-
s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
|
|
2295
|
-
return {
|
|
2296
|
-
code: s.toString(),
|
|
2297
|
-
map: s.generateMap({ hires: "boundary" })
|
|
2298
|
-
};
|
|
2386
|
+
const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
|
|
2387
|
+
if (modUrl?.file) {
|
|
2388
|
+
return modUrl.file;
|
|
2299
2389
|
}
|
|
2390
|
+
return id;
|
|
2300
2391
|
}
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2392
|
+
};
|
|
2393
|
+
this.state = new BrowserServerState();
|
|
2394
|
+
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
2395
|
+
const distRoot = resolve(pkgRoot, "dist");
|
|
2396
|
+
this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
|
|
2397
|
+
this.faviconUrl = `${base}__vitest__/favicon.svg`;
|
|
2398
|
+
this.manifest = (async () => {
|
|
2399
|
+
return JSON.parse(
|
|
2400
|
+
await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
|
|
2401
|
+
);
|
|
2402
|
+
})().then((manifest) => this.manifest = manifest);
|
|
2403
|
+
const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
|
|
2404
|
+
if (!existsSync(testerHtmlPath)) {
|
|
2405
|
+
throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
|
|
2406
|
+
}
|
|
2407
|
+
this.testerFilepath = testerHtmlPath;
|
|
2408
|
+
this.testerHtml = readFile$1(
|
|
2409
|
+
testerHtmlPath,
|
|
2410
|
+
"utf8"
|
|
2411
|
+
).then((html) => this.testerHtml = html);
|
|
2412
|
+
this.orchestratorHtml = (project.config.browser.ui ? readFile$1(resolve(distRoot, "client/__vitest__/index.html"), "utf8") : readFile$1(resolve(distRoot, "client/orchestrator.html"), "utf8")).then((html) => this.orchestratorHtml = html);
|
|
2413
|
+
this.injectorJs = readFile$1(
|
|
2414
|
+
resolve(distRoot, "client/esm-client-injector.js"),
|
|
2415
|
+
"utf8"
|
|
2416
|
+
).then((js) => this.injectorJs = js);
|
|
2417
|
+
this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
|
|
2418
|
+
const builtinProviders = ["playwright", "webdriverio", "preview"];
|
|
2419
|
+
const providerName = project.config.browser.provider || "preview";
|
|
2420
|
+
if (builtinProviders.includes(providerName)) {
|
|
2421
|
+
this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
|
|
2422
|
+
}
|
|
2423
|
+
this.stateJs = readFile$1(
|
|
2424
|
+
resolve(distRoot, "state.js"),
|
|
2425
|
+
"utf-8"
|
|
2426
|
+
).then((js) => this.stateJs = js);
|
|
2427
|
+
}
|
|
2428
|
+
faviconUrl;
|
|
2429
|
+
prefixTesterUrl;
|
|
2430
|
+
orchestratorScripts;
|
|
2431
|
+
testerScripts;
|
|
2432
|
+
manifest;
|
|
2433
|
+
testerHtml;
|
|
2434
|
+
testerFilepath;
|
|
2435
|
+
orchestratorHtml;
|
|
2436
|
+
injectorJs;
|
|
2437
|
+
errorCatcherUrl;
|
|
2438
|
+
locatorsUrl;
|
|
2439
|
+
stateJs;
|
|
2440
|
+
state;
|
|
2441
|
+
provider;
|
|
2442
|
+
vite;
|
|
2443
|
+
stackTraceOptions;
|
|
2444
|
+
setServer(server) {
|
|
2445
|
+
this.vite = server;
|
|
2446
|
+
}
|
|
2447
|
+
getSerializableConfig() {
|
|
2448
|
+
const config = wrapConfig(this.project.getSerializableConfig());
|
|
2449
|
+
config.env ??= {};
|
|
2450
|
+
config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
|
|
2451
|
+
return config;
|
|
2452
|
+
}
|
|
2453
|
+
resolveTesterUrl(pathname) {
|
|
2454
|
+
const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
|
|
2455
|
+
const decodedTestFile = decodeURIComponent(testFile);
|
|
2456
|
+
return { contextId, testFile: decodedTestFile };
|
|
2457
|
+
}
|
|
2458
|
+
async formatScripts(scripts) {
|
|
2459
|
+
if (!scripts?.length) {
|
|
2460
|
+
return [];
|
|
2461
|
+
}
|
|
2462
|
+
const server = this.vite;
|
|
2463
|
+
const promises = scripts.map(
|
|
2464
|
+
async ({ content, src, async, id, type = "module" }, index) => {
|
|
2465
|
+
const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
|
|
2466
|
+
const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
|
|
2467
|
+
await server.moduleGraph.ensureEntryFromUrl(transformId);
|
|
2468
|
+
const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
|
|
2306
2469
|
return {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
"process.env.STL_SKIP_AUTO_CLEANUP": !!process.env.STL_SKIP_AUTO_CLEANUP,
|
|
2315
|
-
// testing-library/vue
|
|
2316
|
-
"process.env.VTL_SKIP_AUTO_CLEANUP": !!process.env.VTL_SKIP_AUTO_CLEANUP,
|
|
2317
|
-
// dom.debug()
|
|
2318
|
-
"process.env.DEBUG_PRINT_LIMIT": process.env.DEBUG_PRINT_LIMIT || 7e3
|
|
2470
|
+
tag: "script",
|
|
2471
|
+
attrs: {
|
|
2472
|
+
type,
|
|
2473
|
+
...async ? { async: "" } : {},
|
|
2474
|
+
...srcLink ? {
|
|
2475
|
+
src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`)
|
|
2476
|
+
} : {}
|
|
2319
2477
|
},
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
plugins: [
|
|
2323
|
-
{
|
|
2324
|
-
name: "test-utils-rewrite",
|
|
2325
|
-
setup(build) {
|
|
2326
|
-
build.onResolve({ filter: /@vue\/test-utils/ }, (args) => {
|
|
2327
|
-
const _require2 = getRequire();
|
|
2328
|
-
const resolved = _require2.resolve(args.path, {
|
|
2329
|
-
paths: [args.importer]
|
|
2330
|
-
});
|
|
2331
|
-
return { path: resolved };
|
|
2332
|
-
});
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
]
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2478
|
+
injectTo: "head",
|
|
2479
|
+
children: contentProcessed || ""
|
|
2338
2480
|
};
|
|
2339
2481
|
}
|
|
2482
|
+
);
|
|
2483
|
+
return await Promise.all(promises);
|
|
2484
|
+
}
|
|
2485
|
+
async initBrowserProvider() {
|
|
2486
|
+
if (this.provider) {
|
|
2487
|
+
return;
|
|
2340
2488
|
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2489
|
+
const Provider = await getBrowserProvider(this.project.config.browser, this.project);
|
|
2490
|
+
this.provider = new Provider();
|
|
2491
|
+
const browser = this.project.config.browser.name;
|
|
2492
|
+
if (!browser) {
|
|
2493
|
+
throw new Error(
|
|
2494
|
+
`[${this.project.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2497
|
+
const supportedBrowsers = this.provider.getSupportedBrowsers();
|
|
2498
|
+
if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
|
|
2499
|
+
throw new Error(
|
|
2500
|
+
`[${this.project.getName()}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
const providerOptions = this.project.config.browser.providerOptions;
|
|
2504
|
+
await this.provider.initialize(this.project, {
|
|
2505
|
+
browser,
|
|
2506
|
+
options: providerOptions
|
|
2507
|
+
});
|
|
2349
2508
|
}
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2509
|
+
parseErrorStacktrace(e, options = {}) {
|
|
2510
|
+
return parseErrorStacktrace(e, {
|
|
2511
|
+
...this.stackTraceOptions,
|
|
2512
|
+
...options
|
|
2513
|
+
});
|
|
2355
2514
|
}
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2515
|
+
parseStacktrace(trace, options = {}) {
|
|
2516
|
+
return parseStacktrace(trace, {
|
|
2517
|
+
...this.stackTraceOptions,
|
|
2518
|
+
...options
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
cdpSessionsPromises = /* @__PURE__ */ new Map();
|
|
2522
|
+
async ensureCDPHandler(contextId, sessionId) {
|
|
2523
|
+
const cachedHandler = this.state.cdps.get(sessionId);
|
|
2524
|
+
if (cachedHandler) {
|
|
2525
|
+
return cachedHandler;
|
|
2363
2526
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2527
|
+
const provider = this.provider;
|
|
2528
|
+
if (!provider.getCDPSession) {
|
|
2529
|
+
throw new Error(`CDP is not supported by the provider "${provider.name}".`);
|
|
2530
|
+
}
|
|
2531
|
+
const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
|
|
2532
|
+
const promise2 = provider.getCDPSession(contextId).finally(() => {
|
|
2533
|
+
this.cdpSessionsPromises.delete(sessionId);
|
|
2534
|
+
});
|
|
2535
|
+
this.cdpSessionsPromises.set(sessionId, promise2);
|
|
2536
|
+
return promise2;
|
|
2537
|
+
})();
|
|
2538
|
+
const session = await promise;
|
|
2539
|
+
const rpc = this.state.testers.get(sessionId);
|
|
2540
|
+
if (!rpc) {
|
|
2541
|
+
throw new Error(`Tester RPC "${sessionId}" was not established.`);
|
|
2542
|
+
}
|
|
2543
|
+
const handler = new BrowserServerCDPHandler(session, rpc);
|
|
2544
|
+
this.state.cdps.set(
|
|
2545
|
+
sessionId,
|
|
2546
|
+
handler
|
|
2547
|
+
);
|
|
2548
|
+
return handler;
|
|
2368
2549
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
|
|
2372
|
-
);
|
|
2373
|
-
const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
|
|
2374
|
-
if (!subdir || typeof subdir !== "string") {
|
|
2375
|
-
return [root, `/${basename(root)}/`];
|
|
2550
|
+
async close() {
|
|
2551
|
+
await this.vite.close();
|
|
2376
2552
|
}
|
|
2377
|
-
return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
|
|
2378
2553
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2554
|
+
function wrapConfig(config) {
|
|
2555
|
+
return {
|
|
2556
|
+
...config,
|
|
2557
|
+
// workaround RegExp serialization
|
|
2558
|
+
testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
|
|
2559
|
+
};
|
|
2382
2560
|
}
|
|
2383
2561
|
|
|
2384
2562
|
const debug = createDebugger("vitest:browser:pool");
|
|
@@ -2439,8 +2617,13 @@ function createBrowserPool(ctx) {
|
|
|
2439
2617
|
[...files2.map((f) => relative(project.config.root, f))].join(", ")
|
|
2440
2618
|
);
|
|
2441
2619
|
const promise = waitForTests(method, contextId, project, files2);
|
|
2442
|
-
|
|
2443
|
-
|
|
2620
|
+
const tester = orchestrator.createTesters(files2).catch((error) => {
|
|
2621
|
+
if (error instanceof Error && error.message.startsWith("[birpc] rpc is closed")) {
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
return Promise.reject(error);
|
|
2625
|
+
});
|
|
2626
|
+
promises.push(promise, tester);
|
|
2444
2627
|
} else {
|
|
2445
2628
|
const contextId = crypto.randomUUID();
|
|
2446
2629
|
const waitPromise = waitForTests(method, contextId, project, files2);
|
|
@@ -2451,8 +2634,8 @@ function createBrowserPool(ctx) {
|
|
|
2451
2634
|
);
|
|
2452
2635
|
const url = new URL("/", origin);
|
|
2453
2636
|
url.searchParams.set("contextId", contextId);
|
|
2454
|
-
const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0]))
|
|
2455
|
-
promises.push(page);
|
|
2637
|
+
const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0]));
|
|
2638
|
+
promises.push(page, waitPromise);
|
|
2456
2639
|
}
|
|
2457
2640
|
});
|
|
2458
2641
|
await Promise.all(promises);
|
|
@@ -2472,6 +2655,7 @@ function createBrowserPool(ctx) {
|
|
|
2472
2655
|
if (isCancelled) {
|
|
2473
2656
|
break;
|
|
2474
2657
|
}
|
|
2658
|
+
await project.initBrowserProvider();
|
|
2475
2659
|
await executeTests(method, project, files);
|
|
2476
2660
|
}
|
|
2477
2661
|
};
|
|
@@ -2491,6 +2675,11 @@ function createBrowserPool(ctx) {
|
|
|
2491
2675
|
async close() {
|
|
2492
2676
|
await Promise.all([...providers].map((provider) => provider.close()));
|
|
2493
2677
|
providers.clear();
|
|
2678
|
+
ctx.resolvedProjects.forEach((project) => {
|
|
2679
|
+
project.browser?.state.orchestrators.forEach((orchestrator) => {
|
|
2680
|
+
orchestrator.$close();
|
|
2681
|
+
});
|
|
2682
|
+
});
|
|
2494
2683
|
},
|
|
2495
2684
|
runTests: (files) => runWorkspaceTests("run", files),
|
|
2496
2685
|
collectTests: (files) => runWorkspaceTests("collect", files)
|
|
@@ -2513,7 +2702,9 @@ Update your dependencies and make sure the versions match.`
|
|
|
2513
2702
|
const server = new BrowserServer(project, "/");
|
|
2514
2703
|
const configPath = typeof configFile === "string" ? configFile : false;
|
|
2515
2704
|
const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
|
|
2516
|
-
const logger = createViteLogger(logLevel
|
|
2705
|
+
const logger = createViteLogger(project.logger, logLevel, {
|
|
2706
|
+
allowClearScreen: false
|
|
2707
|
+
});
|
|
2517
2708
|
const vite = await createViteServer({
|
|
2518
2709
|
...project.options,
|
|
2519
2710
|
// spread project config inlined in root workspace config
|
|
@@ -2556,4 +2747,4 @@ ${c.bold("[vitest]")} Vite unexpectedly reloaded a test. This may cause tests to
|
|
|
2556
2747
|
return server;
|
|
2557
2748
|
}
|
|
2558
2749
|
|
|
2559
|
-
export { createBrowserPool, createBrowserServer };
|
|
2750
|
+
export { createBrowserPool, createBrowserServer, distRoot };
|