@vitest/browser 5.0.0-rc.1 → 5.0.0-rc.2

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 CHANGED
@@ -447,6 +447,9 @@ export interface LocatorOptions {
447
447
  * regular expression. Note that exact match still trims whitespace.
448
448
  */
449
449
  exact?: boolean
450
+ }
451
+
452
+ export interface LocatorFilterOptions {
450
453
  hasText?: string | RegExp
451
454
  hasNotText?: string | RegExp
452
455
  has?: Locator
@@ -766,7 +769,7 @@ export interface Locator extends LocatorSelectors {
766
769
  * Narrows existing locator according to the options.
767
770
  * @see {@link https://vitest.dev/api/browser/locators#filter}
768
771
  */
769
- filter(options: LocatorOptions): Locator
772
+ filter(options: LocatorFilterOptions): Locator
770
773
  /**
771
774
  * This method returns an element matching the locator.
772
775
  * Unlike [`.element()`](https://vitest.dev/api/browser/locators#element),
@@ -19,7 +19,7 @@
19
19
  ]
20
20
  },
21
21
  "tester/tester.html": {
22
- "file": "__vitest_browser__/tester-Ck66x6M6.js",
22
+ "file": "__vitest_browser__/tester-DDRabqAJ.js",
23
23
  "name": "tester",
24
24
  "src": "tester/tester.html",
25
25
  "isEntry": true,
@@ -720,6 +720,9 @@ var VitestBrowserSnapshotEnvironment = class {
720
720
  readSnapshotFile(filepath) {
721
721
  return rpc$1().readSnapshotFile(filepath);
722
722
  }
723
+ readSnapshotFileData(filepath) {
724
+ return rpc$1().readSnapshotFileData(filepath);
725
+ }
723
726
  saveSnapshotFile(filepath, snapshot) {
724
727
  return rpc$1().saveSnapshotFile(filepath, snapshot);
725
728
  }
@@ -1,4 +1,4 @@
1
- import type { LocatorByRoleOptions, LocatorOptions, LocatorScreenshotOptions, MarkOptions, SelectorOptions, UserEventClearOptions, UserEventClickOptions, UserEventDragAndDropOptions, UserEventFillOptions, UserEventHoverOptions, UserEventSelectOptions, UserEventUploadOptions, UserEventWheelOptions } from "vitest/browser";
1
+ import type { LocatorByRoleOptions, LocatorFilterOptions, LocatorOptions, LocatorScreenshotOptions, MarkOptions, SelectorOptions, UserEventClearOptions, UserEventClickOptions, UserEventDragAndDropOptions, UserEventFillOptions, UserEventHoverOptions, UserEventSelectOptions, UserEventUploadOptions, UserEventWheelOptions } from "vitest/browser";
2
2
  import { Ivya } from "ivya";
3
3
  export { ensureAwaited } from "../utils.js";
4
4
  export { convertElementToCssSelector, getIframeScale, processTimeoutOptions } from "./tester-utils.js";
@@ -40,7 +40,7 @@ export declare abstract class Locator {
40
40
  getByTestId(testId: string | RegExp): Locator;
41
41
  getByText(text: string | RegExp, options?: LocatorOptions): Locator;
42
42
  getByTitle(title: string | RegExp, options?: LocatorOptions): Locator;
43
- filter(filter: LocatorOptions): Locator;
43
+ filter(filter: LocatorFilterOptions): Locator;
44
44
  and(locator: Locator): Locator;
45
45
  or(locator: Locator): Locator;
46
46
  query(): HTMLElement | SVGElement | null;
@@ -5,7 +5,7 @@
5
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-Ck66x6M6.js"></script>
8
+ <script type="module" crossorigin src="/__vitest_browser__/tester-DDRabqAJ.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/defineProperty-BOtRrjRA.js">
10
10
  </head>
11
11
  <body>
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { distClientRoot } from '@vitest/ui';
6
6
  import { slash as slash$1, deepMerge, cleanUrl as cleanUrl$1, toArray } from '@vitest/utils/helpers';
7
7
  import sirv from 'sirv';
8
8
  import c from 'tinyrainbow';
9
- import { isFileLoadingAllowed, createDebugger, isValidApiRequest, isCSSRequest, distDir, rolldownVersion, isFileServingAllowed } from 'vitest/node';
9
+ import { isFileLoadingAllowed, createDebugger, isValidApiRequest, BrowserConnectionError, isCSSRequest, distDir, rolldownVersion, isFileServingAllowed } from 'vitest/node';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import crypto from 'node:crypto';
12
12
  import MagicString from 'magic-string';
@@ -209,7 +209,7 @@ const basename = function(p, extension) {
209
209
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
210
210
  };
211
211
 
212
- var version = "5.0.0-rc.1";
212
+ var version = "5.0.0-rc.2";
213
213
 
214
214
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
215
215
  const distRoot = resolve(pkgRoot, "dist");
@@ -2496,6 +2496,348 @@ class ParentBrowserProject {
2496
2496
  }
2497
2497
  }
2498
2498
 
2499
+ function _mergeNamespaces$1(n, m) {
2500
+ m.forEach(function(e) {
2501
+ e && typeof e !== "string" && !Array.isArray(e) && Object.keys(e).forEach(function(k) {
2502
+ if (k !== "default" && !(k in n)) {
2503
+ var d = Object.getOwnPropertyDescriptor(e, k);
2504
+ Object.defineProperty(n, k, d.get ? d : {
2505
+ enumerable: true,
2506
+ get: function() {
2507
+ return e[k];
2508
+ }
2509
+ });
2510
+ }
2511
+ });
2512
+ });
2513
+ return Object.freeze(n);
2514
+ }
2515
+ function getDefaultExportFromCjs(x) {
2516
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
2517
+ }
2518
+ var reactIs$1 = { exports: {} };
2519
+ var reactIs_production = {};
2520
+ /**
2521
+ * @license React
2522
+ * react-is.production.js
2523
+ *
2524
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2525
+ *
2526
+ * This source code is licensed under the MIT license found in the
2527
+ * LICENSE file in the root directory of this source tree.
2528
+ */
2529
+ var hasRequiredReactIs_production;
2530
+ function requireReactIs_production() {
2531
+ if (hasRequiredReactIs_production) return reactIs_production;
2532
+ hasRequiredReactIs_production = 1;
2533
+ var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_VIEW_TRANSITION_TYPE = Symbol.for("react.view_transition"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference");
2534
+ function typeOf(object) {
2535
+ if ("object" === typeof object && null !== object) {
2536
+ var $$typeof = object.$$typeof;
2537
+ switch ($$typeof) {
2538
+ case REACT_ELEMENT_TYPE: switch (object = object.type, object) {
2539
+ case REACT_FRAGMENT_TYPE:
2540
+ case REACT_PROFILER_TYPE:
2541
+ case REACT_STRICT_MODE_TYPE:
2542
+ case REACT_SUSPENSE_TYPE:
2543
+ case REACT_SUSPENSE_LIST_TYPE:
2544
+ case REACT_VIEW_TRANSITION_TYPE: return object;
2545
+ default: switch (object = object && object.$$typeof, object) {
2546
+ case REACT_CONTEXT_TYPE:
2547
+ case REACT_FORWARD_REF_TYPE:
2548
+ case REACT_LAZY_TYPE:
2549
+ case REACT_MEMO_TYPE: return object;
2550
+ case REACT_CONSUMER_TYPE: return object;
2551
+ default: return $$typeof;
2552
+ }
2553
+ }
2554
+ case REACT_PORTAL_TYPE: return $$typeof;
2555
+ }
2556
+ }
2557
+ }
2558
+ reactIs_production.ContextConsumer = REACT_CONSUMER_TYPE;
2559
+ reactIs_production.ContextProvider = REACT_CONTEXT_TYPE;
2560
+ reactIs_production.Element = REACT_ELEMENT_TYPE;
2561
+ reactIs_production.ForwardRef = REACT_FORWARD_REF_TYPE;
2562
+ reactIs_production.Fragment = REACT_FRAGMENT_TYPE;
2563
+ reactIs_production.Lazy = REACT_LAZY_TYPE;
2564
+ reactIs_production.Memo = REACT_MEMO_TYPE;
2565
+ reactIs_production.Portal = REACT_PORTAL_TYPE;
2566
+ reactIs_production.Profiler = REACT_PROFILER_TYPE;
2567
+ reactIs_production.StrictMode = REACT_STRICT_MODE_TYPE;
2568
+ reactIs_production.Suspense = REACT_SUSPENSE_TYPE;
2569
+ reactIs_production.SuspenseList = REACT_SUSPENSE_LIST_TYPE;
2570
+ reactIs_production.isContextConsumer = function(object) {
2571
+ return typeOf(object) === REACT_CONSUMER_TYPE;
2572
+ };
2573
+ reactIs_production.isContextProvider = function(object) {
2574
+ return typeOf(object) === REACT_CONTEXT_TYPE;
2575
+ };
2576
+ reactIs_production.isElement = function(object) {
2577
+ return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
2578
+ };
2579
+ reactIs_production.isForwardRef = function(object) {
2580
+ return typeOf(object) === REACT_FORWARD_REF_TYPE;
2581
+ };
2582
+ reactIs_production.isFragment = function(object) {
2583
+ return typeOf(object) === REACT_FRAGMENT_TYPE;
2584
+ };
2585
+ reactIs_production.isLazy = function(object) {
2586
+ return typeOf(object) === REACT_LAZY_TYPE;
2587
+ };
2588
+ reactIs_production.isMemo = function(object) {
2589
+ return typeOf(object) === REACT_MEMO_TYPE;
2590
+ };
2591
+ reactIs_production.isPortal = function(object) {
2592
+ return typeOf(object) === REACT_PORTAL_TYPE;
2593
+ };
2594
+ reactIs_production.isProfiler = function(object) {
2595
+ return typeOf(object) === REACT_PROFILER_TYPE;
2596
+ };
2597
+ reactIs_production.isStrictMode = function(object) {
2598
+ return typeOf(object) === REACT_STRICT_MODE_TYPE;
2599
+ };
2600
+ reactIs_production.isSuspense = function(object) {
2601
+ return typeOf(object) === REACT_SUSPENSE_TYPE;
2602
+ };
2603
+ reactIs_production.isSuspenseList = function(object) {
2604
+ return typeOf(object) === REACT_SUSPENSE_LIST_TYPE;
2605
+ };
2606
+ reactIs_production.isValidElementType = function(type) {
2607
+ return "string" === typeof type || "function" === typeof type || type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || "object" === typeof type && null !== type && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_CONSUMER_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_CLIENT_REFERENCE || void 0 !== type.getModuleId) ? true : false;
2608
+ };
2609
+ reactIs_production.typeOf = typeOf;
2610
+ return reactIs_production;
2611
+ }
2612
+ var hasRequiredReactIs$1;
2613
+ function requireReactIs$1() {
2614
+ if (hasRequiredReactIs$1) return reactIs$1.exports;
2615
+ hasRequiredReactIs$1 = 1;
2616
+ {
2617
+ reactIs$1.exports = requireReactIs_production();
2618
+ }
2619
+ return reactIs$1.exports;
2620
+ }
2621
+ var reactIsExports$1 = requireReactIs$1();
2622
+ var index$1 = /*@__PURE__*/ getDefaultExportFromCjs(reactIsExports$1);
2623
+ var ReactIs19 = /*#__PURE__*/ _mergeNamespaces$1({
2624
+ __proto__: null,
2625
+ default: index$1
2626
+ }, [reactIsExports$1]);
2627
+ var reactIs = { exports: {} };
2628
+ var reactIs_production_min = {};
2629
+ /**
2630
+ * @license React
2631
+ * react-is.production.min.js
2632
+ *
2633
+ * Copyright (c) Facebook, Inc. and its affiliates.
2634
+ *
2635
+ * This source code is licensed under the MIT license found in the
2636
+ * LICENSE file in the root directory of this source tree.
2637
+ */
2638
+ var hasRequiredReactIs_production_min;
2639
+ function requireReactIs_production_min() {
2640
+ if (hasRequiredReactIs_production_min) return reactIs_production_min;
2641
+ hasRequiredReactIs_production_min = 1;
2642
+ var b = Symbol.for("react.element"), c = Symbol.for("react.portal"), d = Symbol.for("react.fragment"), e = Symbol.for("react.strict_mode"), f = Symbol.for("react.profiler"), g = Symbol.for("react.provider"), h = Symbol.for("react.context"), k = Symbol.for("react.server_context"), l = Symbol.for("react.forward_ref"), m = Symbol.for("react.suspense"), n = Symbol.for("react.suspense_list"), p = Symbol.for("react.memo"), q = Symbol.for("react.lazy"), t = Symbol.for("react.offscreen"), u;
2643
+ u = Symbol.for("react.module.reference");
2644
+ function v(a) {
2645
+ if ("object" === typeof a && null !== a) {
2646
+ var r = a.$$typeof;
2647
+ switch (r) {
2648
+ case b: switch (a = a.type, a) {
2649
+ case d:
2650
+ case f:
2651
+ case e:
2652
+ case m:
2653
+ case n: return a;
2654
+ default: switch (a = a && a.$$typeof, a) {
2655
+ case k:
2656
+ case h:
2657
+ case l:
2658
+ case q:
2659
+ case p:
2660
+ case g: return a;
2661
+ default: return r;
2662
+ }
2663
+ }
2664
+ case c: return r;
2665
+ }
2666
+ }
2667
+ }
2668
+ reactIs_production_min.ContextConsumer = h;
2669
+ reactIs_production_min.ContextProvider = g;
2670
+ reactIs_production_min.Element = b;
2671
+ reactIs_production_min.ForwardRef = l;
2672
+ reactIs_production_min.Fragment = d;
2673
+ reactIs_production_min.Lazy = q;
2674
+ reactIs_production_min.Memo = p;
2675
+ reactIs_production_min.Portal = c;
2676
+ reactIs_production_min.Profiler = f;
2677
+ reactIs_production_min.StrictMode = e;
2678
+ reactIs_production_min.Suspense = m;
2679
+ reactIs_production_min.SuspenseList = n;
2680
+ reactIs_production_min.isAsyncMode = function() {
2681
+ return false;
2682
+ };
2683
+ reactIs_production_min.isConcurrentMode = function() {
2684
+ return false;
2685
+ };
2686
+ reactIs_production_min.isContextConsumer = function(a) {
2687
+ return v(a) === h;
2688
+ };
2689
+ reactIs_production_min.isContextProvider = function(a) {
2690
+ return v(a) === g;
2691
+ };
2692
+ reactIs_production_min.isElement = function(a) {
2693
+ return "object" === typeof a && null !== a && a.$$typeof === b;
2694
+ };
2695
+ reactIs_production_min.isForwardRef = function(a) {
2696
+ return v(a) === l;
2697
+ };
2698
+ reactIs_production_min.isFragment = function(a) {
2699
+ return v(a) === d;
2700
+ };
2701
+ reactIs_production_min.isLazy = function(a) {
2702
+ return v(a) === q;
2703
+ };
2704
+ reactIs_production_min.isMemo = function(a) {
2705
+ return v(a) === p;
2706
+ };
2707
+ reactIs_production_min.isPortal = function(a) {
2708
+ return v(a) === c;
2709
+ };
2710
+ reactIs_production_min.isProfiler = function(a) {
2711
+ return v(a) === f;
2712
+ };
2713
+ reactIs_production_min.isStrictMode = function(a) {
2714
+ return v(a) === e;
2715
+ };
2716
+ reactIs_production_min.isSuspense = function(a) {
2717
+ return v(a) === m;
2718
+ };
2719
+ reactIs_production_min.isSuspenseList = function(a) {
2720
+ return v(a) === n;
2721
+ };
2722
+ reactIs_production_min.isValidElementType = function(a) {
2723
+ return "string" === typeof a || "function" === typeof a || a === d || a === f || a === e || a === m || a === n || a === t || "object" === typeof a && null !== a && (a.$$typeof === q || a.$$typeof === p || a.$$typeof === g || a.$$typeof === h || a.$$typeof === l || a.$$typeof === u || void 0 !== a.getModuleId) ? true : false;
2724
+ };
2725
+ reactIs_production_min.typeOf = v;
2726
+ return reactIs_production_min;
2727
+ }
2728
+ var hasRequiredReactIs;
2729
+ function requireReactIs() {
2730
+ if (hasRequiredReactIs) return reactIs.exports;
2731
+ hasRequiredReactIs = 1;
2732
+ {
2733
+ reactIs.exports = requireReactIs_production_min();
2734
+ }
2735
+ return reactIs.exports;
2736
+ }
2737
+ var reactIsExports = requireReactIs();
2738
+ var index = /*@__PURE__*/ getDefaultExportFromCjs(reactIsExports);
2739
+ var ReactIs18 = /*#__PURE__*/ _mergeNamespaces$1({
2740
+ __proto__: null,
2741
+ default: index
2742
+ }, [reactIsExports]);
2743
+ const reactIsMethods = [
2744
+ "isAsyncMode",
2745
+ "isConcurrentMode",
2746
+ "isContextConsumer",
2747
+ "isContextProvider",
2748
+ "isElement",
2749
+ "isForwardRef",
2750
+ "isFragment",
2751
+ "isLazy",
2752
+ "isMemo",
2753
+ "isPortal",
2754
+ "isProfiler",
2755
+ "isStrictMode",
2756
+ "isSuspense",
2757
+ "isSuspenseList",
2758
+ "isValidElementType"
2759
+ ];
2760
+ Object.fromEntries(reactIsMethods.map((m) => [m, (v) => ReactIs18[m](v) || ReactIs19[m](v)]));
2761
+
2762
+ var naturalCompare = {exports: {}};
2763
+
2764
+ var hasRequiredNaturalCompare;
2765
+
2766
+ function requireNaturalCompare () {
2767
+ if (hasRequiredNaturalCompare) return naturalCompare.exports;
2768
+ hasRequiredNaturalCompare = 1;
2769
+ /*
2770
+ * @version 1.4.0
2771
+ * @date 2015-10-26
2772
+ * @stability 3 - Stable
2773
+ * @author Lauri Rooden (https://github.com/litejs/natural-compare-lite)
2774
+ * @license MIT License
2775
+ */
2776
+
2777
+
2778
+ var naturalCompare$1 = function(a, b) {
2779
+ var i, codeA
2780
+ , codeB = 1
2781
+ , posA = 0
2782
+ , posB = 0
2783
+ , alphabet = String.alphabet;
2784
+
2785
+ function getCode(str, pos, code) {
2786
+ if (code) {
2787
+ for (i = pos; code = getCode(str, i), code < 76 && code > 65;) ++i;
2788
+ return +str.slice(pos - 1, i)
2789
+ }
2790
+ code = alphabet && alphabet.indexOf(str.charAt(pos));
2791
+ return code > -1 ? code + 76 : ((code = str.charCodeAt(pos) || 0), code < 45 || code > 127) ? code
2792
+ : code < 46 ? 65 // -
2793
+ : code < 48 ? code - 1
2794
+ : code < 58 ? code + 18 // 0-9
2795
+ : code < 65 ? code - 11
2796
+ : code < 91 ? code + 11 // A-Z
2797
+ : code < 97 ? code - 37
2798
+ : code < 123 ? code + 5 // a-z
2799
+ : code - 63
2800
+ }
2801
+
2802
+
2803
+ if ((a+="") != (b+="")) for (;codeB;) {
2804
+ codeA = getCode(a, posA++);
2805
+ codeB = getCode(b, posB++);
2806
+
2807
+ if (codeA < 76 && codeB < 76 && codeA > 66 && codeB > 66) {
2808
+ codeA = getCode(a, posA, posA);
2809
+ codeB = getCode(b, posB, posA = i);
2810
+ posB = i;
2811
+ }
2812
+
2813
+ if (codeA != codeB) return (codeA < codeB) ? -1 : 1
2814
+ }
2815
+ return 0
2816
+ };
2817
+
2818
+ try {
2819
+ naturalCompare.exports = naturalCompare$1;
2820
+ } catch (e) {
2821
+ String.naturalCompare = naturalCompare$1;
2822
+ }
2823
+ return naturalCompare.exports;
2824
+ }
2825
+
2826
+ requireNaturalCompare();
2827
+
2828
+ // Evaluate a snapshot file's content into its snapshot key/value pairs.
2829
+ function evaluateSnapshotFile(filepath, content) {
2830
+ const data = Object.create(null);
2831
+ try {
2832
+ // eslint-disable-next-line no-new-func
2833
+ const populate = new Function("exports", content);
2834
+ populate(data);
2835
+ } catch (cause) {
2836
+ throw new Error(`Invalid snapshot file, please manually fix or delete it: ${filepath}`, { cause });
2837
+ }
2838
+ return data;
2839
+ }
2840
+
2499
2841
  //#region src/utils.ts
2500
2842
  function createPromiseWithResolvers() {
2501
2843
  let resolve;
@@ -2697,6 +3039,24 @@ function createBirpc($functions, options) {
2697
3039
 
2698
3040
  const debug = createDebugger("vitest:browser:api");
2699
3041
  const BROWSER_API_PATH = "/__vitest_browser_api__";
3042
+ const DEFAULT_HEARTBEAT_INTERVAL = 15e3;
3043
+ const HEARTBEAT_MAX_MISSED = 2;
3044
+ let warnedInvalidHeartbeatInterval = false;
3045
+ function resolveHeartbeatInterval(vitest) {
3046
+ const rawInterval = process.env.VITEST_BROWSER_HEARTBEAT_INTERVAL;
3047
+ if (!rawInterval) {
3048
+ return DEFAULT_HEARTBEAT_INTERVAL;
3049
+ }
3050
+ const interval = Number(rawInterval);
3051
+ if (Number.isNaN(interval)) {
3052
+ if (!warnedInvalidHeartbeatInterval) {
3053
+ warnedInvalidHeartbeatInterval = true;
3054
+ vitest.logger.warn(`VITEST_BROWSER_HEARTBEAT_INTERVAL is expected to be a number, received "${rawInterval}". Using the default interval of ${DEFAULT_HEARTBEAT_INTERVAL}ms instead.`);
3055
+ }
3056
+ return DEFAULT_HEARTBEAT_INTERVAL;
3057
+ }
3058
+ return interval;
3059
+ }
2700
3060
  function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2701
3061
  const vite = globalServer.vite;
2702
3062
  const vitest = globalServer.vitest;
@@ -2745,8 +3105,31 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2745
3105
  const clients = type === "tester" ? state.testers : state.orchestrators;
2746
3106
  clients.set(rpcId, rpc);
2747
3107
  debug?.("[%s] Browser API connected to %s", rpcId, type);
3108
+ // if the browser stops answering pings, terminate the socket so the
3109
+ // "close" handler below rejects pending calls (like `createTesters`)
3110
+ // instead of the run hanging forever; timeouts that live in the browser
3111
+ // (`testTimeout`, iframe ack) cannot fire once its process is frozen
3112
+ const heartbeatInterval = resolveHeartbeatInterval(vitest);
3113
+ let missedPongs = 0;
3114
+ ws.on("pong", () => {
3115
+ missedPongs = 0;
3116
+ });
3117
+ const heartbeat = heartbeatInterval > 0 ? setInterval(() => {
3118
+ if (ws.readyState !== ws.OPEN) {
3119
+ return;
3120
+ }
3121
+ if (missedPongs >= HEARTBEAT_MAX_MISSED) {
3122
+ debug?.("[%s] %s did not respond to %s heartbeat pings, terminating the connection", rpcId, type, missedPongs);
3123
+ rpc.$close(new Error(`[vitest] The browser ${type} did not respond to a heartbeat ping for ${missedPongs * heartbeatInterval}ms. The browser process might be frozen or killed. Closing the connection.`));
3124
+ ws.terminate();
3125
+ return;
3126
+ }
3127
+ missedPongs++;
3128
+ ws.ping();
3129
+ }, heartbeatInterval).unref() : undefined;
2748
3130
  ws.on("close", () => {
2749
3131
  debug?.("[%s] Browser API disconnected from %s", rpcId, type);
3132
+ clearInterval(heartbeat);
2750
3133
  offCancel();
2751
3134
  clients.delete(rpcId);
2752
3135
  globalServer.removeCDPHandler(rpcId);
@@ -2754,7 +3137,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2754
3137
  sessions.destroySession(sessionId);
2755
3138
  }
2756
3139
  // this will reject any hanging methods if there are any
2757
- rpc.$close(new Error(`[vitest] Browser connection was closed while running tests. Was the page closed unexpectedly?`));
3140
+ rpc.$close(new BrowserConnectionError(`[vitest] Browser connection was closed while running tests. Was the page closed unexpectedly?`));
2758
3141
  });
2759
3142
  });
2760
3143
  });
@@ -2771,8 +3154,11 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2771
3154
  function canWrite(project) {
2772
3155
  return project.config.api.allowWrite && project.vitest.config.api.allowWrite;
2773
3156
  }
3157
+ function canExec(project) {
3158
+ return project.config.api.allowExec && project.vitest.config.api.allowExec;
3159
+ }
2774
3160
  function isCdpAllowed(project) {
2775
- return project.config.api.allowExec && project.vitest.config.api.allowExec && project.config.api.allowWrite && project.vitest.config.api.allowWrite;
3161
+ return canExec(project) && canWrite(project);
2776
3162
  }
2777
3163
  function assertCdpAllowed(project) {
2778
3164
  if (!isCdpAllowed(project)) {
@@ -2881,6 +3267,17 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2881
3267
  }
2882
3268
  return promises.readFile(snapshotPath, "utf-8");
2883
3269
  },
3270
+ async readSnapshotFileData(snapshotPath) {
3271
+ checkFileAccess(snapshotPath);
3272
+ if (!existsSync(snapshotPath)) {
3273
+ return null;
3274
+ }
3275
+ if (!canExec(project)) {
3276
+ throw new Error(`Cannot read snapshot file because browser API exec operations are disabled. See https://vitest.dev/config/api.`);
3277
+ }
3278
+ const content = await promises.readFile(snapshotPath, "utf-8");
3279
+ return evaluateSnapshotFile(snapshotPath, content);
3280
+ },
2884
3281
  async saveSnapshotFile(id, content) {
2885
3282
  checkFileAccess(id);
2886
3283
  if (!canWrite(project)) {
@@ -2987,6 +3384,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
2987
3384
  if (module.type === "redirect") {
2988
3385
  const redirectUrl = new URL(module.redirect);
2989
3386
  module.redirect = join(vite.config.root, redirectUrl.pathname);
3387
+ checkFileAccess(module.redirect);
2990
3388
  }
2991
3389
  defaultMockerRegistry.register(module);
2992
3390
  }
@@ -3339,7 +3737,10 @@ body {
3339
3737
  };
3340
3738
  contribution.plugins = [
3341
3739
  ...BrowserPlugin(contribution),
3342
- interceptorPlugin({ registry: mockerRegistry }),
3740
+ interceptorPlugin({
3741
+ registry: mockerRegistry,
3742
+ registerWebSocketEvents: false
3743
+ }),
3343
3744
  {
3344
3745
  name: "vitest:browser:framework-sourcemaps",
3345
3746
  enforce: "post",
@@ -3420,6 +3821,7 @@ function resolveBrowserOptimizeDeps(projectConfigs, testFiles, harness) {
3420
3821
  "@vitest/browser/utils",
3421
3822
  "@vitest/browser/context",
3422
3823
  "@vitest/browser/locators",
3824
+ "@vitest/browser/client",
3423
3825
  "msw",
3424
3826
  "msw/browser"
3425
3827
  ];
@@ -3446,12 +3848,12 @@ function resolveBrowserOptimizeDeps(projectConfigs, testFiles, harness) {
3446
3848
  }
3447
3849
  // Pre-bundle the vitest runtime so the browser fetches a few optimized
3448
3850
  // chunks instead of ~20 separately-served dist chunks (faster startup).
3449
- // `vitest`, `vitest/internal/browser` and `@vitest/browser/client` are
3450
- // optimized together in a single pass, so esbuild dedupes their shared
3451
- // stateful chunks (the test collector, the runner, the RPC client) to a
3452
- // single instance — preserving module identity between the test files'
3453
- // `import 'vitest'` and the tester. Their transitive deps (@vitest/utils,
3454
- // @vitest/spy, pathe, tinyrainbow, …) are inlined into these bundles.
3851
+ // `vitest` and `vitest/internal/browser` are optimized together in a single
3852
+ // pass, so esbuild dedupes their shared stateful chunks (the test collector,
3853
+ // the runner) to a single instance, preserving module identity between the
3854
+ // test files' `import 'vitest'` and the tester. Their transitive deps
3855
+ // (@vitest/utils, @vitest/spy, pathe, tinyrainbow, …) are inlined into
3856
+ // these bundles.
3455
3857
  const include = [
3456
3858
  "vitest > expect-type",
3457
3859
  "vitest > magic-string",
@@ -3459,8 +3861,7 @@ function resolveBrowserOptimizeDeps(projectConfigs, testFiles, harness) {
3459
3861
  "vitest > vite/module-runner",
3460
3862
  "vitest",
3461
3863
  "vitest/internal/browser",
3462
- "vitest/internal/traces",
3463
- "@vitest/browser/client"
3864
+ "vitest/internal/traces"
3464
3865
  ];
3465
3866
  const provider = testConfig.browser?.provider;
3466
3867
  if (provider?.name === "preview") {
@@ -1,4 +1,4 @@
1
- import { UserEventClickOptions, UserEventWheelOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, MarkOptions, LocatorByRoleOptions, LocatorOptions, SelectorOptions } from 'vitest/browser';
1
+ import { UserEventClickOptions, UserEventWheelOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, MarkOptions, LocatorByRoleOptions, LocatorOptions, LocatorFilterOptions, SelectorOptions } from 'vitest/browser';
2
2
 
3
3
  //#region src/cssParser.d.ts
4
4
  type ClauseCombinator = '' | '>' | '+' | '~' | '>=';
@@ -270,7 +270,7 @@ declare abstract class Locator {
270
270
  getByTestId(testId: string | RegExp): Locator;
271
271
  getByText(text: string | RegExp, options?: LocatorOptions): Locator;
272
272
  getByTitle(title: string | RegExp, options?: LocatorOptions): Locator;
273
- filter(filter: LocatorOptions): Locator;
273
+ filter(filter: LocatorFilterOptions): Locator;
274
274
  and(locator: Locator): Locator;
275
275
  or(locator: Locator): Locator;
276
276
  query(): HTMLElement | SVGElement | null;
package/dist/types.d.ts CHANGED
@@ -18,6 +18,7 @@ export interface WebSocketBrowserHandlers {
18
18
  cancelCurrentRun: (reason: CancelReason) => void;
19
19
  getCountOfFailedTests: () => number;
20
20
  readSnapshotFile: (id: string) => Promise<string | null>;
21
+ readSnapshotFileData: (id: string) => Promise<Record<string, string> | null>;
21
22
  saveSnapshotFile: (id: string, content: string) => Promise<void>;
22
23
  removeSnapshotFile: (id: string) => Promise<void>;
23
24
  sendLog: (method: TestExecutionMethod, log: UserConsoleLog) => void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "5.0.0-rc.1",
4
+ "version": "5.0.0-rc.2",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -63,22 +63,22 @@
63
63
  "providers"
64
64
  ],
65
65
  "peerDependencies": {
66
- "vitest": "5.0.0-rc.1"
66
+ "vitest": "5.0.0-rc.2"
67
67
  },
68
68
  "dependencies": {
69
69
  "@blazediff/core": "1.10.0",
70
- "magic-string": "^1.1.0",
70
+ "magic-string": "^1.2.0",
71
71
  "pngjs": "^7.0.0",
72
72
  "sirv": "^3.0.2",
73
73
  "tinyrainbow": "^3.1.1",
74
74
  "ws": "^8.21.3",
75
- "@vitest/mocker": "5.0.0-rc.1",
76
- "@vitest/utils": "5.0.0-rc.1",
77
- "@vitest/ui": "5.0.0-rc.1"
75
+ "@vitest/mocker": "5.0.0-rc.2",
76
+ "@vitest/ui": "5.0.0-rc.2",
77
+ "@vitest/utils": "5.0.0-rc.2"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@opentelemetry/api": "^1.9.1",
81
- "@testing-library/user-event": "^14.6.3",
81
+ "@testing-library/user-event": "^14.6.4",
82
82
  "@types/node": "24.13.3",
83
83
  "@types/pngjs": "^6.0.5",
84
84
  "@types/ws": "^8.18.1",
@@ -88,7 +88,8 @@
88
88
  "mime": "^4.1.0",
89
89
  "pathe": "^2.0.3",
90
90
  "rrweb-snapshot": "2.1.1",
91
- "vitest": "5.0.0-rc.1"
91
+ "vitest": "5.0.0-rc.2",
92
+ "@vitest/snapshot": "5.0.0-rc.2"
92
93
  },
93
94
  "scripts": {
94
95
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",