@vitest/browser 5.0.2 → 5.0.4

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/aria-role.d.ts CHANGED
@@ -1,96 +1,96 @@
1
1
  type ARIAWidgetRole =
2
- | "button"
3
- | "checkbox"
4
- | "gridcell"
5
- | "link"
6
- | "menuitem"
7
- | "menuitemcheckbox"
8
- | "menuitemradio"
9
- | "option"
10
- | "progressbar"
11
- | "radio"
12
- | "scrollbar"
13
- | "searchbox"
14
- | "slider"
15
- | "spinbutton"
16
- | "switch"
17
- | "tab"
18
- | "tabpanel"
19
- | "textbox"
20
- | "treeitem";
2
+ | 'button'
3
+ | 'checkbox'
4
+ | 'gridcell'
5
+ | 'link'
6
+ | 'menuitem'
7
+ | 'menuitemcheckbox'
8
+ | 'menuitemradio'
9
+ | 'option'
10
+ | 'progressbar'
11
+ | 'radio'
12
+ | 'scrollbar'
13
+ | 'searchbox'
14
+ | 'slider'
15
+ | 'spinbutton'
16
+ | 'switch'
17
+ | 'tab'
18
+ | 'tabpanel'
19
+ | 'textbox'
20
+ | 'treeitem'
21
21
 
22
22
  type ARIACompositeWidgetRole =
23
- | "combobox"
24
- | "grid"
25
- | "listbox"
26
- | "menu"
27
- | "menubar"
28
- | "radiogroup"
29
- | "tablist"
30
- | "tree"
31
- | "treegrid";
23
+ | 'combobox'
24
+ | 'grid'
25
+ | 'listbox'
26
+ | 'menu'
27
+ | 'menubar'
28
+ | 'radiogroup'
29
+ | 'tablist'
30
+ | 'tree'
31
+ | 'treegrid'
32
32
 
33
33
  type ARIADocumentStructureRole =
34
- | "application"
35
- | "article"
36
- | "blockquote"
37
- | "caption"
38
- | "cell"
39
- | "columnheader"
40
- | "definition"
41
- | "deletion"
42
- | "directory"
43
- | "document"
44
- | "emphasis"
45
- | "feed"
46
- | "figure"
47
- | "generic"
48
- | "group"
49
- | "heading"
50
- | "img"
51
- | "insertion"
52
- | "list"
53
- | "listitem"
54
- | "math"
55
- | "meter"
56
- | "none"
57
- | "note"
58
- | "paragraph"
59
- | "presentation"
60
- | "row"
61
- | "rowgroup"
62
- | "rowheader"
63
- | "separator"
64
- | "strong"
65
- | "subscript"
66
- | "superscript"
67
- | "table"
68
- | "term"
69
- | "time"
70
- | "toolbar"
71
- | "tooltip";
34
+ | 'application'
35
+ | 'article'
36
+ | 'blockquote'
37
+ | 'caption'
38
+ | 'cell'
39
+ | 'columnheader'
40
+ | 'definition'
41
+ | 'deletion'
42
+ | 'directory'
43
+ | 'document'
44
+ | 'emphasis'
45
+ | 'feed'
46
+ | 'figure'
47
+ | 'generic'
48
+ | 'group'
49
+ | 'heading'
50
+ | 'img'
51
+ | 'insertion'
52
+ | 'list'
53
+ | 'listitem'
54
+ | 'math'
55
+ | 'meter'
56
+ | 'none'
57
+ | 'note'
58
+ | 'paragraph'
59
+ | 'presentation'
60
+ | 'row'
61
+ | 'rowgroup'
62
+ | 'rowheader'
63
+ | 'separator'
64
+ | 'strong'
65
+ | 'subscript'
66
+ | 'superscript'
67
+ | 'table'
68
+ | 'term'
69
+ | 'time'
70
+ | 'toolbar'
71
+ | 'tooltip'
72
72
 
73
73
  type ARIALandmarkRole =
74
- | "banner"
75
- | "complementary"
76
- | "contentinfo"
77
- | "form"
78
- | "main"
79
- | "navigation"
80
- | "region"
81
- | "search";
74
+ | 'banner'
75
+ | 'complementary'
76
+ | 'contentinfo'
77
+ | 'form'
78
+ | 'main'
79
+ | 'navigation'
80
+ | 'region'
81
+ | 'search'
82
82
 
83
- type ARIALiveRegionRole = "alert" | "log" | "marquee" | "status" | "timer";
83
+ type ARIALiveRegionRole = 'alert' | 'log' | 'marquee' | 'status' | 'timer'
84
84
 
85
- type ARIAWindowRole = "alertdialog" | "dialog";
85
+ type ARIAWindowRole = 'alertdialog' | 'dialog'
86
86
 
87
- type ARIAUncategorizedRole = "code";
87
+ type ARIAUncategorizedRole = 'code'
88
88
 
89
89
  export type ARIARole =
90
- | ARIAWidgetRole
91
- | ARIACompositeWidgetRole
92
- | ARIADocumentStructureRole
93
- | ARIALandmarkRole
94
- | ARIALiveRegionRole
95
- | ARIAWindowRole
96
- | ARIAUncategorizedRole;
90
+ | ARIAWidgetRole
91
+ | ARIACompositeWidgetRole
92
+ | ARIADocumentStructureRole
93
+ | ARIALandmarkRole
94
+ | ARIALiveRegionRole
95
+ | ARIAWindowRole
96
+ | ARIAUncategorizedRole
package/context.d.ts CHANGED
@@ -1,8 +1,14 @@
1
+ import { __ivyaAriaTypes } from '@vitest/browser/internal/vendor-types'
1
2
  import { SerializedConfig } from 'vitest'
2
- import { StringifyOptions, CDPSession, BrowserCommands, BrowserTraceEntryKind, MarkOptions } from 'vitest/internal/browser'
3
+ import {
4
+ StringifyOptions,
5
+ CDPSession,
6
+ BrowserCommands,
7
+ BrowserTraceEntryKind,
8
+ MarkOptions,
9
+ } from 'vitest/internal/browser'
3
10
  import { ARIARole } from './aria-role.js'
4
11
  import {} from './matchers.js'
5
- import { __ivyaAriaTypes } from '@vitest/browser/internal/vendor-types'
6
12
 
7
13
  export type BufferEncoding =
8
14
  | 'ascii'
@@ -18,7 +24,7 @@ export type BufferEncoding =
18
24
  | 'binary'
19
25
  | 'hex'
20
26
 
21
- export { BrowserTraceEntryKind, CDPSession, MarkOptions };
27
+ export { BrowserTraceEntryKind, CDPSession, MarkOptions }
22
28
 
23
29
  export interface ScreenshotOptions extends SelectorOptions {
24
30
  /**
@@ -146,7 +152,7 @@ export type NonStandardScreenshotComparators = Omit<
146
152
  >
147
153
 
148
154
  export interface ScreenshotMatcherOptions<
149
- ComparatorName extends keyof ScreenshotComparatorRegistry = keyof ScreenshotComparatorRegistry
155
+ ComparatorName extends keyof ScreenshotComparatorRegistry = keyof ScreenshotComparatorRegistry,
150
156
  > {
151
157
  /**
152
158
  * The name of the comparator to use for visual diffing.
@@ -320,7 +326,11 @@ export interface UserEvent {
320
326
  * @see {@link https://playwright.dev/docs/api/class-locator#locator-set-input-files} Playwright API
321
327
  * @see {@link https://testing-library.com/docs/user-event/utility#upload} testing-library API
322
328
  */
323
- upload: (element: Element | Locator, files: File | File[] | string | string[], options?: UserEventUploadOptions) => Promise<void>
329
+ upload: (
330
+ element: Element | Locator,
331
+ files: File | File[] | string | string[],
332
+ options?: UserEventUploadOptions,
333
+ ) => Promise<void>
324
334
  /**
325
335
  * Copies the selected content.
326
336
  * @see {@link https://playwright.dev/docs/api/class-keyboard} Playwright API
@@ -360,7 +370,11 @@ export interface UserEvent {
360
370
  * @see {@link https://playwright.dev/docs/api/class-frame#frame-drag-and-drop} Playwright API
361
371
  * @see {@link https://webdriver.io/docs/api/element/dragAndDrop/} WebdriverIO API
362
372
  */
363
- dragAndDrop: (source: Element | Locator, target: Element | Locator, options?: UserEventDragAndDropOptions) => Promise<void>
373
+ dragAndDrop: (
374
+ source: Element | Locator,
375
+ target: Element | Locator,
376
+ options?: UserEventDragAndDropOptions,
377
+ ) => Promise<void>
364
378
  }
365
379
 
366
380
  export interface UserEventFillOptions {}
@@ -851,10 +865,13 @@ export interface BrowserPage extends LocatorSelectors {
851
865
  base64: string
852
866
  }>
853
867
  screenshot(options?: Omit<ScreenshotOptions, 'base64'>): Promise<string>
854
- screenshot(options?: ScreenshotOptions): Promise<string | {
855
- path: string
856
- base64: string
857
- }>
868
+ screenshot(options?: ScreenshotOptions): Promise<
869
+ | string
870
+ | {
871
+ path: string
872
+ base64: string
873
+ }
874
+ >
858
875
  /**
859
876
  * Add a trace marker when browser tracing is enabled.
860
877
  * @see {@link https://vitest.dev/api/browser/context#mark}
@@ -927,7 +944,6 @@ export interface BrowserLocators {
927
944
  }): void
928
945
  }
929
946
 
930
-
931
947
  export type PrettyDOMOptions = Omit<StringifyOptions, 'maxLength'>
932
948
 
933
949
  export const utils: {
package/context.js CHANGED
@@ -12,9 +12,9 @@ export const utils = null
12
12
  const pool = globalThis.__vitest_worker__?.ctx?.pool
13
13
 
14
14
  throw new Error(
15
- // eslint-disable-next-line prefer-template
16
- 'vitest/browser can be imported only inside the Browser Mode. '
17
- + (pool
18
- ? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.`
19
- : 'Instead, it was imported outside of Vitest.'),
15
+ // oxlint-disable-next-line prefer-template
16
+ 'vitest/browser can be imported only inside the Browser Mode. ' +
17
+ (pool
18
+ ? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.`
19
+ : 'Instead, it was imported outside of Vitest.'),
20
20
  )
@@ -10,7 +10,7 @@
10
10
  "name": "defineProperty"
11
11
  },
12
12
  "orchestrator.html": {
13
- "file": "__vitest_browser__/orchestrator-M9828UZ-.js",
13
+ "file": "__vitest_browser__/orchestrator-Czo025Y6.js",
14
14
  "name": "orchestrator",
15
15
  "src": "orchestrator.html",
16
16
  "isEntry": true,
@@ -22,7 +22,7 @@
22
22
  ]
23
23
  },
24
24
  "tester/tester.html": {
25
- "file": "__vitest_browser__/tester-COH68OX9.js",
25
+ "file": "__vitest_browser__/tester-BH7uPfyB.js",
26
26
  "name": "tester",
27
27
  "src": "tester/tester.html",
28
28
  "isEntry": true,
@@ -11,6 +11,7 @@ function getUiAPI() {
11
11
  //#endregion
12
12
  //#region src/client/orchestrator.ts
13
13
  var ID_ALL = "__vitest_all__";
14
+ var IFRAME_UNLOAD_TIMEOUT = 5e3;
14
15
  var IframeOrchestrator = class {
15
16
  constructor() {
16
17
  _defineProperty(this, "cancelled", false);
@@ -22,6 +23,7 @@ var IframeOrchestrator = class {
22
23
  _defineProperty(this, "eventTarget", new EventTarget());
23
24
  _defineProperty(this, "traces", void 0);
24
25
  _defineProperty(this, "loggedIframe", /* @__PURE__ */ new WeakSet());
26
+ _defineProperty(this, "iframeLoads", /* @__PURE__ */ new WeakMap());
25
27
  _defineProperty(this, "iframeEvents", /* @__PURE__ */ new WeakMap());
26
28
  debug("init orchestrator", (/* @__PURE__ */ getBrowserState()).sessionId);
27
29
  if (getConfig().browser.traceView.enabled) {
@@ -151,15 +153,18 @@ var IframeOrchestrator = class {
151
153
  iframe.onload = () => {
152
154
  const href = this.getIframeHref(iframe);
153
155
  debug("iframe loaded with href", href);
154
- 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.
155
-
156
- Received URL: ${href || "unknown due to CORS"}\nExpected: ${iframe.src}`)));
156
+ this.iframeLoads.set(iframe, (this.iframeLoads.get(iframe) ?? 0) + 1);
157
+ if (href !== iframe.src) reject(this.dispatchIframeError(this.createConnectError(iframe, href)));
157
158
  else if (this.iframes.has(iframeId)) {
158
159
  const events = this.iframeEvents.get(iframe);
159
160
  if (events === null || events === void 0 ? void 0 : events.size) this.dispatchIframeError(new Error(this.createWarningMessage(iframeId, "during a test")));
160
- else this.warnReload(iframe, iframeId);
161
+ else {
162
+ this.warnReload(iframe, iframeId);
163
+ this.watchUnload(iframe, iframeId);
164
+ }
161
165
  } else {
162
166
  this.iframes.set(iframeId, iframe);
167
+ this.watchUnload(iframe, iframeId);
163
168
  this.waitForReady(iframeId).then(() => this.sendEventToIframe({
164
169
  event: "prepare",
165
170
  iframeId,
@@ -215,6 +220,22 @@ Received URL: ${href || "unknown due to CORS"}\nExpected: ${iframe.src}`)));
215
220
  }
216
221
  iframe === null || iframe === void 0 || iframe.remove();
217
222
  }
223
+ createConnectError(iframe, href) {
224
+ return /* @__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.
225
+
226
+ Received URL: ${href || "unknown due to CORS"}\nExpected: ${iframe.src}`);
227
+ }
228
+ watchUnload(iframe, iframeId) {
229
+ var _iframe$contentWindow;
230
+ const loads = this.iframeLoads.get(iframe) ?? 0;
231
+ (_iframe$contentWindow = iframe.contentWindow) === null || _iframe$contentWindow === void 0 || _iframe$contentWindow.addEventListener("pagehide", () => {
232
+ setTimeout(() => {
233
+ var _this$iframeEvents$ge;
234
+ if (this.iframes.get(iframeId) !== iframe || !((_this$iframeEvents$ge = this.iframeEvents.get(iframe)) === null || _this$iframeEvents$ge === void 0 ? void 0 : _this$iframeEvents$ge.size) || this.iframeLoads.get(iframe) !== loads) return;
235
+ this.dispatchIframeError(this.createConnectError(iframe, this.getIframeHref(iframe)));
236
+ }, IFRAME_UNLOAD_TIMEOUT);
237
+ }, { once: true });
238
+ }
218
239
  createWarningMessage(iframeId, location) {
219
240
  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`;
220
241
  }
@@ -232,8 +253,8 @@ Received URL: ${href || "unknown due to CORS"}\nExpected: ${iframe.src}`)));
232
253
  }
233
254
  getIframeHref(iframe) {
234
255
  try {
235
- var _iframe$contentWindow;
236
- return (_iframe$contentWindow = iframe.contentWindow) === null || _iframe$contentWindow === void 0 ? void 0 : _iframe$contentWindow.location.href;
256
+ var _iframe$contentWindow2;
257
+ return (_iframe$contentWindow2 = iframe.contentWindow) === null || _iframe$contentWindow2 === void 0 ? void 0 : _iframe$contentWindow2.location.href;
237
258
  } catch {
238
259
  return;
239
260
  }
@@ -561,7 +561,7 @@ function extractLocation(urlLike) {
561
561
  const urlObj = new URL(url);
562
562
  urlObj.searchParams.delete("import");
563
563
  urlObj.searchParams.delete("browserv");
564
- url = urlObj.pathname + urlObj.hash + urlObj.search;
564
+ url = tryDecode(urlObj.pathname, decodeURI) + urlObj.hash + urlObj.search;
565
565
  }
566
566
  if (url.startsWith("/@fs/")) {
567
567
  const isWindows = /^\/@fs\/[a-zA-Z]:\//.test(url);
@@ -574,6 +574,13 @@ function extractLocation(urlLike) {
574
574
  parts[3] || void 0
575
575
  ];
576
576
  }
577
+ function tryDecode(value, decode) {
578
+ try {
579
+ return decode(value);
580
+ } catch {
581
+ return value;
582
+ }
583
+ }
577
584
  function parseSingleFFOrSafariStack(raw) {
578
585
  let line = raw.trim();
579
586
  if (SAFARI_NATIVE_CODE_REGEXP.test(line)) return null;
@@ -613,7 +620,7 @@ function parseSingleV8Stack(raw) {
613
620
  let file = url && ["eval", "<anonymous>"].includes(url) ? void 0 : url;
614
621
  if (!file || !lineNumber || !columnNumber) return null;
615
622
  if (method.startsWith("async ")) method = method.slice(6);
616
- if (file.startsWith("file://")) file = file.slice(7);
623
+ if (file.startsWith("file://")) file = tryDecode(file.slice(7), decodeURIComponent);
617
624
  file = file.startsWith("node:") || file.startsWith("internal:") ? file : resolve(file);
618
625
  if (method) method = method.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g, "$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "").replace(/(Object\.)?__vite_ssr_export_default__\s?/g, "");
619
626
  return {
@@ -818,15 +825,11 @@ function takeSnapshot(serializedLocator) {
818
825
  result.pseudoClassIds[className] = ids;
819
826
  }
820
827
  if (serializedLocator) try {
821
- const el = engine.querySelector(engine.parseSelector(serializedLocator._pwSelector ?? serializedLocator.selector), document.documentElement, false);
822
- if (!el) result.selectorResolution = "missing";
823
- else {
824
- const id = mirror.getId(el);
825
- if (id !== -1) {
826
- result.selectorId = id;
827
- result.selectorResolution = "matched";
828
- } else result.selectorResolution = "missing";
829
- }
828
+ const ids = engine.querySelectorAll(engine.parseSelector(serializedLocator._pwSelector ?? serializedLocator.selector), document.documentElement).map((el) => mirror.getId(el)).filter((id) => id !== -1);
829
+ if (ids.length > 0) {
830
+ result.selectorIds = ids;
831
+ result.selectorResolution = "matched";
832
+ } else result.selectorResolution = "missing";
830
833
  } catch (error) {
831
834
  result.selectorResolution = "error";
832
835
  result.selectorError = error instanceof Error ? error.message : String(error);
@@ -1000,6 +1003,7 @@ function createBrowserRunner(mocker, state, coverageModule) {
1000
1003
  const importpath = `${`/${/^\w:/.test(filepath) ? "@fs/" : ""}`}${filepath}?${`browserv=${hash}`}`.replace(/\/+/g, "/");
1001
1004
  const trace = this.config.browser.trace;
1002
1005
  if (mode === "collect" && trace !== "off") await this.commands.triggerCommand("__vitest_startTracing", []);
1006
+ await mocker.prepare();
1003
1007
  try {
1004
1008
  await __vitePreload(() => import(
1005
1009
  /* @vite-ignore */
@@ -1206,6 +1210,13 @@ function sendLog(type, content, disableStack) {
1206
1210
  });
1207
1211
  }
1208
1212
 
1213
+ //#endregion
1214
+ //#region ../mocker/dist/chunk-helpers.js
1215
+ function withTrailingSlash(path) {
1216
+ if (path.at(-1) !== "/") return `${path}/`;
1217
+ return path;
1218
+ }
1219
+
1209
1220
  //#endregion
1210
1221
  //#region ../mocker/dist/chunk-registry.js
1211
1222
  var MockerRegistry = class {
@@ -1685,7 +1696,7 @@ var ModuleMocker = class {
1685
1696
  }
1686
1697
  async prepare() {
1687
1698
  if (!this.queue.size) return;
1688
- await Promise.all([...this.queue.values()]);
1699
+ await Promise.all(this.queue.values());
1689
1700
  }
1690
1701
  async resolveFactoryModule(id) {
1691
1702
  const mock = this.registry.get(id);
@@ -1699,6 +1710,7 @@ var ModuleMocker = class {
1699
1710
  return mock.cache;
1700
1711
  }
1701
1712
  async invalidate() {
1713
+ await Promise.allSettled(this.queue);
1702
1714
  const ids = Array.from(this.mockedIds);
1703
1715
  if (!ids.length) return;
1704
1716
  await this.rpc.invalidate(ids);
@@ -1822,8 +1834,8 @@ var ModuleMocker = class {
1822
1834
  resolveMockPath(path) {
1823
1835
  const config = this.config;
1824
1836
  const fsRoot = join("/@fs/", config.root);
1825
- if (path.startsWith(config.root)) return path.slice(config.root.length);
1826
- if (path.startsWith(fsRoot)) return path.slice(fsRoot.length);
1837
+ if (path.startsWith(withTrailingSlash(config.root))) return path.slice(config.root.length);
1838
+ if (path.startsWith(withTrailingSlash(fsRoot))) return path.slice(fsRoot.length);
1827
1839
  return path;
1828
1840
  }
1829
1841
  };
@@ -1896,7 +1908,6 @@ var CommandsManager = class {
1896
1908
  const rpc = state.rpc;
1897
1909
  const { sessionId, traces } = /* @__PURE__ */ getBrowserState();
1898
1910
  const filepath = state.filepath || ((_state$current = state.current) === null || _state$current === void 0 || (_state$current = _state$current.file) === null || _state$current === void 0 ? void 0 : _state$current.filepath);
1899
- args = args.filter((arg) => arg !== void 0);
1900
1911
  const actionTraceGroupName = ACTION_TRACE_COMMANDS.has(command) ? `vitest:${command.slice(9)}` : void 0;
1901
1912
  const currentTest = (/* @__PURE__ */ getWorkerState()).current;
1902
1913
  const hasActiveTrace = !!actionTraceGroupName && !!currentTest && (/* @__PURE__ */ getBrowserState()).activeTraceTaskIds.has(currentTest.id);
@@ -1912,7 +1923,7 @@ var CommandsManager = class {
1912
1923
  }]);
1913
1924
  let status = "pass";
1914
1925
  const traceRangeId = hasActiveTraceView ? createBrowserTraceRangeId() : void 0;
1915
- const element = typeof args[0] === "object" && "selector" in args[0] && "locator" in args[0] ? args[0] : void 0;
1926
+ const element = typeof args[0] === "object" && args[0] !== null && "selector" in args[0] && "locator" in args[0] ? args[0] : void 0;
1916
1927
  if (hasActiveTraceView) await recordBrowserTraceEntry(currentTest, {
1917
1928
  name: actionTraceGroupName,
1918
1929
  kind: "action",
@@ -2,9 +2,8 @@ import { client } from '@vitest/browser/client'
2
2
 
3
3
  function serializeError(unhandledError) {
4
4
  const state = globalThis.__vitest_worker__
5
- const VITEST_TEST_NAME = state && state.current && state.current.type === 'test'
6
- ? state.current.name
7
- : undefined
5
+ const VITEST_TEST_NAME =
6
+ state && state.current && state.current.type === 'test' ? state.current.name : undefined
8
7
  const VITEST_TEST_PATH = state && state.filepath ? state.filepath : undefined
9
8
 
10
9
  if (typeof unhandledError !== 'object' || !unhandledError) {
@@ -29,8 +28,7 @@ function catchWindowErrors(errorEvent, prop, cb) {
29
28
  function throwUnhandlerError(e) {
30
29
  if (userErrorListenerCount === 0 && e[prop] != null) {
31
30
  cb(e)
32
- }
33
- else {
31
+ } else {
34
32
  // `ErrorEvent` doesn't necessary have `ErrorEvent.error` defined
35
33
  // but some has `ErrorEvent.message` defined, e.g. ResizeObserver error.
36
34
  // https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/error
@@ -59,24 +57,26 @@ function catchWindowErrors(errorEvent, prop, cb) {
59
57
  }
60
58
 
61
59
  function registerUnexpectedErrors() {
62
- const offError = catchWindowErrors('error', 'error', event =>
63
- reportUnexpectedError('Error', event.error))
64
- const offRejection = catchWindowErrors('unhandledrejection', 'reason', event =>
65
- reportUnexpectedError('Unhandled Rejection', event.reason))
60
+ const offError = catchWindowErrors('error', 'error', (event) =>
61
+ reportUnexpectedError('Error', event.error),
62
+ )
63
+ const offRejection = catchWindowErrors('unhandledrejection', 'reason', (event) =>
64
+ reportUnexpectedError('Unhandled Rejection', event.reason),
65
+ )
66
66
  return () => {
67
67
  offError()
68
68
  offRejection()
69
69
  }
70
70
  }
71
71
 
72
- async function reportUnexpectedError(
73
- type,
74
- error,
75
- ) {
72
+ async function reportUnexpectedError(type, error) {
76
73
  const processedError = serializeError(error)
77
- await client.waitForConnection().then(() => {
78
- return client.rpc.onUnhandledError(processedError, type)
79
- }).catch(console.error)
74
+ await client
75
+ .waitForConnection()
76
+ .then(() => {
77
+ return client.rpc.onUnhandledError(processedError, type)
78
+ })
79
+ .catch(console.error)
80
80
  }
81
81
 
82
82
  globalThis.__vitest_browser_runner__.disposeExceptionTracker = registerUnexpectedErrors()
@@ -1,8 +1,8 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml">
5
+ <link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Vitest Browser Runner</title>
8
8
  <style>
@@ -22,11 +22,12 @@
22
22
  width: 100%;
23
23
  height: 100%;
24
24
  }
25
+ iframe[data-vitest] {
26
+ display: block;
27
+ }
25
28
  </style>
26
- {__VITEST_INJECTOR__}
27
- {__VITEST_ERROR_CATCHER__}
28
- {__VITEST_SCRIPTS__}
29
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-M9828UZ-.js"></script>
29
+ {__VITEST_INJECTOR__} {__VITEST_ERROR_CATCHER__} {__VITEST_SCRIPTS__}
30
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-Czo025Y6.js"></script>
30
31
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/defineProperty-DgkPJYVE.js">
31
32
  </head>
32
33
  <body>
@@ -1,11 +1,11 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml">
5
+ <link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Vitest Browser Tester</title>
8
- <script type="module" crossorigin src="/__vitest_browser__/tester-COH68OX9.js"></script>
8
+ <script type="module" crossorigin src="/__vitest_browser__/tester-BH7uPfyB.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/defineProperty-DgkPJYVE.js">
10
10
  </head>
11
11
  <body>
@@ -40,7 +40,7 @@ interface TraceSnapshot {
40
40
  x: number;
41
41
  y: number;
42
42
  };
43
- selectorId?: number;
43
+ selectorIds?: number[];
44
44
  selectorResolution?: BrowserTraceSelectorResolution;
45
45
  selectorError?: string;
46
46
  pseudoClassIds: Record<PseudoClassName, number[]>;
package/dist/client.js CHANGED
@@ -311,6 +311,15 @@ const stringify = (value, replacer, space) => {
311
311
  }
312
312
  };
313
313
 
314
+ // JSON drops `undefined` in objects and turns it into `null` in arrays
315
+ const UNDEFINED_VALUE = "__vitest_undefined__";
316
+ function replaceUndefined(value) {
317
+ return value === undefined ? UNDEFINED_VALUE : value;
318
+ }
319
+ function reviveUndefined(_key, value) {
320
+ return value === UNDEFINED_VALUE ? undefined : value;
321
+ }
322
+
314
323
  globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
315
324
  /* @__NO_SIDE_EFFECTS__ */
316
325
  function getBrowserState() {
@@ -424,9 +433,9 @@ function createClient() {
424
433
  stack: v.stack
425
434
  };
426
435
  }
427
- return v;
436
+ return replaceUndefined(v);
428
437
  }),
429
- deserialize: parse,
438
+ deserialize: (e) => parse(e, reviveUndefined),
430
439
  timeout: -1
431
440
  });
432
441
  let openPromise;