@vitest/browser 4.0.0-beta.13 → 4.0.0-beta.15
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 +50 -9
- package/context.js +3 -2
- package/dist/client/.vite/manifest.json +2 -2
- package/dist/client/__vitest__/assets/{index-C15NF4dG.js → index-CKAjAT2u.js} +1 -1
- package/dist/client/__vitest__/index.html +1 -1
- package/dist/client/__vitest_browser__/{orchestrator-Pdu7HCGX.js → orchestrator-Ce7D5fGP.js} +8 -6
- package/dist/client/__vitest_browser__/{tester-DYNLfPFH.js → tester-Vm4ppAv-.js} +4 -1
- package/dist/client/orchestrator.html +1 -1
- package/dist/client/tester/tester.html +1 -1
- package/dist/client.js +1 -1
- package/dist/context.js +70 -12
- package/dist/expect-element.js +1 -1
- package/dist/{public-utils-B6exS8fl.js → index-BnLTaCRv.js} +3 -3
- package/dist/index.d.ts +36 -172
- package/dist/index.js +515 -1487
- package/dist/{locators/index.d.ts → locators.d.ts} +25 -2
- package/dist/locators.js +1 -0
- package/jest-dom.d.ts +5 -5
- package/matchers.d.ts +1 -1
- package/package.json +11 -33
- package/utils.d.ts +5 -5
- package/dist/index-BPDFwkoW.js +0 -1
- package/dist/index-CwoiDq7G.js +0 -6
- package/dist/locators/index.js +0 -1
- package/dist/locators/playwright.js +0 -1
- package/dist/locators/preview.js +0 -1
- package/dist/locators/webdriverio.js +0 -1
- package/dist/providers/playwright.d.ts +0 -105
- package/dist/providers/playwright.js +0 -385
- package/dist/providers/preview.d.ts +0 -16
- package/dist/providers/preview.js +0 -44
- package/dist/providers/webdriverio.d.ts +0 -51
- package/dist/providers/webdriverio.js +0 -206
- package/dist/utils.js +0 -1
- package/providers.d.ts +0 -7
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import { createManualModuleSource } from '@vitest/mocker/node';
|
|
2
|
-
import c from 'tinyrainbow';
|
|
3
|
-
import { createDebugger, isCSSRequest } from 'vitest/node';
|
|
4
|
-
|
|
5
|
-
const debug = createDebugger("vitest:browser:playwright");
|
|
6
|
-
const playwrightBrowsers = [
|
|
7
|
-
"firefox",
|
|
8
|
-
"webkit",
|
|
9
|
-
"chromium"
|
|
10
|
-
];
|
|
11
|
-
function playwright(options = {}) {
|
|
12
|
-
return {
|
|
13
|
-
name: "playwright",
|
|
14
|
-
supportedBrowser: playwrightBrowsers,
|
|
15
|
-
options,
|
|
16
|
-
factory(project) {
|
|
17
|
-
return new PlaywrightBrowserProvider(project, options);
|
|
18
|
-
},
|
|
19
|
-
_cli: true
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
class PlaywrightBrowserProvider {
|
|
23
|
-
name = "playwright";
|
|
24
|
-
supportsParallelism = true;
|
|
25
|
-
browser = null;
|
|
26
|
-
contexts = new Map();
|
|
27
|
-
pages = new Map();
|
|
28
|
-
mocker;
|
|
29
|
-
browserName;
|
|
30
|
-
browserPromise = null;
|
|
31
|
-
closing = false;
|
|
32
|
-
tracingContexts = new Set();
|
|
33
|
-
pendingTraces = new Map();
|
|
34
|
-
constructor(project, options) {
|
|
35
|
-
this.project = project;
|
|
36
|
-
this.options = options;
|
|
37
|
-
this.browserName = project.config.browser.name;
|
|
38
|
-
this.mocker = this.createMocker();
|
|
39
|
-
// make sure the traces are finished if the test hangs
|
|
40
|
-
process.on("SIGTERM", () => {
|
|
41
|
-
if (!this.browser) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const promises = [];
|
|
45
|
-
for (const [trace, contextId] of this.pendingTraces.entries()) {
|
|
46
|
-
promises.push((() => {
|
|
47
|
-
const context = this.contexts.get(contextId);
|
|
48
|
-
return context?.tracing.stopChunk({ path: trace });
|
|
49
|
-
})());
|
|
50
|
-
}
|
|
51
|
-
return Promise.allSettled(promises);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
async openBrowser() {
|
|
55
|
-
await this._throwIfClosing();
|
|
56
|
-
if (this.browserPromise) {
|
|
57
|
-
debug?.("[%s] the browser is resolving, reusing the promise", this.browserName);
|
|
58
|
-
return this.browserPromise;
|
|
59
|
-
}
|
|
60
|
-
if (this.browser) {
|
|
61
|
-
debug?.("[%s] the browser is resolved, reusing it", this.browserName);
|
|
62
|
-
return this.browser;
|
|
63
|
-
}
|
|
64
|
-
this.browserPromise = (async () => {
|
|
65
|
-
const options = this.project.config.browser;
|
|
66
|
-
const playwright = await import('playwright');
|
|
67
|
-
if (this.options.connectOptions) {
|
|
68
|
-
if (this.options.launchOptions) {
|
|
69
|
-
this.project.vitest.logger.warn(c.yellow(`Found both ${c.bold(c.italic(c.yellow("connect")))} and ${c.bold(c.italic(c.yellow("launch")))} options in browser instance configuration.
|
|
70
|
-
Ignoring ${c.bold(c.italic(c.yellow("launch")))} options and using ${c.bold(c.italic(c.yellow("connect")))} mode.
|
|
71
|
-
You probably want to remove one of the two options and keep only the one you want to use.`));
|
|
72
|
-
}
|
|
73
|
-
const browser = await playwright[this.browserName].connect(this.options.connectOptions.wsEndpoint, this.options.connectOptions);
|
|
74
|
-
this.browser = browser;
|
|
75
|
-
this.browserPromise = null;
|
|
76
|
-
return this.browser;
|
|
77
|
-
}
|
|
78
|
-
const launchOptions = {
|
|
79
|
-
...this.options.launchOptions,
|
|
80
|
-
headless: options.headless
|
|
81
|
-
};
|
|
82
|
-
if (typeof options.trace === "object" && options.trace.tracesDir) {
|
|
83
|
-
launchOptions.tracesDir = options.trace?.tracesDir;
|
|
84
|
-
}
|
|
85
|
-
const inspector = this.project.vitest.config.inspector;
|
|
86
|
-
if (inspector.enabled) {
|
|
87
|
-
// NodeJS equivalent defaults: https://nodejs.org/en/learn/getting-started/debugging#enable-inspector
|
|
88
|
-
const port = inspector.port || 9229;
|
|
89
|
-
const host = inspector.host || "127.0.0.1";
|
|
90
|
-
launchOptions.args ||= [];
|
|
91
|
-
launchOptions.args.push(`--remote-debugging-port=${port}`);
|
|
92
|
-
launchOptions.args.push(`--remote-debugging-address=${host}`);
|
|
93
|
-
this.project.vitest.logger.log(`Debugger listening on ws://${host}:${port}`);
|
|
94
|
-
}
|
|
95
|
-
// start Vitest UI maximized only on supported browsers
|
|
96
|
-
if (this.project.config.browser.ui && this.browserName === "chromium") {
|
|
97
|
-
if (!launchOptions.args) {
|
|
98
|
-
launchOptions.args = [];
|
|
99
|
-
}
|
|
100
|
-
if (!launchOptions.args.includes("--start-maximized") && !launchOptions.args.includes("--start-fullscreen")) {
|
|
101
|
-
launchOptions.args.push("--start-maximized");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
debug?.("[%s] initializing the browser with launch options: %O", this.browserName, launchOptions);
|
|
105
|
-
this.browser = await playwright[this.browserName].launch(launchOptions);
|
|
106
|
-
this.browserPromise = null;
|
|
107
|
-
return this.browser;
|
|
108
|
-
})();
|
|
109
|
-
return this.browserPromise;
|
|
110
|
-
}
|
|
111
|
-
createMocker() {
|
|
112
|
-
const idPreficates = new Map();
|
|
113
|
-
const sessionIds = new Map();
|
|
114
|
-
function createPredicate(sessionId, url) {
|
|
115
|
-
const moduleUrl = new URL(url, "http://localhost");
|
|
116
|
-
const predicate = (url) => {
|
|
117
|
-
if (url.searchParams.has("_vitest_original")) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
// different modules, ignore request
|
|
121
|
-
if (url.pathname !== moduleUrl.pathname) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
url.searchParams.delete("t");
|
|
125
|
-
url.searchParams.delete("v");
|
|
126
|
-
url.searchParams.delete("import");
|
|
127
|
-
// different search params, ignore request
|
|
128
|
-
if (url.searchParams.size !== moduleUrl.searchParams.size) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
// check that all search params are the same
|
|
132
|
-
for (const [param, value] of url.searchParams.entries()) {
|
|
133
|
-
if (moduleUrl.searchParams.get(param) !== value) {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return true;
|
|
138
|
-
};
|
|
139
|
-
const ids = sessionIds.get(sessionId) || [];
|
|
140
|
-
ids.push(moduleUrl.href);
|
|
141
|
-
sessionIds.set(sessionId, ids);
|
|
142
|
-
idPreficates.set(predicateKey(sessionId, moduleUrl.href), predicate);
|
|
143
|
-
return predicate;
|
|
144
|
-
}
|
|
145
|
-
function predicateKey(sessionId, url) {
|
|
146
|
-
return `${sessionId}:${url}`;
|
|
147
|
-
}
|
|
148
|
-
return {
|
|
149
|
-
register: async (sessionId, module) => {
|
|
150
|
-
const page = this.getPage(sessionId);
|
|
151
|
-
await page.route(createPredicate(sessionId, module.url), async (route) => {
|
|
152
|
-
if (module.type === "manual") {
|
|
153
|
-
const exports = Object.keys(await module.resolve());
|
|
154
|
-
const body = createManualModuleSource(module.url, exports);
|
|
155
|
-
return route.fulfill({
|
|
156
|
-
body,
|
|
157
|
-
headers: getHeaders(this.project.browser.vite.config)
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
// webkit doesn't support redirect responses
|
|
161
|
-
// https://github.com/microsoft/playwright/issues/18318
|
|
162
|
-
const isWebkit = this.browserName === "webkit";
|
|
163
|
-
if (isWebkit) {
|
|
164
|
-
let url;
|
|
165
|
-
if (module.type === "redirect") {
|
|
166
|
-
const redirect = new URL(module.redirect);
|
|
167
|
-
url = redirect.href.slice(redirect.origin.length);
|
|
168
|
-
} else {
|
|
169
|
-
const request = new URL(route.request().url());
|
|
170
|
-
request.searchParams.set("mock", module.type);
|
|
171
|
-
url = request.href.slice(request.origin.length);
|
|
172
|
-
}
|
|
173
|
-
const result = await this.project.browser.vite.transformRequest(url).catch(() => null);
|
|
174
|
-
if (!result) {
|
|
175
|
-
return route.continue();
|
|
176
|
-
}
|
|
177
|
-
let content = result.code;
|
|
178
|
-
if (result.map && "version" in result.map && result.map.mappings) {
|
|
179
|
-
const type = isDirectCSSRequest(url) ? "css" : "js";
|
|
180
|
-
content = getCodeWithSourcemap(type, content.toString(), result.map);
|
|
181
|
-
}
|
|
182
|
-
return route.fulfill({
|
|
183
|
-
body: content,
|
|
184
|
-
headers: getHeaders(this.project.browser.vite.config)
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (module.type === "redirect") {
|
|
188
|
-
return route.fulfill({
|
|
189
|
-
status: 302,
|
|
190
|
-
headers: { Location: module.redirect }
|
|
191
|
-
});
|
|
192
|
-
} else if (module.type === "automock" || module.type === "autospy") {
|
|
193
|
-
const url = new URL(route.request().url());
|
|
194
|
-
url.searchParams.set("mock", module.type);
|
|
195
|
-
return route.fulfill({
|
|
196
|
-
status: 302,
|
|
197
|
-
headers: { Location: url.href }
|
|
198
|
-
});
|
|
199
|
-
} else ;
|
|
200
|
-
});
|
|
201
|
-
},
|
|
202
|
-
delete: async (sessionId, id) => {
|
|
203
|
-
const page = this.getPage(sessionId);
|
|
204
|
-
const key = predicateKey(sessionId, id);
|
|
205
|
-
const predicate = idPreficates.get(key);
|
|
206
|
-
if (predicate) {
|
|
207
|
-
await page.unroute(predicate).finally(() => idPreficates.delete(key));
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
clear: async (sessionId) => {
|
|
211
|
-
const page = this.getPage(sessionId);
|
|
212
|
-
const ids = sessionIds.get(sessionId) || [];
|
|
213
|
-
const promises = ids.map((id) => {
|
|
214
|
-
const key = predicateKey(sessionId, id);
|
|
215
|
-
const predicate = idPreficates.get(key);
|
|
216
|
-
if (predicate) {
|
|
217
|
-
return page.unroute(predicate).finally(() => idPreficates.delete(key));
|
|
218
|
-
}
|
|
219
|
-
return null;
|
|
220
|
-
});
|
|
221
|
-
await Promise.all(promises).finally(() => sessionIds.delete(sessionId));
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
async createContext(sessionId) {
|
|
226
|
-
await this._throwIfClosing();
|
|
227
|
-
if (this.contexts.has(sessionId)) {
|
|
228
|
-
debug?.("[%s][%s] the context already exists, reusing it", sessionId, this.browserName);
|
|
229
|
-
return this.contexts.get(sessionId);
|
|
230
|
-
}
|
|
231
|
-
const browser = await this.openBrowser();
|
|
232
|
-
await this._throwIfClosing(browser);
|
|
233
|
-
const actionTimeout = this.options.actionTimeout;
|
|
234
|
-
const contextOptions = this.options.contextOptions ?? {};
|
|
235
|
-
const options = {
|
|
236
|
-
...contextOptions,
|
|
237
|
-
ignoreHTTPSErrors: true
|
|
238
|
-
};
|
|
239
|
-
if (this.project.config.browser.ui) {
|
|
240
|
-
options.viewport = null;
|
|
241
|
-
}
|
|
242
|
-
const context = await browser.newContext(options);
|
|
243
|
-
await this._throwIfClosing(context);
|
|
244
|
-
if (actionTimeout != null) {
|
|
245
|
-
context.setDefaultTimeout(actionTimeout);
|
|
246
|
-
}
|
|
247
|
-
debug?.("[%s][%s] the context is ready", sessionId, this.browserName);
|
|
248
|
-
this.contexts.set(sessionId, context);
|
|
249
|
-
return context;
|
|
250
|
-
}
|
|
251
|
-
getPage(sessionId) {
|
|
252
|
-
const page = this.pages.get(sessionId);
|
|
253
|
-
if (!page) {
|
|
254
|
-
throw new Error(`Page "${sessionId}" not found in ${this.browserName} browser.`);
|
|
255
|
-
}
|
|
256
|
-
return page;
|
|
257
|
-
}
|
|
258
|
-
getCommandsContext(sessionId) {
|
|
259
|
-
const page = this.getPage(sessionId);
|
|
260
|
-
return {
|
|
261
|
-
page,
|
|
262
|
-
context: this.contexts.get(sessionId),
|
|
263
|
-
frame() {
|
|
264
|
-
return new Promise((resolve, reject) => {
|
|
265
|
-
const frame = page.frame("vitest-iframe");
|
|
266
|
-
if (frame) {
|
|
267
|
-
return resolve(frame);
|
|
268
|
-
}
|
|
269
|
-
const timeout = setTimeout(() => {
|
|
270
|
-
const err = new Error(`Cannot find "vitest-iframe" on the page. This is a bug in Vitest, please report it.`);
|
|
271
|
-
reject(err);
|
|
272
|
-
}, 1e3).unref();
|
|
273
|
-
page.on("frameattached", (frame) => {
|
|
274
|
-
clearTimeout(timeout);
|
|
275
|
-
resolve(frame);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
},
|
|
279
|
-
get iframe() {
|
|
280
|
-
return page.frameLocator("[data-vitest=\"true\"]");
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
async openBrowserPage(sessionId) {
|
|
285
|
-
await this._throwIfClosing();
|
|
286
|
-
if (this.pages.has(sessionId)) {
|
|
287
|
-
debug?.("[%s][%s] the page already exists, closing the old one", sessionId, this.browserName);
|
|
288
|
-
const page = this.pages.get(sessionId);
|
|
289
|
-
await page.close();
|
|
290
|
-
this.pages.delete(sessionId);
|
|
291
|
-
}
|
|
292
|
-
const context = await this.createContext(sessionId);
|
|
293
|
-
const page = await context.newPage();
|
|
294
|
-
debug?.("[%s][%s] the page is ready", sessionId, this.browserName);
|
|
295
|
-
await this._throwIfClosing(page);
|
|
296
|
-
this.pages.set(sessionId, page);
|
|
297
|
-
if (process.env.VITEST_PW_DEBUG) {
|
|
298
|
-
page.on("requestfailed", (request) => {
|
|
299
|
-
console.error("[PW Error]", request.resourceType(), "request failed for", request.url(), "url:", request.failure()?.errorText);
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
return page;
|
|
303
|
-
}
|
|
304
|
-
async openPage(sessionId, url, beforeNavigate) {
|
|
305
|
-
debug?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
|
|
306
|
-
const browserPage = await this.openBrowserPage(sessionId);
|
|
307
|
-
await beforeNavigate?.();
|
|
308
|
-
debug?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
|
|
309
|
-
await browserPage.goto(url, { timeout: 0 });
|
|
310
|
-
await this._throwIfClosing(browserPage);
|
|
311
|
-
}
|
|
312
|
-
async _throwIfClosing(disposable) {
|
|
313
|
-
if (this.closing) {
|
|
314
|
-
debug?.("[%s] provider was closed, cannot perform the action on %s", this.browserName, String(disposable));
|
|
315
|
-
await disposable?.close();
|
|
316
|
-
this.pages.clear();
|
|
317
|
-
this.contexts.clear();
|
|
318
|
-
this.browser = null;
|
|
319
|
-
this.browserPromise = null;
|
|
320
|
-
throw new Error(`[vitest] The provider was closed.`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
async getCDPSession(sessionid) {
|
|
324
|
-
const page = this.getPage(sessionid);
|
|
325
|
-
const cdp = await page.context().newCDPSession(page);
|
|
326
|
-
return {
|
|
327
|
-
async send(method, params) {
|
|
328
|
-
const result = await cdp.send(method, params);
|
|
329
|
-
return result;
|
|
330
|
-
},
|
|
331
|
-
on(event, listener) {
|
|
332
|
-
cdp.on(event, listener);
|
|
333
|
-
},
|
|
334
|
-
off(event, listener) {
|
|
335
|
-
cdp.off(event, listener);
|
|
336
|
-
},
|
|
337
|
-
once(event, listener) {
|
|
338
|
-
cdp.once(event, listener);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
async close() {
|
|
343
|
-
debug?.("[%s] closing provider", this.browserName);
|
|
344
|
-
this.closing = true;
|
|
345
|
-
if (this.browserPromise) {
|
|
346
|
-
await this.browserPromise;
|
|
347
|
-
this.browserPromise = null;
|
|
348
|
-
}
|
|
349
|
-
const browser = this.browser;
|
|
350
|
-
this.browser = null;
|
|
351
|
-
await Promise.all([...this.pages.values()].map((p) => p.close()));
|
|
352
|
-
this.pages.clear();
|
|
353
|
-
await Promise.all([...this.contexts.values()].map((c) => c.close()));
|
|
354
|
-
this.contexts.clear();
|
|
355
|
-
await browser?.close();
|
|
356
|
-
debug?.("[%s] provider is closed", this.browserName);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function getHeaders(config) {
|
|
360
|
-
const headers = { "Content-Type": "application/javascript" };
|
|
361
|
-
for (const name in config.server.headers) {
|
|
362
|
-
headers[name] = String(config.server.headers[name]);
|
|
363
|
-
}
|
|
364
|
-
return headers;
|
|
365
|
-
}
|
|
366
|
-
function getCodeWithSourcemap(type, code, map) {
|
|
367
|
-
if (type === "js") {
|
|
368
|
-
code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}`;
|
|
369
|
-
} else if (type === "css") {
|
|
370
|
-
code += `\n/*# sourceMappingURL=${genSourceMapUrl(map)} */`;
|
|
371
|
-
}
|
|
372
|
-
return code;
|
|
373
|
-
}
|
|
374
|
-
function genSourceMapUrl(map) {
|
|
375
|
-
if (typeof map !== "string") {
|
|
376
|
-
map = JSON.stringify(map);
|
|
377
|
-
}
|
|
378
|
-
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
|
|
379
|
-
}
|
|
380
|
-
const directRequestRE = /[?&]direct\b/;
|
|
381
|
-
function isDirectCSSRequest(request) {
|
|
382
|
-
return isCSSRequest(request) && directRequestRE.test(request);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
export { PlaywrightBrowserProvider, playwright };
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { BrowserProviderOption, BrowserProvider, TestProject } from 'vitest/node';
|
|
2
|
-
|
|
3
|
-
declare function preview(): BrowserProviderOption;
|
|
4
|
-
declare class PreviewBrowserProvider implements BrowserProvider {
|
|
5
|
-
name: "preview";
|
|
6
|
-
supportsParallelism: boolean;
|
|
7
|
-
private project;
|
|
8
|
-
private open;
|
|
9
|
-
constructor(project: TestProject);
|
|
10
|
-
isOpen(): boolean;
|
|
11
|
-
getCommandsContext(): {};
|
|
12
|
-
openPage(_sessionId: string, url: string): Promise<void>;
|
|
13
|
-
close(): Promise<void>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export { PreviewBrowserProvider, preview };
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
function preview() {
|
|
2
|
-
return {
|
|
3
|
-
name: "preview",
|
|
4
|
-
options: {},
|
|
5
|
-
factory(project) {
|
|
6
|
-
return new PreviewBrowserProvider(project);
|
|
7
|
-
},
|
|
8
|
-
_cli: true
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
class PreviewBrowserProvider {
|
|
12
|
-
name = "preview";
|
|
13
|
-
supportsParallelism = false;
|
|
14
|
-
project;
|
|
15
|
-
open = false;
|
|
16
|
-
constructor(project) {
|
|
17
|
-
this.project = project;
|
|
18
|
-
this.open = false;
|
|
19
|
-
if (project.config.browser.headless) {
|
|
20
|
-
throw new Error("You've enabled headless mode for \"preview\" provider but it doesn't support it. Use \"playwright\" or \"webdriverio\" instead: https://vitest.dev/guide/browser/#configuration");
|
|
21
|
-
}
|
|
22
|
-
project.vitest.logger.printBrowserBanner(project);
|
|
23
|
-
}
|
|
24
|
-
isOpen() {
|
|
25
|
-
return this.open;
|
|
26
|
-
}
|
|
27
|
-
getCommandsContext() {
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
async openPage(_sessionId, url) {
|
|
31
|
-
this.open = true;
|
|
32
|
-
if (!this.project.browser) {
|
|
33
|
-
throw new Error("Browser is not initialized");
|
|
34
|
-
}
|
|
35
|
-
const options = this.project.browser.vite.config.server;
|
|
36
|
-
const _open = options.open;
|
|
37
|
-
options.open = url;
|
|
38
|
-
this.project.browser.vite.openBrowser();
|
|
39
|
-
options.open = _open;
|
|
40
|
-
}
|
|
41
|
-
async close() {}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export { PreviewBrowserProvider, preview };
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { ScreenshotMatcherOptions, ScreenshotComparatorRegistry } from '@vitest/browser/context';
|
|
2
|
-
import { BrowserProviderOption, BrowserProvider, TestProject, CDPSession } from 'vitest/node';
|
|
3
|
-
import { ClickOptions, DragAndDropOptions, remote } from 'webdriverio';
|
|
4
|
-
|
|
5
|
-
interface WebdriverProviderOptions extends Partial<Parameters<typeof remote>[0]> {}
|
|
6
|
-
declare function webdriverio(options?: WebdriverProviderOptions): BrowserProviderOption<WebdriverProviderOptions>;
|
|
7
|
-
declare class WebdriverBrowserProvider implements BrowserProvider {
|
|
8
|
-
name: "webdriverio";
|
|
9
|
-
supportsParallelism: boolean;
|
|
10
|
-
browser: WebdriverIO.Browser | null;
|
|
11
|
-
private browserName;
|
|
12
|
-
private project;
|
|
13
|
-
private options?;
|
|
14
|
-
private closing;
|
|
15
|
-
private iframeSwitched;
|
|
16
|
-
private topLevelContext;
|
|
17
|
-
getSupportedBrowsers(): readonly string[];
|
|
18
|
-
constructor(project: TestProject, options: WebdriverProviderOptions);
|
|
19
|
-
isIframeSwitched(): boolean;
|
|
20
|
-
switchToTestFrame(): Promise<void>;
|
|
21
|
-
switchToMainFrame(): Promise<void>;
|
|
22
|
-
setViewport(options: {
|
|
23
|
-
width: number;
|
|
24
|
-
height: number;
|
|
25
|
-
}): Promise<void>;
|
|
26
|
-
getCommandsContext(): {
|
|
27
|
-
browser: WebdriverIO.Browser | null;
|
|
28
|
-
};
|
|
29
|
-
openBrowser(): Promise<WebdriverIO.Browser>;
|
|
30
|
-
private buildCapabilities;
|
|
31
|
-
openPage(sessionId: string, url: string): Promise<void>;
|
|
32
|
-
private _throwIfClosing;
|
|
33
|
-
close(): Promise<void>;
|
|
34
|
-
getCDPSession(_sessionId: string): Promise<CDPSession>;
|
|
35
|
-
}
|
|
36
|
-
declare module "vitest/node" {
|
|
37
|
-
interface UserEventClickOptions extends ClickOptions {}
|
|
38
|
-
interface UserEventDragOptions extends DragAndDropOptions {
|
|
39
|
-
sourceX?: number;
|
|
40
|
-
sourceY?: number;
|
|
41
|
-
targetX?: number;
|
|
42
|
-
targetY?: number;
|
|
43
|
-
}
|
|
44
|
-
interface BrowserCommandContext {
|
|
45
|
-
browser: WebdriverIO.Browser;
|
|
46
|
-
}
|
|
47
|
-
interface ToMatchScreenshotOptions extends Omit<ScreenshotMatcherOptions, "comparatorName" | "comparatorOptions"> {}
|
|
48
|
-
interface ToMatchScreenshotComparators extends ScreenshotComparatorRegistry {}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export { WebdriverBrowserProvider, webdriverio };
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { createDebugger } from 'vitest/node';
|
|
2
|
-
|
|
3
|
-
const debug = createDebugger("vitest:browser:wdio");
|
|
4
|
-
const webdriverBrowsers = [
|
|
5
|
-
"firefox",
|
|
6
|
-
"chrome",
|
|
7
|
-
"edge",
|
|
8
|
-
"safari"
|
|
9
|
-
];
|
|
10
|
-
function webdriverio(options = {}) {
|
|
11
|
-
return {
|
|
12
|
-
name: "webdriverio",
|
|
13
|
-
supportedBrowser: webdriverBrowsers,
|
|
14
|
-
options,
|
|
15
|
-
factory(project) {
|
|
16
|
-
return new WebdriverBrowserProvider(project, options);
|
|
17
|
-
},
|
|
18
|
-
_cli: true
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
class WebdriverBrowserProvider {
|
|
22
|
-
name = "webdriverio";
|
|
23
|
-
supportsParallelism = false;
|
|
24
|
-
browser = null;
|
|
25
|
-
browserName;
|
|
26
|
-
project;
|
|
27
|
-
options;
|
|
28
|
-
closing = false;
|
|
29
|
-
iframeSwitched = false;
|
|
30
|
-
topLevelContext;
|
|
31
|
-
getSupportedBrowsers() {
|
|
32
|
-
return webdriverBrowsers;
|
|
33
|
-
}
|
|
34
|
-
constructor(project, options) {
|
|
35
|
-
// increase shutdown timeout because WDIO takes some extra time to kill the driver
|
|
36
|
-
if (!project.vitest.state._data.timeoutIncreased) {
|
|
37
|
-
project.vitest.state._data.timeoutIncreased = true;
|
|
38
|
-
project.vitest.config.teardownTimeout += 1e4;
|
|
39
|
-
}
|
|
40
|
-
this.closing = false;
|
|
41
|
-
this.project = project;
|
|
42
|
-
this.browserName = project.config.browser.name;
|
|
43
|
-
this.options = options;
|
|
44
|
-
}
|
|
45
|
-
isIframeSwitched() {
|
|
46
|
-
return this.iframeSwitched;
|
|
47
|
-
}
|
|
48
|
-
async switchToTestFrame() {
|
|
49
|
-
const browser = this.browser;
|
|
50
|
-
// support wdio@9
|
|
51
|
-
if (browser.switchFrame) {
|
|
52
|
-
await browser.switchFrame(browser.$("iframe[data-vitest]"));
|
|
53
|
-
} else {
|
|
54
|
-
const iframe = await browser.findElement("css selector", "iframe[data-vitest]");
|
|
55
|
-
await browser.switchToFrame(iframe);
|
|
56
|
-
}
|
|
57
|
-
this.iframeSwitched = true;
|
|
58
|
-
}
|
|
59
|
-
async switchToMainFrame() {
|
|
60
|
-
const page = this.browser;
|
|
61
|
-
if (page.switchFrame) {
|
|
62
|
-
await page.switchFrame(null);
|
|
63
|
-
} else {
|
|
64
|
-
await page.switchToParentFrame();
|
|
65
|
-
}
|
|
66
|
-
this.iframeSwitched = false;
|
|
67
|
-
}
|
|
68
|
-
async setViewport(options) {
|
|
69
|
-
if (this.topLevelContext == null || !this.browser) {
|
|
70
|
-
throw new Error(`The browser has no open pages.`);
|
|
71
|
-
}
|
|
72
|
-
await this.browser.send({
|
|
73
|
-
method: "browsingContext.setViewport",
|
|
74
|
-
params: {
|
|
75
|
-
context: this.topLevelContext,
|
|
76
|
-
devicePixelRatio: 1,
|
|
77
|
-
viewport: options
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
getCommandsContext() {
|
|
82
|
-
return { browser: this.browser };
|
|
83
|
-
}
|
|
84
|
-
async openBrowser() {
|
|
85
|
-
await this._throwIfClosing("opening the browser");
|
|
86
|
-
if (this.browser) {
|
|
87
|
-
debug?.("[%s] the browser is already opened, reusing it", this.browserName);
|
|
88
|
-
return this.browser;
|
|
89
|
-
}
|
|
90
|
-
const options = this.project.config.browser;
|
|
91
|
-
if (this.browserName === "safari") {
|
|
92
|
-
if (options.headless) {
|
|
93
|
-
throw new Error("You've enabled headless mode for Safari but it doesn't currently support it.");
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
const { remote } = await import('webdriverio');
|
|
97
|
-
const remoteOptions = {
|
|
98
|
-
logLevel: "silent",
|
|
99
|
-
...this.options,
|
|
100
|
-
capabilities: this.buildCapabilities()
|
|
101
|
-
};
|
|
102
|
-
debug?.("[%s] opening the browser with options: %O", this.browserName, remoteOptions);
|
|
103
|
-
// TODO: close everything, if browser is closed from the outside
|
|
104
|
-
this.browser = await remote(remoteOptions);
|
|
105
|
-
await this._throwIfClosing();
|
|
106
|
-
return this.browser;
|
|
107
|
-
}
|
|
108
|
-
buildCapabilities() {
|
|
109
|
-
const capabilities = {
|
|
110
|
-
...this.options?.capabilities,
|
|
111
|
-
browserName: this.browserName
|
|
112
|
-
};
|
|
113
|
-
const headlessMap = {
|
|
114
|
-
chrome: ["goog:chromeOptions", ["headless", "disable-gpu"]],
|
|
115
|
-
firefox: ["moz:firefoxOptions", ["-headless"]],
|
|
116
|
-
edge: ["ms:edgeOptions", ["--headless"]]
|
|
117
|
-
};
|
|
118
|
-
const options = this.project.config.browser;
|
|
119
|
-
const browser = this.browserName;
|
|
120
|
-
if (browser !== "safari" && options.headless) {
|
|
121
|
-
const [key, args] = headlessMap[browser];
|
|
122
|
-
const currentValues = (this.options?.capabilities)?.[key] || {};
|
|
123
|
-
const newArgs = [...currentValues.args || [], ...args];
|
|
124
|
-
capabilities[key] = {
|
|
125
|
-
...currentValues,
|
|
126
|
-
args: newArgs
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
// start Vitest UI maximized only on supported browsers
|
|
130
|
-
if (options.ui && (browser === "chrome" || browser === "edge")) {
|
|
131
|
-
const key = browser === "chrome" ? "goog:chromeOptions" : "ms:edgeOptions";
|
|
132
|
-
const args = capabilities[key]?.args || [];
|
|
133
|
-
if (!args.includes("--start-maximized") && !args.includes("--start-fullscreen")) {
|
|
134
|
-
args.push("--start-maximized");
|
|
135
|
-
}
|
|
136
|
-
capabilities[key] ??= {};
|
|
137
|
-
capabilities[key].args = args;
|
|
138
|
-
}
|
|
139
|
-
const inspector = this.project.vitest.config.inspector;
|
|
140
|
-
if (inspector.enabled && (browser === "chrome" || browser === "edge")) {
|
|
141
|
-
const key = browser === "chrome" ? "goog:chromeOptions" : "ms:edgeOptions";
|
|
142
|
-
const args = capabilities[key]?.args || [];
|
|
143
|
-
// NodeJS equivalent defaults: https://nodejs.org/en/learn/getting-started/debugging#enable-inspector
|
|
144
|
-
const port = inspector.port || 9229;
|
|
145
|
-
const host = inspector.host || "127.0.0.1";
|
|
146
|
-
args.push(`--remote-debugging-port=${port}`);
|
|
147
|
-
args.push(`--remote-debugging-address=${host}`);
|
|
148
|
-
this.project.vitest.logger.log(`Debugger listening on ws://${host}:${port}`);
|
|
149
|
-
capabilities[key] ??= {};
|
|
150
|
-
capabilities[key].args = args;
|
|
151
|
-
}
|
|
152
|
-
return capabilities;
|
|
153
|
-
}
|
|
154
|
-
async openPage(sessionId, url) {
|
|
155
|
-
await this._throwIfClosing("creating the browser");
|
|
156
|
-
debug?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
|
|
157
|
-
const browserInstance = await this.openBrowser();
|
|
158
|
-
debug?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
|
|
159
|
-
await browserInstance.url(url);
|
|
160
|
-
this.topLevelContext = await browserInstance.getWindowHandle();
|
|
161
|
-
await this._throwIfClosing("opening the url");
|
|
162
|
-
}
|
|
163
|
-
async _throwIfClosing(action) {
|
|
164
|
-
if (this.closing) {
|
|
165
|
-
debug?.(`[%s] provider was closed, cannot perform the action${action ? ` ${action}` : ""}`, this.browserName);
|
|
166
|
-
await (this.browser?.sessionId ? this.browser?.deleteSession?.() : null);
|
|
167
|
-
throw new Error(`[vitest] The provider was closed.`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
async close() {
|
|
171
|
-
debug?.("[%s] closing provider", this.browserName);
|
|
172
|
-
this.closing = true;
|
|
173
|
-
const browser = this.browser;
|
|
174
|
-
const sessionId = browser?.sessionId;
|
|
175
|
-
if (!browser || !sessionId) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
// https://github.com/webdriverio/webdriverio/blob/ab1a2e82b13a9c7d0e275ae87e7357e1b047d8d3/packages/wdio-runner/src/index.ts#L486
|
|
179
|
-
await browser.deleteSession();
|
|
180
|
-
browser.sessionId = undefined;
|
|
181
|
-
this.browser = null;
|
|
182
|
-
}
|
|
183
|
-
async getCDPSession(_sessionId) {
|
|
184
|
-
return {
|
|
185
|
-
send: (method, params) => {
|
|
186
|
-
if (!this.browser) {
|
|
187
|
-
throw new Error(`The environment was torn down.`);
|
|
188
|
-
}
|
|
189
|
-
return this.browser.sendCommandAndGetResult(method, params ?? {}).catch((error) => {
|
|
190
|
-
return Promise.reject(new Error(`Failed to execute "${method}" command.`, { cause: error }));
|
|
191
|
-
});
|
|
192
|
-
},
|
|
193
|
-
on: () => {
|
|
194
|
-
throw new Error(`webdriverio provider doesn't support cdp.on()`);
|
|
195
|
-
},
|
|
196
|
-
once: () => {
|
|
197
|
-
throw new Error(`webdriverio provider doesn't support cdp.once()`);
|
|
198
|
-
},
|
|
199
|
-
off: () => {
|
|
200
|
-
throw new Error(`webdriverio provider doesn't support cdp.off()`);
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export { WebdriverBrowserProvider, webdriverio };
|
package/dist/utils.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"@vitest/browser/context";export{u as debug,h as getElementError,t as getElementLocatorSelectors,v as prettyDOM}from"./public-utils-B6exS8fl.js";import"vitest/internal/browser";
|