@vitest/browser 5.0.0-beta.1 → 5.0.0-beta.3
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 +40 -1
- package/dist/client/.vite/manifest.json +8 -8
- package/dist/client/__vitest__/assets/index-BlLo6Q_D.css +1 -0
- package/dist/client/__vitest__/assets/index-O8gheoYf.js +89 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/defineProperty-C3k2g8Sk.js +267 -0
- package/dist/client/__vitest_browser__/orchestrator-B44yH1M4.js +343 -0
- package/dist/client/__vitest_browser__/rrweb-snapshot-iZCFA2to.js +4388 -0
- package/dist/client/__vitest_browser__/tester-Byk-s_d6.js +5088 -0
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/locators.d.ts +69 -0
- package/dist/client/tester/tester.html +2 -2
- package/dist/client/tester/trace.d.ts +11 -7
- package/dist/client.js +10 -1
- package/dist/context.js +73 -44
- package/dist/expect-element.js +30 -30
- package/dist/index.d.ts +6 -22
- package/dist/index.js +99 -4573
- package/dist/locators-DUkyvRhY.js +5 -0
- package/dist/locators.d.ts +9 -1
- package/dist/locators.js +1 -1
- package/dist/shared/screenshotMatcher/types.d.ts +4 -3
- package/dist/state.js +64 -14
- package/dist/types.d.ts +2 -0
- package/jest-dom.d.ts +1 -0
- package/matchers.d.ts +2 -1
- package/package.json +10 -7
- package/dist/client/__vitest__/assets/index-BmuVn2L3.js +0 -136
- package/dist/client/__vitest__/assets/index-CxYquQyv.css +0 -1
- package/dist/client/__vitest__/bg.png +0 -0
- package/dist/client/__vitest_browser__/orchestrator-pTEf6o0n.js +0 -383
- package/dist/client/__vitest_browser__/rrweb-snapshot-xhvrgOHx.js +0 -5476
- package/dist/client/__vitest_browser__/tester-CIKiUsoz.js +0 -2431
- package/dist/client/__vitest_browser__/utils-BYUpz6v6.js +0 -3379
- package/dist/index-BlWsE3ij.js +0 -5
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
})();
|
|
24
24
|
</script>
|
|
25
25
|
<!-- !LOAD_METADATA! -->
|
|
26
|
-
<script type="module" src="./assets/index-
|
|
27
|
-
<link rel="stylesheet" href="./assets/index-
|
|
26
|
+
<script type="module" src="./assets/index-O8gheoYf.js"></script>
|
|
27
|
+
<link rel="stylesheet" href="./assets/index-BlLo6Q_D.css">
|
|
28
28
|
</head>
|
|
29
29
|
<body>
|
|
30
30
|
<div id="app"></div>
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
//#region \0vite/modulepreload-polyfill.js
|
|
2
|
+
(function polyfill() {
|
|
3
|
+
const relList = document.createElement("link").relList;
|
|
4
|
+
if (relList && relList.supports && relList.supports("modulepreload")) return;
|
|
5
|
+
for (const link of document.querySelectorAll("link[rel=\"modulepreload\"]")) processPreload(link);
|
|
6
|
+
new MutationObserver((mutations) => {
|
|
7
|
+
for (const mutation of mutations) {
|
|
8
|
+
if (mutation.type !== "childList") continue;
|
|
9
|
+
for (const node of mutation.addedNodes) if (node.tagName === "LINK" && node.rel === "modulepreload") processPreload(node);
|
|
10
|
+
}
|
|
11
|
+
}).observe(document, {
|
|
12
|
+
childList: true,
|
|
13
|
+
subtree: true
|
|
14
|
+
});
|
|
15
|
+
function getFetchOpts(link) {
|
|
16
|
+
const fetchOpts = {};
|
|
17
|
+
if (link.integrity) fetchOpts.integrity = link.integrity;
|
|
18
|
+
if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
|
|
19
|
+
if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include";
|
|
20
|
+
else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
|
|
21
|
+
else fetchOpts.credentials = "same-origin";
|
|
22
|
+
return fetchOpts;
|
|
23
|
+
}
|
|
24
|
+
function processPreload(link) {
|
|
25
|
+
if (link.ep) return;
|
|
26
|
+
link.ep = true;
|
|
27
|
+
const fetchOpts = getFetchOpts(link);
|
|
28
|
+
fetch(link.href, fetchOpts);
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region ../../node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
34
|
+
var _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
35
|
+
function normalizeWindowsPath(input = "") {
|
|
36
|
+
if (!input) return input;
|
|
37
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
38
|
+
}
|
|
39
|
+
var _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
40
|
+
var _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
41
|
+
function cwd() {
|
|
42
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
43
|
+
return "/";
|
|
44
|
+
}
|
|
45
|
+
var resolve = function(...arguments_) {
|
|
46
|
+
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
47
|
+
let resolvedPath = "";
|
|
48
|
+
let resolvedAbsolute = false;
|
|
49
|
+
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
50
|
+
const path = index >= 0 ? arguments_[index] : cwd();
|
|
51
|
+
if (!path || path.length === 0) continue;
|
|
52
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
53
|
+
resolvedAbsolute = isAbsolute(path);
|
|
54
|
+
}
|
|
55
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
56
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
|
|
57
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
58
|
+
};
|
|
59
|
+
function normalizeString(path, allowAboveRoot) {
|
|
60
|
+
let res = "";
|
|
61
|
+
let lastSegmentLength = 0;
|
|
62
|
+
let lastSlash = -1;
|
|
63
|
+
let dots = 0;
|
|
64
|
+
let char = null;
|
|
65
|
+
for (let index = 0; index <= path.length; ++index) {
|
|
66
|
+
if (index < path.length) char = path[index];
|
|
67
|
+
else if (char === "/") break;
|
|
68
|
+
else char = "/";
|
|
69
|
+
if (char === "/") {
|
|
70
|
+
if (lastSlash === index - 1 || dots === 1);
|
|
71
|
+
else if (dots === 2) {
|
|
72
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
73
|
+
if (res.length > 2) {
|
|
74
|
+
const lastSlashIndex = res.lastIndexOf("/");
|
|
75
|
+
if (lastSlashIndex === -1) {
|
|
76
|
+
res = "";
|
|
77
|
+
lastSegmentLength = 0;
|
|
78
|
+
} else {
|
|
79
|
+
res = res.slice(0, lastSlashIndex);
|
|
80
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
81
|
+
}
|
|
82
|
+
lastSlash = index;
|
|
83
|
+
dots = 0;
|
|
84
|
+
continue;
|
|
85
|
+
} else if (res.length > 0) {
|
|
86
|
+
res = "";
|
|
87
|
+
lastSegmentLength = 0;
|
|
88
|
+
lastSlash = index;
|
|
89
|
+
dots = 0;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (allowAboveRoot) {
|
|
94
|
+
res += res.length > 0 ? "/.." : "..";
|
|
95
|
+
lastSegmentLength = 2;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
99
|
+
else res = path.slice(lastSlash + 1, index);
|
|
100
|
+
lastSegmentLength = index - lastSlash - 1;
|
|
101
|
+
}
|
|
102
|
+
lastSlash = index;
|
|
103
|
+
dots = 0;
|
|
104
|
+
} else if (char === "." && dots !== -1) ++dots;
|
|
105
|
+
else dots = -1;
|
|
106
|
+
}
|
|
107
|
+
return res;
|
|
108
|
+
}
|
|
109
|
+
var isAbsolute = function(p) {
|
|
110
|
+
return _IS_ABSOLUTE_RE.test(p);
|
|
111
|
+
};
|
|
112
|
+
var relative = function(from, to) {
|
|
113
|
+
const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
114
|
+
const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
115
|
+
if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) return _to.join("/");
|
|
116
|
+
const _fromCopy = [..._from];
|
|
117
|
+
for (const segment of _fromCopy) {
|
|
118
|
+
if (_to[0] !== segment) break;
|
|
119
|
+
_from.shift();
|
|
120
|
+
_to.shift();
|
|
121
|
+
}
|
|
122
|
+
return [..._from.map(() => ".."), ..._to].join("/");
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region \0vite/preload-helper.js
|
|
127
|
+
var scriptRel = "modulepreload";
|
|
128
|
+
var assetsURL = function(dep) {
|
|
129
|
+
return "/" + dep;
|
|
130
|
+
};
|
|
131
|
+
var seen = {};
|
|
132
|
+
var __vitePreload = function preload(baseModule, deps, importerUrl) {
|
|
133
|
+
let promise = Promise.resolve();
|
|
134
|
+
if (true && deps && deps.length > 0) {
|
|
135
|
+
const links = document.getElementsByTagName("link");
|
|
136
|
+
const cspNonceMeta = document.querySelector("meta[property=csp-nonce]");
|
|
137
|
+
const cspNonce = (cspNonceMeta === null || cspNonceMeta === void 0 ? void 0 : cspNonceMeta.nonce) || (cspNonceMeta === null || cspNonceMeta === void 0 ? void 0 : cspNonceMeta.getAttribute("nonce"));
|
|
138
|
+
function allSettled(promises) {
|
|
139
|
+
return Promise.all(promises.map((p) => Promise.resolve(p).then((value) => ({
|
|
140
|
+
status: "fulfilled",
|
|
141
|
+
value
|
|
142
|
+
}), (reason) => ({
|
|
143
|
+
status: "rejected",
|
|
144
|
+
reason
|
|
145
|
+
}))));
|
|
146
|
+
}
|
|
147
|
+
promise = allSettled(deps.map((dep) => {
|
|
148
|
+
dep = assetsURL(dep, importerUrl);
|
|
149
|
+
if (dep in seen) return;
|
|
150
|
+
seen[dep] = true;
|
|
151
|
+
const isCss = dep.endsWith(".css");
|
|
152
|
+
const cssSelector = isCss ? "[rel=\"stylesheet\"]" : "";
|
|
153
|
+
if (!!importerUrl) for (let i = links.length - 1; i >= 0; i--) {
|
|
154
|
+
const link = links[i];
|
|
155
|
+
if (link.href === dep && (!isCss || link.rel === "stylesheet")) return;
|
|
156
|
+
}
|
|
157
|
+
else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) return;
|
|
158
|
+
const link = document.createElement("link");
|
|
159
|
+
link.rel = isCss ? "stylesheet" : scriptRel;
|
|
160
|
+
if (!isCss) link.as = "script";
|
|
161
|
+
link.crossOrigin = "";
|
|
162
|
+
link.href = dep;
|
|
163
|
+
if (cspNonce) link.setAttribute("nonce", cspNonce);
|
|
164
|
+
document.head.appendChild(link);
|
|
165
|
+
if (isCss) return new Promise((res, rej) => {
|
|
166
|
+
link.addEventListener("load", res);
|
|
167
|
+
link.addEventListener("error", () => rej(/* @__PURE__ */ new Error(`Unable to preload CSS for ${dep}`)));
|
|
168
|
+
});
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
function handlePreloadError(err) {
|
|
172
|
+
const e = new Event("vite:preloadError", { cancelable: true });
|
|
173
|
+
e.payload = err;
|
|
174
|
+
window.dispatchEvent(e);
|
|
175
|
+
if (!e.defaultPrevented) throw err;
|
|
176
|
+
}
|
|
177
|
+
return promise.then((res) => {
|
|
178
|
+
for (const item of res || []) {
|
|
179
|
+
if (item.status !== "rejected") continue;
|
|
180
|
+
handlePreloadError(item.reason);
|
|
181
|
+
}
|
|
182
|
+
return baseModule().catch(handlePreloadError);
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/client/utils.ts
|
|
188
|
+
async function importId(id) {
|
|
189
|
+
const name = `/@id/${id}`.replace(/\\/g, "/");
|
|
190
|
+
return (/* @__PURE__ */ getBrowserState()).wrapModule(() => __vitePreload(() => import(
|
|
191
|
+
/* @vite-ignore */
|
|
192
|
+
name
|
|
193
|
+
), []));
|
|
194
|
+
}
|
|
195
|
+
async function importFs(id) {
|
|
196
|
+
const name = `/@fs/${id}`.replace(/\\/g, "/");
|
|
197
|
+
return (/* @__PURE__ */ getBrowserState()).wrapModule(() => __vitePreload(() => import(
|
|
198
|
+
/* @vite-ignore */
|
|
199
|
+
name
|
|
200
|
+
), []));
|
|
201
|
+
}
|
|
202
|
+
var moduleRunner = {
|
|
203
|
+
isBrowser: true,
|
|
204
|
+
import: (id) => {
|
|
205
|
+
if (id[0] === "/" || id[1] === ":") return importFs(id);
|
|
206
|
+
return importId(id);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
var now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
210
|
+
function getConfig() {
|
|
211
|
+
return (/* @__PURE__ */ getBrowserState()).config;
|
|
212
|
+
}
|
|
213
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
214
|
+
function getBrowserState() {
|
|
215
|
+
return window.__vitest_browser_runner__;
|
|
216
|
+
}
|
|
217
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
218
|
+
function getWorkerState() {
|
|
219
|
+
const state = window.__vitest_worker__;
|
|
220
|
+
if (!state) throw new Error("Worker state is not found. This is an issue with Vitest. Please, open an issue.");
|
|
221
|
+
return state;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region \0@oxc-project+runtime@0.128.0/helpers/typeof.js
|
|
226
|
+
function _typeof(o) {
|
|
227
|
+
"@babel/helpers - typeof";
|
|
228
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
229
|
+
return typeof o;
|
|
230
|
+
} : function(o) {
|
|
231
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
232
|
+
}, _typeof(o);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region \0@oxc-project+runtime@0.128.0/helpers/toPrimitive.js
|
|
237
|
+
function toPrimitive(t, r) {
|
|
238
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
239
|
+
var e = t[Symbol.toPrimitive];
|
|
240
|
+
if (void 0 !== e) {
|
|
241
|
+
var i = e.call(t, r || "default");
|
|
242
|
+
if ("object" != _typeof(i)) return i;
|
|
243
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
244
|
+
}
|
|
245
|
+
return ("string" === r ? String : Number)(t);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region \0@oxc-project+runtime@0.128.0/helpers/toPropertyKey.js
|
|
250
|
+
function toPropertyKey(t) {
|
|
251
|
+
var i = toPrimitive(t, "string");
|
|
252
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region \0@oxc-project+runtime@0.128.0/helpers/defineProperty.js
|
|
257
|
+
function _defineProperty(e, r, t) {
|
|
258
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
259
|
+
value: t,
|
|
260
|
+
enumerable: !0,
|
|
261
|
+
configurable: !0,
|
|
262
|
+
writable: !0
|
|
263
|
+
}) : e[r] = t, e;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
export { moduleRunner as a, relative as c, getWorkerState as i, resolve as l, getBrowserState as n, now as o, getConfig as r, __vitePreload as s, _defineProperty as t };
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { c as relative, n as getBrowserState, r as getConfig, t as _defineProperty } from "./defineProperty-C3k2g8Sk.js";
|
|
2
|
+
import { channel, client, globalChannel } from "@vitest/browser/client";
|
|
3
|
+
import { Traces } from "vitest/internal/traces";
|
|
4
|
+
|
|
5
|
+
//#region src/client/ui.ts
|
|
6
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
7
|
+
function getUiAPI() {
|
|
8
|
+
return window.__vitest_ui_api__;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/client/orchestrator.ts
|
|
13
|
+
var ID_ALL = "__vitest_all__";
|
|
14
|
+
var IframeOrchestrator = class {
|
|
15
|
+
constructor() {
|
|
16
|
+
_defineProperty(this, "cancelled", false);
|
|
17
|
+
_defineProperty(this, "recreateNonIsolatedIframe", false);
|
|
18
|
+
_defineProperty(this, "iframes", /* @__PURE__ */ new Map());
|
|
19
|
+
_defineProperty(this, "eventTarget", new EventTarget());
|
|
20
|
+
_defineProperty(this, "traces", void 0);
|
|
21
|
+
_defineProperty(this, "loggedIframe", /* @__PURE__ */ new WeakSet());
|
|
22
|
+
_defineProperty(this, "iframeEvents", /* @__PURE__ */ new WeakMap());
|
|
23
|
+
debug("init orchestrator", (/* @__PURE__ */ getBrowserState()).sessionId);
|
|
24
|
+
const otelConfig = (/* @__PURE__ */ getBrowserState()).config.experimental.openTelemetry;
|
|
25
|
+
this.traces = new Traces({
|
|
26
|
+
enabled: !!((otelConfig === null || otelConfig === void 0 ? void 0 : otelConfig.enabled) && otelConfig.browserSdkPath),
|
|
27
|
+
sdkPath: `/@fs/${otelConfig === null || otelConfig === void 0 ? void 0 : otelConfig.browserSdkPath}`
|
|
28
|
+
});
|
|
29
|
+
channel.addEventListener("message", (e) => this.onIframeEvent(e));
|
|
30
|
+
globalChannel.addEventListener("message", (e) => this.onGlobalChannelEvent(e));
|
|
31
|
+
}
|
|
32
|
+
async createTesters(options) {
|
|
33
|
+
await this.traces.waitInit();
|
|
34
|
+
this.traces.recordInitSpan(this.traces.getContextFromCarrier((/* @__PURE__ */ getBrowserState()).otelCarrier));
|
|
35
|
+
const orchestratorSpan = this.traces.startContextSpan("vitest.browser.orchestrator.run", this.traces.getContextFromCarrier(options.otelCarrier));
|
|
36
|
+
orchestratorSpan.span.setAttributes({ "vitest.browser.files": options.files.map((f) => f.filepath) });
|
|
37
|
+
const endSpan = async () => {
|
|
38
|
+
orchestratorSpan.span.end();
|
|
39
|
+
await this.traces.flush();
|
|
40
|
+
};
|
|
41
|
+
const startTime = performance.now();
|
|
42
|
+
this.cancelled = false;
|
|
43
|
+
const config = getConfig();
|
|
44
|
+
debug("create testers", ...options.files.join(", "));
|
|
45
|
+
const container = await getContainer(config);
|
|
46
|
+
if (config.browser.ui) {
|
|
47
|
+
container.setAttribute("data-ready", "true");
|
|
48
|
+
if (container.textContent) container.textContent = "";
|
|
49
|
+
}
|
|
50
|
+
if (config.browser.isolate === false) {
|
|
51
|
+
await this.runNonIsolatedTests(container, options, startTime, orchestratorSpan.context);
|
|
52
|
+
await endSpan();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.iframes.forEach((iframe) => iframe.remove());
|
|
56
|
+
this.iframes.clear();
|
|
57
|
+
for (let i = 0; i < options.files.length; i++) {
|
|
58
|
+
if (this.cancelled) {
|
|
59
|
+
await endSpan();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const file = options.files[i];
|
|
63
|
+
debug("create iframe", file.filepath);
|
|
64
|
+
await this.runIsolatedTestInIframe(container, file, options, startTime, orchestratorSpan.context);
|
|
65
|
+
}
|
|
66
|
+
await endSpan();
|
|
67
|
+
}
|
|
68
|
+
async cleanupTesters() {
|
|
69
|
+
if (getConfig().browser.isolate) {
|
|
70
|
+
const files = Array.from(this.iframes.keys());
|
|
71
|
+
const ui = /* @__PURE__ */ getUiAPI();
|
|
72
|
+
if (ui && files[0]) {
|
|
73
|
+
const id = generateFileId(files[0]);
|
|
74
|
+
ui.setCurrentFileId(id);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!this.iframes.get(ID_ALL)) return;
|
|
79
|
+
await this.sendEventToIframe({
|
|
80
|
+
event: "cleanup",
|
|
81
|
+
iframeId: ID_ALL
|
|
82
|
+
});
|
|
83
|
+
this.recreateNonIsolatedIframe = true;
|
|
84
|
+
}
|
|
85
|
+
async runNonIsolatedTests(container, options, startTime, otelContext) {
|
|
86
|
+
if (this.recreateNonIsolatedIframe) {
|
|
87
|
+
this.recreateNonIsolatedIframe = false;
|
|
88
|
+
this.iframes.get(ID_ALL).remove();
|
|
89
|
+
this.iframes.delete(ID_ALL);
|
|
90
|
+
debug("recreate non-isolated iframe");
|
|
91
|
+
}
|
|
92
|
+
if (!this.iframes.has(ID_ALL)) {
|
|
93
|
+
debug("preparing non-isolated iframe");
|
|
94
|
+
await this.prepareIframe(container, ID_ALL, startTime, otelContext);
|
|
95
|
+
}
|
|
96
|
+
const { width, height } = getConfig().browser.viewport;
|
|
97
|
+
await setIframeViewport(width, height);
|
|
98
|
+
debug("run non-isolated tests", options.files.join(", "));
|
|
99
|
+
await this.sendEventToIframe({
|
|
100
|
+
event: "execute",
|
|
101
|
+
iframeId: ID_ALL,
|
|
102
|
+
files: options.files,
|
|
103
|
+
method: options.method,
|
|
104
|
+
context: options.providedContext
|
|
105
|
+
});
|
|
106
|
+
debug("finished running tests", options.files.join(", "));
|
|
107
|
+
}
|
|
108
|
+
async runIsolatedTestInIframe(container, spec, options, startTime, otelContext) {
|
|
109
|
+
const { width, height } = getConfig().browser.viewport;
|
|
110
|
+
const file = spec.filepath;
|
|
111
|
+
if (this.iframes.has(file)) {
|
|
112
|
+
this.iframes.get(file).remove();
|
|
113
|
+
this.iframes.delete(file);
|
|
114
|
+
}
|
|
115
|
+
await this.prepareIframe(container, file, startTime, otelContext);
|
|
116
|
+
await setIframeViewport(width, height);
|
|
117
|
+
await this.sendEventToIframe({
|
|
118
|
+
event: "execute",
|
|
119
|
+
files: [spec],
|
|
120
|
+
method: options.method,
|
|
121
|
+
iframeId: file,
|
|
122
|
+
context: options.providedContext
|
|
123
|
+
});
|
|
124
|
+
await this.sendEventToIframe({
|
|
125
|
+
event: "cleanup",
|
|
126
|
+
iframeId: file
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
dispatchIframeError(error) {
|
|
130
|
+
const event = new CustomEvent("iframeerror", { detail: error });
|
|
131
|
+
this.eventTarget.dispatchEvent(event);
|
|
132
|
+
return error;
|
|
133
|
+
}
|
|
134
|
+
async prepareIframe(container, iframeId, startTime, otelContext) {
|
|
135
|
+
const iframe = this.createTestIframe(iframeId);
|
|
136
|
+
container.appendChild(iframe);
|
|
137
|
+
await new Promise((resolve, reject) => {
|
|
138
|
+
iframe.onload = () => {
|
|
139
|
+
const href = this.getIframeHref(iframe);
|
|
140
|
+
debug("iframe loaded with href", href);
|
|
141
|
+
if (href !== iframe.src) reject(this.dispatchIframeError(/* @__PURE__ */ new Error(`Cannot connect to the iframe. Did you change the location or submitted a form? If so, don't forget to call \`event.preventDefault()\` to avoid reloading the page.
|
|
142
|
+
|
|
143
|
+
Received URL: ${href || "unknown due to CORS"}\nExpected: ${iframe.src}`)));
|
|
144
|
+
else if (this.iframes.has(iframeId)) {
|
|
145
|
+
const events = this.iframeEvents.get(iframe);
|
|
146
|
+
if (events === null || events === void 0 ? void 0 : events.size) this.dispatchIframeError(new Error(this.createWarningMessage(iframeId, "during a test")));
|
|
147
|
+
else this.warnReload(iframe, iframeId);
|
|
148
|
+
} else {
|
|
149
|
+
this.iframes.set(iframeId, iframe);
|
|
150
|
+
this.sendEventToIframe({
|
|
151
|
+
event: "prepare",
|
|
152
|
+
iframeId,
|
|
153
|
+
startTime,
|
|
154
|
+
otelCarrier: this.traces.getContextCarrier(otelContext)
|
|
155
|
+
}).then(resolve, (error) => reject(this.dispatchIframeError(error)));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
iframe.onerror = (e) => {
|
|
159
|
+
if (typeof e === "string") reject(this.dispatchIframeError(new Error(e)));
|
|
160
|
+
else if (e instanceof ErrorEvent) reject(this.dispatchIframeError(e.error));
|
|
161
|
+
else reject(this.dispatchIframeError(/* @__PURE__ */ new Error(`Cannot load the iframe ${iframeId}.`)));
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
return iframe;
|
|
165
|
+
}
|
|
166
|
+
createWarningMessage(iframeId, location) {
|
|
167
|
+
return `The iframe${iframeId === ID_ALL ? "" : ` for "${iframeId}"`} was reloaded ${location}. This can lead to unexpected behavior during tests, duplicated test results or tests hanging.\n\nMake sure that your test code does not change window's location, submit forms without preventing default behavior, or imports unoptimized dependencies.\nIf you are using a framework that manipulates browser history (like React Router), consider using memory-based routing for tests. If you think this is a false positive, open an issue with a reproduction: https://github.com/vitest-dev/vitest/issues/new`;
|
|
168
|
+
}
|
|
169
|
+
warnReload(iframe, iframeId) {
|
|
170
|
+
if (this.loggedIframe.has(iframe)) return;
|
|
171
|
+
this.loggedIframe.add(iframe);
|
|
172
|
+
const message = `\x1B[41m WARNING \x1B[49m ${this.createWarningMessage(iframeId, "multiple times")}`;
|
|
173
|
+
client.rpc.sendLog("run", {
|
|
174
|
+
type: "stderr",
|
|
175
|
+
time: Date.now(),
|
|
176
|
+
content: message,
|
|
177
|
+
size: message.length,
|
|
178
|
+
taskId: iframeId === ID_ALL ? void 0 : generateFileId(iframeId)
|
|
179
|
+
}).catch(() => {});
|
|
180
|
+
}
|
|
181
|
+
getIframeHref(iframe) {
|
|
182
|
+
try {
|
|
183
|
+
var _iframe$contentWindow;
|
|
184
|
+
return (_iframe$contentWindow = iframe.contentWindow) === null || _iframe$contentWindow === void 0 ? void 0 : _iframe$contentWindow.location.href;
|
|
185
|
+
} catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
createTestIframe(iframeId) {
|
|
190
|
+
const iframe = document.createElement("iframe");
|
|
191
|
+
const src = `/?sessionId=${(/* @__PURE__ */ getBrowserState()).sessionId}&iframeId=${iframeId}`;
|
|
192
|
+
const config = getConfig();
|
|
193
|
+
iframe.setAttribute("loading", "eager");
|
|
194
|
+
iframe.setAttribute("src", src);
|
|
195
|
+
iframe.setAttribute("data-vitest", "true");
|
|
196
|
+
iframe.setAttribute("allowfullscreen", "true");
|
|
197
|
+
iframe.setAttribute("allow", "clipboard-write;");
|
|
198
|
+
iframe.setAttribute("name", "vitest-iframe");
|
|
199
|
+
iframe.style.setProperty("border", "none");
|
|
200
|
+
iframe.style.setProperty("background-color", "#fff");
|
|
201
|
+
iframe.style.setProperty("width", "var(--viewport-width)");
|
|
202
|
+
iframe.style.setProperty("height", "var(--viewport-height)");
|
|
203
|
+
if (config.browser.ui) {
|
|
204
|
+
if (config.browser.name !== "firefox") iframe.style.setProperty("transform", "scale(min(1, calc(100cqw / var(--viewport-width)), calc(100cqh / var(--viewport-height))))");
|
|
205
|
+
else {
|
|
206
|
+
iframe.style.setProperty("--container-width", "100cqw");
|
|
207
|
+
iframe.style.setProperty("--container-height", "100cqh");
|
|
208
|
+
iframe.style.setProperty("transform", "scale(min(1, tan(atan2(var(--container-width), var(--viewport-width))), tan(atan2(var(--container-height), var(--viewport-height)))))");
|
|
209
|
+
}
|
|
210
|
+
iframe.style.setProperty("transform-origin", "top left");
|
|
211
|
+
}
|
|
212
|
+
return iframe;
|
|
213
|
+
}
|
|
214
|
+
async onGlobalChannelEvent(e) {
|
|
215
|
+
debug("global channel event", JSON.stringify(e.data));
|
|
216
|
+
switch (e.data.type) {
|
|
217
|
+
case "cancel":
|
|
218
|
+
this.cancelled = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async onIframeEvent(e) {
|
|
223
|
+
debug("iframe event", JSON.stringify(e.data));
|
|
224
|
+
switch (e.data.event) {
|
|
225
|
+
case "viewport": {
|
|
226
|
+
const { width, height, iframeId: id } = e.data;
|
|
227
|
+
if (!this.iframes.get(id)) {
|
|
228
|
+
const error = `Cannot find iframe with id ${id}`;
|
|
229
|
+
channel.postMessage({
|
|
230
|
+
event: "viewport:fail",
|
|
231
|
+
iframeId: id,
|
|
232
|
+
error
|
|
233
|
+
});
|
|
234
|
+
await client.rpc.onUnhandledError({
|
|
235
|
+
name: "Teardown Error",
|
|
236
|
+
message: error
|
|
237
|
+
}, "Teardown Error");
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
await setIframeViewport(width, height);
|
|
241
|
+
channel.postMessage({
|
|
242
|
+
event: "viewport:done",
|
|
243
|
+
iframeId: id
|
|
244
|
+
});
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
default:
|
|
248
|
+
if (typeof e.data.event === "string" && e.data.event.startsWith("response:")) break;
|
|
249
|
+
await client.rpc.onUnhandledError({
|
|
250
|
+
name: "Unexpected Event",
|
|
251
|
+
message: `Unexpected event: ${e.data.event}`
|
|
252
|
+
}, "Unexpected Event");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async sendEventToIframe(event) {
|
|
256
|
+
const iframe = this.iframes.get(event.iframeId);
|
|
257
|
+
if (!iframe) throw new Error(`Cannot find iframe with id ${event.iframeId}`);
|
|
258
|
+
let events = this.iframeEvents.get(iframe);
|
|
259
|
+
if (!events) {
|
|
260
|
+
events = /* @__PURE__ */ new Set();
|
|
261
|
+
this.iframeEvents.set(iframe, events);
|
|
262
|
+
}
|
|
263
|
+
events.add(event.event);
|
|
264
|
+
channel.postMessage(event);
|
|
265
|
+
return new Promise((resolve, reject) => {
|
|
266
|
+
const cleanupEvents = () => {
|
|
267
|
+
channel.removeEventListener("message", onReceived);
|
|
268
|
+
this.eventTarget.removeEventListener("iframeerror", onError);
|
|
269
|
+
};
|
|
270
|
+
function onReceived(e) {
|
|
271
|
+
if (e.data.iframeId === event.iframeId && e.data.event === `response:${event.event}`) {
|
|
272
|
+
resolve();
|
|
273
|
+
cleanupEvents();
|
|
274
|
+
events.delete(event.event);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function onError(e) {
|
|
278
|
+
reject(e.detail);
|
|
279
|
+
cleanupEvents();
|
|
280
|
+
events.delete(event.event);
|
|
281
|
+
}
|
|
282
|
+
this.eventTarget.addEventListener("iframeerror", onError);
|
|
283
|
+
channel.addEventListener("message", onReceived);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
var orchestrator = new IframeOrchestrator();
|
|
288
|
+
(/* @__PURE__ */ getBrowserState()).orchestrator = orchestrator;
|
|
289
|
+
async function getContainer(config) {
|
|
290
|
+
if (config.browser.ui) {
|
|
291
|
+
const element = document.querySelector("#tester-ui");
|
|
292
|
+
if (!element) return new Promise((resolve) => {
|
|
293
|
+
queueMicrotask(() => {
|
|
294
|
+
resolve(getContainer(config));
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
return element;
|
|
298
|
+
}
|
|
299
|
+
return document.querySelector("#vitest-tester");
|
|
300
|
+
}
|
|
301
|
+
function generateFileId(file) {
|
|
302
|
+
const config = getConfig();
|
|
303
|
+
return generateFileHash(relative(config.root, file), config.name, {
|
|
304
|
+
typecheck: config.pool === "typescript",
|
|
305
|
+
__vitest_label__: config.mergeReportsLabel
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function generateFileHash(file, projectName, meta) {
|
|
309
|
+
return generateHash([
|
|
310
|
+
file,
|
|
311
|
+
projectName || "",
|
|
312
|
+
(meta === null || meta === void 0 ? void 0 : meta.typecheck) ? "__typecheck__" : "",
|
|
313
|
+
(meta === null || meta === void 0 ? void 0 : meta.__vitest_label__) || ""
|
|
314
|
+
].join("\0"));
|
|
315
|
+
}
|
|
316
|
+
function generateHash(str) {
|
|
317
|
+
let hash = 0;
|
|
318
|
+
if (str.length === 0) return `${hash}`;
|
|
319
|
+
for (let i = 0; i < str.length; i++) {
|
|
320
|
+
const char = str.charCodeAt(i);
|
|
321
|
+
hash = (hash << 5) - hash + char;
|
|
322
|
+
hash = hash & hash;
|
|
323
|
+
}
|
|
324
|
+
return `${hash}`;
|
|
325
|
+
}
|
|
326
|
+
async function setIframeViewport(width, height) {
|
|
327
|
+
const ui = /* @__PURE__ */ getUiAPI();
|
|
328
|
+
if (ui) await ui.setIframeViewport(width, height);
|
|
329
|
+
else {
|
|
330
|
+
document.body.style.setProperty("--viewport-width", `${width}px`);
|
|
331
|
+
document.body.style.setProperty("--viewport-height", `${height}px`);
|
|
332
|
+
await client.rpc.triggerCommand((/* @__PURE__ */ getBrowserState()).sessionId, "__vitest_viewport", void 0, [{
|
|
333
|
+
width,
|
|
334
|
+
height
|
|
335
|
+
}]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function debug(...args) {
|
|
339
|
+
const debug = getConfig().env.VITEST_BROWSER_DEBUG;
|
|
340
|
+
if (debug && debug !== "false") client.rpc.debug(...args.map(String));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
//#endregion
|