@rstest/browser 0.9.2 → 0.9.3

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.
@@ -1,12 +1,12 @@
1
- /*! LICENSE: lib-react.ce60b6aea5.js.LICENSE.txt */
2
- /*! LICENSE: lib-react.ce60b6aea5.js.LICENSE.txt */ "use strict";
1
+ /*! LICENSE: lib-react.f905279759.js.LICENSE.txt */
2
+ /*! LICENSE: lib-react.f905279759.js.LICENSE.txt */ "use strict";
3
3
  (self.rspackChunk_rstest_browser_ui = self.rspackChunk_rstest_browser_ui || []).push([
4
4
  [
5
5
  "783"
6
6
  ],
7
7
  {
8
8
  7816 (e, t, n) {
9
- var r, l = n(1099), a = n(162), o = n(3085);
9
+ var r, l = n(1099), a = n(162), o = n(5466);
10
10
  function i(e) {
11
11
  var t = "https://react.dev/errors/" + e;
12
12
  if (1 < arguments.length) {
@@ -7947,7 +7947,7 @@
7947
7947
  }
7948
7948
  })(), e.exports = n(7816);
7949
7949
  },
7950
- 3085 (e, t, n) {
7950
+ 5466 (e, t, n) {
7951
7951
  (function e() {
7952
7952
  if ("u" > typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" == typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE) try {
7953
7953
  __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e);
@@ -0,0 +1 @@
1
+ /*! LICENSE: lib-react.f905279759.js.LICENSE.txt */
@@ -12,7 +12,7 @@
12
12
  <script>
13
13
  window.__RSTEST_BROWSER_OPTIONS__ = __RSTEST_OPTIONS_PLACEHOLDER__;
14
14
  </script>
15
- <script defer src="/container-static/js/lib-react.ce60b6aea5.js"></script><script defer src="/container-static/js/101.82cdbbe145.js"></script><script defer src="/container-static/js/index.602d6770fe.js"></script><link href="/container-static/css/index.5c72297783.css" rel="stylesheet"></head>
15
+ <script defer src="/container-static/js/lib-react.f905279759.js"></script><script defer src="/container-static/js/927.514b181bd2.js"></script><script defer src="/container-static/js/index.5acf502b10.js"></script><link href="/container-static/css/index.5c72297783.css" rel="stylesheet"></head>
16
16
  <body>
17
17
  <div id="root"></div>
18
18
  </body>
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { __webpack_require__ } from "./rslib-runtime.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import promises from "node:fs/promises";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { isDeepStrictEqual } from "node:util";
5
6
  import { TEMP_RSTEST_OUTPUT_DIR, color, getSetupFiles, getTestEntries, isDebug, loadCoverageProvider, logger, rsbuild, serializableConfig } from "@rstest/core/browser";
6
7
  import open_editor from "open-editor";
7
8
  import { basename, dirname, join, normalize, relative, resolve as external_pathe_resolve } from "pathe";
@@ -2214,27 +2215,42 @@ async function dispatchPlaywrightBrowserRpc({ containerPage, runnerPage, request
2214
2215
  }
2215
2216
  throw new Error(`Unknown browser rpc kind: ${request.kind}`);
2216
2217
  }
2217
- async function launchPlaywrightBrowser({ browserName, headless }) {
2218
+ async function launchPlaywrightBrowser({ browserName, headless, providerOptions }) {
2218
2219
  const playwright = await import("playwright");
2219
2220
  const browserType = playwright[browserName];
2221
+ const launchOptions = providerOptions.launch;
2222
+ const launchArgs = Array.isArray(launchOptions?.args) ? launchOptions.args : 'chromium' === browserName ? [
2223
+ '--disable-popup-blocking',
2224
+ '--no-first-run',
2225
+ '--no-default-browser-check'
2226
+ ] : void 0;
2220
2227
  const browser = await browserType.launch({
2228
+ ...launchOptions,
2221
2229
  headless,
2222
- args: 'chromium' === browserName ? [
2223
- '--disable-popup-blocking',
2224
- '--no-first-run',
2225
- '--no-default-browser-check'
2226
- ] : void 0
2230
+ args: launchArgs
2227
2231
  });
2232
+ const wrappedBrowser = {
2233
+ close: async ()=>browser.close(),
2234
+ newContext: async ({ providerOptions: contextProviderOptions, viewport })=>{
2235
+ const contextOptions = contextProviderOptions?.context;
2236
+ const context = await browser.newContext({
2237
+ ...contextOptions,
2238
+ viewport
2239
+ });
2240
+ return context;
2241
+ }
2242
+ };
2228
2243
  return {
2229
- browser: browser
2244
+ browser: wrappedBrowser
2230
2245
  };
2231
2246
  }
2232
2247
  const playwrightProviderImplementation = {
2233
2248
  name: 'playwright',
2234
- async launchRuntime ({ browserName, headless }) {
2249
+ async launchRuntime ({ browserName, headless, providerOptions }) {
2235
2250
  return launchPlaywrightBrowser({
2236
2251
  browserName,
2237
- headless
2252
+ headless,
2253
+ providerOptions
2238
2254
  });
2239
2255
  },
2240
2256
  async dispatchRpc ({ containerPage, runnerPage, request, timeoutFallbackMs }) {
@@ -2567,22 +2583,44 @@ const planWatchRerun = ({ projectEntries, previousTestFiles, affectedTestFiles }
2567
2583
  };
2568
2584
  };
2569
2585
  const picomatch = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js");
2586
+ var picomatch_default = /*#__PURE__*/ __webpack_require__.n(picomatch);
2570
2587
  const { createRsbuild: createRsbuild, rspack: rspack } = rsbuild;
2571
2588
  const hostController_dirname = dirname(fileURLToPath(import.meta.url));
2572
2589
  const OPTIONS_PLACEHOLDER = '__RSTEST_OPTIONS_PLACEHOLDER__';
2573
2590
  const serializeForInlineScript = (value)=>JSON.stringify(value).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
2591
+ const getBrowserProviderOptions = (project)=>{
2592
+ const browserConfig = project.normalizedConfig.browser;
2593
+ return browserConfig.providerOptions ?? {};
2594
+ };
2595
+ const createDeferredPromise = ()=>{
2596
+ let resolve;
2597
+ let reject;
2598
+ const promise = new Promise((res, rej)=>{
2599
+ resolve = res;
2600
+ reject = rej;
2601
+ });
2602
+ return {
2603
+ promise,
2604
+ resolve,
2605
+ reject
2606
+ };
2607
+ };
2574
2608
  class ContainerRpcManager {
2575
2609
  wss;
2576
2610
  ws = null;
2577
2611
  rpc = null;
2578
2612
  methods;
2579
- constructor(wss, methods){
2613
+ onDisconnect;
2614
+ detachActiveSocketListeners = null;
2615
+ constructor(wss, methods, onDisconnect){
2580
2616
  this.wss = wss;
2581
2617
  this.methods = methods;
2618
+ this.onDisconnect = onDisconnect;
2582
2619
  this.setupConnectionHandler();
2583
2620
  }
2584
- updateMethods(methods) {
2621
+ updateMethods(methods, onDisconnect) {
2585
2622
  this.methods = methods;
2623
+ this.onDisconnect = onDisconnect;
2586
2624
  if (this.ws && this.ws.readyState === this.ws.OPEN) this.attachWebSocket(this.ws);
2587
2625
  }
2588
2626
  setupConnectionHandler() {
@@ -2593,26 +2631,47 @@ class ContainerRpcManager {
2593
2631
  });
2594
2632
  }
2595
2633
  attachWebSocket(ws) {
2634
+ this.detachActiveSocketListeners?.();
2635
+ if (this.rpc && !this.rpc.$closed) this.rpc.$close(new Error('Container RPC transport reattached'));
2596
2636
  this.ws = ws;
2637
+ const messageHandlers = new WeakMap();
2597
2638
  this.rpc = createBirpc(this.methods, {
2639
+ timeout: -1,
2598
2640
  post: (data)=>{
2599
2641
  if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(data));
2600
2642
  },
2601
2643
  on: (fn)=>{
2602
- ws.on('message', (message)=>{
2644
+ const handler = (message)=>{
2603
2645
  try {
2604
2646
  const data = JSON.parse(message.toString());
2605
2647
  fn(data);
2606
2648
  } catch {}
2607
- });
2649
+ };
2650
+ messageHandlers.set(fn, handler);
2651
+ ws.on('message', handler);
2652
+ },
2653
+ off: (fn)=>{
2654
+ const handler = messageHandlers.get(fn);
2655
+ if (!handler) return;
2656
+ ws.off('message', handler);
2657
+ messageHandlers.delete(fn);
2608
2658
  }
2609
2659
  });
2610
- ws.on('close', ()=>{
2611
- if (this.ws === ws) {
2612
- this.ws = null;
2613
- this.rpc = null;
2660
+ const handleClose = ()=>{
2661
+ if (this.ws === ws) this.ws = null;
2662
+ this.detachActiveSocketListeners?.();
2663
+ this.detachActiveSocketListeners = null;
2664
+ if (this.rpc && !this.rpc.$closed) {
2665
+ const disconnectError = new Error('Browser UI WebSocket disconnected before reload completed');
2666
+ this.rpc.$close(disconnectError);
2667
+ this.onDisconnect?.(disconnectError);
2614
2668
  }
2615
- });
2669
+ this.rpc = null;
2670
+ };
2671
+ ws.on('close', handleClose);
2672
+ this.detachActiveSocketListeners = ()=>{
2673
+ ws.off('close', handleClose);
2674
+ };
2616
2675
  }
2617
2676
  get isConnected() {
2618
2677
  return null !== this.ws && this.ws.readyState === this.ws.OPEN;
@@ -2628,9 +2687,9 @@ class ContainerRpcManager {
2628
2687
  }
2629
2688
  async reloadTestFile(testFile, testNamePattern) {
2630
2689
  logger.debug(`[Browser UI] reloadTestFile called, rpc: ${this.rpc ? 'exists' : 'null'}, ws: ${this.ws ? 'exists' : 'null'}`);
2631
- if (!this.rpc) return void logger.debug('[Browser UI] RPC not available, skipping reloadTestFile');
2690
+ if (!this.rpc) throw new Error('Browser UI RPC not available for reloadTestFile');
2632
2691
  logger.debug(`[Browser UI] Calling reloadTestFile: ${testFile}`);
2633
- await this.rpc.reloadTestFile(testFile, testNamePattern);
2692
+ return this.rpc.reloadTestFile(testFile, testNamePattern);
2634
2693
  }
2635
2694
  }
2636
2695
  const watchContext = {
@@ -2701,7 +2760,7 @@ const createBrowserRsbuildDevConfig = (isWatchMode)=>({
2701
2760
  }
2702
2761
  });
2703
2762
  const globToRegexp = (glob)=>{
2704
- const regex = picomatch.makeRe(glob, {
2763
+ const regex = picomatch_default().makeRe(glob, {
2705
2764
  fastpaths: false,
2706
2765
  noglobstar: false,
2707
2766
  bash: false
@@ -2813,7 +2872,8 @@ const getBrowserLaunchOptions = (project)=>({
2813
2872
  browser: project.normalizedConfig.browser.browser,
2814
2873
  headless: project.normalizedConfig.browser.headless,
2815
2874
  port: project.normalizedConfig.browser.port,
2816
- strictPort: project.normalizedConfig.browser.strictPort
2875
+ strictPort: project.normalizedConfig.browser.strictPort,
2876
+ providerOptions: getBrowserProviderOptions(project)
2817
2877
  });
2818
2878
  const ensureConsistentBrowserLaunchOptions = (projects)=>{
2819
2879
  if (0 === projects.length) throw new Error('No browser-enabled projects found.');
@@ -2821,7 +2881,7 @@ const ensureConsistentBrowserLaunchOptions = (projects)=>{
2821
2881
  const firstOptions = getBrowserLaunchOptions(firstProject);
2822
2882
  for (const project of projects.slice(1)){
2823
2883
  const options = getBrowserLaunchOptions(project);
2824
- if (options.provider !== firstOptions.provider || options.browser !== firstOptions.browser || options.headless !== firstOptions.headless || options.port !== firstOptions.port || options.strictPort !== firstOptions.strictPort) throw new Error(`Browser launch config mismatch between projects "${firstProject.name}" and "${project.name}". All browser-enabled projects in one run must share provider/browser/headless/port/strictPort.`);
2884
+ if (options.provider !== firstOptions.provider || options.browser !== firstOptions.browser || options.headless !== firstOptions.headless || options.port !== firstOptions.port || options.strictPort !== firstOptions.strictPort || !isDeepStrictEqual(options.providerOptions, firstOptions.providerOptions)) throw new Error(`Browser launch config mismatch between projects "${firstProject.name}" and "${project.name}". All browser-enabled projects in one run must share provider/browser/headless/port/strictPort/providerOptions.`);
2825
2885
  }
2826
2886
  return firstOptions;
2827
2887
  };
@@ -3241,12 +3301,14 @@ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tem
3241
3301
  const providerImplementation = getBrowserProviderImplementation(browserLaunchOptions.provider);
3242
3302
  const runtime = await providerImplementation.launchRuntime({
3243
3303
  browserName,
3244
- headless: forceHeadless ?? browserLaunchOptions.headless
3304
+ headless: forceHeadless ?? browserLaunchOptions.headless,
3305
+ providerOptions: browserLaunchOptions.providerOptions
3245
3306
  });
3246
3307
  return {
3247
3308
  rsbuildInstance,
3248
3309
  devServer,
3249
3310
  browser: runtime.browser,
3311
+ browserLaunchOptions,
3250
3312
  port,
3251
3313
  wsPort,
3252
3314
  manifestPath,
@@ -3445,7 +3507,7 @@ const runBrowserController = async (context, options)=>{
3445
3507
  });
3446
3508
  }
3447
3509
  }
3448
- const { browser, port, wsPort, wss } = runtime;
3510
+ const { browser, browserLaunchOptions, port, wsPort, wss } = runtime;
3449
3511
  const buildTime = Date.now() - buildStart;
3450
3512
  const allTestFiles = projectEntries.flatMap((entry)=>entry.testFiles.map((testPath)=>({
3451
3513
  testPath: normalize(testPath),
@@ -3665,6 +3727,7 @@ const runBrowserController = async (context, options)=>{
3665
3727
  if (run.cancelled || runLifecycle.isTokenStale(run.token)) return;
3666
3728
  const viewport = viewportByProject.get(file.projectName);
3667
3729
  const browserContext = await browser.newContext({
3730
+ providerOptions: browserLaunchOptions.providerOptions,
3668
3731
  viewport: viewport ?? null
3669
3732
  });
3670
3733
  run.contexts.add(browserContext);
@@ -3970,6 +4033,7 @@ const runBrowserController = async (context, options)=>{
3970
4033
  } else {
3971
4034
  isNewPage = true;
3972
4035
  containerContext = await browser.newContext({
4036
+ providerOptions: browserLaunchOptions.providerOptions,
3973
4037
  viewport: null
3974
4038
  });
3975
4039
  containerPage = await containerContext.newPage();
@@ -3991,21 +4055,61 @@ const runBrowserController = async (context, options)=>{
3991
4055
  activeContainerPage = containerPage;
3992
4056
  const dispatchRouter = createDispatchRouter();
3993
4057
  const headedReloadQueue = createHeadedSerialTaskQueue();
4058
+ const pendingHeadedReloads = new Map();
3994
4059
  let enqueueHeadedReload = async (_file, _testNamePattern)=>{
3995
4060
  throw new Error('Headed reload queue is not initialized');
3996
4061
  };
4062
+ const rejectPendingHeadedReload = (testPath, error, runId)=>{
4063
+ const pending = pendingHeadedReloads.get(testPath);
4064
+ if (!pending) return;
4065
+ if (runId && pending.runId !== runId) return;
4066
+ pendingHeadedReloads.delete(testPath);
4067
+ pending.deferred.reject(error);
4068
+ };
4069
+ const rejectAllPendingHeadedReloads = (error)=>{
4070
+ for (const [testPath, pending] of pendingHeadedReloads){
4071
+ pendingHeadedReloads.delete(testPath);
4072
+ pending.deferred.reject(error);
4073
+ }
4074
+ };
4075
+ const registerPendingHeadedReload = (testPath, runId)=>{
4076
+ const previousPending = pendingHeadedReloads.get(testPath);
4077
+ if (previousPending) {
4078
+ previousPending.deferred.reject(new Error(`Reload for "${testPath}" was superseded by a newer request.`));
4079
+ pendingHeadedReloads.delete(testPath);
4080
+ }
4081
+ const deferred = createDeferredPromise();
4082
+ pendingHeadedReloads.set(testPath, {
4083
+ runId,
4084
+ deferred
4085
+ });
4086
+ return deferred.promise;
4087
+ };
4088
+ const resolvePendingHeadedReload = (testPath, runId)=>{
4089
+ const pending = pendingHeadedReloads.get(testPath);
4090
+ if (!pending) return;
4091
+ if (runId && pending.runId !== runId) return void logger.debug(`[Browser UI] Ignoring stale file-complete for ${testPath}. current=${pending.runId}, incoming=${runId}`);
4092
+ pendingHeadedReloads.delete(testPath);
4093
+ pending.deferred.resolve();
4094
+ };
3997
4095
  const reloadTestFileWithTimeout = async (file, testNamePattern)=>{
3998
4096
  const timeoutMs = getHeadedPerFileTimeoutMs(file);
3999
4097
  let timeoutId;
4098
+ let reloadAck;
4000
4099
  try {
4100
+ reloadAck = await rpcManager.reloadTestFile(file.testPath, testNamePattern);
4101
+ const completionPromise = registerPendingHeadedReload(file.testPath, reloadAck.runId);
4001
4102
  await Promise.race([
4002
- rpcManager.reloadTestFile(file.testPath, testNamePattern),
4103
+ completionPromise,
4003
4104
  new Promise((_, reject)=>{
4004
4105
  timeoutId = setTimeout(()=>{
4005
4106
  reject(new Error(`Headed test execution timeout after ${timeoutMs / 1000}s for ${file.testPath}.`));
4006
4107
  }, timeoutMs);
4007
4108
  })
4008
4109
  ]);
4110
+ } catch (error) {
4111
+ if (reloadAck?.runId) rejectPendingHeadedReload(file.testPath, toError(error), reloadAck.runId);
4112
+ throw error;
4009
4113
  } finally{
4010
4114
  if (timeoutId) clearTimeout(timeoutId);
4011
4115
  }
@@ -4031,12 +4135,21 @@ const runBrowserController = async (context, options)=>{
4031
4135
  await handleTestCaseResult(payload);
4032
4136
  },
4033
4137
  async onTestFileComplete (payload) {
4034
- await handleTestFileComplete(payload);
4138
+ try {
4139
+ await handleTestFileComplete(payload);
4140
+ resolvePendingHeadedReload(payload.testPath, payload.runId);
4141
+ } catch (error) {
4142
+ rejectPendingHeadedReload(payload.testPath, toError(error), payload.runId);
4143
+ throw error;
4144
+ }
4035
4145
  },
4036
4146
  async onLog (payload) {
4037
4147
  await handleLog(payload);
4038
4148
  },
4039
4149
  async onFatal (payload) {
4150
+ const error = new Error(payload.message);
4151
+ error.stack = payload.stack;
4152
+ rejectAllPendingHeadedReloads(error);
4040
4153
  await handleFatal(payload);
4041
4154
  },
4042
4155
  async dispatch (request) {
@@ -4046,11 +4159,11 @@ const runBrowserController = async (context, options)=>{
4046
4159
  let rpcManager;
4047
4160
  if (isWatchMode && runtime.rpcManager) {
4048
4161
  rpcManager = runtime.rpcManager;
4049
- rpcManager.updateMethods(createRpcMethods());
4162
+ rpcManager.updateMethods(createRpcMethods(), rejectAllPendingHeadedReloads);
4050
4163
  const existingWs = rpcManager.currentWebSocket;
4051
4164
  if (existingWs) rpcManager.reattach(existingWs);
4052
4165
  } else {
4053
- rpcManager = new ContainerRpcManager(wss, createRpcMethods());
4166
+ rpcManager = new ContainerRpcManager(wss, createRpcMethods(), rejectAllPendingHeadedReloads);
4054
4167
  if (isWatchMode) runtime.rpcManager = rpcManager;
4055
4168
  }
4056
4169
  if (isNewPage) {
@@ -4200,7 +4313,7 @@ const listBrowserTests = async (context, options)=>{
4200
4313
  logger.error(color.red(`Failed to initialize browser provider runtime (${providers.join(', ')}).`), error);
4201
4314
  throw error;
4202
4315
  }
4203
- const { browser, port } = runtime;
4316
+ const { browser, browserLaunchOptions, port } = runtime;
4204
4317
  const projectRuntimeConfigs = browserProjects.map((project)=>({
4205
4318
  name: project.name,
4206
4319
  environmentName: project.environmentName,
@@ -4228,6 +4341,7 @@ const listBrowserTests = async (context, options)=>{
4228
4341
  resolveCollect = resolve;
4229
4342
  });
4230
4343
  const browserContext = await browser.newContext({
4344
+ providerOptions: browserLaunchOptions.providerOptions,
4231
4345
  viewport: null
4232
4346
  });
4233
4347
  const page = await browserContext.newPage();
@@ -4341,6 +4455,7 @@ const validateBrowserConfig = (context)=>{
4341
4455
  if (!browser.provider) throw new Error('browser.provider is required when browser.enabled is true.');
4342
4456
  if (!SUPPORTED_PROVIDERS.includes(browser.provider)) throw new Error(`browser.provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}.`);
4343
4457
  validateViewport(browser.viewport);
4458
+ if (!isPlainObject(browser.providerOptions)) throw new Error('browser.providerOptions must be a plain object.');
4344
4459
  }
4345
4460
  }
4346
4461
  };
@@ -4,6 +4,14 @@ import type { BrowserRpcRequest } from '../rpcProtocol';
4
4
  *
5
5
  * When adding a new built-in provider, implement `BrowserProviderImplementation`
6
6
  * and register it in `providerImplementations` below.
7
+ *
8
+ * Provider-agnostic policy:
9
+ * - keep shared contracts and behavior provider-neutral
10
+ * - `browser.providerOptions` stays opaque at the framework boundary
11
+ * - do not export provider-owned config types from `@rstest/browser`
12
+ * - do not reference optional peer provider types from public declarations
13
+ * - keep provider-specific behavior and config decoding inside provider implementations
14
+ * - prefer direct passthrough to provider APIs over provider-specific translation
7
15
  */
8
16
  export type BrowserProvider = 'playwright';
9
17
  /** Minimal console shape needed by host logging bridge. */
@@ -46,6 +54,7 @@ export type BrowserProviderBrowser = {
46
54
  width: number;
47
55
  height: number;
48
56
  } | null;
57
+ providerOptions?: Record<string, unknown>;
49
58
  }) => Promise<BrowserProviderContext>;
50
59
  };
51
60
  /** Provider launch result consumed by hostController. */
@@ -56,6 +65,7 @@ export type BrowserProviderRuntime = {
56
65
  export type LaunchBrowserInput = {
57
66
  browserName: 'chromium' | 'firefox' | 'webkit';
58
67
  headless: boolean | undefined;
68
+ providerOptions: Record<string, unknown>;
59
69
  };
60
70
  /** Input contract for provider-side browser RPC dispatch. */
61
71
  export type DispatchBrowserRpcInput = {
@@ -1,5 +1,6 @@
1
1
  import type { BrowserProviderRuntime } from '../index';
2
- export declare function launchPlaywrightBrowser({ browserName, headless, }: {
2
+ export declare function launchPlaywrightBrowser({ browserName, headless, providerOptions, }: {
3
3
  browserName: 'chromium' | 'firefox' | 'webkit';
4
4
  headless: boolean | undefined;
5
+ providerOptions: Record<string, unknown>;
5
6
  }): Promise<BrowserProviderRuntime>;
@@ -10,9 +10,29 @@ function __webpack_require__(moduleId) {
10
10
  return module.exports;
11
11
  }
12
12
  __webpack_require__.m = __webpack_modules__;
13
+ (()=>{
14
+ __webpack_require__.n = (module)=>{
15
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
16
+ __webpack_require__.d(getter, {
17
+ a: getter
18
+ });
19
+ return getter;
20
+ };
21
+ })();
22
+ (()=>{
23
+ __webpack_require__.d = (exports, definition)=>{
24
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
25
+ enumerable: true,
26
+ get: definition[key]
27
+ });
28
+ };
29
+ })();
13
30
  (()=>{
14
31
  __webpack_require__.add = function(modules) {
15
32
  Object.assign(__webpack_require__.m, modules);
16
33
  };
17
34
  })();
35
+ (()=>{
36
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
37
+ })();
18
38
  export { __webpack_require__ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rstest/browser",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Browser mode support for Rstest testing framework.",
5
5
  "bugs": {
6
6
  "url": "https://github.com/web-infra-dev/rstest/issues"
@@ -52,18 +52,19 @@
52
52
  "@types/convert-source-map": "^2.0.3",
53
53
  "@types/picomatch": "^4.0.2",
54
54
  "@types/ws": "^8.18.1",
55
+ "@typescript/native-preview": "7.0.0-dev.20260317.1",
55
56
  "@vitest/snapshot": "^3.2.4",
56
57
  "birpc": "^4.0.0",
57
58
  "picocolors": "^1.1.1",
58
59
  "picomatch": "^4.0.3",
59
60
  "playwright": "^1.58.2",
60
- "@rstest/core": "0.9.2",
61
+ "@rstest/core": "0.9.3",
61
62
  "@rstest/tsconfig": "0.0.1",
62
63
  "@rstest/browser-ui": "0.0.0"
63
64
  },
64
65
  "peerDependencies": {
65
66
  "playwright": "^1.49.1",
66
- "@rstest/core": "^0.9.2"
67
+ "@rstest/core": "^0.9.3"
67
68
  },
68
69
  "peerDependenciesMeta": {
69
70
  "playwright": {
package/src/AGENTS.md CHANGED
@@ -42,7 +42,7 @@ flowchart LR
42
42
  RPC --> CH
43
43
  CH --> UR
44
44
 
45
- HC -."headless bridge:\nexposeFunction(\_\_rstest_dispatch\_\_, \_\_rstest_dispatch_rpc\_\_)".-> Runner
45
+ HC -."headless bridge:\nexposeFunction(__rstest_dispatch__, __rstest_dispatch_rpc__)".-> Runner
46
46
  ```
47
47
 
48
48
  ## Headed transport path
@@ -6,7 +6,7 @@ This document is architecture-only and focuses on the browser runner runtime in
6
6
 
7
7
  ```mermaid
8
8
  flowchart TD
9
- A["waitForConfig()"] --> B["read \_\_RSTEST_BROWSER_OPTIONS\_\_ + URL overrides"]
9
+ A["waitForConfig()"] --> B["read __RSTEST_BROWSER_OPTIONS__ + URL overrides"]
10
10
  B --> R["send ready"]
11
11
  R --> C["setRealTimers()"]
12
12
  C --> D["preloadRunnerSourceMap()"]
@@ -23,7 +23,7 @@ flowchart TD
23
23
  L --> N["send file-complete per file"]
24
24
  N --> O["send complete after all files"]
25
25
 
26
- H --> M["window.\_\_RSTEST_DONE\_\_ = true"]
26
+ H --> M["window.__RSTEST_DONE__ = true"]
27
27
  O --> M
28
28
  ```
29
29
 
@@ -32,19 +32,19 @@ flowchart TD
32
32
  ```mermaid
33
33
  flowchart LR
34
34
  subgraph IframePath["Iframe path (headed)"]
35
- S1["send()"] --> P1["parent.postMessage(\_\_rstest_dispatch\_\_)"]
35
+ S1["send()"] --> P1["parent.postMessage(__rstest_dispatch__)"]
36
36
  R1["dispatchRunnerLifecycle()"] --> P2["postMessage dispatch-rpc-request"]
37
- SN1["snapshot.sendRpcRequest()"] --> P3["postMessage dispatch-rpc-request + wait \_\_rstest_dispatch_response\_\_"]
37
+ SN1["snapshot.sendRpcRequest()"] --> P3["postMessage dispatch-rpc-request + wait __rstest_dispatch_response__"]
38
38
  end
39
39
 
40
40
  subgraph TopLevelRunPath["Top-level page path (headless run)"]
41
- S2["send()"] --> D1["window.\_\_rstest_dispatch\_\_"]
42
- R2["dispatchRunnerLifecycle()"] --> D2["window.\_\_rstest_dispatch_rpc\_\_"]
43
- SN2["snapshot.sendRpcRequest()"] --> D3["window.\_\_rstest_dispatch_rpc\_\_"]
41
+ S2["send()"] --> D1["window.__rstest_dispatch__"]
42
+ R2["dispatchRunnerLifecycle()"] --> D2["window.__rstest_dispatch_rpc__"]
43
+ SN2["snapshot.sendRpcRequest()"] --> D3["window.__rstest_dispatch_rpc__"]
44
44
  end
45
45
 
46
46
  subgraph TopLevelCollectPath["Top-level page path (list collect)"]
47
- S3["send()"] --> C1["window.\_\_rstest_dispatch\_\_"]
47
+ S3["send()"] --> C1["window.__rstest_dispatch__"]
48
48
  end
49
49
  ```
50
50
 
@@ -62,5 +62,9 @@ export const validateBrowserConfig = (context: Rstest): void => {
62
62
  }
63
63
 
64
64
  validateViewport(browser.viewport);
65
+
66
+ if (!isPlainObject(browser.providerOptions)) {
67
+ throw new Error('browser.providerOptions must be a plain object.');
68
+ }
65
69
  }
66
70
  };