@rstest/browser 0.9.10 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/augmentExpect.d.ts +1 -1
  2. package/dist/browser-container/container-static/js/{102.e63e4aa16d.js → 188.480c3c49b5.js} +4543 -4250
  3. package/dist/browser-container/container-static/js/188.480c3c49b5.js.LICENSE.txt +1 -0
  4. package/dist/browser-container/container-static/js/{index.af52585634.js → index.29fc60be33.js} +10 -10
  5. package/dist/browser-container/container-static/js/{lib-react.9e703e1b29.js → lib-react.5ae9b90ee5.js} +24 -24
  6. package/dist/browser-container/container-static/js/lib-react.5ae9b90ee5.js.LICENSE.txt +1 -0
  7. package/dist/browser-container/index.html +1 -1
  8. package/dist/browser.d.ts +2 -2
  9. package/dist/client/api.d.ts +2 -2
  10. package/dist/client/browserRpc.d.ts +2 -2
  11. package/dist/client/dispatchTransport.d.ts +1 -1
  12. package/dist/client/locator.d.ts +2 -2
  13. package/dist/concurrency.d.ts +1 -1
  14. package/dist/dispatchCapabilities.d.ts +2 -2
  15. package/dist/dispatchRouter.d.ts +1 -1
  16. package/dist/headlessTransport.d.ts +2 -2
  17. package/dist/hostController.d.ts +2 -2
  18. package/dist/index.d.ts +3 -3
  19. package/dist/index.js +205 -23
  20. package/dist/protocol.d.ts +6 -2
  21. package/dist/providers/index.d.ts +1 -1
  22. package/dist/providers/playwright/compileLocator.d.ts +1 -1
  23. package/dist/providers/playwright/dispatchBrowserRpc.d.ts +1 -1
  24. package/dist/providers/playwright/expectUtils.d.ts +1 -1
  25. package/dist/providers/playwright/implementation.d.ts +1 -1
  26. package/dist/providers/playwright/index.d.ts +1 -1
  27. package/dist/providers/playwright/runtime.d.ts +1 -1
  28. package/dist/providers/playwright/textMatcher.d.ts +2 -2
  29. package/dist/sessionRegistry.d.ts +1 -1
  30. package/dist/viewportPresets.d.ts +1 -1
  31. package/dist/watchRerunPlanner.d.ts +1 -1
  32. package/package.json +9 -9
  33. package/src/client/entry.ts +75 -14
  34. package/src/hostController.ts +405 -70
  35. package/src/protocol.ts +4 -0
  36. package/dist/browser-container/container-static/js/102.e63e4aa16d.js.LICENSE.txt +0 -1
  37. package/dist/browser-container/container-static/js/lib-react.9e703e1b29.js.LICENSE.txt +0 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ 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 { color, createCoverageProvider, getSetupFiles, getTestEntries, isDebug, loadCoverageProvider, logger, rsbuild, serializableConfig } from "@rstest/core/browser";
5
+ import { PhaseTracker, color, createCoverageProvider, getNoTestFilesMessage, getSetupFiles, getTestEntries, isDebug, loadCoverageProvider, logger, resolveProjectBuildCache, rsbuild, serializableConfig } from "@rstest/core/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";
@@ -2800,11 +2800,14 @@ var picomatch_default = /*#__PURE__*/ __webpack_require__.n(picomatch);
2800
2800
  const { createRsbuild: createRsbuild, rspack: rspack } = rsbuild;
2801
2801
  const hostController_dirname = dirname(fileURLToPath(import.meta.url));
2802
2802
  const OPTIONS_PLACEHOLDER = '__RSTEST_OPTIONS_PLACEHOLDER__';
2803
+ let nextBrowserFilePid = 1000000000;
2803
2804
  const serializeForInlineScript = (value)=>JSON.stringify(value).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
2804
2805
  const getBrowserProviderOptions = (project)=>{
2805
2806
  const browserConfig = project.normalizedConfig.browser;
2806
2807
  return browserConfig.providerOptions ?? {};
2807
2808
  };
2809
+ const getFileTaskId = (testPath)=>`file:${testPath}`;
2810
+ const getBufferedLogTaskId = (log)=>log.taskId ?? getFileTaskId(log.testPath);
2808
2811
  const createDeferredPromise = ()=>{
2809
2812
  let resolve;
2810
2813
  let reject;
@@ -3059,9 +3062,12 @@ const getAffectedTestFiles = (chunks, entryTestFiles)=>{
3059
3062
  return Array.from(affectedFiles);
3060
3063
  };
3061
3064
  const getRuntimeConfigFromProject = (project)=>{
3062
- const { testNamePattern, testTimeout, passWithNoTests, retry, globals, clearMocks, resetMocks, restoreMocks, unstubEnvs, unstubGlobals, maxConcurrency, printConsoleTrace, disableConsoleIntercept, testEnvironment, hookTimeout, isolate, coverage, snapshotFormat, env, bail, logHeapUsage, chaiConfig, includeTaskLocation } = project.normalizedConfig;
3065
+ const { testNamePattern, testTimeout, passWithNoTests, retry, globals, clearMocks, resetMocks, restoreMocks, unstubEnvs, unstubGlobals, maxConcurrency, printConsoleTrace, disableConsoleIntercept, testEnvironment, hookTimeout, isolate, coverage, snapshotFormat, env, bail, logHeapUsage, detectAsyncLeaks, chaiConfig, includeTaskLocation, silent } = project.normalizedConfig;
3063
3066
  return {
3064
- env,
3067
+ env: {
3068
+ NODE_ENV: process.env.NODE_ENV,
3069
+ ...env
3070
+ },
3065
3071
  testNamePattern,
3066
3072
  testTimeout,
3067
3073
  hookTimeout,
@@ -3082,8 +3088,10 @@ const getRuntimeConfigFromProject = (project)=>{
3082
3088
  snapshotFormat,
3083
3089
  bail,
3084
3090
  logHeapUsage,
3091
+ detectAsyncLeaks,
3085
3092
  chaiConfig,
3086
- includeTaskLocation
3093
+ includeTaskLocation,
3094
+ silent
3087
3095
  };
3088
3096
  };
3089
3097
  const getBrowserProjects = (context)=>context.projects.filter((project)=>project.normalizedConfig.browser.enabled);
@@ -3123,7 +3131,8 @@ const collectProjectEntries = async (context)=>{
3123
3131
  includeSource,
3124
3132
  rootPath: context.rootPath,
3125
3133
  projectRoot: project.rootPath,
3126
- fileFilters: context.fileFilters || []
3134
+ fileFilters: context.fileFilters || [],
3135
+ fileFilterMode: context.fileFilterMode
3127
3136
  });
3128
3137
  const setup = getSetupFiles(setupFiles, project.rootPath);
3129
3138
  return {
@@ -3326,8 +3335,18 @@ const createBrowserRuntime = async ({ context, manifestPath, manifestSource, tem
3326
3335
  const project = projectByEnvironmentName.get(name);
3327
3336
  if (!project) return config;
3328
3337
  const userRsbuildConfig = project.normalizedConfig;
3338
+ const buildCache = resolveProjectBuildCache({
3339
+ context,
3340
+ project
3341
+ });
3329
3342
  const setupFiles = Object.values(getSetupFiles(project.normalizedConfig.setupFiles, project.rootPath));
3330
- const merged = mergeEnvironmentConfig(config, userRsbuildConfig, {
3343
+ const merged = mergeEnvironmentConfig(config, {
3344
+ ...userRsbuildConfig,
3345
+ performance: buildCache ? {
3346
+ ...userRsbuildConfig.performance,
3347
+ buildCache
3348
+ } : userRsbuildConfig.performance
3349
+ }, {
3331
3350
  resolve: {
3332
3351
  alias: rstestInternalAliases
3333
3352
  },
@@ -3572,8 +3591,10 @@ async function resolveProjectEntries(context, shardedEntries) {
3572
3591
  return collectProjectEntries(context);
3573
3592
  }
3574
3593
  const runBrowserController = async (context, options)=>{
3575
- const { skipOnTestRunEnd = false } = options ?? {};
3594
+ const { skipOnTestRunEnd = false, allowEmptyWatchRun = false, onTraceEvents } = options ?? {};
3576
3595
  const buildStart = Date.now();
3596
+ const isWatchMode = 'watch' === context.command;
3597
+ const phaseTrackers = onTraceEvents ? new Map() : void 0;
3577
3598
  const browserProjects = getBrowserProjects(context);
3578
3599
  const useHeadlessDirect = browserProjects.every((project)=>project.normalizedConfig.browser.headless);
3579
3600
  const browserSourceMapCache = new Map();
@@ -3696,18 +3717,24 @@ const runBrowserController = async (context, options)=>{
3696
3717
  }
3697
3718
  const projectEntries = await resolveProjectEntries(context, options?.shardedEntries);
3698
3719
  const totalTests = projectEntries.reduce((total, item)=>total + item.testFiles.length, 0);
3720
+ const shouldKeepWatchingWithEmptySet = isWatchMode && allowEmptyWatchRun;
3699
3721
  if (0 === totalTests) {
3700
3722
  const code = context.normalizedConfig.passWithNoTests ? 0 : 1;
3701
3723
  if (!skipOnTestRunEnd) {
3702
- const message = `No test files found, exiting with code ${code}.`;
3724
+ const message = shouldKeepWatchingWithEmptySet ? 'No test files found.' : getNoTestFilesMessage({
3725
+ context,
3726
+ code,
3727
+ defaultMessage: `No test files found, exiting with code ${code}.`
3728
+ });
3703
3729
  if (0 === code) logger.log(color.yellow(message));
3704
3730
  else logger.error(color.red(message));
3731
+ if (context.relatedFilters?.length) logger.log(color.gray('related: '), context.relatedFilters.join(color.gray(', ')));
3732
+ else if (context.fileFilters?.length) logger.log(color.gray('filter: '), context.fileFilters.join(color.gray(', ')));
3705
3733
  }
3706
- if (0 !== code) ensureProcessExitCode(code);
3707
- return;
3734
+ if (0 !== code && !shouldKeepWatchingWithEmptySet) ensureProcessExitCode(code);
3735
+ if (!shouldKeepWatchingWithEmptySet) return;
3708
3736
  }
3709
3737
  await notifyTestRunStart();
3710
- const isWatchMode = 'watch' === context.command;
3711
3738
  const enableCliShortcuts = isWatchMode && isBrowserWatchCliShortcutsEnabled();
3712
3739
  const browserTempOutputRoot = context.normalizedConfig.output.distPath.root;
3713
3740
  const tempDir = isWatchMode && watchContext.runtime ? watchContext.runtime.tempDir : isWatchMode ? join(context.rootPath, browserTempOutputRoot, 'browser', 'watch') : join(context.rootPath, browserTempOutputRoot, 'browser', Date.now().toString());
@@ -3843,26 +3870,57 @@ const runBrowserController = async (context, options)=>{
3843
3870
  }
3844
3871
  };
3845
3872
  const handleTestFileStart = async (payload)=>{
3873
+ if (phaseTrackers) {
3874
+ const tracker = new PhaseTracker({
3875
+ trace: {
3876
+ testPath: payload.testPath,
3877
+ project: payload.projectName
3878
+ },
3879
+ pid: nextBrowserFilePid++
3880
+ });
3881
+ tracker.transition('prepare');
3882
+ phaseTrackers.set(payload.testPath, tracker);
3883
+ }
3846
3884
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileStart?.({
3885
+ testId: getFileTaskId(payload.testPath),
3847
3886
  testPath: payload.testPath,
3848
3887
  tests: []
3849
3888
  })));
3850
3889
  };
3851
3890
  const handleTestFileReady = async (payload)=>{
3891
+ phaseTrackers?.get(payload.testPath)?.transition('tests');
3852
3892
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileReady?.(payload)));
3853
3893
  };
3854
3894
  const handleTestSuiteStart = async (payload)=>{
3895
+ phaseTrackers?.get(payload.testPath)?.recordSuiteStart(payload);
3855
3896
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestSuiteStart?.(payload)));
3856
3897
  };
3857
3898
  const handleTestSuiteResult = async (payload)=>{
3899
+ phaseTrackers?.get(payload.testPath)?.recordSuiteResult(payload);
3858
3900
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestSuiteResult?.(payload)));
3901
+ if ('passed-only' === context.normalizedConfig.silent) await flushBufferedLogsForTask({
3902
+ taskId: payload.testId,
3903
+ status: payload.status,
3904
+ taskParentNames: payload.parentNames,
3905
+ taskType: 'suite',
3906
+ testPath: payload.testPath
3907
+ });
3859
3908
  };
3860
3909
  const handleTestCaseStart = async (payload)=>{
3910
+ phaseTrackers?.get(payload.testPath)?.recordCaseStart(payload);
3861
3911
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestCaseStart?.(payload)));
3862
3912
  };
3863
3913
  const handleTestCaseResult = async (payload)=>{
3864
3914
  caseResults.push(payload);
3915
+ phaseTrackers?.get(payload.testPath)?.recordCaseResult(payload);
3865
3916
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestCaseResult?.(payload)));
3917
+ if ('passed-only' === context.normalizedConfig.silent) await flushBufferedLogsForTask({
3918
+ taskId: payload.testId,
3919
+ status: payload.status,
3920
+ taskParentNames: payload.parentNames,
3921
+ taskType: 'case',
3922
+ testPath: payload.testPath
3923
+ });
3866
3924
  };
3867
3925
  const handleTestFileComplete = async (payload)=>{
3868
3926
  reporterResults.push(payload);
@@ -3870,6 +3928,22 @@ const runBrowserController = async (context, options)=>{
3870
3928
  payload
3871
3929
  ], payload.results);
3872
3930
  if (payload.snapshotResult) context.snapshotManager.add(payload.snapshotResult);
3931
+ if (phaseTrackers) {
3932
+ const tracker = phaseTrackers.get(payload.testPath);
3933
+ if (tracker) {
3934
+ tracker.end();
3935
+ const events = tracker.getTraceEvents();
3936
+ if (events) onTraceEvents?.(events);
3937
+ phaseTrackers.delete(payload.testPath);
3938
+ }
3939
+ }
3940
+ if ('passed-only' === context.normalizedConfig.silent) await flushBufferedLogsForTask({
3941
+ taskId: payload.testId,
3942
+ status: payload.status,
3943
+ taskParentNames: payload.parentNames,
3944
+ taskType: 'file',
3945
+ testPath: payload.testPath
3946
+ });
3873
3947
  await Promise.all(context.reporters.map((reporter)=>reporter.onTestFileResult?.(payload)));
3874
3948
  if ('fail' === payload.status) ensureProcessExitCode(1);
3875
3949
  };
@@ -3877,12 +3951,18 @@ const runBrowserController = async (context, options)=>{
3877
3951
  const log = {
3878
3952
  content: payload.content,
3879
3953
  name: payload.level,
3954
+ taskId: payload.taskId,
3955
+ taskName: payload.taskName,
3956
+ taskParentNames: payload.taskParentNames,
3957
+ taskType: payload.taskType,
3880
3958
  testPath: payload.testPath,
3881
3959
  type: payload.type,
3882
3960
  trace: payload.trace
3883
3961
  };
3884
- const shouldLog = context.normalizedConfig.onConsoleLog?.(log.content) ?? true;
3885
- if (shouldLog) await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
3962
+ if (true === context.normalizedConfig.silent) return;
3963
+ if ('passed-only' === context.normalizedConfig.silent) return void bufferConsoleLog(log);
3964
+ if (context.normalizedConfig.disableConsoleIntercept) return;
3965
+ await emitUserConsoleLog(log);
3886
3966
  };
3887
3967
  const handleFatal = async (payload)=>{
3888
3968
  const error = new Error(payload.message);
@@ -3890,6 +3970,52 @@ const runBrowserController = async (context, options)=>{
3890
3970
  fatalError = error;
3891
3971
  ensureProcessExitCode(1);
3892
3972
  };
3973
+ const bufferedConsoleLogs = new Map();
3974
+ const suiteIdsByChain = new Map();
3975
+ const getSuiteChainKey = (names)=>names.join('\u0000');
3976
+ const pushTaskId = (taskIds, taskId)=>{
3977
+ if (!taskIds.includes(taskId)) taskIds.push(taskId);
3978
+ };
3979
+ const shouldEmitUserConsoleLog = (log)=>context.normalizedConfig.onConsoleLog?.(log.content) !== false;
3980
+ const emitUserConsoleLog = async (log)=>{
3981
+ if (!shouldEmitUserConsoleLog(log)) return;
3982
+ await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
3983
+ };
3984
+ const bufferConsoleLog = (log)=>{
3985
+ const taskId = getBufferedLogTaskId(log);
3986
+ const logs = bufferedConsoleLogs.get(taskId) || [];
3987
+ logs.push(log);
3988
+ bufferedConsoleLogs.set(taskId, logs);
3989
+ if ('suite' === log.taskType && log.taskId) suiteIdsByChain.set(getSuiteChainKey([
3990
+ ...log.taskParentNames || [],
3991
+ log.taskName || ''
3992
+ ]), log.taskId);
3993
+ };
3994
+ const flushBufferedLogsForTask = async ({ taskId, status, taskParentNames, taskType, testPath })=>{
3995
+ if ('fail' !== status) return void bufferedConsoleLogs.delete(taskId);
3996
+ const taskIdsToFlush = [];
3997
+ if ('case' === taskType) {
3998
+ pushTaskId(taskIdsToFlush, getFileTaskId(testPath));
3999
+ const suiteNames = taskParentNames || [];
4000
+ for(let i = 0; i < suiteNames.length; i++){
4001
+ const suiteId = suiteIdsByChain.get(getSuiteChainKey(suiteNames.slice(0, i + 1)));
4002
+ if (suiteId) pushTaskId(taskIdsToFlush, suiteId);
4003
+ }
4004
+ pushTaskId(taskIdsToFlush, taskId);
4005
+ }
4006
+ if ('suite' === taskType) {
4007
+ pushTaskId(taskIdsToFlush, getFileTaskId(testPath));
4008
+ pushTaskId(taskIdsToFlush, taskId);
4009
+ }
4010
+ if ('file' === taskType) pushTaskId(taskIdsToFlush, taskId);
4011
+ for (const bufferedTaskId of taskIdsToFlush){
4012
+ const logs = bufferedConsoleLogs.get(bufferedTaskId);
4013
+ if (logs) {
4014
+ bufferedConsoleLogs.delete(bufferedTaskId);
4015
+ for (const log of logs)await Promise.all(context.reporters.map((reporter)=>reporter.onUserConsoleLog?.(log)));
4016
+ }
4017
+ }
4018
+ };
3893
4019
  const runSnapshotRpc = async (request)=>{
3894
4020
  switch(request.method){
3895
4021
  case 'resolveSnapshotPath':
@@ -4148,6 +4274,54 @@ const runBrowserController = async (context, options)=>{
4148
4274
  logger.debug(`[Headless] Interrupting active run token ${run.token} before scheduling latest rerun`);
4149
4275
  }
4150
4276
  });
4277
+ if (0 === allTestFiles.length) {
4278
+ const duration = {
4279
+ totalTime: buildTime,
4280
+ buildTime,
4281
+ testTime: 0
4282
+ };
4283
+ const result = {
4284
+ results: reporterResults,
4285
+ testResults: caseResults,
4286
+ duration,
4287
+ hasFailure: false,
4288
+ getSourcemap: getBrowserSourcemap,
4289
+ resolveSourcemap: resolveBrowserSourcemap,
4290
+ close: skipOnTestRunEnd ? async ()=>{
4291
+ sessionRegistry.clear();
4292
+ await destroyBrowserRuntime(runtime);
4293
+ } : void 0
4294
+ };
4295
+ if (!skipOnTestRunEnd) await notifyTestRunEnd({
4296
+ duration
4297
+ });
4298
+ if (isWatchMode) {
4299
+ triggerRerun = async ()=>{
4300
+ const newProjectEntries = await collectProjectEntries(context);
4301
+ const rerunPlan = planWatchRerun({
4302
+ projectEntries: newProjectEntries,
4303
+ previousTestFiles: watchContext.lastTestFiles,
4304
+ affectedTestFiles: watchContext.affectedTestFiles
4305
+ });
4306
+ watchContext.affectedTestFiles = [];
4307
+ if (rerunPlan.filesChanged) {
4308
+ watchContext.lastTestFiles = rerunPlan.currentTestFiles;
4309
+ if (0 === rerunPlan.currentTestFiles.length) {
4310
+ logger.log(color.cyan('No browser test files remain after update.\n'));
4311
+ logBrowserWatchReadyMessage(enableCliShortcuts);
4312
+ return;
4313
+ }
4314
+ logger.log(color.cyan(`Test file set changed, re-running ${rerunPlan.currentTestFiles.length} file(s)...\n`));
4315
+ latestRerunScheduler.enqueueLatest(rerunPlan.currentTestFiles);
4316
+ return;
4317
+ }
4318
+ logBrowserWatchReadyMessage(enableCliShortcuts);
4319
+ };
4320
+ watchContext.hooksEnabled = true;
4321
+ logBrowserWatchReadyMessage(enableCliShortcuts);
4322
+ }
4323
+ return result;
4324
+ }
4151
4325
  const testStart = Date.now();
4152
4326
  await runFilesWithPool(allTestFiles);
4153
4327
  const testTime = Date.now() - testStart;
@@ -4418,18 +4592,21 @@ const runBrowserController = async (context, options)=>{
4418
4592
  if (fatalError) return;
4419
4593
  await reloadTestFileWithTimeout(file, testNamePattern);
4420
4594
  });
4421
- const testStart = Date.now();
4422
- try {
4423
- await waitForRunnerFramesReady(currentTestFiles.map((file)=>file.testPath));
4424
- for (const file of currentTestFiles){
4425
- await enqueueHeadedReload(file);
4426
- if (fatalError) break;
4595
+ let testTime = 0;
4596
+ if (currentTestFiles.length > 0) {
4597
+ const testStart = Date.now();
4598
+ try {
4599
+ await waitForRunnerFramesReady(currentTestFiles.map((file)=>file.testPath));
4600
+ for (const file of currentTestFiles){
4601
+ await enqueueHeadedReload(file);
4602
+ if (fatalError) break;
4603
+ }
4604
+ } catch (error) {
4605
+ fatalError = fatalError ?? toError(error);
4606
+ ensureProcessExitCode(1);
4427
4607
  }
4428
- } catch (error) {
4429
- fatalError = fatalError ?? toError(error);
4430
- ensureProcessExitCode(1);
4608
+ testTime = Date.now() - testStart;
4431
4609
  }
4432
- const testTime = Date.now() - testStart;
4433
4610
  if (isWatchMode) triggerRerun = async ()=>{
4434
4611
  const newProjectEntries = await collectProjectEntries(context);
4435
4612
  const rerunPlan = planWatchRerun({
@@ -4444,6 +4621,11 @@ const runBrowserController = async (context, options)=>{
4444
4621
  watchContext.lastTestFiles = rerunPlan.currentTestFiles;
4445
4622
  currentTestFiles = rerunPlan.currentTestFiles;
4446
4623
  await rpcManager.notifyTestFileUpdate(currentTestFiles);
4624
+ if (0 === currentTestFiles.length) {
4625
+ logger.log(color.cyan('No browser test files remain after update.\n'));
4626
+ logBrowserWatchReadyMessage(enableCliShortcuts);
4627
+ return;
4628
+ }
4447
4629
  await waitForRunnerFramesReady(currentTestFiles.map((file)=>file.testPath));
4448
4630
  }
4449
4631
  if (rerunPlan.normalizedAffectedTestFiles.length > 0) {
@@ -1,8 +1,8 @@
1
1
  import type { DevicePreset } from '@rstest/core/browser';
2
2
  import type { RuntimeConfig, TestFileResult, TestInfo, TestResult } from '@rstest/core/browser-runtime';
3
3
  import type { SnapshotUpdateState } from '@vitest/snapshot';
4
- export type { BrowserLocatorIR, BrowserRpcRequest, SnapshotRpcRequest, } from './rpcProtocol';
5
- export { validateBrowserRpcRequest } from './rpcProtocol';
4
+ export type { BrowserLocatorIR, BrowserRpcRequest, SnapshotRpcRequest, } from './rpcProtocol.js';
5
+ export { validateBrowserRpcRequest } from './rpcProtocol.js';
6
6
  export declare const DISPATCH_MESSAGE_TYPE = "__rstest_dispatch__";
7
7
  export declare const DISPATCH_RESPONSE_TYPE = "__rstest_dispatch_response__";
8
8
  export declare const DISPATCH_RPC_BRIDGE_NAME = "__rstest_dispatch_rpc__";
@@ -91,6 +91,10 @@ export type BrowserClientMessage = {
91
91
  payload: {
92
92
  level: 'log' | 'warn' | 'error' | 'info' | 'debug';
93
93
  content: string;
94
+ taskId?: string;
95
+ taskName?: string;
96
+ taskParentNames?: string[];
97
+ taskType?: 'file' | 'suite' | 'case';
94
98
  testPath: string;
95
99
  type: 'stdout' | 'stderr';
96
100
  trace?: string;
@@ -1,4 +1,4 @@
1
- import type { BrowserRpcRequest } from '../rpcProtocol';
1
+ import type { BrowserRpcRequest } from '../rpcProtocol.js';
2
2
  /**
3
3
  * Browser provider contract hub.
4
4
  *
@@ -1,3 +1,3 @@
1
1
  import type { FrameLocator, Locator, Page } from 'playwright';
2
- import type { BrowserLocatorIR } from '../../protocol';
2
+ import type { BrowserLocatorIR } from '../../protocol.js';
3
3
  export declare const compilePlaywrightLocator: (frame: FrameLocator | Page, locatorIR: BrowserLocatorIR) => Locator;
@@ -4,7 +4,7 @@
4
4
  * Copyright (c) Microsoft Corporation, Apache-2.0.
5
5
  */
6
6
  import type { Page } from 'playwright';
7
- import type { BrowserRpcRequest } from '../../rpcProtocol';
7
+ import type { BrowserRpcRequest } from '../../rpcProtocol.js';
8
8
  export declare function dispatchPlaywrightBrowserRpc({ containerPage, runnerPage, request, timeoutFallbackMs, }: {
9
9
  containerPage?: Page;
10
10
  runnerPage?: Page;
@@ -3,7 +3,7 @@
3
3
  * https://github.com/microsoft/playwright/blob/main/packages/playwright/src/matchers/matchers.ts
4
4
  * Copyright (c) Microsoft Corporation, Apache-2.0.
5
5
  */
6
- import type { BrowserLocatorText } from '../../rpcProtocol';
6
+ import type { BrowserLocatorText } from '../../rpcProtocol.js';
7
7
  type ExpectedTextValue = {
8
8
  string?: string;
9
9
  regexSource?: string;
@@ -1,2 +1,2 @@
1
- import type { BrowserProviderImplementation } from '../index';
1
+ import type { BrowserProviderImplementation } from '../index.js';
2
2
  export declare const playwrightProviderImplementation: BrowserProviderImplementation;
@@ -1 +1 @@
1
- export { playwrightProviderImplementation } from './implementation';
1
+ export { playwrightProviderImplementation } from './implementation.js';
@@ -1,4 +1,4 @@
1
- import type { BrowserProviderRuntime } from '../index';
1
+ import type { BrowserProviderRuntime } from '../index.js';
2
2
  export declare function launchPlaywrightBrowser({ browserName, headless, providerOptions, }: {
3
3
  browserName: 'chromium' | 'firefox' | 'webkit';
4
4
  headless: boolean | undefined;
@@ -1,8 +1,8 @@
1
1
  export declare const reviveBrowserLocatorText: (text: {
2
- type: "string";
2
+ type: 'string';
3
3
  value: string;
4
4
  } | {
5
- type: "regexp";
5
+ type: 'regexp';
6
6
  source: string;
7
7
  flags?: string;
8
8
  }) => string | RegExp;
@@ -1,4 +1,4 @@
1
- import type { BrowserProviderContext, BrowserProviderPage } from './providers';
1
+ import type { BrowserProviderContext, BrowserProviderPage } from './providers/index.js';
2
2
  type RunnerSessionRecord = {
3
3
  id: string;
4
4
  testFile: string;
@@ -5,7 +5,7 @@
5
5
  * `@rstest/core` (`packages/core/src/types/config.ts`) so `defineConfig`
6
6
  * autocomplete and runtime validation stay consistent.
7
7
  */
8
- export declare const BROWSER_VIEWPORT_PRESET_IDS: readonly ["iPhoneSE", "iPhoneXR", "iPhone12Pro", "iPhone14ProMax", "Pixel7", "SamsungGalaxyS8Plus", "SamsungGalaxyS20Ultra", "iPadMini", "iPadAir", "iPadPro", "SurfacePro7", "SurfaceDuo", "GalaxyZFold5", "AsusZenbookFold", "SamsungGalaxyA51A71", "NestHub", "NestHubMax"];
8
+ export declare const BROWSER_VIEWPORT_PRESET_IDS: readonly ['iPhoneSE', 'iPhoneXR', 'iPhone12Pro', 'iPhone14ProMax', 'Pixel7', 'SamsungGalaxyS8Plus', 'SamsungGalaxyS20Ultra', 'iPadMini', 'iPadAir', 'iPadPro', 'SurfacePro7', 'SurfaceDuo', 'GalaxyZFold5', 'AsusZenbookFold', 'SamsungGalaxyA51A71', 'NestHub', 'NestHubMax'];
9
9
  type BrowserViewportPresetId = (typeof BROWSER_VIEWPORT_PRESET_IDS)[number];
10
10
  type BrowserViewportSize = {
11
11
  width: number;
@@ -1,4 +1,4 @@
1
- import type { TestFileInfo } from './protocol';
1
+ import type { TestFileInfo } from './protocol.js';
2
2
  type WatchPlannerProjectEntry = {
3
3
  project: {
4
4
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rstest/browser",
3
- "version": "0.9.10",
3
+ "version": "0.10.1",
4
4
  "description": "Browser mode support for Rstest testing framework.",
5
5
  "keywords": [
6
6
  "rstest",
@@ -32,7 +32,7 @@
32
32
  "default": "./package.json"
33
33
  }
34
34
  },
35
- "main": "./dist/index.js",
35
+ "main": "./dist/browser.js",
36
36
  "files": [
37
37
  "dist",
38
38
  "src",
@@ -45,25 +45,25 @@
45
45
  "open-editor": "^6.0.0",
46
46
  "pathe": "^2.0.3",
47
47
  "sirv": "^3.0.2",
48
- "ws": "^8.20.0"
48
+ "ws": "^8.20.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@rslib/core": "0.21.3",
51
+ "@rslib/core": "0.21.5",
52
52
  "@types/convert-source-map": "^2.0.3",
53
53
  "@types/picomatch": "^4.0.3",
54
54
  "@types/ws": "^8.18.1",
55
- "@typescript/native-preview": "7.0.0-dev.20260428.1",
55
+ "@typescript/native-preview": "7.0.0-dev.20260516.1",
56
56
  "@vitest/snapshot": "^3.2.4",
57
57
  "birpc": "^4.0.0",
58
58
  "picomatch": "^4.0.4",
59
- "playwright": "^1.59.1",
60
- "@rstest/browser-ui": "0.0.0",
59
+ "playwright": "^1.60.0",
61
60
  "@rstest/tsconfig": "0.0.1",
62
- "@rstest/core": "0.9.10"
61
+ "@rstest/browser-ui": "0.0.0",
62
+ "@rstest/core": "0.10.1"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "playwright": "^1.49.1",
66
- "@rstest/core": "^0.9.10"
66
+ "@rstest/core": "^0.10.1"
67
67
  },
68
68
  "peerDependenciesMeta": {
69
69
  "playwright": {