@vitest/browser 4.0.0-beta.9 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -14
- package/context.d.ts +86 -29
- package/context.js +3 -2
- package/dist/client/.vite/manifest.json +6 -6
- package/dist/client/__vitest__/assets/index-COTh6lXR.css +1 -0
- package/dist/client/__vitest__/assets/index-DOkKC3NI.js +53 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/orchestrator-CFVVvVT1.js +313 -0
- package/dist/client/__vitest_browser__/tester-BNxij3za.js +2133 -0
- package/dist/client/__vitest_browser__/{utils-FY_Qin7d.js → utils-uxqdqUz8.js} +48 -24
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/tester.html +2 -2
- package/dist/client.js +1 -1
- package/dist/context.js +80 -19
- package/dist/expect-element.js +14 -14
- package/dist/index-BnLTaCRv.js +6 -0
- package/dist/index.d.ts +64 -165
- package/dist/index.js +572 -1431
- package/dist/{locators/index.d.ts → locators.d.ts} +27 -3
- package/dist/locators.js +1 -0
- package/dist/state.js +0 -1
- package/dist/types.d.ts +0 -1
- package/jest-dom.d.ts +5 -5
- package/matchers.d.ts +2 -2
- package/package.json +18 -54
- package/utils.d.ts +5 -5
- package/dist/client/__vitest__/assets/index-CBcuRGkf.js +0 -57
- package/dist/client/__vitest__/assets/index-KbpJLW--.css +0 -1
- package/dist/client/__vitest_browser__/orchestrator-C2rrmv36.js +0 -3198
- package/dist/client/__vitest_browser__/tester-mSVktQ7a.js +0 -3282
- package/dist/index-B7Hfmz-h.js +0 -1
- 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.js +0 -47
- package/dist/public-utils-Kx5DUGWa.js +0 -6
- package/dist/utils.js +0 -1
- package/dist/webdriver-AHRa6U3j.js +0 -517
- package/providers/playwright.d.ts +0 -97
- package/providers/webdriverio.d.ts +0 -35
- package/providers.d.ts +0 -7
|
@@ -1,517 +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$1 = createDebugger("vitest:browser:playwright");
|
|
6
|
-
const playwrightBrowsers = [
|
|
7
|
-
"firefox",
|
|
8
|
-
"webkit",
|
|
9
|
-
"chromium"
|
|
10
|
-
];
|
|
11
|
-
class PlaywrightBrowserProvider {
|
|
12
|
-
name = "playwright";
|
|
13
|
-
supportsParallelism = true;
|
|
14
|
-
browser = null;
|
|
15
|
-
browserName;
|
|
16
|
-
project;
|
|
17
|
-
options;
|
|
18
|
-
contexts = new Map();
|
|
19
|
-
pages = new Map();
|
|
20
|
-
browserPromise = null;
|
|
21
|
-
mocker;
|
|
22
|
-
closing = false;
|
|
23
|
-
getSupportedBrowsers() {
|
|
24
|
-
return playwrightBrowsers;
|
|
25
|
-
}
|
|
26
|
-
initialize(project, { browser, options }) {
|
|
27
|
-
this.closing = false;
|
|
28
|
-
this.project = project;
|
|
29
|
-
this.browserName = browser;
|
|
30
|
-
this.options = options;
|
|
31
|
-
this.mocker = this.createMocker();
|
|
32
|
-
}
|
|
33
|
-
async openBrowser() {
|
|
34
|
-
await this._throwIfClosing();
|
|
35
|
-
if (this.browserPromise) {
|
|
36
|
-
debug$1?.("[%s] the browser is resolving, reusing the promise", this.browserName);
|
|
37
|
-
return this.browserPromise;
|
|
38
|
-
}
|
|
39
|
-
if (this.browser) {
|
|
40
|
-
debug$1?.("[%s] the browser is resolved, reusing it", this.browserName);
|
|
41
|
-
return this.browser;
|
|
42
|
-
}
|
|
43
|
-
this.browserPromise = (async () => {
|
|
44
|
-
const options = this.project.config.browser;
|
|
45
|
-
const playwright = await import('playwright');
|
|
46
|
-
if (this.options?.connect) {
|
|
47
|
-
if (this.options.launch) {
|
|
48
|
-
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.
|
|
49
|
-
Ignoring ${c.bold(c.italic(c.yellow("launch")))} options and using ${c.bold(c.italic(c.yellow("connect")))} mode.
|
|
50
|
-
You probably want to remove one of the two options and keep only the one you want to use.`));
|
|
51
|
-
}
|
|
52
|
-
const browser = await playwright[this.browserName].connect(this.options.connect.wsEndpoint, this.options.connect.options);
|
|
53
|
-
this.browser = browser;
|
|
54
|
-
this.browserPromise = null;
|
|
55
|
-
return this.browser;
|
|
56
|
-
}
|
|
57
|
-
const launchOptions = {
|
|
58
|
-
...this.options?.launch,
|
|
59
|
-
headless: options.headless
|
|
60
|
-
};
|
|
61
|
-
if (this.project.config.inspector.enabled) {
|
|
62
|
-
// NodeJS equivalent defaults: https://nodejs.org/en/learn/getting-started/debugging#enable-inspector
|
|
63
|
-
const port = this.project.config.inspector.port || 9229;
|
|
64
|
-
const host = this.project.config.inspector.host || "127.0.0.1";
|
|
65
|
-
launchOptions.args ||= [];
|
|
66
|
-
launchOptions.args.push(`--remote-debugging-port=${port}`);
|
|
67
|
-
launchOptions.args.push(`--remote-debugging-address=${host}`);
|
|
68
|
-
this.project.vitest.logger.log(`Debugger listening on ws://${host}:${port}`);
|
|
69
|
-
}
|
|
70
|
-
// start Vitest UI maximized only on supported browsers
|
|
71
|
-
if (this.project.config.browser.ui && this.browserName === "chromium") {
|
|
72
|
-
if (!launchOptions.args) {
|
|
73
|
-
launchOptions.args = [];
|
|
74
|
-
}
|
|
75
|
-
if (!launchOptions.args.includes("--start-maximized") && !launchOptions.args.includes("--start-fullscreen")) {
|
|
76
|
-
launchOptions.args.push("--start-maximized");
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
debug$1?.("[%s] initializing the browser with launch options: %O", this.browserName, launchOptions);
|
|
80
|
-
this.browser = await playwright[this.browserName].launch(launchOptions);
|
|
81
|
-
this.browserPromise = null;
|
|
82
|
-
return this.browser;
|
|
83
|
-
})();
|
|
84
|
-
return this.browserPromise;
|
|
85
|
-
}
|
|
86
|
-
createMocker() {
|
|
87
|
-
const idPreficates = new Map();
|
|
88
|
-
const sessionIds = new Map();
|
|
89
|
-
function createPredicate(sessionId, url) {
|
|
90
|
-
const moduleUrl = new URL(url, "http://localhost");
|
|
91
|
-
const predicate = (url) => {
|
|
92
|
-
if (url.searchParams.has("_vitest_original")) {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
// different modules, ignore request
|
|
96
|
-
if (url.pathname !== moduleUrl.pathname) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
url.searchParams.delete("t");
|
|
100
|
-
url.searchParams.delete("v");
|
|
101
|
-
url.searchParams.delete("import");
|
|
102
|
-
// different search params, ignore request
|
|
103
|
-
if (url.searchParams.size !== moduleUrl.searchParams.size) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
// check that all search params are the same
|
|
107
|
-
for (const [param, value] of url.searchParams.entries()) {
|
|
108
|
-
if (moduleUrl.searchParams.get(param) !== value) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return true;
|
|
113
|
-
};
|
|
114
|
-
const ids = sessionIds.get(sessionId) || [];
|
|
115
|
-
ids.push(moduleUrl.href);
|
|
116
|
-
sessionIds.set(sessionId, ids);
|
|
117
|
-
idPreficates.set(predicateKey(sessionId, moduleUrl.href), predicate);
|
|
118
|
-
return predicate;
|
|
119
|
-
}
|
|
120
|
-
function predicateKey(sessionId, url) {
|
|
121
|
-
return `${sessionId}:${url}`;
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
register: async (sessionId, module) => {
|
|
125
|
-
const page = this.getPage(sessionId);
|
|
126
|
-
await page.route(createPredicate(sessionId, module.url), async (route) => {
|
|
127
|
-
if (module.type === "manual") {
|
|
128
|
-
const exports = Object.keys(await module.resolve());
|
|
129
|
-
const body = createManualModuleSource(module.url, exports);
|
|
130
|
-
return route.fulfill({
|
|
131
|
-
body,
|
|
132
|
-
headers: getHeaders(this.project.browser.vite.config)
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
// webkit doesn't support redirect responses
|
|
136
|
-
// https://github.com/microsoft/playwright/issues/18318
|
|
137
|
-
const isWebkit = this.browserName === "webkit";
|
|
138
|
-
if (isWebkit) {
|
|
139
|
-
let url;
|
|
140
|
-
if (module.type === "redirect") {
|
|
141
|
-
const redirect = new URL(module.redirect);
|
|
142
|
-
url = redirect.href.slice(redirect.origin.length);
|
|
143
|
-
} else {
|
|
144
|
-
const request = new URL(route.request().url());
|
|
145
|
-
request.searchParams.set("mock", module.type);
|
|
146
|
-
url = request.href.slice(request.origin.length);
|
|
147
|
-
}
|
|
148
|
-
const result = await this.project.browser.vite.transformRequest(url).catch(() => null);
|
|
149
|
-
if (!result) {
|
|
150
|
-
return route.continue();
|
|
151
|
-
}
|
|
152
|
-
let content = result.code;
|
|
153
|
-
if (result.map && "version" in result.map && result.map.mappings) {
|
|
154
|
-
const type = isDirectCSSRequest(url) ? "css" : "js";
|
|
155
|
-
content = getCodeWithSourcemap(type, content.toString(), result.map);
|
|
156
|
-
}
|
|
157
|
-
return route.fulfill({
|
|
158
|
-
body: content,
|
|
159
|
-
headers: getHeaders(this.project.browser.vite.config)
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
if (module.type === "redirect") {
|
|
163
|
-
return route.fulfill({
|
|
164
|
-
status: 302,
|
|
165
|
-
headers: { Location: module.redirect }
|
|
166
|
-
});
|
|
167
|
-
} else if (module.type === "automock" || module.type === "autospy") {
|
|
168
|
-
const url = new URL(route.request().url());
|
|
169
|
-
url.searchParams.set("mock", module.type);
|
|
170
|
-
return route.fulfill({
|
|
171
|
-
status: 302,
|
|
172
|
-
headers: { Location: url.href }
|
|
173
|
-
});
|
|
174
|
-
} else ;
|
|
175
|
-
});
|
|
176
|
-
},
|
|
177
|
-
delete: async (sessionId, id) => {
|
|
178
|
-
const page = this.getPage(sessionId);
|
|
179
|
-
const key = predicateKey(sessionId, id);
|
|
180
|
-
const predicate = idPreficates.get(key);
|
|
181
|
-
if (predicate) {
|
|
182
|
-
await page.unroute(predicate).finally(() => idPreficates.delete(key));
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
clear: async (sessionId) => {
|
|
186
|
-
const page = this.getPage(sessionId);
|
|
187
|
-
const ids = sessionIds.get(sessionId) || [];
|
|
188
|
-
const promises = ids.map((id) => {
|
|
189
|
-
const key = predicateKey(sessionId, id);
|
|
190
|
-
const predicate = idPreficates.get(key);
|
|
191
|
-
if (predicate) {
|
|
192
|
-
return page.unroute(predicate).finally(() => idPreficates.delete(key));
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
});
|
|
196
|
-
await Promise.all(promises).finally(() => sessionIds.delete(sessionId));
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
async createContext(sessionId) {
|
|
201
|
-
await this._throwIfClosing();
|
|
202
|
-
if (this.contexts.has(sessionId)) {
|
|
203
|
-
debug$1?.("[%s][%s] the context already exists, reusing it", sessionId, this.browserName);
|
|
204
|
-
return this.contexts.get(sessionId);
|
|
205
|
-
}
|
|
206
|
-
const browser = await this.openBrowser();
|
|
207
|
-
await this._throwIfClosing(browser);
|
|
208
|
-
const { actionTimeout,...contextOptions } = this.options?.context ?? {};
|
|
209
|
-
const options = {
|
|
210
|
-
...contextOptions,
|
|
211
|
-
ignoreHTTPSErrors: true
|
|
212
|
-
};
|
|
213
|
-
if (this.project.config.browser.ui) {
|
|
214
|
-
options.viewport = null;
|
|
215
|
-
}
|
|
216
|
-
const context = await browser.newContext(options);
|
|
217
|
-
await this._throwIfClosing(context);
|
|
218
|
-
if (actionTimeout) {
|
|
219
|
-
context.setDefaultTimeout(actionTimeout);
|
|
220
|
-
}
|
|
221
|
-
debug$1?.("[%s][%s] the context is ready", sessionId, this.browserName);
|
|
222
|
-
this.contexts.set(sessionId, context);
|
|
223
|
-
return context;
|
|
224
|
-
}
|
|
225
|
-
getPage(sessionId) {
|
|
226
|
-
const page = this.pages.get(sessionId);
|
|
227
|
-
if (!page) {
|
|
228
|
-
throw new Error(`Page "${sessionId}" not found in ${this.browserName} browser.`);
|
|
229
|
-
}
|
|
230
|
-
return page;
|
|
231
|
-
}
|
|
232
|
-
getCommandsContext(sessionId) {
|
|
233
|
-
const page = this.getPage(sessionId);
|
|
234
|
-
return {
|
|
235
|
-
page,
|
|
236
|
-
context: this.contexts.get(sessionId),
|
|
237
|
-
frame() {
|
|
238
|
-
return new Promise((resolve, reject) => {
|
|
239
|
-
const frame = page.frame("vitest-iframe");
|
|
240
|
-
if (frame) {
|
|
241
|
-
return resolve(frame);
|
|
242
|
-
}
|
|
243
|
-
const timeout = setTimeout(() => {
|
|
244
|
-
const err = new Error(`Cannot find "vitest-iframe" on the page. This is a bug in Vitest, please report it.`);
|
|
245
|
-
reject(err);
|
|
246
|
-
}, 1e3).unref();
|
|
247
|
-
page.on("frameattached", (frame) => {
|
|
248
|
-
clearTimeout(timeout);
|
|
249
|
-
resolve(frame);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
},
|
|
253
|
-
get iframe() {
|
|
254
|
-
return page.frameLocator("[data-vitest=\"true\"]");
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
async openBrowserPage(sessionId) {
|
|
259
|
-
await this._throwIfClosing();
|
|
260
|
-
if (this.pages.has(sessionId)) {
|
|
261
|
-
debug$1?.("[%s][%s] the page already exists, closing the old one", sessionId, this.browserName);
|
|
262
|
-
const page = this.pages.get(sessionId);
|
|
263
|
-
await page.close();
|
|
264
|
-
this.pages.delete(sessionId);
|
|
265
|
-
}
|
|
266
|
-
const context = await this.createContext(sessionId);
|
|
267
|
-
const page = await context.newPage();
|
|
268
|
-
debug$1?.("[%s][%s] the page is ready", sessionId, this.browserName);
|
|
269
|
-
await this._throwIfClosing(page);
|
|
270
|
-
this.pages.set(sessionId, page);
|
|
271
|
-
if (process.env.VITEST_PW_DEBUG) {
|
|
272
|
-
page.on("requestfailed", (request) => {
|
|
273
|
-
console.error("[PW Error]", request.resourceType(), "request failed for", request.url(), "url:", request.failure()?.errorText);
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
return page;
|
|
277
|
-
}
|
|
278
|
-
async openPage(sessionId, url, beforeNavigate) {
|
|
279
|
-
debug$1?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
|
|
280
|
-
const browserPage = await this.openBrowserPage(sessionId);
|
|
281
|
-
await beforeNavigate?.();
|
|
282
|
-
debug$1?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
|
|
283
|
-
await browserPage.goto(url, { timeout: 0 });
|
|
284
|
-
await this._throwIfClosing(browserPage);
|
|
285
|
-
}
|
|
286
|
-
async _throwIfClosing(disposable) {
|
|
287
|
-
if (this.closing) {
|
|
288
|
-
debug$1?.("[%s] provider was closed, cannot perform the action on %s", this.browserName, String(disposable));
|
|
289
|
-
await disposable?.close();
|
|
290
|
-
this.pages.clear();
|
|
291
|
-
this.contexts.clear();
|
|
292
|
-
this.browser = null;
|
|
293
|
-
this.browserPromise = null;
|
|
294
|
-
throw new Error(`[vitest] The provider was closed.`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
async getCDPSession(sessionid) {
|
|
298
|
-
const page = this.getPage(sessionid);
|
|
299
|
-
const cdp = await page.context().newCDPSession(page);
|
|
300
|
-
return {
|
|
301
|
-
async send(method, params) {
|
|
302
|
-
const result = await cdp.send(method, params);
|
|
303
|
-
return result;
|
|
304
|
-
},
|
|
305
|
-
on(event, listener) {
|
|
306
|
-
cdp.on(event, listener);
|
|
307
|
-
},
|
|
308
|
-
off(event, listener) {
|
|
309
|
-
cdp.off(event, listener);
|
|
310
|
-
},
|
|
311
|
-
once(event, listener) {
|
|
312
|
-
cdp.once(event, listener);
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
async close() {
|
|
317
|
-
debug$1?.("[%s] closing provider", this.browserName);
|
|
318
|
-
this.closing = true;
|
|
319
|
-
const browser = this.browser;
|
|
320
|
-
this.browser = null;
|
|
321
|
-
if (this.browserPromise) {
|
|
322
|
-
await this.browserPromise;
|
|
323
|
-
this.browserPromise = null;
|
|
324
|
-
}
|
|
325
|
-
await Promise.all([...this.pages.values()].map((p) => p.close()));
|
|
326
|
-
this.pages.clear();
|
|
327
|
-
await Promise.all([...this.contexts.values()].map((c) => c.close()));
|
|
328
|
-
this.contexts.clear();
|
|
329
|
-
await browser?.close();
|
|
330
|
-
debug$1?.("[%s] provider is closed", this.browserName);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
function getHeaders(config) {
|
|
334
|
-
const headers = { "Content-Type": "application/javascript" };
|
|
335
|
-
for (const name in config.server.headers) {
|
|
336
|
-
headers[name] = String(config.server.headers[name]);
|
|
337
|
-
}
|
|
338
|
-
return headers;
|
|
339
|
-
}
|
|
340
|
-
function getCodeWithSourcemap(type, code, map) {
|
|
341
|
-
if (type === "js") {
|
|
342
|
-
code += `\n//# sourceMappingURL=${genSourceMapUrl(map)}`;
|
|
343
|
-
} else if (type === "css") {
|
|
344
|
-
code += `\n/*# sourceMappingURL=${genSourceMapUrl(map)} */`;
|
|
345
|
-
}
|
|
346
|
-
return code;
|
|
347
|
-
}
|
|
348
|
-
function genSourceMapUrl(map) {
|
|
349
|
-
if (typeof map !== "string") {
|
|
350
|
-
map = JSON.stringify(map);
|
|
351
|
-
}
|
|
352
|
-
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
|
|
353
|
-
}
|
|
354
|
-
const directRequestRE = /[?&]direct\b/;
|
|
355
|
-
function isDirectCSSRequest(request) {
|
|
356
|
-
return isCSSRequest(request) && directRequestRE.test(request);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const debug = createDebugger("vitest:browser:wdio");
|
|
360
|
-
const webdriverBrowsers = [
|
|
361
|
-
"firefox",
|
|
362
|
-
"chrome",
|
|
363
|
-
"edge",
|
|
364
|
-
"safari"
|
|
365
|
-
];
|
|
366
|
-
class WebdriverBrowserProvider {
|
|
367
|
-
name = "webdriverio";
|
|
368
|
-
supportsParallelism = false;
|
|
369
|
-
browser = null;
|
|
370
|
-
browserName;
|
|
371
|
-
project;
|
|
372
|
-
options;
|
|
373
|
-
closing = false;
|
|
374
|
-
iframeSwitched = false;
|
|
375
|
-
topLevelContext;
|
|
376
|
-
getSupportedBrowsers() {
|
|
377
|
-
return webdriverBrowsers;
|
|
378
|
-
}
|
|
379
|
-
async initialize(project, { browser, options }) {
|
|
380
|
-
// increase shutdown timeout because WDIO takes some extra time to kill the driver
|
|
381
|
-
if (!project.vitest.state._data.timeoutIncreased) {
|
|
382
|
-
project.vitest.state._data.timeoutIncreased = true;
|
|
383
|
-
project.vitest.config.teardownTimeout += 1e4;
|
|
384
|
-
}
|
|
385
|
-
this.closing = false;
|
|
386
|
-
this.project = project;
|
|
387
|
-
this.browserName = browser;
|
|
388
|
-
this.options = options;
|
|
389
|
-
}
|
|
390
|
-
isIframeSwitched() {
|
|
391
|
-
return this.iframeSwitched;
|
|
392
|
-
}
|
|
393
|
-
async switchToTestFrame() {
|
|
394
|
-
const browser = this.browser;
|
|
395
|
-
// support wdio@9
|
|
396
|
-
if (browser.switchFrame) {
|
|
397
|
-
await browser.switchFrame(browser.$("iframe[data-vitest]"));
|
|
398
|
-
} else {
|
|
399
|
-
const iframe = await browser.findElement("css selector", "iframe[data-vitest]");
|
|
400
|
-
await browser.switchToFrame(iframe);
|
|
401
|
-
}
|
|
402
|
-
this.iframeSwitched = true;
|
|
403
|
-
}
|
|
404
|
-
async switchToMainFrame() {
|
|
405
|
-
const page = this.browser;
|
|
406
|
-
if (page.switchFrame) {
|
|
407
|
-
await page.switchFrame(null);
|
|
408
|
-
} else {
|
|
409
|
-
await page.switchToParentFrame();
|
|
410
|
-
}
|
|
411
|
-
this.iframeSwitched = false;
|
|
412
|
-
}
|
|
413
|
-
async setViewport(options) {
|
|
414
|
-
if (this.topLevelContext == null || !this.browser) {
|
|
415
|
-
throw new Error(`The browser has no open pages.`);
|
|
416
|
-
}
|
|
417
|
-
await this.browser.send({
|
|
418
|
-
method: "browsingContext.setViewport",
|
|
419
|
-
params: {
|
|
420
|
-
context: this.topLevelContext,
|
|
421
|
-
devicePixelRatio: 1,
|
|
422
|
-
viewport: options
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
getCommandsContext() {
|
|
427
|
-
return { browser: this.browser };
|
|
428
|
-
}
|
|
429
|
-
async openBrowser() {
|
|
430
|
-
await this._throwIfClosing("opening the browser");
|
|
431
|
-
if (this.browser) {
|
|
432
|
-
debug?.("[%s] the browser is already opened, reusing it", this.browserName);
|
|
433
|
-
return this.browser;
|
|
434
|
-
}
|
|
435
|
-
const options = this.project.config.browser;
|
|
436
|
-
if (this.browserName === "safari") {
|
|
437
|
-
if (options.headless) {
|
|
438
|
-
throw new Error("You've enabled headless mode for Safari but it doesn't currently support it.");
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
const { remote } = await import('webdriverio');
|
|
442
|
-
const remoteOptions = {
|
|
443
|
-
...this.options,
|
|
444
|
-
logLevel: "error",
|
|
445
|
-
capabilities: this.buildCapabilities()
|
|
446
|
-
};
|
|
447
|
-
debug?.("[%s] opening the browser with options: %O", this.browserName, remoteOptions);
|
|
448
|
-
// TODO: close everything, if browser is closed from the outside
|
|
449
|
-
this.browser = await remote(remoteOptions);
|
|
450
|
-
await this._throwIfClosing();
|
|
451
|
-
return this.browser;
|
|
452
|
-
}
|
|
453
|
-
buildCapabilities() {
|
|
454
|
-
const capabilities = {
|
|
455
|
-
...this.options?.capabilities,
|
|
456
|
-
browserName: this.browserName
|
|
457
|
-
};
|
|
458
|
-
const headlessMap = {
|
|
459
|
-
chrome: ["goog:chromeOptions", ["headless", "disable-gpu"]],
|
|
460
|
-
firefox: ["moz:firefoxOptions", ["-headless"]],
|
|
461
|
-
edge: ["ms:edgeOptions", ["--headless"]]
|
|
462
|
-
};
|
|
463
|
-
const options = this.project.config.browser;
|
|
464
|
-
const browser = this.browserName;
|
|
465
|
-
if (browser !== "safari" && options.headless) {
|
|
466
|
-
const [key, args] = headlessMap[browser];
|
|
467
|
-
const currentValues = (this.options?.capabilities)?.[key] || {};
|
|
468
|
-
const newArgs = [...currentValues.args || [], ...args];
|
|
469
|
-
capabilities[key] = {
|
|
470
|
-
...currentValues,
|
|
471
|
-
args: newArgs
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
// start Vitest UI maximized only on supported browsers
|
|
475
|
-
if (options.ui && (browser === "chrome" || browser === "edge")) {
|
|
476
|
-
const key = browser === "chrome" ? "goog:chromeOptions" : "ms:edgeOptions";
|
|
477
|
-
const args = capabilities[key]?.args || [];
|
|
478
|
-
if (!args.includes("--start-maximized") && !args.includes("--start-fullscreen")) {
|
|
479
|
-
args.push("--start-maximized");
|
|
480
|
-
}
|
|
481
|
-
capabilities[key] ??= {};
|
|
482
|
-
capabilities[key].args = args;
|
|
483
|
-
}
|
|
484
|
-
return capabilities;
|
|
485
|
-
}
|
|
486
|
-
async openPage(sessionId, url) {
|
|
487
|
-
await this._throwIfClosing("creating the browser");
|
|
488
|
-
debug?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
|
|
489
|
-
const browserInstance = await this.openBrowser();
|
|
490
|
-
debug?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
|
|
491
|
-
await browserInstance.url(url);
|
|
492
|
-
this.topLevelContext = await browserInstance.getWindowHandle();
|
|
493
|
-
await this._throwIfClosing("opening the url");
|
|
494
|
-
}
|
|
495
|
-
async _throwIfClosing(action) {
|
|
496
|
-
if (this.closing) {
|
|
497
|
-
debug?.(`[%s] provider was closed, cannot perform the action${action ? ` ${action}` : ""}`, this.browserName);
|
|
498
|
-
await (this.browser?.sessionId ? this.browser?.deleteSession?.() : null);
|
|
499
|
-
throw new Error(`[vitest] The provider was closed.`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
async close() {
|
|
503
|
-
debug?.("[%s] closing provider", this.browserName);
|
|
504
|
-
this.closing = true;
|
|
505
|
-
const browser = this.browser;
|
|
506
|
-
const sessionId = browser?.sessionId;
|
|
507
|
-
if (!browser || !sessionId) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
// https://github.com/webdriverio/webdriverio/blob/ab1a2e82b13a9c7d0e275ae87e7357e1b047d8d3/packages/wdio-runner/src/index.ts#L486
|
|
511
|
-
await browser.deleteSession();
|
|
512
|
-
browser.sessionId = undefined;
|
|
513
|
-
this.browser = null;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
export { PlaywrightBrowserProvider as P, WebdriverBrowserProvider as W };
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BrowserContext,
|
|
3
|
-
BrowserContextOptions,
|
|
4
|
-
Frame,
|
|
5
|
-
FrameLocator,
|
|
6
|
-
LaunchOptions,
|
|
7
|
-
Page,
|
|
8
|
-
CDPSession,
|
|
9
|
-
ConnectOptions
|
|
10
|
-
} from 'playwright'
|
|
11
|
-
import { Protocol } from 'playwright-core/types/protocol'
|
|
12
|
-
import '../matchers.js'
|
|
13
|
-
import type {} from "vitest/node"
|
|
14
|
-
import type {
|
|
15
|
-
Locator,
|
|
16
|
-
ScreenshotComparatorRegistry,
|
|
17
|
-
ScreenshotMatcherOptions,
|
|
18
|
-
} from "@vitest/browser/context"
|
|
19
|
-
|
|
20
|
-
declare module 'vitest/node' {
|
|
21
|
-
export interface BrowserProviderOptions {
|
|
22
|
-
launch?: LaunchOptions
|
|
23
|
-
connect?: {
|
|
24
|
-
wsEndpoint: string
|
|
25
|
-
options?: ConnectOptions
|
|
26
|
-
}
|
|
27
|
-
context?: Omit<
|
|
28
|
-
BrowserContextOptions,
|
|
29
|
-
'ignoreHTTPSErrors' | 'serviceWorkers'
|
|
30
|
-
> & {
|
|
31
|
-
/**
|
|
32
|
-
* The maximum time in milliseconds to wait for `userEvent` action to complete.
|
|
33
|
-
* @default 0 (no timeout)
|
|
34
|
-
*/
|
|
35
|
-
actionTimeout?: number
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface BrowserCommandContext {
|
|
40
|
-
page: Page
|
|
41
|
-
frame(): Promise<Frame>
|
|
42
|
-
iframe: FrameLocator
|
|
43
|
-
context: BrowserContext
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface ToMatchScreenshotOptions
|
|
47
|
-
extends Omit<
|
|
48
|
-
ScreenshotMatcherOptions,
|
|
49
|
-
"comparatorName" | "comparatorOptions"
|
|
50
|
-
> {}
|
|
51
|
-
|
|
52
|
-
export interface ToMatchScreenshotComparators
|
|
53
|
-
extends ScreenshotComparatorRegistry {}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type PWHoverOptions = NonNullable<Parameters<Page['hover']>[1]>
|
|
57
|
-
type PWClickOptions = NonNullable<Parameters<Page['click']>[1]>
|
|
58
|
-
type PWDoubleClickOptions = NonNullable<Parameters<Page['dblclick']>[1]>
|
|
59
|
-
type PWFillOptions = NonNullable<Parameters<Page['fill']>[2]>
|
|
60
|
-
type PWScreenshotOptions = NonNullable<Parameters<Page['screenshot']>[0]>
|
|
61
|
-
type PWSelectOptions = NonNullable<Parameters<Page['selectOption']>[2]>
|
|
62
|
-
type PWDragAndDropOptions = NonNullable<Parameters<Page['dragAndDrop']>[2]>
|
|
63
|
-
type PWSetInputFiles = NonNullable<Parameters<Page['setInputFiles']>[2]>
|
|
64
|
-
|
|
65
|
-
declare module '@vitest/browser/context' {
|
|
66
|
-
export interface UserEventHoverOptions extends PWHoverOptions {}
|
|
67
|
-
export interface UserEventClickOptions extends PWClickOptions {}
|
|
68
|
-
export interface UserEventDoubleClickOptions extends PWDoubleClickOptions {}
|
|
69
|
-
export interface UserEventTripleClickOptions extends PWClickOptions {}
|
|
70
|
-
export interface UserEventFillOptions extends PWFillOptions {}
|
|
71
|
-
export interface UserEventSelectOptions extends PWSelectOptions {}
|
|
72
|
-
export interface UserEventDragAndDropOptions extends PWDragAndDropOptions {}
|
|
73
|
-
export interface UserEventUploadOptions extends PWSetInputFiles {}
|
|
74
|
-
|
|
75
|
-
export interface ScreenshotOptions extends Omit<PWScreenshotOptions, 'mask'> {
|
|
76
|
-
mask?: ReadonlyArray<Element | Locator> | undefined
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface CDPSession {
|
|
80
|
-
send<T extends keyof Protocol.CommandParameters>(
|
|
81
|
-
method: T,
|
|
82
|
-
params?: Protocol.CommandParameters[T]
|
|
83
|
-
): Promise<Protocol.CommandReturnValues[T]>
|
|
84
|
-
on<T extends keyof Protocol.Events>(
|
|
85
|
-
event: T,
|
|
86
|
-
listener: (payload: Protocol.Events[T]) => void
|
|
87
|
-
): this;
|
|
88
|
-
once<T extends keyof Protocol.Events>(
|
|
89
|
-
event: T,
|
|
90
|
-
listener: (payload: Protocol.Events[T]) => void
|
|
91
|
-
): this;
|
|
92
|
-
off<T extends keyof Protocol.Events>(
|
|
93
|
-
event: T,
|
|
94
|
-
listener: (payload: Protocol.Events[T]) => void
|
|
95
|
-
): this;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { remote, ClickOptions, DragAndDropOptions } from 'webdriverio'
|
|
2
|
-
import '../matchers.js'
|
|
3
|
-
import type {} from "vitest/node"
|
|
4
|
-
import type {
|
|
5
|
-
ScreenshotComparatorRegistry,
|
|
6
|
-
ScreenshotMatcherOptions,
|
|
7
|
-
} from "@vitest/browser/context";
|
|
8
|
-
|
|
9
|
-
declare module 'vitest/node' {
|
|
10
|
-
export interface BrowserProviderOptions extends Partial<
|
|
11
|
-
Parameters<typeof remote>[0]
|
|
12
|
-
> {}
|
|
13
|
-
|
|
14
|
-
export interface UserEventClickOptions extends ClickOptions {}
|
|
15
|
-
|
|
16
|
-
export interface UserEventDragOptions extends DragAndDropOptions {
|
|
17
|
-
sourceX?: number
|
|
18
|
-
sourceY?: number
|
|
19
|
-
targetX?: number
|
|
20
|
-
targetY?: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface BrowserCommandContext {
|
|
24
|
-
browser: WebdriverIO.Browser
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface ToMatchScreenshotOptions
|
|
28
|
-
extends Omit<
|
|
29
|
-
ScreenshotMatcherOptions,
|
|
30
|
-
"comparatorName" | "comparatorOptions"
|
|
31
|
-
> {}
|
|
32
|
-
|
|
33
|
-
export interface ToMatchScreenshotComparators
|
|
34
|
-
extends ScreenshotComparatorRegistry {}
|
|
35
|
-
}
|
package/providers.d.ts
DELETED