@rstest/browser 0.10.2 → 0.10.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.
Files changed (43) hide show
  1. package/dist/{323.js → 626.js} +2 -3
  2. package/dist/browser-container/container-static/js/{188.480c3c49b5.js → 3.d3d6c6e4cf.js} +2908 -2915
  3. package/dist/browser-container/container-static/js/3.d3d6c6e4cf.js.LICENSE.txt +1 -0
  4. package/dist/browser-container/container-static/js/{index.29fc60be33.js → index.8fb1588e4a.js} +431 -455
  5. package/dist/browser-container/container-static/js/{lib-react.5ae9b90ee5.js → lib-react.62b27a21db.js} +21 -21
  6. package/dist/browser-container/container-static/js/lib-react.62b27a21db.js.LICENSE.txt +1 -0
  7. package/dist/browser-container/index.html +1 -1
  8. package/dist/browser.js +5 -5
  9. package/dist/client/dispatchTransport.d.ts +19 -0
  10. package/dist/client/public.d.ts +3 -3
  11. package/dist/client/sourceMapSupport.d.ts +1 -1
  12. package/dist/concurrency.d.ts +3 -3
  13. package/dist/configValidation.d.ts +2 -2
  14. package/dist/dispatchCapabilities.d.ts +1 -1
  15. package/dist/hostController.d.ts +3 -3
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.js +209 -221
  18. package/dist/protocol.d.ts +45 -18
  19. package/dist/providers/index.d.ts +11 -1
  20. package/dist/rpcProtocol.d.ts +32 -18
  21. package/dist/rslib-runtime.js +9 -5
  22. package/dist/viewportPresets.d.ts +5 -0
  23. package/package.json +8 -8
  24. package/src/AGENTS.md +7 -1
  25. package/src/client/dispatchTransport.ts +63 -1
  26. package/src/client/entry.ts +25 -58
  27. package/src/client/public.ts +3 -3
  28. package/src/client/snapshot.ts +24 -22
  29. package/src/client/sourceMapSupport.ts +1 -1
  30. package/src/concurrency.ts +6 -20
  31. package/src/configValidation.ts +2 -2
  32. package/src/dispatchCapabilities.ts +102 -83
  33. package/src/env.d.ts +6 -2
  34. package/src/hostController.ts +76 -42
  35. package/src/index.ts +22 -11
  36. package/src/protocol.ts +66 -22
  37. package/src/providers/index.ts +11 -1
  38. package/src/providers/playwright/runtime.ts +104 -4
  39. package/src/rpcProtocol.ts +34 -21
  40. package/src/viewportPresets.ts +5 -0
  41. package/src/watchCliShortcuts.ts +2 -4
  42. package/dist/browser-container/container-static/js/188.480c3c49b5.js.LICENSE.txt +0 -1
  43. package/dist/browser-container/container-static/js/lib-react.5ae9b90ee5.js.LICENSE.txt +0 -1
package/dist/index.js CHANGED
@@ -2,15 +2,14 @@ import { existsSync } from "node:fs";
2
2
  import promises from "node:fs/promises";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { isDeepStrictEqual } from "node:util";
5
- import { PhaseTracker, color, createCoverageProvider, getNoTestFilesMessage, getSetupFiles, getTestEntries, isDebug, loadCoverageProvider, logger, resolveProjectBuildCache, rsbuild, serializableConfig } from "@rstest/core/browser";
5
+ import { DEFAULT_TEST_TIMEOUT, PhaseTracker, RSTEST_ENV_SYMBOL_KEY, color, createCoverageProvider, getNoTestFilesMessage, getNumCpus, getSetupFiles, getTestEntries, isDebug, isTTY, loadCoverageProvider, logger, parseWorkers, resolveProjectBuildCache, rsbuild, serializableConfig } from "@rstest/core/internal/browser";
6
6
  import open_editor from "open-editor";
7
7
  import { basename, dirname, join, normalize, relative, resolve as external_pathe_resolve } from "pathe";
8
8
  import sirv from "sirv";
9
9
  import { WebSocketServer } from "ws";
10
- import node_os from "node:os";
11
10
  import convert_source_map from "convert-source-map";
12
11
  import { __webpack_require__ } from "./rslib-runtime.js";
13
- import { DISPATCH_RPC_BRIDGE_NAME, DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_RUNNER } from "./323.js";
12
+ import { DISPATCH_RPC_BRIDGE_NAME, DISPATCH_NAMESPACE_SNAPSHOT, DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER } from "./626.js";
14
13
  __webpack_require__.add({
15
14
  "../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/index.js" (module, __unused_rspack_exports, __webpack_require__) {
16
15
  const pico = __webpack_require__("../../node_modules/.pnpm/picomatch@4.0.4/node_modules/picomatch/lib/picomatch.js");
@@ -1632,6 +1631,113 @@ __webpack_require__.add({
1632
1631
  };
1633
1632
  }
1634
1633
  });
1634
+ const BROWSER_VIEWPORT_PRESET_DIMENSIONS = {
1635
+ iPhoneSE: {
1636
+ width: 375,
1637
+ height: 667
1638
+ },
1639
+ iPhoneXR: {
1640
+ width: 414,
1641
+ height: 896
1642
+ },
1643
+ iPhone12Pro: {
1644
+ width: 390,
1645
+ height: 844
1646
+ },
1647
+ iPhone14ProMax: {
1648
+ width: 430,
1649
+ height: 932
1650
+ },
1651
+ Pixel7: {
1652
+ width: 412,
1653
+ height: 915
1654
+ },
1655
+ SamsungGalaxyS8Plus: {
1656
+ width: 360,
1657
+ height: 740
1658
+ },
1659
+ SamsungGalaxyS20Ultra: {
1660
+ width: 412,
1661
+ height: 915
1662
+ },
1663
+ iPadMini: {
1664
+ width: 768,
1665
+ height: 1024
1666
+ },
1667
+ iPadAir: {
1668
+ width: 820,
1669
+ height: 1180
1670
+ },
1671
+ iPadPro: {
1672
+ width: 1024,
1673
+ height: 1366
1674
+ },
1675
+ SurfacePro7: {
1676
+ width: 912,
1677
+ height: 1368
1678
+ },
1679
+ SurfaceDuo: {
1680
+ width: 540,
1681
+ height: 720
1682
+ },
1683
+ GalaxyZFold5: {
1684
+ width: 344,
1685
+ height: 882
1686
+ },
1687
+ AsusZenbookFold: {
1688
+ width: 853,
1689
+ height: 1280
1690
+ },
1691
+ SamsungGalaxyA51A71: {
1692
+ width: 412,
1693
+ height: 914
1694
+ },
1695
+ NestHub: {
1696
+ width: 1024,
1697
+ height: 600
1698
+ },
1699
+ NestHubMax: {
1700
+ width: 1280,
1701
+ height: 800
1702
+ }
1703
+ };
1704
+ const resolveBrowserViewportPreset = (presetId)=>{
1705
+ const size = BROWSER_VIEWPORT_PRESET_DIMENSIONS[presetId];
1706
+ return size ?? null;
1707
+ };
1708
+ const SUPPORTED_PROVIDERS = [
1709
+ 'playwright'
1710
+ ];
1711
+ const isPlainObject = (value)=>'[object Object]' === Object.prototype.toString.call(value);
1712
+ const validateViewport = (viewport)=>{
1713
+ if (null == viewport) return;
1714
+ if ('string' == typeof viewport) {
1715
+ const presetId = viewport.trim();
1716
+ if (!presetId) throw new Error('browser.viewport must be a non-empty preset id.');
1717
+ if (!resolveBrowserViewportPreset(presetId)) throw new Error(`browser.viewport must be a valid preset id. Received: ${viewport}`);
1718
+ return;
1719
+ }
1720
+ if (isPlainObject(viewport)) {
1721
+ const width = viewport.width;
1722
+ const height = viewport.height;
1723
+ if (!Number.isFinite(width) || width <= 0) throw new Error('browser.viewport.width must be a positive number.');
1724
+ if (!Number.isFinite(height) || height <= 0) throw new Error('browser.viewport.height must be a positive number.');
1725
+ return;
1726
+ }
1727
+ throw new Error('browser.viewport must be either a preset id or { width, height }.');
1728
+ };
1729
+ const validateBrowserConfig = (context)=>{
1730
+ for (const project of context.projects){
1731
+ const { browser, output } = project.normalizedConfig;
1732
+ if (browser.enabled) {
1733
+ if (!browser.provider) throw new Error('browser.provider is required when browser.enabled is true.');
1734
+ if (!SUPPORTED_PROVIDERS.includes(browser.provider)) throw new Error(`browser.provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}.`);
1735
+ validateViewport(browser.viewport);
1736
+ if (!isPlainObject(browser.providerOptions)) throw new Error('browser.providerOptions must be a plain object.');
1737
+ if (output?.bundleDependencies === false) throw new Error('output.bundleDependencies false is not supported in browser mode.');
1738
+ }
1739
+ }
1740
+ };
1635
1741
  const TYPE_REQUEST = "q";
1636
1742
  const TYPE_RESPONSE = "s";
1637
1743
  function createPromiseWithResolvers() {
@@ -1828,15 +1934,6 @@ function createBirpc($functions, options) {
1828
1934
  return rpc;
1829
1935
  }
1830
1936
  const DEFAULT_MAX_HEADLESS_WORKERS = 12;
1831
- const getNumCpus = ()=>node_os.availableParallelism?.() ?? node_os.cpus().length;
1832
- const parseWorkers = (maxWorkers, numCpus = getNumCpus())=>{
1833
- const parsed = Number.parseInt(maxWorkers.toString(), 10);
1834
- if ('string' == typeof maxWorkers && maxWorkers.trim().endsWith('%')) {
1835
- const workers = Math.floor(parsed / 100 * numCpus);
1836
- return Math.max(workers, 1);
1837
- }
1838
- return parsed > 0 ? parsed : 1;
1839
- };
1840
1937
  const resolveDefaultHeadlessWorkers = (command, numCpus = getNumCpus())=>{
1841
1938
  const baseWorkers = Math.max(Math.min(DEFAULT_MAX_HEADLESS_WORKERS, numCpus - 1), 1);
1842
1939
  return 'watch' === command ? Math.max(Math.floor(baseWorkers / 2), 1) : baseWorkers;
@@ -1895,72 +1992,49 @@ class HostDispatchRouter {
1895
1992
  }
1896
1993
  }
1897
1994
  }
1995
+ const snapshotRequestBuilders = {
1996
+ resolveSnapshotPath: (id, args)=>({
1997
+ id,
1998
+ method: 'resolveSnapshotPath',
1999
+ args: args
2000
+ }),
2001
+ readSnapshotFile: (id, args)=>({
2002
+ id,
2003
+ method: 'readSnapshotFile',
2004
+ args: args
2005
+ }),
2006
+ saveSnapshotFile: (id, args)=>({
2007
+ id,
2008
+ method: 'saveSnapshotFile',
2009
+ args: args
2010
+ }),
2011
+ removeSnapshotFile: (id, args)=>({
2012
+ id,
2013
+ method: 'removeSnapshotFile',
2014
+ args: args
2015
+ })
2016
+ };
1898
2017
  const toSnapshotRpcRequest = (request)=>{
1899
- switch(request.method){
1900
- case 'resolveSnapshotPath':
1901
- return {
1902
- id: request.requestId,
1903
- method: 'resolveSnapshotPath',
1904
- args: request.args
1905
- };
1906
- case 'readSnapshotFile':
1907
- return {
1908
- id: request.requestId,
1909
- method: 'readSnapshotFile',
1910
- args: request.args
1911
- };
1912
- case 'saveSnapshotFile':
1913
- return {
1914
- id: request.requestId,
1915
- method: 'saveSnapshotFile',
1916
- args: request.args
1917
- };
1918
- case 'removeSnapshotFile':
1919
- return {
1920
- id: request.requestId,
1921
- method: 'removeSnapshotFile',
1922
- args: request.args
1923
- };
1924
- default:
1925
- return null;
1926
- }
2018
+ const builder = snapshotRequestBuilders[request.method];
2019
+ return builder ? builder(request.requestId, request.args) : null;
1927
2020
  };
1928
2021
  const createHostDispatchRouter = ({ routerOptions, runnerCallbacks, runSnapshotRpc, extensionHandlers, onDuplicateNamespace })=>{
1929
2022
  const router = new HostDispatchRouter(routerOptions);
1930
- router.register('runner', async (request)=>{
1931
- switch(request.method){
1932
- case 'file-start':
1933
- await runnerCallbacks.onTestFileStart(request.args);
1934
- break;
1935
- case 'file-ready':
1936
- await runnerCallbacks.onTestFileReady(request.args);
1937
- break;
1938
- case 'suite-start':
1939
- await runnerCallbacks.onTestSuiteStart(request.args);
1940
- break;
1941
- case 'suite-result':
1942
- await runnerCallbacks.onTestSuiteResult(request.args);
1943
- break;
1944
- case 'case-start':
1945
- await runnerCallbacks.onTestCaseStart(request.args);
1946
- break;
1947
- case 'case-result':
1948
- await runnerCallbacks.onTestCaseResult(request.args);
1949
- break;
1950
- case 'file-complete':
1951
- await runnerCallbacks.onTestFileComplete(request.args);
1952
- break;
1953
- case 'log':
1954
- await runnerCallbacks.onLog(request.args);
1955
- break;
1956
- case 'fatal':
1957
- await runnerCallbacks.onFatal(request.args);
1958
- break;
1959
- default:
1960
- break;
1961
- }
2023
+ const runnerMethodHandlers = {
2024
+ 'file-start': (args)=>runnerCallbacks.onTestFileStart(args),
2025
+ 'file-ready': (args)=>runnerCallbacks.onTestFileReady(args),
2026
+ 'suite-start': (args)=>runnerCallbacks.onTestSuiteStart(args),
2027
+ 'suite-result': (args)=>runnerCallbacks.onTestSuiteResult(args),
2028
+ 'case-start': (args)=>runnerCallbacks.onTestCaseStart(args),
2029
+ 'case-result': (args)=>runnerCallbacks.onTestCaseResult(args),
2030
+ 'file-complete': (args)=>runnerCallbacks.onTestFileComplete(args),
2031
+ log: (args)=>runnerCallbacks.onLog(args),
2032
+ fatal: (args)=>runnerCallbacks.onFatal(args)
2033
+ };
2034
+ router.register("runner", async (request)=>{
2035
+ await runnerMethodHandlers[request.method]?.(request.args);
1962
2036
  });
1963
- router.register('snapshot', async (request)=>{
2037
+ router.register(DISPATCH_NAMESPACE_SNAPSHOT, async (request)=>{
1964
2038
  const snapshotRequest = toSnapshotRpcRequest(request);
1965
2039
  if (!snapshotRequest) return;
1966
2040
  return runSnapshotRpc(snapshotRequest);
@@ -2428,6 +2502,41 @@ async function dispatchPlaywrightBrowserRpc({ containerPage, runnerPage, request
2428
2502
  }
2429
2503
  throw new Error(`Unknown browser rpc kind: ${request.kind}`);
2430
2504
  }
2505
+ const patchedPages = new WeakSet();
2506
+ const patchedContexts = new WeakSet();
2507
+ const withAsyncDispose = (resource)=>{
2508
+ if ('symbol' == typeof Symbol.asyncDispose) {
2509
+ const existing = Object.getOwnPropertyDescriptor(resource, Symbol.asyncDispose);
2510
+ if (!existing) Object.defineProperty(resource, Symbol.asyncDispose, {
2511
+ configurable: true,
2512
+ value: ()=>resource.close()
2513
+ });
2514
+ }
2515
+ return resource;
2516
+ };
2517
+ const wrapPage = (page)=>{
2518
+ if (patchedPages.has(page)) return withAsyncDispose(page);
2519
+ patchedPages.add(page);
2520
+ const originalOn = page.on.bind(page);
2521
+ page.on = (event, listener)=>{
2522
+ if ('popup' === event) return void originalOn(event, (popup)=>{
2523
+ listener(wrapPage(popup));
2524
+ });
2525
+ originalOn(event, listener);
2526
+ };
2527
+ return withAsyncDispose(page);
2528
+ };
2529
+ const wrapContext = (context)=>{
2530
+ if (patchedContexts.has(context)) return withAsyncDispose(context);
2531
+ patchedContexts.add(context);
2532
+ const originalNewPage = context.newPage.bind(context);
2533
+ context.newPage = async ()=>wrapPage(await originalNewPage());
2534
+ const originalOn = context.on.bind(context);
2535
+ context.on = (event, listener)=>{
2536
+ originalOn(event, (page)=>listener(wrapPage(page)));
2537
+ };
2538
+ return withAsyncDispose(context);
2539
+ };
2431
2540
  async function launchPlaywrightBrowser({ browserName, headless, providerOptions }) {
2432
2541
  const playwright = await import("playwright");
2433
2542
  const browserType = playwright[browserName];
@@ -2442,7 +2551,7 @@ async function launchPlaywrightBrowser({ browserName, headless, providerOptions
2442
2551
  headless,
2443
2552
  args: launchArgs
2444
2553
  });
2445
- const wrappedBrowser = {
2554
+ const wrappedBrowser = withAsyncDispose({
2446
2555
  close: async ()=>browser.close(),
2447
2556
  newContext: async ({ providerOptions: contextProviderOptions, viewport })=>{
2448
2557
  const contextOptions = contextProviderOptions?.context;
@@ -2450,9 +2559,9 @@ async function launchPlaywrightBrowser({ browserName, headless, providerOptions
2450
2559
  ...contextOptions,
2451
2560
  viewport
2452
2561
  });
2453
- return context;
2562
+ return wrapContext(context);
2454
2563
  }
2455
- };
2564
+ });
2456
2565
  return {
2457
2566
  browser: wrappedBrowser
2458
2567
  };
@@ -2637,101 +2746,7 @@ const loadSourceMapWithCache = async ({ jsUrl, cache, force = false, origin, fet
2637
2746
  return null;
2638
2747
  }
2639
2748
  };
2640
- const BROWSER_VIEWPORT_PRESET_IDS = [
2641
- 'iPhoneSE',
2642
- 'iPhoneXR',
2643
- 'iPhone12Pro',
2644
- 'iPhone14ProMax',
2645
- 'Pixel7',
2646
- 'SamsungGalaxyS8Plus',
2647
- 'SamsungGalaxyS20Ultra',
2648
- 'iPadMini',
2649
- 'iPadAir',
2650
- 'iPadPro',
2651
- 'SurfacePro7',
2652
- 'SurfaceDuo',
2653
- 'GalaxyZFold5',
2654
- 'AsusZenbookFold',
2655
- 'SamsungGalaxyA51A71',
2656
- 'NestHub',
2657
- 'NestHubMax'
2658
- ];
2659
- const BROWSER_VIEWPORT_PRESET_DIMENSIONS = {
2660
- iPhoneSE: {
2661
- width: 375,
2662
- height: 667
2663
- },
2664
- iPhoneXR: {
2665
- width: 414,
2666
- height: 896
2667
- },
2668
- iPhone12Pro: {
2669
- width: 390,
2670
- height: 844
2671
- },
2672
- iPhone14ProMax: {
2673
- width: 430,
2674
- height: 932
2675
- },
2676
- Pixel7: {
2677
- width: 412,
2678
- height: 915
2679
- },
2680
- SamsungGalaxyS8Plus: {
2681
- width: 360,
2682
- height: 740
2683
- },
2684
- SamsungGalaxyS20Ultra: {
2685
- width: 412,
2686
- height: 915
2687
- },
2688
- iPadMini: {
2689
- width: 768,
2690
- height: 1024
2691
- },
2692
- iPadAir: {
2693
- width: 820,
2694
- height: 1180
2695
- },
2696
- iPadPro: {
2697
- width: 1024,
2698
- height: 1366
2699
- },
2700
- SurfacePro7: {
2701
- width: 912,
2702
- height: 1368
2703
- },
2704
- SurfaceDuo: {
2705
- width: 540,
2706
- height: 720
2707
- },
2708
- GalaxyZFold5: {
2709
- width: 344,
2710
- height: 882
2711
- },
2712
- AsusZenbookFold: {
2713
- width: 853,
2714
- height: 1280
2715
- },
2716
- SamsungGalaxyA51A71: {
2717
- width: 412,
2718
- height: 914
2719
- },
2720
- NestHub: {
2721
- width: 1024,
2722
- height: 600
2723
- },
2724
- NestHubMax: {
2725
- width: 1280,
2726
- height: 800
2727
- }
2728
- };
2729
- const resolveBrowserViewportPreset = (presetId)=>{
2730
- const size = BROWSER_VIEWPORT_PRESET_DIMENSIONS[presetId];
2731
- return size ?? null;
2732
- };
2733
- const isTTY = ()=>Boolean(process.stdin.isTTY && !process.env.CI);
2734
- const isBrowserWatchCliShortcutsEnabled = ()=>isTTY();
2749
+ const isBrowserWatchCliShortcutsEnabled = ()=>isTTY('stdin');
2735
2750
  const getBrowserWatchCliShortcutsHintMessage = ()=>` ${color.dim('press')} ${color.bold('q')} ${color.dim('to quit')}\n`;
2736
2751
  const logBrowserWatchReadyMessage = (enableCliShortcuts)=>{
2737
2752
  logger.log(color.green(' Waiting for file changes...'));
@@ -2800,6 +2815,7 @@ var picomatch_default = /*#__PURE__*/ __webpack_require__.n(picomatch);
2800
2815
  const { createRsbuild: createRsbuild, rspack: rspack } = rsbuild;
2801
2816
  const hostController_dirname = dirname(fileURLToPath(import.meta.url));
2802
2817
  const OPTIONS_PLACEHOLDER = '__RSTEST_OPTIONS_PLACEHOLDER__';
2818
+ const PER_FILE_TIMEOUT_BUFFER_MS = 30000;
2803
2819
  let nextBrowserFilePid = 1000000000;
2804
2820
  const serializeForInlineScript = (value)=>JSON.stringify(value).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
2805
2821
  const getBrowserProviderOptions = (project)=>{
@@ -3066,6 +3082,7 @@ const getRuntimeConfigFromProject = (project)=>{
3066
3082
  return {
3067
3083
  env: {
3068
3084
  NODE_ENV: process.env.NODE_ENV,
3085
+ RSTEST: 'true',
3069
3086
  ...env
3070
3087
  },
3071
3088
  testNamePattern,
@@ -3293,12 +3310,12 @@ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tem
3293
3310
  ]));
3294
3311
  const userPlugins = browserProjects.flatMap((project)=>project.normalizedConfig.plugins || []);
3295
3312
  const browserLaunchOptions = ensureConsistentBrowserLaunchOptions(browserProjects);
3296
- const browserRuntimePath = fileURLToPath(import.meta.resolve('@rstest/core/browser-runtime'));
3313
+ const browserRuntimePath = fileURLToPath(import.meta.resolve('@rstest/core/internal/browser-runtime'));
3297
3314
  const rstestInternalAliases = {
3298
3315
  '@rstest/browser-manifest': manifestPath,
3299
3316
  '@rstest/core': resolveBrowserFile('client/public.ts'),
3300
3317
  '@rstest/browser': resolveBrowserFile('browser.ts'),
3301
- '@rstest/core/browser-runtime': browserRuntimePath,
3318
+ '@rstest/core/internal/browser-runtime': browserRuntimePath,
3302
3319
  '@sinonjs/fake-timers': resolveBrowserFile('client/fakeTimersStub.ts')
3303
3320
  };
3304
3321
  const rsbuildInstance = await createRsbuild({
@@ -3340,6 +3357,7 @@ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tem
3340
3357
  project
3341
3358
  });
3342
3359
  const setupFiles = Object.values(getSetupFiles(project.normalizedConfig.setupFiles, project.rootPath));
3360
+ const rstestEnvDefine = `globalThis[Symbol.for(${JSON.stringify(RSTEST_ENV_SYMBOL_KEY)})]`;
3343
3361
  const merged = mergeEnvironmentConfig(config, {
3344
3362
  ...userRsbuildConfig,
3345
3363
  performance: buildCache ? {
@@ -3352,8 +3370,8 @@ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tem
3352
3370
  },
3353
3371
  source: {
3354
3372
  define: {
3355
- 'process.env': 'globalThis[Symbol.for("rstest.env")]',
3356
- 'import.meta.env': 'globalThis[Symbol.for("rstest.env")]'
3373
+ 'process.env': rstestEnvDefine,
3374
+ 'import.meta.env': rstestEnvDefine
3357
3375
  }
3358
3376
  },
3359
3377
  output: {
@@ -3789,7 +3807,7 @@ const runBrowserController = async (context, options)=>{
3789
3807
  runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project)),
3790
3808
  viewport: project.normalizedConfig.browser.viewport
3791
3809
  }));
3792
- const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
3810
+ const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? DEFAULT_TEST_TIMEOUT));
3793
3811
  const hostOptions = {
3794
3812
  rootPath: normalize(context.rootPath),
3795
3813
  projects: projectRuntimeConfigs,
@@ -3832,7 +3850,7 @@ const runBrowserController = async (context, options)=>{
3832
3850
  throw String(error);
3833
3851
  }
3834
3852
  };
3835
- runtime.dispatchHandlers.set('browser', async (dispatchRequest)=>{
3853
+ runtime.dispatchHandlers.set(DISPATCH_NAMESPACE_BROWSER, async (dispatchRequest)=>{
3836
3854
  const request = validateBrowserRpcRequest(dispatchRequest.args);
3837
3855
  return dispatchBrowserRpcRequest({
3838
3856
  request,
@@ -3976,7 +3994,7 @@ const runBrowserController = async (context, options)=>{
3976
3994
  const pushTaskId = (taskIds, taskId)=>{
3977
3995
  if (!taskIds.includes(taskId)) taskIds.push(taskId);
3978
3996
  };
3979
- const shouldEmitUserConsoleLog = (log)=>context.normalizedConfig.onConsoleLog?.(log.content) !== false;
3997
+ const shouldEmitUserConsoleLog = (log)=>context.normalizedConfig.onConsoleLog?.(log.content, log.type) !== false;
3980
3998
  const emitUserConsoleLog = async (log)=>{
3981
3999
  if (!shouldEmitUserConsoleLog(log)) return;
3982
4000
  await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
@@ -4027,7 +4045,10 @@ const runBrowserController = async (context, options)=>{
4027
4045
  case 'removeSnapshotFile':
4028
4046
  return snapshotRpcMethods.removeSnapshotFile(request.args.filepath);
4029
4047
  default:
4030
- return;
4048
+ {
4049
+ const _exhaustive = request;
4050
+ return _exhaustive;
4051
+ }
4031
4052
  }
4032
4053
  };
4033
4054
  const createDispatchRouter = (options)=>createHostDispatchRouter({
@@ -4072,14 +4093,14 @@ const runBrowserController = async (context, options)=>{
4072
4093
  const dispatchRouter = createDispatchRouter({
4073
4094
  isRunTokenStale: (runToken)=>runLifecycle.isTokenStale(runToken),
4074
4095
  onStale: (request)=>{
4075
- if (request.namespace === DISPATCH_NAMESPACE_RUNNER) logger.debug(`[Headless] Dropped stale message "${request.method}" for ${request.target?.testFile ?? 'unknown'}`);
4096
+ if ("runner" === request.namespace) logger.debug(`[Headless] Dropped stale message "${request.method}" for ${request.target?.testFile ?? 'unknown'}`);
4076
4097
  }
4077
4098
  });
4078
4099
  const dispatchRunnerMessage = async (run, file, sessionId, message)=>{
4079
4100
  const response = await dispatchRouter.dispatch({
4080
- requestId: nextDispatchRequestId('runner'),
4101
+ requestId: nextDispatchRequestId("runner"),
4081
4102
  runToken: run.token,
4082
- namespace: DISPATCH_NAMESPACE_RUNNER,
4103
+ namespace: "runner",
4083
4104
  method: message.type,
4084
4105
  args: 'payload' in message ? message.payload : void 0,
4085
4106
  target: {
@@ -4113,7 +4134,7 @@ const runBrowserController = async (context, options)=>{
4113
4134
  resolveDone = resolve;
4114
4135
  });
4115
4136
  const projectRuntime = projectRuntimeConfigs.find((project)=>project.name === file.projectName);
4116
- const perFileTimeoutMs = (projectRuntime?.runtimeConfig.testTimeout ?? maxTestTimeoutForRpc) + 30000;
4137
+ const perFileTimeoutMs = (projectRuntime?.runtimeConfig.testTimeout ?? maxTestTimeoutForRpc) + PER_FILE_TIMEOUT_BUFFER_MS;
4117
4138
  let timeoutId;
4118
4139
  try {
4119
4140
  page = await browserContext.newPage();
@@ -4436,7 +4457,7 @@ const runBrowserController = async (context, options)=>{
4436
4457
  };
4437
4458
  const getHeadedPerFileTimeoutMs = (file)=>{
4438
4459
  const projectRuntime = projectRuntimeConfigs.find((project)=>project.name === file.projectName);
4439
- return (projectRuntime?.runtimeConfig.testTimeout ?? maxTestTimeoutForRpc) + 30000;
4460
+ return (projectRuntime?.runtimeConfig.testTimeout ?? maxTestTimeoutForRpc) + PER_FILE_TIMEOUT_BUFFER_MS;
4440
4461
  };
4441
4462
  let containerContext;
4442
4463
  let containerPage;
@@ -4744,7 +4765,7 @@ const listBrowserTests = async (context, options)=>{
4744
4765
  runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project)),
4745
4766
  viewport: project.normalizedConfig.browser.viewport
4746
4767
  }));
4747
- const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? 5000));
4768
+ const maxTestTimeoutForRpc = Math.max(...browserProjects.map((p)=>p.normalizedConfig.testTimeout ?? DEFAULT_TEST_TIMEOUT));
4748
4769
  const hostOptions = {
4749
4770
  rootPath: normalize(context.rootPath),
4750
4771
  projects: projectRuntimeConfigs,
@@ -4850,43 +4871,10 @@ const listBrowserTests = async (context, options)=>{
4850
4871
  close: cleanup
4851
4872
  };
4852
4873
  };
4853
- const SUPPORTED_PROVIDERS = [
4854
- 'playwright'
4855
- ];
4856
- const isPlainObject = (value)=>'[object Object]' === Object.prototype.toString.call(value);
4857
- const validateViewport = (viewport)=>{
4858
- if (null == viewport) return;
4859
- if ('string' == typeof viewport) {
4860
- const presetId = viewport.trim();
4861
- if (!presetId) throw new Error('browser.viewport must be a non-empty preset id.');
4862
- if (!resolveBrowserViewportPreset(presetId)) throw new Error(`browser.viewport must be a valid preset id. Received: ${viewport}`);
4863
- return;
4864
- }
4865
- if (isPlainObject(viewport)) {
4866
- const width = viewport.width;
4867
- const height = viewport.height;
4868
- if (!Number.isFinite(width) || width <= 0) throw new Error('browser.viewport.width must be a positive number.');
4869
- if (!Number.isFinite(height) || height <= 0) throw new Error('browser.viewport.height must be a positive number.');
4870
- return;
4871
- }
4872
- throw new Error('browser.viewport must be either a preset id or { width, height }.');
4873
- };
4874
- const validateBrowserConfig = (context)=>{
4875
- for (const project of context.projects){
4876
- const { browser, output } = project.normalizedConfig;
4877
- if (browser.enabled) {
4878
- if (!browser.provider) throw new Error('browser.provider is required when browser.enabled is true.');
4879
- if (!SUPPORTED_PROVIDERS.includes(browser.provider)) throw new Error(`browser.provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}.`);
4880
- validateViewport(browser.viewport);
4881
- if (!isPlainObject(browser.providerOptions)) throw new Error('browser.providerOptions must be a plain object.');
4882
- if (output?.bundleDependencies === false) throw new Error('output.bundleDependencies false is not supported in browser mode.');
4883
- }
4884
- }
4885
- };
4886
4874
  async function runBrowserTests(context, options) {
4887
4875
  return runBrowserController(context, options);
4888
4876
  }
4889
- async function src_listBrowserTests(context) {
4890
- return listBrowserTests(context);
4877
+ async function src_listBrowserTests(context, options) {
4878
+ return listBrowserTests(context, options);
4891
4879
  }
4892
- export { BROWSER_VIEWPORT_PRESET_DIMENSIONS, BROWSER_VIEWPORT_PRESET_IDS, resolveBrowserViewportPreset, runBrowserTests, src_listBrowserTests as listBrowserTests, validateBrowserConfig };
4880
+ export { runBrowserTests, src_listBrowserTests as listBrowserTests, validateBrowserConfig };