@vitest/browser 2.0.0-beta.11 → 2.0.0-beta.13
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/context.d.ts +26 -5
- package/dist/client/.vite/manifest.json +7 -7
- package/dist/client/__vitest__/assets/index-C-LD6Fid.js +52 -0
- package/dist/client/__vitest__/assets/index-DEM1IsBG.css +1 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/{rpc-D6HtJ5Rb.js → client-dLyjuL0K.js} +34 -133
- package/dist/client/__vitest_browser__/{orchestrator-DQ4hbmZ_.js → orchestrator-x0A1t8rC.js} +193 -162
- package/dist/client/__vitest_browser__/tester-BdcP5piS.js +12070 -0
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/tester.html +2 -2
- package/dist/context.js +82 -51
- package/dist/index.d.ts +30 -4
- package/dist/index.js +344 -98
- package/dist/providers.js +2 -2
- package/dist/state.js +1 -1
- package/dist/{webdriver-BRud6NtS.js → webdriver-BdVqnfdE.js} +75 -6
- package/jest-dom.d.ts +816 -0
- package/matchers.d.ts +22 -0
- package/package.json +15 -11
- package/providers/playwright.d.ts +25 -1
- package/providers/webdriverio.d.ts +1 -0
- package/dist/client/__vitest__/assets/index-BfnqOMHY.js +0 -51
- package/dist/client/__vitest__/assets/index-qZYZB8Y3.css +0 -1
- package/dist/client/__vitest_browser__/tester-IF8AbWCS.js +0 -837
package/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { createDebugger, isFileServingAllowed as isFileServingAllowed$1, getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir, createServer } from 'vitest/node';
|
|
2
|
-
import fs, { existsSync, readdirSync,
|
|
2
|
+
import fs, { existsSync, readdirSync, readFileSync, promises } from 'node:fs';
|
|
3
3
|
import { WebSocketServer } from 'ws';
|
|
4
4
|
import { isFileServingAllowed } from 'vite';
|
|
5
5
|
import { builtinModules, createRequire } from 'node:module';
|
|
6
6
|
import { readFile as readFile$1, mkdir } from 'node:fs/promises';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { createDefer, slash, toArray } from '@vitest/utils';
|
|
9
|
+
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
9
10
|
import sirv from 'sirv';
|
|
10
11
|
import { defaultBrowserPort, coverageConfigDefaults } from 'vitest/config';
|
|
11
|
-
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-
|
|
12
|
+
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-BdVqnfdE.js';
|
|
12
13
|
import { resolve as resolve$1, dirname as dirname$1, normalize as normalize$1 } from 'node:path';
|
|
13
14
|
import MagicString from 'magic-string';
|
|
14
15
|
import { esmWalker } from '@vitest/utils/ast';
|
|
@@ -406,7 +407,9 @@ const stringify = (value, replacer, space) => {
|
|
|
406
407
|
async function resolveMock(project, rawId, importer, hasFactory) {
|
|
407
408
|
const { id, fsPath, external } = await resolveId(project, rawId, importer);
|
|
408
409
|
if (hasFactory) {
|
|
409
|
-
|
|
410
|
+
const needsInteropMap = viteDepsInteropMap(project.browser.vite.config);
|
|
411
|
+
const needsInterop = needsInteropMap?.get(fsPath) ?? false;
|
|
412
|
+
return { type: "factory", resolvedId: id, needsInterop };
|
|
410
413
|
}
|
|
411
414
|
const mockPath = resolveMockPath(project.config.root, fsPath, external);
|
|
412
415
|
return {
|
|
@@ -497,6 +500,29 @@ const postfixRE = /[?#].*$/;
|
|
|
497
500
|
function cleanUrl(url) {
|
|
498
501
|
return url.replace(postfixRE, "");
|
|
499
502
|
}
|
|
503
|
+
const metadata = /* @__PURE__ */ new WeakMap();
|
|
504
|
+
function viteDepsInteropMap(config) {
|
|
505
|
+
if (metadata.has(config)) {
|
|
506
|
+
return metadata.get(config);
|
|
507
|
+
}
|
|
508
|
+
const cacheDirPath = getDepsCacheDir(config);
|
|
509
|
+
const metadataPath = resolve(cacheDirPath, "_metadata.json");
|
|
510
|
+
if (!existsSync(metadataPath)) {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
const { optimized } = JSON.parse(readFileSync(metadataPath, "utf-8"));
|
|
514
|
+
const needsInteropMap = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const name in optimized) {
|
|
516
|
+
const dep = optimized[name];
|
|
517
|
+
const file = resolve(cacheDirPath, dep.file);
|
|
518
|
+
needsInteropMap.set(file, dep.needsInterop);
|
|
519
|
+
}
|
|
520
|
+
metadata.set(config, needsInteropMap);
|
|
521
|
+
return needsInteropMap;
|
|
522
|
+
}
|
|
523
|
+
function getDepsCacheDir(config) {
|
|
524
|
+
return resolve(config.cacheDir, "deps");
|
|
525
|
+
}
|
|
500
526
|
|
|
501
527
|
const debug$1 = createDebugger("vitest:browser:api");
|
|
502
528
|
const BROWSER_API_PATH = "/__vitest_browser_api__";
|
|
@@ -517,7 +543,7 @@ function setupBrowserRpc(server) {
|
|
|
517
543
|
const sessionId = searchParams.get("sessionId") ?? "0";
|
|
518
544
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
519
545
|
wss.emit("connection", ws, request);
|
|
520
|
-
const rpc = setupClient(ws);
|
|
546
|
+
const rpc = setupClient(sessionId, ws);
|
|
521
547
|
const state = server.state;
|
|
522
548
|
const clients = type === "tester" ? state.testers : state.orchestrators;
|
|
523
549
|
clients.set(sessionId, rpc);
|
|
@@ -525,6 +551,7 @@ function setupBrowserRpc(server) {
|
|
|
525
551
|
ws.on("close", () => {
|
|
526
552
|
debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
|
|
527
553
|
clients.delete(sessionId);
|
|
554
|
+
server.state.removeCDPHandler(sessionId);
|
|
528
555
|
});
|
|
529
556
|
});
|
|
530
557
|
});
|
|
@@ -535,7 +562,7 @@ function setupBrowserRpc(server) {
|
|
|
535
562
|
);
|
|
536
563
|
}
|
|
537
564
|
}
|
|
538
|
-
function setupClient(ws) {
|
|
565
|
+
function setupClient(sessionId, ws) {
|
|
539
566
|
const rpc = createBirpc(
|
|
540
567
|
{
|
|
541
568
|
async onUnhandledError(error, type) {
|
|
@@ -653,12 +680,21 @@ function setupBrowserRpc(server) {
|
|
|
653
680
|
moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true);
|
|
654
681
|
}
|
|
655
682
|
});
|
|
683
|
+
},
|
|
684
|
+
// CDP
|
|
685
|
+
async sendCdpEvent(contextId, event, payload) {
|
|
686
|
+
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
687
|
+
return cdp.send(event, payload);
|
|
688
|
+
},
|
|
689
|
+
async trackCdpEvent(contextId, type, event, listenerId) {
|
|
690
|
+
const cdp = await server.ensureCDPHandler(contextId, sessionId);
|
|
691
|
+
cdp[type](event, listenerId);
|
|
656
692
|
}
|
|
657
693
|
},
|
|
658
694
|
{
|
|
659
695
|
post: (msg) => ws.send(msg),
|
|
660
696
|
on: (fn) => ws.on("message", fn),
|
|
661
|
-
eventNames: ["onCancel"],
|
|
697
|
+
eventNames: ["onCancel", "cdpEvent"],
|
|
662
698
|
serialize: (data) => stringify(data, stringifyReplace),
|
|
663
699
|
deserialize: parse,
|
|
664
700
|
onTimeoutError(functionName) {
|
|
@@ -696,14 +732,16 @@ function stringifyReplace(key, value) {
|
|
|
696
732
|
class BrowserServerState {
|
|
697
733
|
orchestrators = /* @__PURE__ */ new Map();
|
|
698
734
|
testers = /* @__PURE__ */ new Map();
|
|
735
|
+
cdps = /* @__PURE__ */ new Map();
|
|
699
736
|
contexts = /* @__PURE__ */ new Map();
|
|
700
737
|
getContext(contextId) {
|
|
701
738
|
return this.contexts.get(contextId);
|
|
702
739
|
}
|
|
703
|
-
createAsyncContext(contextId, files) {
|
|
740
|
+
createAsyncContext(method, contextId, files) {
|
|
704
741
|
const defer = createDefer();
|
|
705
742
|
this.contexts.set(contextId, {
|
|
706
743
|
files,
|
|
744
|
+
method,
|
|
707
745
|
resolve: () => {
|
|
708
746
|
defer.resolve();
|
|
709
747
|
this.contexts.delete(contextId);
|
|
@@ -712,6 +750,9 @@ class BrowserServerState {
|
|
|
712
750
|
});
|
|
713
751
|
return defer;
|
|
714
752
|
}
|
|
753
|
+
async removeCDPHandler(sessionId) {
|
|
754
|
+
this.cdps.delete(sessionId);
|
|
755
|
+
}
|
|
715
756
|
}
|
|
716
757
|
|
|
717
758
|
function replacer(code, values) {
|
|
@@ -743,10 +784,71 @@ async function getBrowserProvider(options, project) {
|
|
|
743
784
|
return customProviderModule.default;
|
|
744
785
|
}
|
|
745
786
|
|
|
787
|
+
class BrowserServerCDPHandler {
|
|
788
|
+
constructor(session, tester) {
|
|
789
|
+
this.session = session;
|
|
790
|
+
this.tester = tester;
|
|
791
|
+
}
|
|
792
|
+
listenerIds = {};
|
|
793
|
+
listeners = {};
|
|
794
|
+
send(method, params) {
|
|
795
|
+
return this.session.send(method, params);
|
|
796
|
+
}
|
|
797
|
+
on(event, id, once = false) {
|
|
798
|
+
if (!this.listenerIds[event]) {
|
|
799
|
+
this.listenerIds[event] = [];
|
|
800
|
+
}
|
|
801
|
+
this.listenerIds[event].push(id);
|
|
802
|
+
if (!this.listeners[event]) {
|
|
803
|
+
this.listeners[event] = (payload) => {
|
|
804
|
+
this.tester.cdpEvent(
|
|
805
|
+
event,
|
|
806
|
+
payload
|
|
807
|
+
);
|
|
808
|
+
if (once) {
|
|
809
|
+
this.off(event, id);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
this.session.on(event, this.listeners[event]);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
off(event, id) {
|
|
816
|
+
if (!this.listenerIds[event]) {
|
|
817
|
+
this.listenerIds[event] = [];
|
|
818
|
+
}
|
|
819
|
+
this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
|
|
820
|
+
if (!this.listenerIds[event].length) {
|
|
821
|
+
this.session.off(event, this.listeners[event]);
|
|
822
|
+
delete this.listeners[event];
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
once(event, listener) {
|
|
826
|
+
this.on(event, listener, true);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
746
830
|
class BrowserServer {
|
|
747
831
|
constructor(project, base) {
|
|
748
832
|
this.project = project;
|
|
749
833
|
this.base = base;
|
|
834
|
+
this.stackTraceOptions = {
|
|
835
|
+
frameFilter: project.config.onStackTrace,
|
|
836
|
+
getSourceMap: (id) => {
|
|
837
|
+
const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
|
|
838
|
+
return result?.map;
|
|
839
|
+
},
|
|
840
|
+
getFileName: (id) => {
|
|
841
|
+
const mod = this.vite.moduleGraph.getModuleById(id);
|
|
842
|
+
if (mod?.file) {
|
|
843
|
+
return mod.file;
|
|
844
|
+
}
|
|
845
|
+
const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
|
|
846
|
+
if (modUrl?.file) {
|
|
847
|
+
return modUrl.file;
|
|
848
|
+
}
|
|
849
|
+
return id;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
750
852
|
this.state = new BrowserServerState();
|
|
751
853
|
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
752
854
|
const distRoot = resolve(pkgRoot, "dist");
|
|
@@ -783,6 +885,7 @@ class BrowserServer {
|
|
|
783
885
|
state;
|
|
784
886
|
provider;
|
|
785
887
|
vite;
|
|
888
|
+
stackTraceOptions;
|
|
786
889
|
setServer(server) {
|
|
787
890
|
this.vite = server;
|
|
788
891
|
}
|
|
@@ -837,6 +940,47 @@ class BrowserServer {
|
|
|
837
940
|
options: providerOptions
|
|
838
941
|
});
|
|
839
942
|
}
|
|
943
|
+
parseErrorStacktrace(e, options = {}) {
|
|
944
|
+
return parseErrorStacktrace(e, {
|
|
945
|
+
...this.stackTraceOptions,
|
|
946
|
+
...options
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
parseStacktrace(trace, options = {}) {
|
|
950
|
+
return parseStacktrace(trace, {
|
|
951
|
+
...this.stackTraceOptions,
|
|
952
|
+
...options
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
cdpSessions = /* @__PURE__ */ new Map();
|
|
956
|
+
async ensureCDPHandler(contextId, sessionId) {
|
|
957
|
+
const cachedHandler = this.state.cdps.get(sessionId);
|
|
958
|
+
if (cachedHandler) {
|
|
959
|
+
return cachedHandler;
|
|
960
|
+
}
|
|
961
|
+
const provider = this.provider;
|
|
962
|
+
if (!provider.getCDPSession) {
|
|
963
|
+
throw new Error(`CDP is not supported by the provider "${provider.name}".`);
|
|
964
|
+
}
|
|
965
|
+
const promise = this.cdpSessions.get(sessionId) ?? await (async () => {
|
|
966
|
+
const promise2 = provider.getCDPSession(contextId).finally(() => {
|
|
967
|
+
this.cdpSessions.delete(sessionId);
|
|
968
|
+
});
|
|
969
|
+
this.cdpSessions.set(sessionId, promise2);
|
|
970
|
+
return promise2;
|
|
971
|
+
})();
|
|
972
|
+
const session = await promise;
|
|
973
|
+
const rpc = this.state.testers.get(sessionId);
|
|
974
|
+
if (!rpc) {
|
|
975
|
+
throw new Error(`Tester RPC "${sessionId}" was not established.`);
|
|
976
|
+
}
|
|
977
|
+
const handler = new BrowserServerCDPHandler(session, rpc);
|
|
978
|
+
this.state.cdps.set(
|
|
979
|
+
sessionId,
|
|
980
|
+
handler
|
|
981
|
+
);
|
|
982
|
+
return handler;
|
|
983
|
+
}
|
|
840
984
|
async close() {
|
|
841
985
|
await this.vite.close();
|
|
842
986
|
}
|
|
@@ -849,35 +993,49 @@ function wrapConfig(config) {
|
|
|
849
993
|
};
|
|
850
994
|
}
|
|
851
995
|
|
|
852
|
-
const click = async (context,
|
|
996
|
+
const click = async (context, selector, options = {}) => {
|
|
853
997
|
const provider = context.provider;
|
|
854
998
|
if (provider instanceof PlaywrightBrowserProvider) {
|
|
855
|
-
const tester = context.
|
|
856
|
-
await tester.locator(`
|
|
999
|
+
const tester = context.iframe;
|
|
1000
|
+
await tester.locator(`css=${selector}`).click({
|
|
857
1001
|
timeout: 1e3,
|
|
858
1002
|
...options
|
|
859
1003
|
});
|
|
860
1004
|
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
861
1005
|
const browser = context.browser;
|
|
862
|
-
|
|
863
|
-
await browser.$(markedXpath).click(options);
|
|
1006
|
+
await browser.$(selector).click(options);
|
|
864
1007
|
} else {
|
|
865
1008
|
throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
|
|
866
1009
|
}
|
|
867
1010
|
};
|
|
868
|
-
const dblClick = async (context,
|
|
1011
|
+
const dblClick = async (context, selector, options = {}) => {
|
|
869
1012
|
const provider = context.provider;
|
|
870
1013
|
if (provider instanceof PlaywrightBrowserProvider) {
|
|
871
|
-
const tester = context.
|
|
872
|
-
await tester.locator(`
|
|
1014
|
+
const tester = context.iframe;
|
|
1015
|
+
await tester.locator(`css=${selector}`).dblclick(options);
|
|
873
1016
|
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
874
1017
|
const browser = context.browser;
|
|
875
|
-
|
|
876
|
-
await browser.$(markedXpath).doubleClick();
|
|
1018
|
+
await browser.$(selector).doubleClick();
|
|
877
1019
|
} else {
|
|
878
1020
|
throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
|
|
879
1021
|
}
|
|
880
1022
|
};
|
|
1023
|
+
const tripleClick = async (context, selector, options = {}) => {
|
|
1024
|
+
const provider = context.provider;
|
|
1025
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1026
|
+
const tester = context.iframe;
|
|
1027
|
+
await tester.locator(`css=${selector}`).click({
|
|
1028
|
+
timeout: 1e3,
|
|
1029
|
+
...options,
|
|
1030
|
+
clickCount: 3
|
|
1031
|
+
});
|
|
1032
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
1033
|
+
const browser = context.browser;
|
|
1034
|
+
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();
|
|
1035
|
+
} else {
|
|
1036
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
881
1039
|
|
|
882
1040
|
var clickableInputTypes;
|
|
883
1041
|
(function(clickableInputTypes) {
|
|
@@ -1250,7 +1408,8 @@ const keyboard = async (context, text) => {
|
|
|
1250
1408
|
}
|
|
1251
1409
|
}
|
|
1252
1410
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1253
|
-
await context.frame
|
|
1411
|
+
const frame = await context.frame();
|
|
1412
|
+
await frame.evaluate(focusIframe);
|
|
1254
1413
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1255
1414
|
await context.browser.execute(focusIframe);
|
|
1256
1415
|
}
|
|
@@ -1266,7 +1425,8 @@ const keyboard = async (context, text) => {
|
|
|
1266
1425
|
}
|
|
1267
1426
|
}
|
|
1268
1427
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1269
|
-
await context.frame
|
|
1428
|
+
const frame = await context.frame();
|
|
1429
|
+
await frame.evaluate(selectAll);
|
|
1270
1430
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1271
1431
|
await context.browser.execute(selectAll);
|
|
1272
1432
|
} else {
|
|
@@ -1347,11 +1507,11 @@ async function keyboardImplementation(provider, contextId, text, selectAll, skip
|
|
|
1347
1507
|
};
|
|
1348
1508
|
}
|
|
1349
1509
|
|
|
1350
|
-
const type = async (context,
|
|
1510
|
+
const type = async (context, selector, text, options = {}) => {
|
|
1351
1511
|
const { skipClick = false, skipAutoClose = false } = options;
|
|
1352
1512
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1353
|
-
const {
|
|
1354
|
-
const element =
|
|
1513
|
+
const { iframe } = context;
|
|
1514
|
+
const element = iframe.locator(`css=${selector}`);
|
|
1355
1515
|
if (!skipClick) {
|
|
1356
1516
|
await element.focus();
|
|
1357
1517
|
}
|
|
@@ -1364,8 +1524,7 @@ const type = async (context, xpath, text, options = {}) => {
|
|
|
1364
1524
|
);
|
|
1365
1525
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1366
1526
|
const browser = context.browser;
|
|
1367
|
-
const
|
|
1368
|
-
const element = browser.$(markedXpath);
|
|
1527
|
+
const element = browser.$(selector);
|
|
1369
1528
|
if (!skipClick && !await element.isFocused()) {
|
|
1370
1529
|
await element.click();
|
|
1371
1530
|
}
|
|
@@ -1386,47 +1545,45 @@ const type = async (context, xpath, text, options = {}) => {
|
|
|
1386
1545
|
}
|
|
1387
1546
|
};
|
|
1388
1547
|
|
|
1389
|
-
const clear = async (context,
|
|
1548
|
+
const clear = async (context, selector) => {
|
|
1390
1549
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1391
|
-
const {
|
|
1392
|
-
const element =
|
|
1550
|
+
const { iframe } = context;
|
|
1551
|
+
const element = iframe.locator(`css=${selector}`);
|
|
1393
1552
|
await element.clear({
|
|
1394
1553
|
timeout: 1e3
|
|
1395
1554
|
});
|
|
1396
1555
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1397
1556
|
const browser = context.browser;
|
|
1398
|
-
const
|
|
1399
|
-
const element = await browser.$(markedXpath);
|
|
1557
|
+
const element = await browser.$(selector);
|
|
1400
1558
|
await element.clearValue();
|
|
1401
1559
|
} else {
|
|
1402
1560
|
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
1403
1561
|
}
|
|
1404
1562
|
};
|
|
1405
1563
|
|
|
1406
|
-
const fill = async (context,
|
|
1564
|
+
const fill = async (context, selector, text, options = {}) => {
|
|
1407
1565
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1408
|
-
const {
|
|
1409
|
-
const element =
|
|
1566
|
+
const { iframe } = context;
|
|
1567
|
+
const element = iframe.locator(`css=${selector}`);
|
|
1410
1568
|
await element.fill(text, { timeout: 1e3, ...options });
|
|
1411
1569
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1412
1570
|
const browser = context.browser;
|
|
1413
|
-
|
|
1414
|
-
await browser.$(markedXpath).setValue(text);
|
|
1571
|
+
await browser.$(selector).setValue(text);
|
|
1415
1572
|
} else {
|
|
1416
1573
|
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
1417
1574
|
}
|
|
1418
1575
|
};
|
|
1419
1576
|
|
|
1420
|
-
const selectOptions = async (context,
|
|
1577
|
+
const selectOptions = async (context, selector, userValues, options = {}) => {
|
|
1421
1578
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1422
1579
|
const value = userValues;
|
|
1423
|
-
const {
|
|
1424
|
-
const selectElement =
|
|
1580
|
+
const { iframe } = context;
|
|
1581
|
+
const selectElement = iframe.locator(`css=${selector}`);
|
|
1425
1582
|
const values = await Promise.all(value.map(async (v) => {
|
|
1426
1583
|
if (typeof v === "string") {
|
|
1427
1584
|
return v;
|
|
1428
1585
|
}
|
|
1429
|
-
const elementHandler = await
|
|
1586
|
+
const elementHandler = await iframe.locator(`css=${v.element}`).elementHandle();
|
|
1430
1587
|
if (!elementHandler) {
|
|
1431
1588
|
throw new Error(`Element not found: ${v.element}`);
|
|
1432
1589
|
}
|
|
@@ -1441,10 +1598,9 @@ const selectOptions = async (context, xpath, userValues, options = {}) => {
|
|
|
1441
1598
|
if (!values.length) {
|
|
1442
1599
|
return;
|
|
1443
1600
|
}
|
|
1444
|
-
const markedXpath = `//${xpath}`;
|
|
1445
1601
|
const browser = context.browser;
|
|
1446
1602
|
if (values.length === 1 && "index" in values[0]) {
|
|
1447
|
-
const selectElement = browser.$(
|
|
1603
|
+
const selectElement = browser.$(selector);
|
|
1448
1604
|
await selectElement.selectByIndex(values[0].index);
|
|
1449
1605
|
} else {
|
|
1450
1606
|
throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
|
|
@@ -1472,19 +1628,18 @@ const tab = async (context, options = {}) => {
|
|
|
1472
1628
|
|
|
1473
1629
|
const dragAndDrop = async (context, source, target, options) => {
|
|
1474
1630
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1475
|
-
await context.frame
|
|
1476
|
-
|
|
1477
|
-
`
|
|
1631
|
+
const frame = await context.frame();
|
|
1632
|
+
await frame.dragAndDrop(
|
|
1633
|
+
`css=${source}`,
|
|
1634
|
+
`css=${target}`,
|
|
1478
1635
|
{
|
|
1479
1636
|
timeout: 1e3,
|
|
1480
1637
|
...options
|
|
1481
1638
|
}
|
|
1482
1639
|
);
|
|
1483
1640
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1484
|
-
const
|
|
1485
|
-
const
|
|
1486
|
-
const $source = context.browser.$(sourceXpath);
|
|
1487
|
-
const $target = context.browser.$(targetXpath);
|
|
1641
|
+
const $source = context.browser.$(source);
|
|
1642
|
+
const $target = context.browser.$(target);
|
|
1488
1643
|
const duration = options?.duration ?? 10;
|
|
1489
1644
|
await context.browser.action("pointer").move({ duration: 0, origin: $source, x: 0, y: 0 }).down({ button: 0 }).move({ duration: 0, origin: "pointer", x: 0, y: 0 }).pause(duration).move({ duration: 0, origin: $target, x: 0, y: 0 }).move({ duration: 0, origin: "pointer", x: 1, y: 0 }).move({ duration: 0, origin: "pointer", x: -1, y: 0 }).up({ button: 0 }).perform();
|
|
1490
1645
|
} else {
|
|
@@ -1492,16 +1647,15 @@ const dragAndDrop = async (context, source, target, options) => {
|
|
|
1492
1647
|
}
|
|
1493
1648
|
};
|
|
1494
1649
|
|
|
1495
|
-
const hover = async (context,
|
|
1650
|
+
const hover = async (context, selector, options = {}) => {
|
|
1496
1651
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1497
|
-
await context.
|
|
1652
|
+
await context.iframe.locator(`css=${selector}`).hover({
|
|
1498
1653
|
timeout: 1e3,
|
|
1499
1654
|
...options
|
|
1500
1655
|
});
|
|
1501
1656
|
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1502
1657
|
const browser = context.browser;
|
|
1503
|
-
|
|
1504
|
-
await browser.$(markedXpath).moveTo(options);
|
|
1658
|
+
await browser.$(selector).moveTo(options);
|
|
1505
1659
|
} else {
|
|
1506
1660
|
throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
|
|
1507
1661
|
}
|
|
@@ -1550,26 +1704,31 @@ const screenshot = async (context, name, options = {}) => {
|
|
|
1550
1704
|
await mkdir(dirname(path), { recursive: true });
|
|
1551
1705
|
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1552
1706
|
if (options.element) {
|
|
1553
|
-
const { element:
|
|
1554
|
-
const
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1707
|
+
const { element: css, ...config } = options;
|
|
1708
|
+
const element = context.iframe.locator(`css=${css}`);
|
|
1709
|
+
const buffer2 = await element.screenshot({
|
|
1710
|
+
timeout: 1e3,
|
|
1711
|
+
...config,
|
|
1712
|
+
path: savePath
|
|
1713
|
+
});
|
|
1714
|
+
return returnResult(options, path, buffer2);
|
|
1559
1715
|
}
|
|
1560
|
-
|
|
1716
|
+
const buffer = await context.iframe.locator("body").screenshot({
|
|
1717
|
+
...options,
|
|
1718
|
+
path: savePath
|
|
1719
|
+
});
|
|
1720
|
+
return returnResult(options, path, buffer);
|
|
1561
1721
|
}
|
|
1562
1722
|
if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1563
1723
|
const page = context.provider.browser;
|
|
1564
1724
|
if (!options.element) {
|
|
1565
1725
|
const body = await page.$("body");
|
|
1566
|
-
await body.saveScreenshot(savePath);
|
|
1567
|
-
return path;
|
|
1726
|
+
const buffer2 = await body.saveScreenshot(savePath);
|
|
1727
|
+
return returnResult(options, path, buffer2);
|
|
1568
1728
|
}
|
|
1569
|
-
const
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
return path;
|
|
1729
|
+
const element = await page.$(`${options.element}`);
|
|
1730
|
+
const buffer = await element.saveScreenshot(savePath);
|
|
1731
|
+
return returnResult(options, path, buffer);
|
|
1573
1732
|
}
|
|
1574
1733
|
throw new Error(
|
|
1575
1734
|
`Provider "${context.provider.name}" does not support screenshots`
|
|
@@ -1588,6 +1747,12 @@ function resolveScreenshotPath(testPath, name, config) {
|
|
|
1588
1747
|
}
|
|
1589
1748
|
return resolve(dir, "__screenshots__", base, name);
|
|
1590
1749
|
}
|
|
1750
|
+
function returnResult(options, path, buffer) {
|
|
1751
|
+
if (options.base64) {
|
|
1752
|
+
return { path, base64: buffer.toString("base64") };
|
|
1753
|
+
}
|
|
1754
|
+
return path;
|
|
1755
|
+
}
|
|
1591
1756
|
|
|
1592
1757
|
var builtinCommands = {
|
|
1593
1758
|
readFile,
|
|
@@ -1595,6 +1760,7 @@ var builtinCommands = {
|
|
|
1595
1760
|
writeFile,
|
|
1596
1761
|
__vitest_click: click,
|
|
1597
1762
|
__vitest_dblClick: dblClick,
|
|
1763
|
+
__vitest_tripleClick: tripleClick,
|
|
1598
1764
|
__vitest_screenshot: screenshot,
|
|
1599
1765
|
__vitest_type: type,
|
|
1600
1766
|
__vitest_clear: clear,
|
|
@@ -1650,7 +1816,7 @@ async function generateContextFile(server) {
|
|
|
1650
1816
|
);
|
|
1651
1817
|
const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
|
|
1652
1818
|
return `
|
|
1653
|
-
import { page, userEvent as __userEvent_CDP__ } from '${distContextPath}'
|
|
1819
|
+
import { page, userEvent as __userEvent_CDP__, cdp } from '${distContextPath}'
|
|
1654
1820
|
${userEventNonProviderImport}
|
|
1655
1821
|
const filepath = () => ${filepathCode}
|
|
1656
1822
|
const rpc = () => __vitest_worker__.rpc
|
|
@@ -1667,7 +1833,7 @@ export const server = {
|
|
|
1667
1833
|
}
|
|
1668
1834
|
export const commands = server.commands
|
|
1669
1835
|
export const userEvent = ${getUserEvent(provider)}
|
|
1670
|
-
export { page }
|
|
1836
|
+
export { page, cdp }
|
|
1671
1837
|
`;
|
|
1672
1838
|
}
|
|
1673
1839
|
function getUserEvent(provider) {
|
|
@@ -1713,11 +1879,13 @@ ${err.message}`);
|
|
|
1713
1879
|
onImportMeta() {
|
|
1714
1880
|
},
|
|
1715
1881
|
onDynamicImport(node) {
|
|
1716
|
-
const
|
|
1882
|
+
const replaceString = "__vitest_browser_runner__.wrapModule(() => import(";
|
|
1883
|
+
const importSubstring = code.substring(node.start, node.end);
|
|
1884
|
+
const hasIgnore = importSubstring.includes("/* @vite-ignore */");
|
|
1717
1885
|
s.overwrite(
|
|
1718
1886
|
node.start,
|
|
1719
1887
|
node.source.start,
|
|
1720
|
-
|
|
1888
|
+
replaceString + (hasIgnore ? "/* @vite-ignore */ " : "")
|
|
1721
1889
|
);
|
|
1722
1890
|
s.overwrite(node.end - 1, node.end, "))");
|
|
1723
1891
|
}
|
|
@@ -1807,7 +1975,9 @@ async function resolveTester(server, url, res) {
|
|
|
1807
1975
|
const testFiles = await project.globTestFiles();
|
|
1808
1976
|
const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
|
|
1809
1977
|
const iframeId = JSON.stringify(testFile);
|
|
1810
|
-
const
|
|
1978
|
+
const context = state.getContext(contextId);
|
|
1979
|
+
const files = context?.files ?? [];
|
|
1980
|
+
const method = context?.method ?? "run";
|
|
1811
1981
|
const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
|
|
1812
1982
|
const config = server.getSerializableConfig();
|
|
1813
1983
|
const injector = replacer(injectorJs, {
|
|
@@ -1839,7 +2009,7 @@ async function resolveTester(server, url, res) {
|
|
|
1839
2009
|
__VITEST_APPEND__: `<script type="module">
|
|
1840
2010
|
__vitest_browser_runner__.runningFiles = ${tests}
|
|
1841
2011
|
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
1842
|
-
__vitest_browser_runner__
|
|
2012
|
+
__vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
|
|
1843
2013
|
<\/script>`
|
|
1844
2014
|
});
|
|
1845
2015
|
}
|
|
@@ -1848,6 +2018,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
1848
2018
|
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
1849
2019
|
const distRoot = resolve(pkgRoot, "dist");
|
|
1850
2020
|
const project = browserServer.project;
|
|
2021
|
+
let loupePath;
|
|
1851
2022
|
return [
|
|
1852
2023
|
{
|
|
1853
2024
|
enforce: "pre",
|
|
@@ -1926,16 +2097,48 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
1926
2097
|
(file) => getFilePoolName(project, file) === "browser"
|
|
1927
2098
|
);
|
|
1928
2099
|
const setupFiles = toArray(project.config.setupFiles);
|
|
2100
|
+
const define = {};
|
|
2101
|
+
for (const env in project.config.env || {}) {
|
|
2102
|
+
const stringValue = JSON.stringify(project.config.env[env]);
|
|
2103
|
+
define[`process.env.${env}`] = stringValue;
|
|
2104
|
+
define[`import.meta.env.${env}`] = stringValue;
|
|
2105
|
+
}
|
|
2106
|
+
const entries = [
|
|
2107
|
+
...browserTestFiles,
|
|
2108
|
+
...setupFiles,
|
|
2109
|
+
resolve(distDir, "index.js"),
|
|
2110
|
+
resolve(distDir, "browser.js"),
|
|
2111
|
+
resolve(distDir, "runners.js"),
|
|
2112
|
+
resolve(distDir, "utils.js"),
|
|
2113
|
+
...project.config.snapshotSerializers || []
|
|
2114
|
+
];
|
|
2115
|
+
if (project.config.diff) {
|
|
2116
|
+
entries.push(project.config.diff);
|
|
2117
|
+
}
|
|
2118
|
+
if (project.ctx.coverageProvider) {
|
|
2119
|
+
const coverage = project.ctx.config.coverage;
|
|
2120
|
+
const provider = coverage.provider;
|
|
2121
|
+
if (provider === "v8") {
|
|
2122
|
+
const path = tryResolve("@vitest/coverage-v8", [project.ctx.config.root]);
|
|
2123
|
+
if (path) {
|
|
2124
|
+
entries.push(path);
|
|
2125
|
+
}
|
|
2126
|
+
} else if (provider === "istanbul") {
|
|
2127
|
+
const path = tryResolve("@vitest/coverage-istanbul", [project.ctx.config.root]);
|
|
2128
|
+
if (path) {
|
|
2129
|
+
entries.push(path);
|
|
2130
|
+
}
|
|
2131
|
+
} else if (provider === "custom" && coverage.customProviderModule) {
|
|
2132
|
+
entries.push(coverage.customProviderModule);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1929
2135
|
return {
|
|
2136
|
+
define,
|
|
2137
|
+
resolve: {
|
|
2138
|
+
dedupe: ["vitest"]
|
|
2139
|
+
},
|
|
1930
2140
|
optimizeDeps: {
|
|
1931
|
-
entries
|
|
1932
|
-
...browserTestFiles,
|
|
1933
|
-
...setupFiles,
|
|
1934
|
-
resolve(distDir, "index.js"),
|
|
1935
|
-
resolve(distDir, "browser.js"),
|
|
1936
|
-
resolve(distDir, "runners.js"),
|
|
1937
|
-
resolve(distDir, "utils.js")
|
|
1938
|
-
],
|
|
2141
|
+
entries,
|
|
1939
2142
|
exclude: [
|
|
1940
2143
|
"vitest",
|
|
1941
2144
|
"vitest/utils",
|
|
@@ -1964,8 +2167,9 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
1964
2167
|
"vitest > pretty-format > ansi-regex",
|
|
1965
2168
|
"vitest > chai",
|
|
1966
2169
|
"vitest > chai > loupe",
|
|
1967
|
-
"vitest > @vitest/runner >
|
|
2170
|
+
"vitest > @vitest/runner > pretty-format",
|
|
1968
2171
|
"vitest > @vitest/utils > diff-sequences",
|
|
2172
|
+
"vitest > @vitest/utils > loupe",
|
|
1969
2173
|
"@vitest/browser > @testing-library/user-event",
|
|
1970
2174
|
"@vitest/browser > @testing-library/dom"
|
|
1971
2175
|
]
|
|
@@ -1992,7 +2196,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
1992
2196
|
if (rawId.startsWith("/__virtual_vitest__")) {
|
|
1993
2197
|
const url = new URL(rawId, "http://localhost");
|
|
1994
2198
|
if (!url.searchParams.has("id")) {
|
|
1995
|
-
|
|
2199
|
+
return;
|
|
1996
2200
|
}
|
|
1997
2201
|
const id = decodeURIComponent(url.searchParams.get("id"));
|
|
1998
2202
|
const resolved = await this.resolve(id, distRoot, {
|
|
@@ -2013,6 +2217,15 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
2013
2217
|
if (id.startsWith("/__vitest_browser__/") || id.startsWith("/__vitest__/")) {
|
|
2014
2218
|
return resolve(distRoot, "client", id.slice(1));
|
|
2015
2219
|
}
|
|
2220
|
+
},
|
|
2221
|
+
configResolved(config) {
|
|
2222
|
+
loupePath = resolve(config.cacheDir, "deps/loupe.js");
|
|
2223
|
+
},
|
|
2224
|
+
transform(code, id) {
|
|
2225
|
+
if (id.startsWith(loupePath)) {
|
|
2226
|
+
const utilRequire = "nodeUtil = require_util();";
|
|
2227
|
+
return code.replace(utilRequire, " ".repeat(utilRequire.length));
|
|
2228
|
+
}
|
|
2016
2229
|
}
|
|
2017
2230
|
},
|
|
2018
2231
|
BrowserContext(browserServer),
|
|
@@ -2022,10 +2235,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
2022
2235
|
enforce: "post",
|
|
2023
2236
|
async config(viteConfig) {
|
|
2024
2237
|
// Enables using ignore hint for coverage providers with @preserve keyword
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
viteConfig.esbuild.legalComments = "inline";
|
|
2028
|
-
}
|
|
2238
|
+
viteConfig.esbuild ||= {};
|
|
2239
|
+
viteConfig.esbuild.legalComments = "inline";
|
|
2029
2240
|
const server = resolveApiServerConfig(
|
|
2030
2241
|
viteConfig.test?.browser || {},
|
|
2031
2242
|
defaultBrowserPort
|
|
@@ -2055,18 +2266,31 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
2055
2266
|
},
|
|
2056
2267
|
// TODO: remove this when @testing-library/vue supports ESM
|
|
2057
2268
|
{
|
|
2058
|
-
name: "vitest:browser:support-
|
|
2269
|
+
name: "vitest:browser:support-testing-library",
|
|
2059
2270
|
config() {
|
|
2060
2271
|
return {
|
|
2272
|
+
define: {
|
|
2273
|
+
// testing-library/preact
|
|
2274
|
+
"process.env.PTL_SKIP_AUTO_CLEANUP": !!process.env.PTL_SKIP_AUTO_CLEANUP,
|
|
2275
|
+
// testing-library/react
|
|
2276
|
+
"process.env.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
|
|
2277
|
+
"process.env?.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
|
|
2278
|
+
// testing-library/svelte, testing-library/solid
|
|
2279
|
+
"process.env.STL_SKIP_AUTO_CLEANUP": !!process.env.STL_SKIP_AUTO_CLEANUP,
|
|
2280
|
+
// testing-library/vue
|
|
2281
|
+
"process.env.VTL_SKIP_AUTO_CLEANUP": !!process.env.VTL_SKIP_AUTO_CLEANUP,
|
|
2282
|
+
// dom.debug()
|
|
2283
|
+
"process.env.DEBUG_PRINT_LIMIT": process.env.DEBUG_PRINT_LIMIT || 7e3
|
|
2284
|
+
},
|
|
2061
2285
|
optimizeDeps: {
|
|
2062
2286
|
esbuildOptions: {
|
|
2063
2287
|
plugins: [
|
|
2064
2288
|
{
|
|
2065
2289
|
name: "test-utils-rewrite",
|
|
2066
2290
|
setup(build) {
|
|
2067
|
-
const _require = createRequire(import.meta.url);
|
|
2068
2291
|
build.onResolve({ filter: /@vue\/test-utils/ }, (args) => {
|
|
2069
|
-
const
|
|
2292
|
+
const _require2 = getRequire();
|
|
2293
|
+
const resolved = _require2.resolve(args.path, {
|
|
2070
2294
|
paths: [args.importer]
|
|
2071
2295
|
});
|
|
2072
2296
|
return { path: resolved };
|
|
@@ -2081,6 +2305,21 @@ var BrowserPlugin = (browserServer, base = "/") => {
|
|
|
2081
2305
|
}
|
|
2082
2306
|
];
|
|
2083
2307
|
};
|
|
2308
|
+
function tryResolve(path, paths) {
|
|
2309
|
+
try {
|
|
2310
|
+
const _require2 = getRequire();
|
|
2311
|
+
return _require2.resolve(path, { paths });
|
|
2312
|
+
} catch {
|
|
2313
|
+
return void 0;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
let _require;
|
|
2317
|
+
function getRequire() {
|
|
2318
|
+
if (!_require) {
|
|
2319
|
+
_require = createRequire(import.meta.url);
|
|
2320
|
+
}
|
|
2321
|
+
return _require;
|
|
2322
|
+
}
|
|
2084
2323
|
function resolveCoverageFolder(project) {
|
|
2085
2324
|
const options = project.ctx.config;
|
|
2086
2325
|
const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
|
|
@@ -2106,11 +2345,11 @@ function resolveCoverageFolder(project) {
|
|
|
2106
2345
|
const debug = createDebugger("vitest:browser:pool");
|
|
2107
2346
|
function createBrowserPool(ctx) {
|
|
2108
2347
|
const providers = /* @__PURE__ */ new Set();
|
|
2109
|
-
const waitForTests = async (contextId, project, files) => {
|
|
2110
|
-
const context = project.browser.state.createAsyncContext(contextId, files);
|
|
2348
|
+
const waitForTests = async (method, contextId, project, files) => {
|
|
2349
|
+
const context = project.browser.state.createAsyncContext(method, contextId, files);
|
|
2111
2350
|
return await context;
|
|
2112
2351
|
};
|
|
2113
|
-
const
|
|
2352
|
+
const executeTests = async (method, project, files) => {
|
|
2114
2353
|
ctx.state.clearFiles(project, files);
|
|
2115
2354
|
const browser = project.browser;
|
|
2116
2355
|
const threadsCount = getThreadsCount(project);
|
|
@@ -2146,12 +2385,12 @@ function createBrowserPool(ctx) {
|
|
|
2146
2385
|
contextId,
|
|
2147
2386
|
[...files2.map((f) => relative(project.config.root, f))].join(", ")
|
|
2148
2387
|
);
|
|
2149
|
-
const promise = waitForTests(contextId, project, files2);
|
|
2388
|
+
const promise = waitForTests(method, contextId, project, files2);
|
|
2150
2389
|
promises.push(promise);
|
|
2151
2390
|
orchestrator.createTesters(files2);
|
|
2152
2391
|
} else {
|
|
2153
2392
|
const contextId = crypto.randomUUID();
|
|
2154
|
-
const waitPromise = waitForTests(contextId, project, files2);
|
|
2393
|
+
const waitPromise = waitForTests(method, contextId, project, files2);
|
|
2155
2394
|
debug?.(
|
|
2156
2395
|
"Opening a new context %s for files: %s",
|
|
2157
2396
|
contextId,
|
|
@@ -2165,15 +2404,22 @@ function createBrowserPool(ctx) {
|
|
|
2165
2404
|
});
|
|
2166
2405
|
await Promise.all(promises);
|
|
2167
2406
|
};
|
|
2168
|
-
const runWorkspaceTests = async (specs) => {
|
|
2407
|
+
const runWorkspaceTests = async (method, specs) => {
|
|
2169
2408
|
const groupedFiles = /* @__PURE__ */ new Map();
|
|
2170
2409
|
for (const [project, file] of specs) {
|
|
2171
2410
|
const files = groupedFiles.get(project) || [];
|
|
2172
2411
|
files.push(file);
|
|
2173
2412
|
groupedFiles.set(project, files);
|
|
2174
2413
|
}
|
|
2414
|
+
let isCancelled = false;
|
|
2415
|
+
ctx.onCancel(() => {
|
|
2416
|
+
isCancelled = true;
|
|
2417
|
+
});
|
|
2175
2418
|
for (const [project, files] of groupedFiles.entries()) {
|
|
2176
|
-
|
|
2419
|
+
if (isCancelled) {
|
|
2420
|
+
break;
|
|
2421
|
+
}
|
|
2422
|
+
await executeTests(method, project, files);
|
|
2177
2423
|
}
|
|
2178
2424
|
};
|
|
2179
2425
|
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
|
|
@@ -2193,7 +2439,8 @@ function createBrowserPool(ctx) {
|
|
|
2193
2439
|
await Promise.all([...providers].map((provider) => provider.close()));
|
|
2194
2440
|
providers.clear();
|
|
2195
2441
|
},
|
|
2196
|
-
runTests: runWorkspaceTests
|
|
2442
|
+
runTests: (files) => runWorkspaceTests("run", files),
|
|
2443
|
+
collectTests: (files) => runWorkspaceTests("collect", files)
|
|
2197
2444
|
};
|
|
2198
2445
|
}
|
|
2199
2446
|
|
|
@@ -2206,14 +2453,13 @@ async function createBrowserServer(project, configFile, prePlugins = [], postPlu
|
|
|
2206
2453
|
...project.options,
|
|
2207
2454
|
// spread project config inlined in root workspace config
|
|
2208
2455
|
base: "/",
|
|
2209
|
-
logLevel: "
|
|
2456
|
+
logLevel: process.env.VITEST_BROWSER_DEBUG ?? "info",
|
|
2210
2457
|
mode: project.config.mode,
|
|
2211
2458
|
configFile: configPath,
|
|
2212
2459
|
// watch is handled by Vitest
|
|
2213
2460
|
server: {
|
|
2214
2461
|
hmr: false,
|
|
2215
|
-
watch: null
|
|
2216
|
-
preTransformRequests: false
|
|
2462
|
+
watch: null
|
|
2217
2463
|
},
|
|
2218
2464
|
plugins: [
|
|
2219
2465
|
...prePlugins,
|