@vitest/browser 2.0.5 → 2.1.0-beta.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.
@@ -2,7 +2,8 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" href="./favicon.svg" type="image/svg+xml" />
5
+ <link rel="icon" href="./favicon.ico" sizes="48x48">
6
+ <link rel="icon" href="./favicon.svg" sizes="any" type="image/svg+xml">
6
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
8
  <title>Vitest</title>
8
9
  <link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -22,8 +23,8 @@
22
23
  })();
23
24
  </script>
24
25
  <!-- !LOAD_METADATA! -->
25
- <script type="module" crossorigin src="./assets/index--EAEh9Ei.js"></script>
26
- <link rel="stylesheet" crossorigin href="./assets/index-C0SwoDYm.css">
26
+ <script type="module" crossorigin src="./assets/index-uh8h5Yzr.js"></script>
27
+ <link rel="stylesheet" crossorigin href="./assets/index-JTfsnJeu.css">
27
28
  </head>
28
29
  <body>
29
30
  <div id="app"></div>
@@ -186,7 +186,8 @@ class IframeOrchestrator {
186
186
  const config = getConfig();
187
187
  const container = await getContainer(config);
188
188
  if (config.browser.ui) {
189
- container.className = "scrolls";
189
+ container.className = "absolute origin-top mt-[8px]";
190
+ container.parentElement.setAttribute("data-ready", "true");
190
191
  container.textContent = "";
191
192
  }
192
193
  const { width, height } = config.browser.viewport;
@@ -229,10 +230,9 @@ class IframeOrchestrator {
229
230
  `${url.pathname}__vitest_test__/__test__/${getBrowserState().contextId}/${encodeURIComponent(file)}`
230
231
  );
231
232
  iframe.setAttribute("data-vitest", "true");
232
- iframe.style.display = "block";
233
233
  iframe.style.border = "none";
234
- iframe.style.zIndex = "1";
235
- iframe.style.position = "relative";
234
+ iframe.style.width = "100%";
235
+ iframe.style.height = "100%";
236
236
  iframe.setAttribute("allowfullscreen", "true");
237
237
  iframe.setAttribute("allow", "clipboard-write;");
238
238
  iframe.setAttribute("name", "vitest-iframe");
@@ -11880,8 +11880,16 @@ const matchers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
11880
11880
  }, Symbol.toStringTag, { value: "Module" }));
11881
11881
  async function setupExpectDom() {
11882
11882
  expect.extend(matchers);
11883
- expect.element = (element, options) => {
11884
- return expect.poll(() => element, options);
11883
+ expect.element = (elementOrLocator, options) => {
11884
+ if (!(elementOrLocator instanceof Element) && !("element" in elementOrLocator)) {
11885
+ throw new Error(`Invalid element or locator: ${elementOrLocator}. Expected an instance of Element or Locator, received ${typeof elementOrLocator}`);
11886
+ }
11887
+ return expect.poll(() => {
11888
+ if (elementOrLocator instanceof Element) {
11889
+ return elementOrLocator;
11890
+ }
11891
+ return elementOrLocator.element();
11892
+ }, options);
11885
11893
  };
11886
11894
  }
11887
11895
  const url = new URL(location.href);
@@ -1,10 +1,5 @@
1
1
  import { channel, client } from '@vitest/browser/client'
2
2
 
3
- function on(event, listener) {
4
- window.addEventListener(event, listener)
5
- return () => window.removeEventListener(event, listener)
6
- }
7
-
8
3
  function serializeError(unhandledError) {
9
4
  if (typeof unhandledError !== 'object' || !unhandledError) {
10
5
  return {
@@ -19,41 +14,40 @@ function serializeError(unhandledError) {
19
14
  }
20
15
  }
21
16
 
22
- function catchWindowErrors(cb) {
17
+ function catchWindowErrors(errorEvent, prop, cb) {
23
18
  let userErrorListenerCount = 0
24
19
  function throwUnhandlerError(e) {
25
- if (userErrorListenerCount === 0 && e.error != null) {
20
+ if (userErrorListenerCount === 0 && e[prop] != null) {
26
21
  cb(e)
27
22
  }
28
23
  else {
29
- console.error(e.error)
24
+ console.error(e[prop])
30
25
  }
31
26
  }
32
27
  const addEventListener = window.addEventListener.bind(window)
33
28
  const removeEventListener = window.removeEventListener.bind(window)
34
- window.addEventListener('error', throwUnhandlerError)
29
+ window.addEventListener(errorEvent, throwUnhandlerError)
35
30
  window.addEventListener = function (...args) {
36
- if (args[0] === 'error') {
31
+ if (args[0] === errorEvent) {
37
32
  userErrorListenerCount++
38
33
  }
39
34
  return addEventListener.apply(this, args)
40
35
  }
41
36
  window.removeEventListener = function (...args) {
42
- if (args[0] === 'error' && userErrorListenerCount) {
37
+ if (args[0] === errorEvent && userErrorListenerCount) {
43
38
  userErrorListenerCount--
44
39
  }
45
40
  return removeEventListener.apply(this, args)
46
41
  }
47
42
  return function clearErrorHandlers() {
48
- window.removeEventListener('error', throwUnhandlerError)
43
+ window.removeEventListener(errorEvent, throwUnhandlerError)
49
44
  }
50
45
  }
51
46
 
52
47
  function registerUnexpectedErrors() {
53
- catchWindowErrors(event =>
54
- reportUnexpectedError('Error', event.error),
55
- )
56
- on('unhandledrejection', event =>
48
+ catchWindowErrors('error', 'error', event =>
49
+ reportUnexpectedError('Error', event.error))
50
+ catchWindowErrors('unhandledrejection', 'reason', event =>
57
51
  reportUnexpectedError('Unhandled Rejection', event.reason))
58
52
  }
59
53
 
@@ -26,7 +26,7 @@
26
26
  {__VITEST_INJECTOR__}
27
27
  {__VITEST_ERROR_CATCHER__}
28
28
  {__VITEST_SCRIPTS__}
29
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-CNOAigTE.js"></script>
29
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-BObhEEvc.js"></script>
30
30
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/preload-helper-Btt6SgIy.js">
31
31
  </head>
32
32
  <body>
@@ -18,20 +18,12 @@
18
18
  </style>
19
19
  {__VITEST_INJECTOR__}
20
20
  <script>{__VITEST_STATE__}</script>
21
- {__VITEST_ERROR_CATCHER__}
21
+ {__VITEST_INTERNAL_SCRIPTS__}
22
22
  {__VITEST_SCRIPTS__}
23
- <script type="module" crossorigin src="/__vitest_browser__/tester-l8kjusoM.js"></script>
23
+ <script type="module" crossorigin src="/__vitest_browser__/tester-CRcWWhrn.js"></script>
24
24
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/preload-helper-Btt6SgIy.js">
25
25
  </head>
26
- <body
27
- data-vitest-body
28
- style="
29
- width: 100%;
30
- height: 100%;
31
- transform: scale(1);
32
- transform-origin: left top;
33
- "
34
- >
26
+ <body data-vitest-body>
35
27
  {__VITEST_APPEND__}
36
28
  </body>
37
29
  </html>
package/dist/context.js CHANGED
@@ -1,15 +1,16 @@
1
- const state = () => __vitest_worker__;
2
- const runner = () => __vitest_browser_runner__;
3
- function filepath() {
4
- return state().filepath || state().current?.file?.filepath || void 0;
1
+ // @__NO_SIDE_EFFECTS__
2
+ function getBrowserState() {
3
+ return window.__vitest_browser_runner__;
5
4
  }
6
- const rpc = () => state().rpc;
7
- const contextId = runner().contextId;
8
- const channel = new BroadcastChannel(`vitest:${contextId}`);
9
- function triggerCommand(command, ...args) {
10
- return rpc().triggerCommand(contextId, command, filepath(), args);
5
+ // @__NO_SIDE_EFFECTS__
6
+ function getWorkerState() {
7
+ const state = window.__vitest_worker__;
8
+ if (!state) {
9
+ throw new Error("Worker state is not found. This is an issue with Vitest. Please, open an issue.");
10
+ }
11
+ return state;
11
12
  }
12
- const provider = runner().provider;
13
+ // @__NO_SIDE_EFFECTS__
13
14
  function convertElementToCssSelector(element) {
14
15
  if (!element || !(element instanceof Element)) {
15
16
  throw new Error(
@@ -68,7 +69,7 @@ function getUniqueCssSelector(el) {
68
69
  }
69
70
  el = parent;
70
71
  }
71
- return `${provider === "webdriverio" && hasShadowRoot ? ">>>" : ""}${path.reverse().join(" > ")}`;
72
+ return `${(/* @__PURE__ */ getBrowserState()).provider === "webdriverio" && hasShadowRoot ? ">>>" : ""}${path.reverse().join(" > ")}`;
72
73
  }
73
74
  function getParent(el) {
74
75
  const parent = el.parentNode;
@@ -77,6 +78,18 @@ function getParent(el) {
77
78
  }
78
79
  return parent;
79
80
  }
81
+
82
+ const state = () => getWorkerState();
83
+ const provider = __vitest_browser_runner__.provider;
84
+ function filepath() {
85
+ return getWorkerState().filepath || getWorkerState().current?.file?.filepath || void 0;
86
+ }
87
+ const rpc = () => getWorkerState().rpc;
88
+ const contextId = getBrowserState().contextId;
89
+ const channel = new BroadcastChannel(`vitest:${contextId}`);
90
+ function triggerCommand(command, ...args) {
91
+ return rpc().triggerCommand(contextId, command, filepath(), args);
92
+ }
80
93
  function createUserEvent() {
81
94
  const keyboard = {
82
95
  unreleased: []
@@ -86,35 +99,29 @@ function createUserEvent() {
86
99
  return createUserEvent();
87
100
  },
88
101
  click(element, options = {}) {
89
- const css = convertElementToCssSelector(element);
90
- return triggerCommand("__vitest_click", css, options);
102
+ return convertToLocator(element).click(processClickOptions(options));
91
103
  },
92
104
  dblClick(element, options = {}) {
93
- const css = convertElementToCssSelector(element);
94
- return triggerCommand("__vitest_dblClick", css, options);
105
+ return convertToLocator(element).dblClick(processClickOptions(options));
95
106
  },
96
107
  tripleClick(element, options = {}) {
97
- const css = convertElementToCssSelector(element);
98
- return triggerCommand("__vitest_tripleClick", css, options);
108
+ return convertToLocator(element).tripleClick(processClickOptions(options));
99
109
  },
100
110
  selectOptions(element, value) {
101
- const values = provider === "webdriverio" ? getWebdriverioSelectOptions(element, value) : getSimpleSelectOptions(element, value);
102
- const css = convertElementToCssSelector(element);
103
- return triggerCommand("__vitest_selectOptions", css, values);
111
+ return convertToLocator(element).selectOptions(value);
104
112
  },
105
113
  async type(element, text, options = {}) {
106
- const css = convertElementToCssSelector(element);
114
+ const selector = convertToSelector(element);
107
115
  const { unreleased } = await triggerCommand(
108
116
  "__vitest_type",
109
- css,
117
+ selector,
110
118
  text,
111
119
  { ...options, unreleased: keyboard.unreleased }
112
120
  );
113
121
  keyboard.unreleased = unreleased;
114
122
  },
115
123
  clear(element) {
116
- const css = convertElementToCssSelector(element);
117
- return triggerCommand("__vitest_clear", css);
124
+ return convertToLocator(element).clear();
118
125
  },
119
126
  tab(options = {}) {
120
127
  return triggerCommand("__vitest_tab", options);
@@ -128,73 +135,30 @@ function createUserEvent() {
128
135
  keyboard.unreleased = unreleased;
129
136
  },
130
137
  hover(element, options = {}) {
131
- const css = convertElementToCssSelector(element);
132
- return triggerCommand("__vitest_hover", css, options);
138
+ return convertToLocator(element).hover(processHoverOptions(options));
133
139
  },
134
140
  unhover(element, options = {}) {
135
- const css = convertElementToCssSelector(element.ownerDocument.body);
136
- return triggerCommand("__vitest_hover", css, options);
141
+ return convertToLocator(element).unhover(options);
137
142
  },
138
143
  // non userEvent events, but still useful
139
144
  fill(element, text, options) {
140
- const css = convertElementToCssSelector(element);
141
- return triggerCommand("__vitest_fill", css, text, options);
145
+ return convertToLocator(element).fill(text, options);
142
146
  },
143
147
  dragAndDrop(source, target, options = {}) {
144
- const sourceCss = convertElementToCssSelector(source);
145
- const targetCss = convertElementToCssSelector(target);
146
- return triggerCommand("__vitest_dragAndDrop", sourceCss, targetCss, options);
148
+ const sourceLocator = convertToLocator(source);
149
+ const targetLocator = convertToLocator(target);
150
+ return sourceLocator.dropTo(targetLocator, processDragAndDropOptions(options));
147
151
  }
148
152
  };
149
153
  }
150
154
  const userEvent = createUserEvent();
151
- function getWebdriverioSelectOptions(element, value) {
152
- const options = [...element.querySelectorAll("option")];
153
- const arrayValues = Array.isArray(value) ? value : [value];
154
- if (!arrayValues.length) {
155
- return [];
156
- }
157
- if (arrayValues.length > 1) {
158
- throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
159
- }
160
- const optionValue = arrayValues[0];
161
- if (typeof optionValue !== "string") {
162
- const index = options.indexOf(optionValue);
163
- if (index === -1) {
164
- throw new Error(`The element ${convertElementToCssSelector(optionValue)} was not found in the "select" options.`);
165
- }
166
- return [{ index }];
167
- }
168
- const valueIndex = options.findIndex((option) => option.value === optionValue);
169
- if (valueIndex !== -1) {
170
- return [{ index: valueIndex }];
171
- }
172
- const labelIndex = options.findIndex(
173
- (option) => option.textContent?.trim() === optionValue || option.ariaLabel === optionValue
174
- );
175
- if (labelIndex === -1) {
176
- throw new Error(`The option "${optionValue}" was not found in the "select" options.`);
177
- }
178
- return [{ index: labelIndex }];
179
- }
180
- function getSimpleSelectOptions(element, value) {
181
- return (Array.isArray(value) ? value : [value]).map((v) => {
182
- if (typeof v !== "string") {
183
- return { element: convertElementToCssSelector(v) };
184
- }
185
- return v;
186
- });
187
- }
188
155
  function cdp() {
189
- return runner().cdp;
156
+ return getBrowserState().cdp;
190
157
  }
191
158
  const screenshotIds = {};
192
159
  const page = {
193
- get config() {
194
- return runner().config;
195
- },
196
160
  viewport(width, height) {
197
- const id = runner().iframeId;
161
+ const id = getBrowserState().iframeId;
198
162
  channel.postMessage({ type: "viewport", width, height, id });
199
163
  return new Promise((resolve, reject) => {
200
164
  channel.addEventListener("message", function handler(e) {
@@ -210,7 +174,7 @@ const page = {
210
174
  });
211
175
  },
212
176
  async screenshot(options = {}) {
213
- const currentTest = state().current;
177
+ const currentTest = getWorkerState().current;
214
178
  if (!currentTest) {
215
179
  throw new Error("Cannot take a screenshot outside of a test.");
216
180
  }
@@ -227,12 +191,165 @@ const page = {
227
191
  const name = options.path || `${taskName.replace(/[^a-z0-9]/gi, "-")}-${number}.png`;
228
192
  return triggerCommand("__vitest_screenshot", name, {
229
193
  ...options,
230
- element: options.element ? convertElementToCssSelector(options.element) : void 0
194
+ element: options.element ? convertToSelector(options.element) : void 0
231
195
  });
196
+ },
197
+ getByRole() {
198
+ throw new Error('Method "getByRole" is not implemented in the current provider.');
199
+ },
200
+ getByLabelText() {
201
+ throw new Error('Method "getByLabelText" is not implemented in the current provider.');
202
+ },
203
+ getByTestId() {
204
+ throw new Error('Method "getByTestId" is not implemented in the current provider.');
205
+ },
206
+ getByAltText() {
207
+ throw new Error('Method "getByAltText" is not implemented in the current provider.');
208
+ },
209
+ getByPlaceholder() {
210
+ throw new Error('Method "getByPlaceholder" is not implemented in the current provider.');
211
+ },
212
+ getByText() {
213
+ throw new Error('Method "getByText" is not implemented in the current provider.');
214
+ },
215
+ getByTitle() {
216
+ throw new Error('Method "getByTitle" is not implemented in the current provider.');
217
+ },
218
+ elementLocator() {
219
+ throw new Error('Method "elementLocator" is not implemented in the current provider.');
220
+ },
221
+ extend(methods) {
222
+ for (const key in methods) {
223
+ page[key] = methods[key];
224
+ }
225
+ return page;
232
226
  }
233
227
  };
228
+ function convertToLocator(element) {
229
+ if (element instanceof Element) {
230
+ return page.elementLocator(element);
231
+ }
232
+ return element;
233
+ }
234
+ function convertToSelector(elementOrLocator) {
235
+ if (!elementOrLocator) {
236
+ throw new Error("Expected element or locator to be defined.");
237
+ }
238
+ if (elementOrLocator instanceof Element) {
239
+ return convertElementToCssSelector(elementOrLocator);
240
+ }
241
+ if ("selector" in elementOrLocator) {
242
+ return elementOrLocator.selector;
243
+ }
244
+ throw new Error("Expected element or locator to be an instance of Element or Locator.");
245
+ }
234
246
  function getTaskFullName(task) {
235
247
  return task.suite ? `${getTaskFullName(task.suite)} ${task.name}` : task.name;
236
248
  }
249
+ function processClickOptions(options_) {
250
+ if (!options_ || !state().config.browser.ui) {
251
+ return options_;
252
+ }
253
+ if (provider === "playwright") {
254
+ const options = options_;
255
+ if (options.position) {
256
+ options.position = processPlaywrightPosition(options.position);
257
+ }
258
+ }
259
+ if (provider === "webdriverio") {
260
+ const options = options_;
261
+ if (options.x != null || options.y != null) {
262
+ const cache = {};
263
+ if (options.x != null) {
264
+ options.x = scaleCoordinate(options.x, cache);
265
+ }
266
+ if (options.y != null) {
267
+ options.y = scaleCoordinate(options.y, cache);
268
+ }
269
+ }
270
+ }
271
+ return options_;
272
+ }
273
+ function processHoverOptions(options_) {
274
+ if (!options_ || !state().config.browser.ui) {
275
+ return options_;
276
+ }
277
+ if (provider === "playwright") {
278
+ const options = options_;
279
+ if (options.position) {
280
+ options.position = processPlaywrightPosition(options.position);
281
+ }
282
+ }
283
+ if (provider === "webdriverio") {
284
+ const options = options_;
285
+ const cache = {};
286
+ if (options.xOffset != null) {
287
+ options.xOffset = scaleCoordinate(options.xOffset, cache);
288
+ }
289
+ if (options.yOffset != null) {
290
+ options.yOffset = scaleCoordinate(options.yOffset, cache);
291
+ }
292
+ }
293
+ return options_;
294
+ }
295
+ function processDragAndDropOptions(options_) {
296
+ if (!options_ || !state().config.browser.ui) {
297
+ return options_;
298
+ }
299
+ if (provider === "playwright") {
300
+ const options = options_;
301
+ if (options.sourcePosition) {
302
+ options.sourcePosition = processPlaywrightPosition(options.sourcePosition);
303
+ }
304
+ if (options.targetPosition) {
305
+ options.targetPosition = processPlaywrightPosition(options.targetPosition);
306
+ }
307
+ }
308
+ if (provider === "webdriverio") {
309
+ const cache = {};
310
+ const options = options_;
311
+ if (options.sourceX != null) {
312
+ options.sourceX = scaleCoordinate(options.sourceX, cache);
313
+ }
314
+ if (options.sourceY != null) {
315
+ options.sourceY = scaleCoordinate(options.sourceY, cache);
316
+ }
317
+ if (options.targetX != null) {
318
+ options.targetX = scaleCoordinate(options.targetX, cache);
319
+ }
320
+ if (options.targetY != null) {
321
+ options.targetY = scaleCoordinate(options.targetY, cache);
322
+ }
323
+ }
324
+ return options_;
325
+ }
326
+ function scaleCoordinate(coordinate, cache) {
327
+ return Math.round(coordinate * getCachedScale(cache));
328
+ }
329
+ function getCachedScale(cache) {
330
+ return cache.scale ??= getIframeScale();
331
+ }
332
+ function processPlaywrightPosition(position) {
333
+ const scale = getIframeScale();
334
+ if (position.x != null) {
335
+ position.x *= scale;
336
+ }
337
+ if (position.y != null) {
338
+ position.y *= scale;
339
+ }
340
+ return position;
341
+ }
342
+ function getIframeScale() {
343
+ const testerUi = window.parent.document.querySelector("#tester-ui");
344
+ if (!testerUi) {
345
+ throw new Error(`Cannot find Tester element. This is a bug in Vitest. Please, open a new issue with reproduction.`);
346
+ }
347
+ const scaleAttribute = testerUi.getAttribute("data-scale");
348
+ const scale = Number(scaleAttribute);
349
+ if (Number.isNaN(scale)) {
350
+ throw new TypeError(`Cannot parse scale value from Tester element (${scaleAttribute}). This is a bug in Vitest. Please, open a new issue with reproduction.`);
351
+ }
352
+ return scale;
353
+ }
237
354
 
238
355
  export { cdp, page, userEvent };