@rstest/core 0.8.3 → 0.8.5

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/dist/0~1472.js CHANGED
@@ -4,7 +4,7 @@ import { pathToFileURL } from "./6198.js";
4
4
  import "./1157.js";
5
5
  import { logger as logger_logger, color } from "./3160.js";
6
6
  async function loadBrowserModule(options = {}) {
7
- const coreVersion = "0.8.3";
7
+ const coreVersion = "0.8.5";
8
8
  const { projectRoots = [] } = options;
9
9
  let browserModule;
10
10
  let browserVersion;
package/dist/0~2173.js CHANGED
@@ -147,9 +147,10 @@ async function setupCliShortcuts({ closeServer, runAll, updateSnapshot, runFaile
147
147
  }
148
148
  async function runBrowserModeTests(context, browserProjects, options) {
149
149
  const projectRoots = browserProjects.map((p)=>p.rootPath);
150
- const { runBrowserTests } = await loadBrowserModule({
150
+ const { validateBrowserConfig, runBrowserTests } = await loadBrowserModule({
151
151
  projectRoots
152
152
  });
153
+ validateBrowserConfig(context);
153
154
  return runBrowserTests(context, options);
154
155
  }
155
156
  async function runTests(context) {
@@ -165,7 +166,7 @@ async function runTests(context) {
165
166
  const browserResult = await runBrowserModeTests(context, browserProjects, {
166
167
  skipOnTestRunEnd: false
167
168
  });
168
- if (coverage.enabled && browserResult?.results) {
169
+ if (coverage.enabled && browserResult?.results.length && !browserResult.unhandledErrors?.length) {
169
170
  const coverageProvider = await createCoverageProvider(coverage, context.rootPath);
170
171
  if (coverageProvider) {
171
172
  const { generateCoverage } = await import("./0~4403.js").then((mod)=>({
@@ -213,6 +214,7 @@ async function runTests(context) {
213
214
  skipOnTestRunEnd: shouldUnifyReporter,
214
215
  shardedEntries: shard ? browserEntries : void 0
215
216
  });
217
+ browserResultPromise.catch(()=>{});
216
218
  }
217
219
  if (!hasNodeTestsToRun) {
218
220
  if (browserResultPromise) await browserResultPromise;
@@ -338,6 +340,7 @@ async function runTests(context) {
338
340
  const errors = returns.flatMap((r)=>r.errors || []);
339
341
  if (shouldUnifyReporter && browserResult?.results) results.push(...browserResult.results);
340
342
  if (shouldUnifyReporter && browserResult?.testResults) testResults.push(...browserResult.testResults);
343
+ if (shouldUnifyReporter && browserResult?.unhandledErrors) errors.push(...browserResult.unhandledErrors);
341
344
  context.updateReporterResultState(results, testResults, currentDeletedEntries);
342
345
  const nodeHasFailure = results.some((r)=>'fail' === r.status) || errors.length;
343
346
  const browserHasFailure = shouldUnifyReporter && browserResult?.hasFailure;
package/dist/0~5835.js CHANGED
@@ -5,11 +5,7 @@ import { createRequire as external_node_module_createRequire } from "./4881.js";
5
5
  import { pathToFileURL } from "./6198.js";
6
6
  import { node_vm, asModule, shouldInterop, interopModule } from "./0~3346.js";
7
7
  import { posix } from "./7011.js";
8
- const external_node_path_ = __webpack_require__("node:path");
9
- let latestAssetFiles = {};
10
- const updateLatestAssetFiles = (assetFiles)=>{
11
- latestAssetFiles = assetFiles;
12
- };
8
+ const external_node_path_ = __webpack_require__("path");
13
9
  const isRelativePath = (p)=>/^\.\.?\//.test(p);
14
10
  const createRequire = (filename, distPath, rstestContext, assetFiles, interopDefault)=>{
15
11
  const _require = (()=>{
@@ -22,7 +18,7 @@ const createRequire = (filename, distPath, rstestContext, assetFiles, interopDef
22
18
  const require = (id)=>{
23
19
  const currentDirectory = posix.dirname(distPath);
24
20
  const joinedPath = isRelativePath(id) ? posix.join(currentDirectory, id) : id;
25
- const content = assetFiles[joinedPath] || latestAssetFiles[joinedPath];
21
+ const content = assetFiles[joinedPath];
26
22
  if (content) try {
27
23
  return cacheableLoadModule({
28
24
  codeContent: content,
@@ -104,7 +100,7 @@ const loadModule = ({ codeContent, distPath, testPath, rstestContext, assetFiles
104
100
  require: createRequire(testPath, distPath, rstestContext, assetFiles, interopDefault),
105
101
  readWasmFile: (wasmPath, callback)=>{
106
102
  const joinedPath = isRelativePath(wasmPath) ? posix.join(posix.dirname(distPath), wasmPath) : wasmPath;
107
- const content = assetFiles[posix.normalize(joinedPath)] || latestAssetFiles[posix.normalize(joinedPath)];
103
+ const content = assetFiles[posix.normalize(joinedPath)];
108
104
  if (content) callback(null, Buffer.from(content, 'base64'));
109
105
  else callback(new Error(`WASM file ${joinedPath} not found in asset files.`));
110
106
  },
@@ -145,4 +141,5 @@ const cacheableLoadModule = ({ codeContent, distPath, testPath, rstestContext, a
145
141
  moduleCache.set(testPath, mod);
146
142
  return mod;
147
143
  };
148
- export { cacheableLoadModule, loadModule, updateLatestAssetFiles };
144
+ const clearModuleCache = ()=>moduleCache.clear();
145
+ export { cacheableLoadModule, clearModuleCache, loadModule };
package/dist/0~6588.js CHANGED
@@ -27,7 +27,7 @@ async function createChokidar(pathOrGlobs, root, options) {
27
27
  }
28
28
  return chokidar.watch(Array.from(watchFiles), options);
29
29
  }
30
- const external_node_path_ = __webpack_require__("node:path");
30
+ const external_node_path_ = __webpack_require__("path");
31
31
  const picocolors = __webpack_require__("../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js");
32
32
  let cleaners = [];
33
33
  const onBeforeRestart = (cleaner)=>{
package/dist/0~6923.js CHANGED
@@ -4,7 +4,7 @@ import { logger as logger_logger } from "./3160.js";
4
4
  import { pathToFileURL } from "./6198.js";
5
5
  import { node_vm, interopModule, shouldInterop } from "./0~3346.js";
6
6
  import { posix } from "./7011.js";
7
- const external_node_path_ = __webpack_require__("node:path");
7
+ const external_node_path_ = __webpack_require__("path");
8
8
  var loadEsModule_EsmMode = /*#__PURE__*/ function(EsmMode) {
9
9
  EsmMode[EsmMode["Unknown"] = 0] = "Unknown";
10
10
  EsmMode[EsmMode["Evaluated"] = 1] = "Evaluated";
@@ -12,14 +12,10 @@ var loadEsModule_EsmMode = /*#__PURE__*/ function(EsmMode) {
12
12
  return EsmMode;
13
13
  }({});
14
14
  const isRelativePath = (p)=>/^\.\.?\//.test(p);
15
- let latestAssetFiles = {};
16
- const updateLatestAssetFiles = (assetFiles)=>{
17
- latestAssetFiles = assetFiles;
18
- };
19
15
  const defineRstestDynamicImport = ({ distPath, testPath, assetFiles, interopDefault, returnModule, esmMode })=>async (specifier, importAttributes)=>{
20
16
  const currentDirectory = posix.dirname(distPath);
21
17
  const joinedPath = isRelativePath(specifier) ? posix.join(currentDirectory, specifier) : specifier;
22
- const content = assetFiles[joinedPath] || latestAssetFiles[joinedPath];
18
+ const content = assetFiles[joinedPath];
23
19
  if (content) try {
24
20
  return await loadModule({
25
21
  codeContent: content,
@@ -117,7 +113,7 @@ const loadModule = async ({ codeContent, distPath, testPath, assetFiles, interop
117
113
  });
118
114
  meta.readWasmFile = (wasmPath, callback)=>{
119
115
  const joinedPath = isRelativePath(wasmPath.pathname) ? posix.join(posix.dirname(distPath), wasmPath.pathname) : wasmPath.pathname;
120
- const content = assetFiles[posix.normalize(joinedPath)] || latestAssetFiles[posix.normalize(joinedPath)];
116
+ const content = assetFiles[posix.normalize(joinedPath)];
121
117
  if (content) callback(null, Buffer.from(content, 'base64'));
122
118
  else callback(new Error(`WASM file ${joinedPath} not found in asset files.`));
123
119
  };
@@ -150,4 +146,5 @@ const loadModule = async ({ codeContent, distPath, testPath, assetFiles, interop
150
146
  const ns = esm.namespace;
151
147
  return ns.default && ns.default instanceof Promise ? ns.default : ns;
152
148
  };
153
- export { asModule, loadEsModule_EsmMode, loadModule, updateLatestAssetFiles };
149
+ const clearModuleCache = ()=>esmCache.clear();
150
+ export { asModule, clearModuleCache, loadEsModule_EsmMode, loadModule };
package/dist/0~7583.js CHANGED
@@ -6,7 +6,7 @@ import "./3160.js";
6
6
  import { readdir as promises_readdir, lstat as promises_lstat, promises_stat as promises_promises_stat, realpath as external_node_fs_promises_realpath } from "./1157.js";
7
7
  import { Readable } from "./0~1981.js";
8
8
  import { EventEmitter } from "./9131.js";
9
- const external_node_path_ = __webpack_require__("node:path");
9
+ const external_node_path_ = __webpack_require__("path");
10
10
  const EntryTypes = {
11
11
  FILE_TYPE: 'files',
12
12
  DIR_TYPE: 'directories',
package/dist/0~7882.js CHANGED
@@ -5,7 +5,7 @@ import { detect, resolveCommand } from "./9131.js";
5
5
  import { Ie, Me, ye, M, dist_Y, Se, ve, pD, xe } from "./0~9348.js";
6
6
  import "./1157.js";
7
7
  const external_node_fs_ = __webpack_require__("node:fs");
8
- const external_node_path_ = __webpack_require__("node:path");
8
+ const external_node_path_ = __webpack_require__("path");
9
9
  function getUniqueBaseName(dir, baseName, ext) {
10
10
  const fullPath = external_node_path_["default"].join(dir, `${baseName}${ext}`);
11
11
  if (!external_node_fs_["default"].existsSync(fullPath)) return baseName;
@@ -372,7 +372,7 @@ async function createInteractive(cwd, projectInfo, isAgent) {
372
372
  }
373
373
  const provider = providerSelection;
374
374
  const preview = computeFilePreview(cwd, projectInfo);
375
- const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.8.3");
375
+ const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.8.5");
376
376
  const depsList = Object.entries(deps).map(([name, version])=>`${name}@${version}`).join(', ');
377
377
  const previewLines = [
378
378
  `${color.cyan('+')} Create ${preview.configFile}`,
@@ -450,7 +450,7 @@ async function generateFiles(cwd, projectInfo, provider) {
450
450
  updatePackageJsonScripts(cwd, {
451
451
  'test:browser': 'rstest --config=rstest.browser.config.ts'
452
452
  });
453
- const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.8.3");
453
+ const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.8.5");
454
454
  updatePackageJsonDevDeps(cwd, deps);
455
455
  return createdFiles;
456
456
  }
package/dist/0~89.js CHANGED
@@ -5,7 +5,7 @@ import { Tinypool } from "tinypool";
5
5
  import node_inspector from "node:inspector";
6
6
  import { basename, needFlagExperimentalDetectModule, isDeno, dirname, castArray, resolve as pathe_M_eThtNZ_resolve, serializableConfig, node_process, isDebug, color, getForceColorEnv, ADDITIONAL_NODE_BUILTINS, bgColor, join } from "./3160.js";
7
7
  import { fileURLToPath } from "./6198.js";
8
- import { node_v8, createBirpc } from "./3216.js";
8
+ import { node_v8, parseWorkerMetaMessage, createBirpc } from "./5960.js";
9
9
  import { TEMP_RSTEST_OUTPUT_DIR, TEMP_RSTEST_OUTPUT_DIR_GLOB } from "./1157.js";
10
10
  import { posix } from "./7011.js";
11
11
  import { isBuiltin } from "./4881.js";
@@ -21,11 +21,185 @@ function memory_isMemorySufficient(options) {
21
21
  const isMemorySufficient = memoryUsageRatio < memoryThreshold && heapUsed < maxHeapSize;
22
22
  return isMemorySufficient;
23
23
  }
24
+ const MAX_CAPTURED_STDERR_BYTES = 131072;
25
+ const STDERR_SETTLE_MAX_WAIT = 200;
26
+ const WORKER_EXIT_ERROR = 'Worker exited unexpectedly';
27
+ const MAX_STDERR_MESSAGE_BYTES = 65536;
28
+ const createDeferred = ()=>{
29
+ let resolvePromise = ()=>void 0;
30
+ const deferred = {
31
+ settled: false,
32
+ resolve (value) {
33
+ if (deferred.settled) return;
34
+ deferred.settled = true;
35
+ resolvePromise(value);
36
+ },
37
+ promise: new Promise((resolve)=>{
38
+ resolvePromise = resolve;
39
+ })
40
+ };
41
+ return deferred;
42
+ };
43
+ const withTimeout = (promise, timeout)=>new Promise((resolve)=>{
44
+ let finished = false;
45
+ const timer = setTimeout(()=>{
46
+ if (finished) return;
47
+ finished = true;
48
+ resolve(void 0);
49
+ }, timeout);
50
+ timer.unref();
51
+ promise.then((value)=>{
52
+ if (finished) return;
53
+ finished = true;
54
+ clearTimeout(timer);
55
+ resolve(value);
56
+ }, ()=>{
57
+ if (finished) return;
58
+ finished = true;
59
+ clearTimeout(timer);
60
+ resolve(void 0);
61
+ });
62
+ });
63
+ const formatCapturedStderr = (text)=>{
64
+ const bytes = Buffer.byteLength(text);
65
+ if (bytes <= MAX_STDERR_MESSAGE_BYTES) return text;
66
+ const half = Math.floor(MAX_STDERR_MESSAGE_BYTES / 2);
67
+ const head = text.slice(0, half);
68
+ const tail = text.slice(-half);
69
+ const hiddenBytes = bytes - Buffer.byteLength(head) - Buffer.byteLength(tail);
70
+ return `${head}\n\n... [truncated ${hiddenBytes} bytes of stderr] ...\n\n${tail}`;
71
+ };
72
+ const getChildProcessByPid = (pool, pid)=>{
73
+ for (const worker of pool.threads){
74
+ const childProcess = worker.process;
75
+ if (childProcess?.pid === pid) return childProcess;
76
+ }
77
+ };
78
+ const createWorkerStderrCapture = (pool)=>{
79
+ const workerStates = new Map();
80
+ const taskBindings = new Map();
81
+ const taskBindingWaiters = new Map();
82
+ const workerCloseWaiters = new Map();
83
+ const trackedWorkers = new Map();
84
+ const getWorkerState = (pid)=>{
85
+ let state = workerStates.get(pid);
86
+ if (!state) {
87
+ state = {
88
+ bytes: 0,
89
+ chunks: [],
90
+ seq: 0
91
+ };
92
+ workerStates.set(pid, state);
93
+ }
94
+ return state;
95
+ };
96
+ const appendStderr = (pid, text)=>{
97
+ if (!text) return;
98
+ const state = getWorkerState(pid);
99
+ const bytes = Buffer.byteLength(text);
100
+ state.seq += 1;
101
+ state.bytes += bytes;
102
+ state.chunks.push({
103
+ bytes,
104
+ seq: state.seq,
105
+ text
106
+ });
107
+ while(state.bytes > MAX_CAPTURED_STDERR_BYTES && state.chunks.length > 0){
108
+ const dropped = state.chunks.shift();
109
+ if (dropped) state.bytes -= dropped.bytes;
110
+ }
111
+ };
112
+ const attachWorker = (pid)=>{
113
+ if (trackedWorkers.has(pid)) return;
114
+ const childProcess = getChildProcessByPid(pool, pid);
115
+ const stderr = childProcess?.stderr;
116
+ if (!childProcess || !stderr) return;
117
+ const closeWaiter = createDeferred();
118
+ workerCloseWaiters.set(pid, closeWaiter);
119
+ const onData = (chunk)=>{
120
+ const text = 'string' == typeof chunk ? chunk : chunk.toString();
121
+ appendStderr(pid, text);
122
+ };
123
+ stderr.on('data', onData);
124
+ trackedWorkers.set(pid, {
125
+ onData,
126
+ stream: stderr
127
+ });
128
+ const detachTrackedWorker = ()=>{
129
+ const tracked = trackedWorkers.get(pid);
130
+ if (!tracked) return;
131
+ tracked.stream.off('data', tracked.onData);
132
+ trackedWorkers.delete(pid);
133
+ };
134
+ childProcess.once('close', ()=>{
135
+ detachTrackedWorker();
136
+ closeWaiter.resolve(void 0);
137
+ });
138
+ };
139
+ const bindTaskToPid = (taskId, pid)=>{
140
+ attachWorker(pid);
141
+ const binding = {
142
+ pid,
143
+ startSeq: getWorkerState(pid).seq
144
+ };
145
+ taskBindings.set(taskId, binding);
146
+ taskBindingWaiters.get(taskId)?.resolve(binding);
147
+ };
148
+ const waitForTaskBinding = async (taskId)=>{
149
+ const binding = taskBindings.get(taskId);
150
+ if (binding) return binding;
151
+ const waiter = taskBindingWaiters.get(taskId);
152
+ if (!waiter) return;
153
+ return withTimeout(waiter.promise, STDERR_SETTLE_MAX_WAIT);
154
+ };
155
+ const waitForWorkerClose = async (pid)=>{
156
+ attachWorker(pid);
157
+ const waiter = workerCloseWaiters.get(pid);
158
+ if (!waiter) return;
159
+ await withTimeout(waiter.promise, STDERR_SETTLE_MAX_WAIT);
160
+ };
161
+ const getTaskStderr = (taskId)=>{
162
+ const binding = taskBindings.get(taskId);
163
+ if (!binding) return '';
164
+ const state = workerStates.get(binding.pid);
165
+ if (!state || 0 === state.chunks.length) return '';
166
+ return state.chunks.filter((chunk)=>chunk.seq > binding.startSeq).map((chunk)=>chunk.text).join('').trim();
167
+ };
168
+ const enhanceWorkerExitError = async (taskId, err)=>{
169
+ if (!(err instanceof Error) || !err.message.includes(WORKER_EXIT_ERROR)) return;
170
+ const binding = taskBindings.get(taskId) ?? await waitForTaskBinding(taskId);
171
+ if (!binding) return;
172
+ await waitForWorkerClose(binding.pid);
173
+ const stderr = formatCapturedStderr(getTaskStderr(taskId));
174
+ if (stderr.length > 0) err.message += `\n\nMaybe related stderr:\n${stderr}`;
175
+ };
176
+ const createTask = (taskId)=>{
177
+ if (!taskBindingWaiters.has(taskId)) taskBindingWaiters.set(taskId, createDeferred());
178
+ };
179
+ const clearTask = (taskId)=>{
180
+ taskBindings.delete(taskId);
181
+ taskBindingWaiters.delete(taskId);
182
+ };
183
+ const cleanup = ()=>{
184
+ for (const { onData, stream } of trackedWorkers.values())stream.off('data', onData);
185
+ trackedWorkers.clear();
186
+ workerStates.clear();
187
+ taskBindings.clear();
188
+ taskBindingWaiters.clear();
189
+ workerCloseWaiters.clear();
190
+ };
191
+ return {
192
+ createTask,
193
+ bindTaskToPid,
194
+ clearTask,
195
+ enhanceWorkerExitError,
196
+ cleanup
197
+ };
198
+ };
24
199
  const forks_filename = fileURLToPath(import.meta.url);
25
200
  const forks_dirname = dirname(forks_filename);
26
- function createChannel(rpcMethods) {
201
+ function createForksChannel(rpcMethods, onWorkerMeta, createBirpcImpl = createBirpc) {
27
202
  const emitter = new node_events();
28
- const cleanup = ()=>emitter.removeAllListeners();
29
203
  const events = {
30
204
  message: 'message',
31
205
  response: 'response'
@@ -35,12 +209,15 @@ function createChannel(rpcMethods) {
35
209
  emitter.on(events.message, callback);
36
210
  },
37
211
  postMessage: (message)=>{
212
+ const workerMeta = parseWorkerMetaMessage(message);
213
+ if (workerMeta) return void onWorkerMeta?.(workerMeta);
38
214
  emitter.emit(events.response, message);
39
215
  }
40
216
  };
41
- createBirpc(rpcMethods, {
217
+ const rpc = createBirpcImpl(rpcMethods, {
42
218
  serialize: node_v8.serialize,
43
219
  deserialize: (v)=>node_v8.deserialize(Buffer.from(v)),
220
+ timeout: -1,
44
221
  post (v) {
45
222
  emitter.emit(events.message, v);
46
223
  },
@@ -48,9 +225,13 @@ function createChannel(rpcMethods) {
48
225
  emitter.on(events.response, fn);
49
226
  }
50
227
  });
228
+ const cleanup = ()=>{
229
+ rpc.$close(new Error('[rstest-pool]: Pending methods while closing rpc'));
230
+ emitter.removeAllListeners();
231
+ };
51
232
  return {
52
- channel,
53
- cleanup
233
+ channel: channel,
234
+ cleanup: cleanup
54
235
  };
55
236
  }
56
237
  const createForksPool = (poolOptions)=>{
@@ -66,29 +247,50 @@ const createForksPool = (poolOptions)=>{
66
247
  isolateWorkers: isolate
67
248
  };
68
249
  const pool = new Tinypool(options);
250
+ const stderrCapture = createWorkerStderrCapture(pool);
251
+ let nextTaskId = 0;
69
252
  return {
70
253
  name: 'forks',
71
254
  runTest: async ({ options, rpcMethods })=>{
72
- const { channel, cleanup } = createChannel(rpcMethods);
255
+ const taskId = ++nextTaskId;
256
+ stderrCapture.createTask(taskId);
257
+ const { channel, cleanup } = createForksChannel(rpcMethods, ({ pid })=>{
258
+ stderrCapture.bindTaskToPid(taskId, pid);
259
+ });
73
260
  try {
74
261
  return await pool.run(options, {
75
262
  channel
76
263
  });
264
+ } catch (err) {
265
+ await stderrCapture.enhanceWorkerExitError(taskId, err);
266
+ throw err;
77
267
  } finally{
78
268
  cleanup();
269
+ stderrCapture.clearTask(taskId);
79
270
  }
80
271
  },
81
272
  collectTests: async ({ options, rpcMethods })=>{
82
- const { channel, cleanup } = createChannel(rpcMethods);
273
+ const taskId = ++nextTaskId;
274
+ stderrCapture.createTask(taskId);
275
+ const { channel, cleanup } = createForksChannel(rpcMethods, ({ pid })=>{
276
+ stderrCapture.bindTaskToPid(taskId, pid);
277
+ });
83
278
  try {
84
279
  return await pool.run(options, {
85
280
  channel
86
281
  });
282
+ } catch (err) {
283
+ await stderrCapture.enhanceWorkerExitError(taskId, err);
284
+ throw err;
87
285
  } finally{
88
286
  cleanup();
287
+ stderrCapture.clearTask(taskId);
89
288
  }
90
289
  },
91
- close: ()=>pool.destroy()
290
+ close: async ()=>{
291
+ stderrCapture.cleanup();
292
+ await pool.destroy();
293
+ }
92
294
  };
93
295
  };
94
296
  const external_node_os_ = __webpack_require__("node:os");
@@ -399,7 +601,7 @@ async function runGlobalTeardown() {
399
601
  process.exitCode = 1;
400
602
  }
401
603
  }
402
- const external_node_path_ = __webpack_require__("node:path");
604
+ const external_node_path_ = __webpack_require__("path");
403
605
  const RUNTIME_CHUNK_NAME = 'runtime';
404
606
  const requireShim = `// Rstest ESM shims
405
607
  import __rstest_shim_module__ from 'node:module';
package/dist/0~9634.js CHANGED
@@ -4,7 +4,7 @@ import { logger as logger_logger, getTaskNameWithPrefix, color, bgColor } from "
4
4
  import { prepareRsbuild, createPool, createRsbuildServer, runGlobalTeardown, runGlobalSetup } from "./0~89.js";
5
5
  import { getTestEntries, resolveShardedEntries, prettyTestPath, ROOT_SUITE_NAME } from "./1157.js";
6
6
  const external_node_fs_ = __webpack_require__("node:fs");
7
- const external_node_path_ = __webpack_require__("node:path");
7
+ const external_node_path_ = __webpack_require__("path");
8
8
  const collectNodeTests = async ({ context, nodeProjects, globTestSourceEntries })=>{
9
9
  const { getSetupFiles } = await import("./6973.js").then((mod)=>({
10
10
  getSetupFiles: mod.getSetupFiles
@@ -107,9 +107,10 @@ const collectBrowserTests = async ({ context, browserProjects, shardedEntries })
107
107
  loadBrowserModule: mod.loadBrowserModule
108
108
  }));
109
109
  const projectRoots = browserProjects.map((p)=>p.rootPath);
110
- const { listBrowserTests } = await loadBrowserModule({
110
+ const { validateBrowserConfig, listBrowserTests } = await loadBrowserModule({
111
111
  projectRoots
112
112
  });
113
+ validateBrowserConfig(context);
113
114
  return listBrowserTests(context, {
114
115
  shardedEntries
115
116
  });
package/dist/1157.js CHANGED
@@ -1441,7 +1441,7 @@ const globalApis = [
1441
1441
  'onTestFailed'
1442
1442
  ];
1443
1443
  const TS_CONFIG_FILE = 'tsconfig.json';
1444
- const external_node_path_ = __webpack_require__("node:path");
1444
+ const external_node_path_ = __webpack_require__("path");
1445
1445
  const external_node_fs_ = __webpack_require__("node:fs");
1446
1446
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
1447
1447
  function cleanPath(path) {
package/dist/3160.js CHANGED
@@ -79,7 +79,7 @@ __webpack_require__.add({
79
79
  "node:os" (module) {
80
80
  module.exports = __rspack_external_node_os_74b4b876;
81
81
  },
82
- "node:path" (module) {
82
+ path (module) {
83
83
  module.exports = __rspack_external_node_path_c5b9b54f;
84
84
  },
85
85
  "node:tty" (module) {
@@ -1011,19 +1011,12 @@ const isDebug = ()=>{
1011
1011
  ].some((key)=>values.includes(key));
1012
1012
  };
1013
1013
  const getForceColorEnv = ()=>{
1014
- if (void 0 !== process.env.FORCE_COLOR) return {};
1015
- const noColorEnabled = '1' === process.env.NO_COLOR;
1016
- const isAgent = determineAgent().isAgent;
1017
- if (noColorEnabled) return {
1018
- FORCE_COLOR: '0'
1019
- };
1020
- if (isAgent || !picocolors.isColorSupported) return {
1014
+ const userSetColorEnv = void 0 !== process.env.FORCE_COLOR || void 0 !== process.env.NO_COLOR;
1015
+ if (determineAgent().isAgent && !userSetColorEnv) return {
1021
1016
  NO_COLOR: '1',
1022
1017
  FORCE_COLOR: '0'
1023
1018
  };
1024
- return {
1025
- FORCE_COLOR: '1'
1026
- };
1019
+ return {};
1027
1020
  };
1028
1021
  const color = (0, picocolors.createColors)();
1029
1022
  if (isDebug()) src_logger.level = 'verbose';
package/dist/487.js CHANGED
@@ -35,7 +35,7 @@ __webpack_require__.add({
35
35
  "../../node_modules/.pnpm/source-map-support@0.5.21/node_modules/source-map-support/source-map-support.js" (module, exports, __webpack_require__) {
36
36
  module = __webpack_require__.nmd(module);
37
37
  var SourceMapConsumer = __webpack_require__("../../node_modules/.pnpm/source-map@0.6.1/node_modules/source-map/source-map.js").SourceMapConsumer;
38
- var path = __webpack_require__("node:path");
38
+ var path = __webpack_require__("path");
39
39
  var fs;
40
40
  try {
41
41
  fs = __webpack_require__("node:fs");
@@ -1,15 +1,50 @@
1
1
  import "node:module";
2
+ const WORKER_META_MESSAGE_TYPE = 'rstest:worker-meta';
3
+ const WORKER_META_MESSAGE_VERSION = 1;
4
+ const WORKER_META_MESSAGE_NAMESPACE = 'rstest';
5
+ const isRecord = (value)=>'object' == typeof value && null !== value;
6
+ const createWorkerMetaMessage = (pid)=>({
7
+ __rstest_internal__: WORKER_META_MESSAGE_NAMESPACE,
8
+ payload: {
9
+ pid
10
+ },
11
+ type: WORKER_META_MESSAGE_TYPE,
12
+ version: WORKER_META_MESSAGE_VERSION
13
+ });
14
+ const parseWorkerMetaMessage = (message)=>{
15
+ if (!isRecord(message)) return;
16
+ if (message.__rstest_internal__ === WORKER_META_MESSAGE_NAMESPACE && message.type === WORKER_META_MESSAGE_TYPE && message.version === WORKER_META_MESSAGE_VERSION && isRecord(message.payload) && 'number' == typeof message.payload.pid) return {
17
+ pid: message.payload.pid
18
+ };
19
+ };
2
20
  const TYPE_REQUEST = "q";
3
21
  const TYPE_RESPONSE = "s";
4
- const DEFAULT_TIMEOUT = 6e4;
5
- function defaultSerialize(i) {
6
- return i;
22
+ function createPromiseWithResolvers() {
23
+ let resolve;
24
+ let reject;
25
+ return {
26
+ promise: new Promise((res, rej)=>{
27
+ resolve = res;
28
+ reject = rej;
29
+ }),
30
+ resolve,
31
+ reject
32
+ };
33
+ }
34
+ const random = Math.random.bind(Math);
35
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
36
+ function nanoid(size = 21) {
37
+ let id = "";
38
+ let i = size;
39
+ while(i--)id += urlAlphabet[64 * random() | 0];
40
+ return id;
7
41
  }
42
+ const DEFAULT_TIMEOUT = 6e4;
43
+ const defaultSerialize = (i)=>i;
8
44
  const defaultDeserialize = defaultSerialize;
9
45
  const { clearTimeout: dist_clearTimeout, setTimeout: dist_setTimeout } = globalThis;
10
- const random = Math.random.bind(Math);
11
46
  function createBirpc($functions, options) {
12
- const { post, on, off = ()=>{}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT } = options;
47
+ const { post, on, off = ()=>{}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT, proxify = true } = options;
13
48
  let $closed = false;
14
49
  const _rpcPromiseMap = /* @__PURE__ */ new Map();
15
50
  let _promiseInit;
@@ -37,8 +72,7 @@ function createBirpc($functions, options) {
37
72
  if (timeout >= 0) {
38
73
  timeoutId = dist_setTimeout(()=>{
39
74
  try {
40
- const handleResult = options.onTimeoutError?.call(rpc, method, args);
41
- if (true !== handleResult) throw new Error(`[birpc] timeout on calling "${method}"`);
75
+ if (options.onTimeoutError?.call(rpc, method, args) !== true) throw new Error(`[birpc] timeout on calling "${method}"`);
42
76
  } catch (e) {
43
77
  reject(e);
44
78
  }
@@ -67,15 +101,11 @@ function createBirpc($functions, options) {
67
101
  }
68
102
  return promise;
69
103
  }
70
- const $call = (method, ...args)=>_call(method, args, false);
71
- const $callOptional = (method, ...args)=>_call(method, args, false, true);
72
- const $callEvent = (method, ...args)=>_call(method, args, true);
73
- const $callRaw = (options2)=>_call(options2.method, options2.args, options2.event, options2.optional);
74
104
  const builtinMethods = {
75
- $call,
76
- $callOptional,
77
- $callEvent,
78
- $callRaw,
105
+ $call: (method, ...args)=>_call(method, args, false),
106
+ $callOptional: (method, ...args)=>_call(method, args, false, true),
107
+ $callEvent: (method, ...args)=>_call(method, args, true),
108
+ $callRaw: (options$1)=>_call(options$1.method, options$1.args, options$1.event, options$1.optional),
79
109
  $rejectPendingCalls,
80
110
  get $closed () {
81
111
  return $closed;
@@ -86,7 +116,7 @@ function createBirpc($functions, options) {
86
116
  $close,
87
117
  $functions
88
118
  };
89
- rpc = new Proxy({}, {
119
+ rpc = proxify ? new Proxy({}, {
90
120
  get (_, method) {
91
121
  if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
92
122
  if ("then" === method && !eventNames.includes("then") && !("then" in $functions)) return;
@@ -99,11 +129,11 @@ function createBirpc($functions, options) {
99
129
  sendCall.asEvent = sendEvent;
100
130
  return sendCall;
101
131
  }
102
- });
132
+ }) : builtinMethods;
103
133
  function $close(customError) {
104
134
  $closed = true;
105
135
  _rpcPromiseMap.forEach(({ reject, method })=>{
106
- const error = new Error(`[birpc] rpc is closed, cannot call "${method}"`);
136
+ const error = /* @__PURE__ */ new Error(`[birpc] rpc is closed, cannot call "${method}"`);
107
137
  if (customError) {
108
138
  customError.cause ??= error;
109
139
  return reject(customError);
@@ -114,9 +144,8 @@ function createBirpc($functions, options) {
114
144
  off(onMessage);
115
145
  }
116
146
  function $rejectPendingCalls(handler) {
117
- const entries = Array.from(_rpcPromiseMap.values());
118
- const handlerResults = entries.map(({ method, reject })=>{
119
- if (!handler) return reject(new Error(`[birpc]: rejected pending call "${method}".`));
147
+ const handlerResults = Array.from(_rpcPromiseMap.values()).map(({ method, reject })=>{
148
+ if (!handler) return reject(/* @__PURE__ */ new Error(`[birpc]: rejected pending call "${method}".`));
120
149
  return handler({
121
150
  method,
122
151
  reject
@@ -143,9 +172,8 @@ function createBirpc($functions, options) {
143
172
  } catch (e) {
144
173
  error = e;
145
174
  }
146
- else error = new Error(`[birpc] function "${method}" not found`);
175
+ else error = /* @__PURE__ */ new Error(`[birpc] function "${method}" not found`);
147
176
  if (msg.i) {
148
- if (error && options.onError) options.onError.call(rpc, error, method, args);
149
177
  if (error && options.onFunctionError) {
150
178
  if (true === options.onFunctionError.call(rpc, error, method, args)) return;
151
179
  }
@@ -184,25 +212,5 @@ function createBirpc($functions, options) {
184
212
  _promiseInit = on(onMessage);
185
213
  return rpc;
186
214
  }
187
- function createPromiseWithResolvers() {
188
- let resolve;
189
- let reject;
190
- const promise = new Promise((res, rej)=>{
191
- resolve = res;
192
- reject = rej;
193
- });
194
- return {
195
- promise,
196
- resolve,
197
- reject
198
- };
199
- }
200
- const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
201
- function nanoid(size = 21) {
202
- let id = "";
203
- let i = size;
204
- while(i--)id += urlAlphabet[64 * random() | 0];
205
- return id;
206
- }
207
215
  export { default as node_v8 } from "node:v8";
208
- export { createBirpc };
216
+ export { createBirpc, createWorkerMetaMessage, parseWorkerMetaMessage };
package/dist/6151.js CHANGED
@@ -13473,6 +13473,14 @@ const initSpy = ()=>{
13473
13473
  }, defaultName, mockFn);
13474
13474
  };
13475
13475
  const spyOn = (obj, methodName, accessType)=>{
13476
+ if (accessType) {
13477
+ const descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
13478
+ const accessor = 'get' === accessType ? Reflect.get(descriptor ?? {}, 'get') : Reflect.get(descriptor ?? {}, 'set');
13479
+ if ('function' == typeof accessor && spy_isMockFunction(accessor)) return accessor;
13480
+ } else {
13481
+ const method = obj[methodName];
13482
+ if (spy_isMockFunction(method)) return method;
13483
+ }
13476
13484
  const accessTypeMap = {
13477
13485
  get: 'getter',
13478
13486
  set: 'setter'
package/dist/9131.js CHANGED
@@ -504,7 +504,7 @@ function prepareCli() {
504
504
  if (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) logger_logger.log();
505
505
  }
506
506
  function showRstest() {
507
- logger_logger.greet(" Rstest v0.8.3");
507
+ logger_logger.greet(" Rstest v0.8.5");
508
508
  logger_logger.log('');
509
509
  }
510
510
  const applyCommonOptions = (cli)=>{
@@ -562,7 +562,7 @@ const runRest = async ({ options, filters, command })=>{
562
562
  function setupCommands() {
563
563
  const cli = dist('rstest');
564
564
  cli.help();
565
- cli.version("0.8.3");
565
+ cli.version("0.8.5");
566
566
  applyCommonOptions(cli);
567
567
  cli.command('[...filters]', 'run tests').option('-w, --watch', 'Run tests in watch mode').action(async (filters, options)=>{
568
568
  if (!determineAgent().isAgent) showRstest();
@@ -1134,13 +1134,6 @@ const createDefaultConfig = ()=>({
1134
1134
  }
1135
1135
  });
1136
1136
  const withDefaultConfig = (config)=>{
1137
- if (config.browser?.enabled === true) {
1138
- if (!config.browser.provider) throw new Error('browser.provider is required when browser.enabled is true.');
1139
- const supportedProviders = [
1140
- 'playwright'
1141
- ];
1142
- if (!supportedProviders.includes(config.browser.provider)) throw new Error(`browser.provider must be one of: ${supportedProviders.join(', ')}.`);
1143
- }
1144
1137
  const merged = mergeRstestConfig(createDefaultConfig(), config);
1145
1138
  merged.setupFiles = castArray(merged.setupFiles);
1146
1139
  merged.globalSetup = castArray(merged.globalSetup);
@@ -1159,7 +1152,8 @@ const withDefaultConfig = (config)=>{
1159
1152
  browser: merged.browser?.browser ?? 'chromium',
1160
1153
  headless: merged.browser?.headless ?? T,
1161
1154
  port: merged.browser?.port,
1162
- strictPort: merged.browser?.strictPort ?? false
1155
+ strictPort: merged.browser?.strictPort ?? false,
1156
+ viewport: merged.browser?.viewport
1163
1157
  };
1164
1158
  return {
1165
1159
  ...merged,
@@ -1763,13 +1757,9 @@ class DefaultReporter {
1763
1757
  this.projectConfigs = projectConfigs ?? new Map();
1764
1758
  this.options = options;
1765
1759
  this.testState = testState;
1766
- }
1767
- ensureStatusRenderer() {
1768
- if (this.statusRenderer) return;
1769
- if (isTTY() || this.options.logger) this.statusRenderer = new StatusRenderer(this.rootPath, this.testState, this.options.logger);
1760
+ if (isTTY() || options.logger) this.statusRenderer = new StatusRenderer(rootPath, testState, options.logger);
1770
1761
  }
1771
1762
  onTestFileStart() {
1772
- this.ensureStatusRenderer();
1773
1763
  this.statusRenderer?.onTestFileStart();
1774
1764
  }
1775
1765
  onTestFileResult(test) {
@@ -2201,7 +2191,7 @@ function traceSegmentInternal(segments, memo, line, column, bias) {
2201
2191
  if (-1 === index || index === segments.length) return -1;
2202
2192
  return index;
2203
2193
  }
2204
- const external_node_path_ = __webpack_require__("node:path");
2194
+ const external_node_path_ = __webpack_require__("path");
2205
2195
  const isRelativePath = (p)=>/^\.\.?\//.test(p);
2206
2196
  const hintNotDefinedError = (message)=>{
2207
2197
  const [, varName] = message.match(/(\w+) is not defined/) || [];
@@ -3462,7 +3452,7 @@ class MdReporter {
3462
3452
  }
3463
3453
  renderFrontMatter(lines) {
3464
3454
  const frontMatter = {
3465
- tool: "@rstest/core@0.8.3",
3455
+ tool: "@rstest/core@0.8.5",
3466
3456
  timestamp: new Date().toISOString()
3467
3457
  };
3468
3458
  if (this.options.header.env) frontMatter.runtime = {
@@ -3832,7 +3822,7 @@ class Rstest {
3832
3822
  updateSnapshot: rstestConfig.update ? 'all' : T ? 'none' : 'new'
3833
3823
  });
3834
3824
  this.snapshotManager = snapshotManager;
3835
- this.version = "0.8.3";
3825
+ this.version = "0.8.5";
3836
3826
  this.rootPath = rootPath;
3837
3827
  this.originalConfig = userConfig;
3838
3828
  this.normalizedConfig = rstestConfig;
@@ -22392,6 +22392,14 @@ const initSpy = ()=>{
22392
22392
  }, defaultName, mockFn);
22393
22393
  };
22394
22394
  const spyOn = (obj, methodName, accessType)=>{
22395
+ if (accessType) {
22396
+ const descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
22397
+ const accessor = 'get' === accessType ? Reflect.get(descriptor ?? {}, 'get') : Reflect.get(descriptor ?? {}, 'set');
22398
+ if ('function' == typeof accessor && spy_isMockFunction(accessor)) return accessor;
22399
+ } else {
22400
+ const method = obj[methodName];
22401
+ if (spy_isMockFunction(method)) return method;
22402
+ }
22395
22403
  const accessTypeMap = {
22396
22404
  get: 'getter',
22397
22405
  set: 'setter'
@@ -319,6 +319,12 @@ declare type BrowserModeConfig = {
319
319
  * If not specified, a random available port will be used.
320
320
  */
321
321
  port?: number;
322
+ /**
323
+ * Default runner iframe viewport.
324
+ *
325
+ * When not specified, the browser UI fills the preview panel.
326
+ */
327
+ viewport?: BrowserViewport;
322
328
  /**
323
329
  * Whether to exit if the specified port is already in use.
324
330
  *
@@ -336,6 +342,11 @@ declare type BrowserModeConfig = {
336
342
  */
337
343
  declare type BrowserName = 'chromium' | 'firefox' | 'webkit';
338
344
 
345
+ declare type BrowserViewport = {
346
+ width: number;
347
+ height: number;
348
+ } | DevicePreset;
349
+
339
350
  declare type BuiltInReporterNames = 'default' | 'verbose' | 'md' | 'github-actions' | 'junit';
340
351
 
341
352
  declare type BuiltinReporterOptions = {
@@ -633,6 +644,20 @@ declare type DescribeFn = (description: string, fn?: () => void) => void;
633
644
 
634
645
  declare type DescribeForFn = <T>(cases: readonly T[]) => (description: string, fn?: (param: T) => MaybePromise<void>) => void;
635
646
 
647
+ /**
648
+ * Device presets aligned with Chrome DevTools device toolbar.
649
+ *
650
+ * These values are stable identifiers (not user-facing labels).
651
+ *
652
+ * IMPORTANT: Keep this union in sync with
653
+ * `@rstest/browser` preset runtime source:
654
+ * `packages/browser/src/viewportPresets.ts`.
655
+ *
656
+ * `@rstest/core` owns `defineConfig` typing, while `@rstest/browser` owns
657
+ * runtime validation and resolution for preset ids.
658
+ */
659
+ declare type DevicePreset = 'iPhoneSE' | 'iPhoneXR' | 'iPhone12Pro' | 'iPhone14ProMax' | 'Pixel7' | 'SamsungGalaxyS8Plus' | 'SamsungGalaxyS20Ultra' | 'iPadMini' | 'iPadAir' | 'iPadPro' | 'SurfacePro7' | 'SurfaceDuo' | 'GalaxyZFold5' | 'AsusZenbookFold' | 'SamsungGalaxyA51A71' | 'NestHub' | 'NestHubMax';
660
+
636
661
  /** The test file output path */
637
662
  declare type DistPath = string;
638
663
 
@@ -1680,6 +1705,7 @@ declare type NormalizedBrowserModeConfig = {
1680
1705
  headless: boolean;
1681
1706
  port?: number;
1682
1707
  strictPort: boolean;
1708
+ viewport?: BrowserViewport;
1683
1709
  };
1684
1710
 
1685
1711
  declare type NormalizedConfig = Required<Omit<RstestConfig, OptionalKeys | 'pool' | 'projects' | 'coverage' | 'setupFiles' | 'globalSetup' | 'exclude' | 'testEnvironment' | 'browser'>> & Partial<Pick<RstestConfig, OptionalKeys>> & {
package/dist/browser.d.ts CHANGED
@@ -315,6 +315,12 @@ declare type BrowserModeConfig = {
315
315
  * If not specified, a random available port will be used.
316
316
  */
317
317
  port?: number;
318
+ /**
319
+ * Default runner iframe viewport.
320
+ *
321
+ * When not specified, the browser UI fills the preview panel.
322
+ */
323
+ viewport?: BrowserViewport;
318
324
  /**
319
325
  * Whether to exit if the specified port is already in use.
320
326
  *
@@ -367,8 +373,15 @@ export declare interface BrowserTestRunResult {
367
373
  };
368
374
  /** Whether the test run had failures */
369
375
  hasFailure: boolean;
376
+ /** Errors that occurred before/outside test execution (e.g., browser launch failure) */
377
+ unhandledErrors?: Error[];
370
378
  }
371
379
 
380
+ declare type BrowserViewport = {
381
+ width: number;
382
+ height: number;
383
+ } | DevicePreset;
384
+
372
385
  declare type BuiltInReporterNames = 'default' | 'verbose' | 'md' | 'github-actions' | 'junit';
373
386
 
374
387
  declare type BuiltinReporterOptions = {
@@ -766,6 +779,20 @@ declare type DescribeFn = (description: string, fn?: () => void) => void;
766
779
 
767
780
  declare type DescribeForFn = <T>(cases: readonly T[]) => (description: string, fn?: (param: T) => MaybePromise<void>) => void;
768
781
 
782
+ /**
783
+ * Device presets aligned with Chrome DevTools device toolbar.
784
+ *
785
+ * These values are stable identifiers (not user-facing labels).
786
+ *
787
+ * IMPORTANT: Keep this union in sync with
788
+ * `@rstest/browser` preset runtime source:
789
+ * `packages/browser/src/viewportPresets.ts`.
790
+ *
791
+ * `@rstest/core` owns `defineConfig` typing, while `@rstest/browser` owns
792
+ * runtime validation and resolution for preset ids.
793
+ */
794
+ export declare type DevicePreset = 'iPhoneSE' | 'iPhoneXR' | 'iPhone12Pro' | 'iPhone14ProMax' | 'Pixel7' | 'SamsungGalaxyS8Plus' | 'SamsungGalaxyS20Ultra' | 'iPadMini' | 'iPadAir' | 'iPadPro' | 'SurfacePro7' | 'SurfaceDuo' | 'GalaxyZFold5' | 'AsusZenbookFold' | 'SamsungGalaxyA51A71' | 'NestHub' | 'NestHubMax';
795
+
769
796
  /**
770
797
  * @param a Expected value
771
798
  * @param b Received value
@@ -2195,6 +2222,7 @@ declare type NormalizedBrowserModeConfig = {
2195
2222
  headless: boolean;
2196
2223
  port?: number;
2197
2224
  strictPort: boolean;
2225
+ viewport?: BrowserViewport;
2198
2226
  };
2199
2227
 
2200
2228
  declare type NormalizedConfig = Required<Omit<RstestConfig, OptionalKeys | 'pool' | 'projects' | 'coverage' | 'setupFiles' | 'globalSetup' | 'exclude' | 'testEnvironment' | 'browser'>> & Partial<Pick<RstestConfig, OptionalKeys>> & {
@@ -42,12 +42,12 @@ const runGlobalSetup = async (data)=>{
42
42
  const { loadModule } = data.outputModule ? await import("./0~6923.js").then((mod)=>({
43
43
  EsmMode: mod.loadEsModule_EsmMode,
44
44
  asModule: mod.asModule,
45
- loadModule: mod.loadModule,
46
- updateLatestAssetFiles: mod.updateLatestAssetFiles
45
+ clearModuleCache: mod.clearModuleCache,
46
+ loadModule: mod.loadModule
47
47
  })) : await import("./0~5835.js").then((mod)=>({
48
48
  cacheableLoadModule: mod.cacheableLoadModule,
49
- loadModule: mod.loadModule,
50
- updateLatestAssetFiles: mod.updateLatestAssetFiles
49
+ clearModuleCache: mod.clearModuleCache,
50
+ loadModule: mod.loadModule
51
51
  }));
52
52
  const module = await loadModule({
53
53
  codeContent: setupCodeContent,
package/dist/index.d.ts CHANGED
@@ -295,6 +295,12 @@ declare type BrowserModeConfig = {
295
295
  * If not specified, a random available port will be used.
296
296
  */
297
297
  port?: number;
298
+ /**
299
+ * Default runner iframe viewport.
300
+ *
301
+ * When not specified, the browser UI fills the preview panel.
302
+ */
303
+ viewport?: BrowserViewport;
298
304
  /**
299
305
  * Whether to exit if the specified port is already in use.
300
306
  *
@@ -312,6 +318,11 @@ declare type BrowserModeConfig = {
312
318
  */
313
319
  declare type BrowserName = 'chromium' | 'firefox' | 'webkit';
314
320
 
321
+ declare type BrowserViewport = {
322
+ width: number;
323
+ height: number;
324
+ } | DevicePreset;
325
+
315
326
  declare type BuiltInReporterNames = 'default' | 'verbose' | 'md' | 'github-actions' | 'junit';
316
327
 
317
328
  declare type BuiltinReporterOptions = {
@@ -780,6 +791,20 @@ declare type DescribeFn = (description: string, fn?: () => void) => void;
780
791
 
781
792
  declare type DescribeForFn = <T>(cases: readonly T[]) => (description: string, fn?: (param: T) => MaybePromise<void>) => void;
782
793
 
794
+ /**
795
+ * Device presets aligned with Chrome DevTools device toolbar.
796
+ *
797
+ * These values are stable identifiers (not user-facing labels).
798
+ *
799
+ * IMPORTANT: Keep this union in sync with
800
+ * `@rstest/browser` preset runtime source:
801
+ * `packages/browser/src/viewportPresets.ts`.
802
+ *
803
+ * `@rstest/core` owns `defineConfig` typing, while `@rstest/browser` owns
804
+ * runtime validation and resolution for preset ids.
805
+ */
806
+ declare type DevicePreset = 'iPhoneSE' | 'iPhoneXR' | 'iPhone12Pro' | 'iPhone14ProMax' | 'Pixel7' | 'SamsungGalaxyS8Plus' | 'SamsungGalaxyS20Ultra' | 'iPadMini' | 'iPadAir' | 'iPadPro' | 'SurfacePro7' | 'SurfaceDuo' | 'GalaxyZFold5' | 'AsusZenbookFold' | 'SamsungGalaxyA51A71' | 'NestHub' | 'NestHubMax';
807
+
783
808
  /**
784
809
  * @param a Expected value
785
810
  * @param b Received value
@@ -2215,6 +2240,7 @@ declare type NormalizedBrowserModeConfig = {
2215
2240
  headless: boolean;
2216
2241
  port?: number;
2217
2242
  strictPort: boolean;
2243
+ viewport?: BrowserViewport;
2218
2244
  };
2219
2245
 
2220
2246
  declare type NormalizedConfig = Required<Omit<RstestConfig, OptionalKeys | 'pool' | 'projects' | 'coverage' | 'setupFiles' | 'globalSetup' | 'exclude' | 'testEnvironment' | 'browser'>> & Partial<Pick<RstestConfig, OptionalKeys>> & {
@@ -40,7 +40,9 @@ __webpack_require__.rstest_require_actual = __webpack_require__.rstest_import_ac
40
40
  }
41
41
  return __webpack_require__(id);
42
42
  };
43
- const getMockImplementation = (mockType = 'mock')=>(id, modFactory)=>{
43
+ const getMockImplementation = (mockType = 'mock')=>{
44
+ const isMockRequire = 'mockRequire' === mockType || 'doMockRequire' === mockType;
45
+ return (id, modFactory)=>{
44
46
  let requiredModule = __webpack_module_cache__[id]?.exports;
45
47
  const wasAlreadyLoaded = !!requiredModule;
46
48
  if (requiredModule) {
@@ -66,7 +68,11 @@ const getMockImplementation = (mockType = 'mock')=>(id, modFactory)=>{
66
68
  const mockedModule = globalThis.RSTEST_API?.rstest?.mockObject(originalModule, {
67
69
  spy: isSpy
68
70
  }) || originalModule;
69
- const finalModFactory = function(__unused_webpack_module, __webpack_exports__, __webpack_require__1) {
71
+ const finalModFactory = function(__webpack_module__, __webpack_exports__, __webpack_require__1) {
72
+ if (isMockRequire) {
73
+ __webpack_module__.exports = mockedModule;
74
+ return;
75
+ }
70
76
  __webpack_require__1.r(__webpack_exports__);
71
77
  for(const key in mockedModule)__webpack_require__1.d(__webpack_exports__, {
72
78
  [key]: ()=>mockedModule[key]
@@ -83,9 +89,13 @@ const getMockImplementation = (mockType = 'mock')=>(id, modFactory)=>{
83
89
  exports: __webpack_require__(modFactory)
84
90
  };
85
91
  else if ('function' == typeof modFactory) {
86
- const finalModFactory = function(__unused_webpack_module, __webpack_exports__, __webpack_require__1) {
87
- __webpack_require__1.r(__webpack_exports__);
92
+ const finalModFactory = function(__webpack_module__, __webpack_exports__, __webpack_require__1) {
88
93
  const res = modFactory();
94
+ if (isMockRequire) {
95
+ __webpack_module__.exports = res;
96
+ return;
97
+ }
98
+ __webpack_require__1.r(__webpack_exports__);
89
99
  for(const key in res)__webpack_require__1.d(__webpack_exports__, {
90
100
  [key]: ()=>res[key]
91
101
  });
@@ -94,6 +104,7 @@ const getMockImplementation = (mockType = 'mock')=>(id, modFactory)=>{
94
104
  delete __webpack_module_cache__[id];
95
105
  }
96
106
  };
107
+ };
97
108
  __webpack_require__.rstest_mock = getMockImplementation('mock');
98
109
  __webpack_require__.rstest_mock_require = getMockImplementation('mockRequire');
99
110
  __webpack_require__.rstest_do_mock = getMockImplementation('doMock');
package/dist/worker.d.ts CHANGED
@@ -282,6 +282,12 @@ declare type BrowserModeConfig = {
282
282
  * If not specified, a random available port will be used.
283
283
  */
284
284
  port?: number;
285
+ /**
286
+ * Default runner iframe viewport.
287
+ *
288
+ * When not specified, the browser UI fills the preview panel.
289
+ */
290
+ viewport?: BrowserViewport;
285
291
  /**
286
292
  * Whether to exit if the specified port is already in use.
287
293
  *
@@ -299,6 +305,11 @@ declare type BrowserModeConfig = {
299
305
  */
300
306
  declare type BrowserName = 'chromium' | 'firefox' | 'webkit';
301
307
 
308
+ declare type BrowserViewport = {
309
+ width: number;
310
+ height: number;
311
+ } | DevicePreset;
312
+
302
313
  declare type BuiltInReporterNames = 'default' | 'verbose' | 'md' | 'github-actions' | 'junit';
303
314
 
304
315
  declare type BuiltinReporterOptions = {
@@ -635,6 +646,20 @@ declare type DescribeFn = (description: string, fn?: () => void) => void;
635
646
 
636
647
  declare type DescribeForFn = <T>(cases: readonly T[]) => (description: string, fn?: (param: T) => MaybePromise<void>) => void;
637
648
 
649
+ /**
650
+ * Device presets aligned with Chrome DevTools device toolbar.
651
+ *
652
+ * These values are stable identifiers (not user-facing labels).
653
+ *
654
+ * IMPORTANT: Keep this union in sync with
655
+ * `@rstest/browser` preset runtime source:
656
+ * `packages/browser/src/viewportPresets.ts`.
657
+ *
658
+ * `@rstest/core` owns `defineConfig` typing, while `@rstest/browser` owns
659
+ * runtime validation and resolution for preset ids.
660
+ */
661
+ declare type DevicePreset = 'iPhoneSE' | 'iPhoneXR' | 'iPhone12Pro' | 'iPhone14ProMax' | 'Pixel7' | 'SamsungGalaxyS8Plus' | 'SamsungGalaxyS20Ultra' | 'iPadMini' | 'iPadAir' | 'iPadPro' | 'SurfacePro7' | 'SurfaceDuo' | 'GalaxyZFold5' | 'AsusZenbookFold' | 'SamsungGalaxyA51A71' | 'NestHub' | 'NestHubMax';
662
+
638
663
  /**
639
664
  * @param a Expected value
640
665
  * @param b Received value
@@ -1765,6 +1790,7 @@ declare type NormalizedBrowserModeConfig = {
1765
1790
  headless: boolean;
1766
1791
  port?: number;
1767
1792
  strictPort: boolean;
1793
+ viewport?: BrowserViewport;
1768
1794
  };
1769
1795
 
1770
1796
  declare type NormalizedConfig = Required<Omit<RstestConfig, OptionalKeys | 'pool' | 'projects' | 'coverage' | 'setupFiles' | 'globalSetup' | 'exclude' | 'testEnvironment' | 'browser'>> & Partial<Pick<RstestConfig, OptionalKeys>> & {
package/dist/worker.js CHANGED
@@ -2,7 +2,7 @@ import "node:module";
2
2
  import { __webpack_require__ } from "./rslib-runtime.js";
3
3
  import { basename, isAbsolute, undoSerializableConfig, dirname, color, resolve as pathe_M_eThtNZ_resolve, join } from "./3160.js";
4
4
  import "./487.js";
5
- import { node_v8, createBirpc } from "./3216.js";
5
+ import { node_v8, createWorkerMetaMessage, createBirpc } from "./5960.js";
6
6
  import { createCoverageProvider } from "./5734.js";
7
7
  import { formatTestError, setRealTimers, getRealTimers } from "./1294.js";
8
8
  import { globalApis } from "./1157.js";
@@ -26,30 +26,10 @@ function createForksRpcOptions({ nodeV8 = node_v8, dispose = [] }) {
26
26
  }
27
27
  };
28
28
  }
29
- function createRuntimeRpc(options, { originalConsole }) {
30
- const rpc = createBirpc({}, {
29
+ function createRuntimeRpc(options, createBirpcImpl = createBirpc) {
30
+ const rpc = createBirpcImpl({}, {
31
31
  ...options,
32
- onTimeoutError: (functionName, error)=>{
33
- switch(functionName){
34
- case 'onTestCaseStart':
35
- {
36
- const caseTest = error[0];
37
- console.error(`[Rstest] timeout on calling "onTestCaseStart" rpc method (Case: "${caseTest.name}")`);
38
- return true;
39
- }
40
- case 'onTestCaseResult':
41
- {
42
- const caseResult = error[0];
43
- console.error(`[Rstest] timeout on calling "onTestCaseResult" rpc method (Case: "${caseResult.name}", Result: "${caseResult.status}")`);
44
- return true;
45
- }
46
- case 'onConsoleLog':
47
- originalConsole.error(`[Rstest] timeout on calling "onConsoleLog" rpc method (Original log: ${error[0].content})`);
48
- return true;
49
- default:
50
- return false;
51
- }
52
- }
32
+ timeout: -1
53
33
  });
54
34
  return {
55
35
  rpc
@@ -135,13 +115,10 @@ const preparePool = async ({ entryInfo: { distPath, testPath }, updateSnapshot,
135
115
  context.runtimeConfig = undoSerializableConfig(context.runtimeConfig);
136
116
  process.env.RSTEST_WORKER_ID = String(process.__tinypool_state__.workerId || context.taskId);
137
117
  const cleanupFns = [];
138
- const originalConsole = global.console;
139
118
  const disposeFns = [];
140
119
  const { rpc } = createRuntimeRpc(createForksRpcOptions({
141
120
  dispose: disposeFns
142
- }), {
143
- originalConsole
144
- });
121
+ }));
145
122
  globalCleanups.push(()=>{
146
123
  disposeFns.forEach((fn)=>{
147
124
  fn();
@@ -242,29 +219,26 @@ const preparePool = async ({ entryInfo: { distPath, testPath }, updateSnapshot,
242
219
  };
243
220
  };
244
221
  const loadFiles = async ({ setupEntries, assetFiles, rstestContext, distPath, testPath, interopDefault, isolate, outputModule })=>{
245
- const { loadModule, updateLatestAssetFiles } = outputModule ? await import("./0~6923.js").then((mod)=>({
222
+ const { loadModule } = outputModule ? await import("./0~6923.js").then((mod)=>({
246
223
  EsmMode: mod.loadEsModule_EsmMode,
247
224
  asModule: mod.asModule,
248
- loadModule: mod.loadModule,
249
- updateLatestAssetFiles: mod.updateLatestAssetFiles
225
+ clearModuleCache: mod.clearModuleCache,
226
+ loadModule: mod.loadModule
250
227
  })) : await import("./0~5835.js").then((mod)=>({
251
228
  cacheableLoadModule: mod.cacheableLoadModule,
252
- loadModule: mod.loadModule,
253
- updateLatestAssetFiles: mod.updateLatestAssetFiles
229
+ clearModuleCache: mod.clearModuleCache,
230
+ loadModule: mod.loadModule
254
231
  }));
255
- if (!isolate) {
256
- updateLatestAssetFiles(assetFiles);
257
- await loadModule({
258
- codeContent: `if (global && typeof global.__rstest_clean_core_cache__ === 'function') {
232
+ if (!isolate) await loadModule({
233
+ codeContent: `if (global && typeof global.__rstest_clean_core_cache__ === 'function') {
259
234
  global.__rstest_clean_core_cache__();
260
235
  }`,
261
- distPath: '',
262
- testPath,
263
- rstestContext,
264
- assetFiles,
265
- interopDefault
266
- });
267
- }
236
+ distPath: '',
237
+ testPath,
238
+ rstestContext,
239
+ assetFiles,
240
+ interopDefault
241
+ });
268
242
  for (const { distPath, testPath } of setupEntries){
269
243
  const setupCodeContent = assetFiles[distPath];
270
244
  await loadModule({
@@ -286,6 +260,7 @@ const loadFiles = async ({ setupEntries, assetFiles, rstestContext, distPath, te
286
260
  });
287
261
  };
288
262
  const runInPool = async (options)=>{
263
+ if ('function' == typeof process.send) process.send(createWorkerMetaMessage(process.pid));
289
264
  isTeardown = false;
290
265
  const { entryInfo: { distPath, testPath }, setupEntries, assets, type, context: { project, runtimeConfig: { isolate, bail } } } = options;
291
266
  const cleanups = [];
@@ -305,6 +280,19 @@ const runInPool = async (options)=>{
305
280
  const teardown = async ()=>{
306
281
  await new Promise((resolve)=>getRealTimers().setTimeout(resolve));
307
282
  await Promise.all(cleanups.map((fn)=>fn()));
283
+ if (!isolate) {
284
+ const { clearModuleCache } = options.context.outputModule ? await import("./0~6923.js").then((mod)=>({
285
+ EsmMode: mod.loadEsModule_EsmMode,
286
+ asModule: mod.asModule,
287
+ clearModuleCache: mod.clearModuleCache,
288
+ loadModule: mod.loadModule
289
+ })) : await import("./0~5835.js").then((mod)=>({
290
+ cacheableLoadModule: mod.cacheableLoadModule,
291
+ clearModuleCache: mod.clearModuleCache,
292
+ loadModule: mod.loadModule
293
+ }));
294
+ clearModuleCache();
295
+ }
308
296
  isTeardown = true;
309
297
  };
310
298
  if ('collect' === type) try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rstest/core",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "The Rsbuild-based test tool.",
5
5
  "bugs": {
6
6
  "url": "https://github.com/web-infra-dev/rstest/issues"
@@ -76,7 +76,7 @@
76
76
  "@types/source-map-support": "^0.5.10",
77
77
  "@vitest/expect": "^3.2.4",
78
78
  "@vitest/snapshot": "^3.2.4",
79
- "birpc": "2.9.0",
79
+ "birpc": "^4.0.0",
80
80
  "cac": "^6.7.14",
81
81
  "chai": "^5.3.3",
82
82
  "chokidar": "^4.0.3",
@@ -98,8 +98,8 @@
98
98
  "tinyspy": "^4.0.4",
99
99
  "url-extras": "^0.1.0",
100
100
  "webpack-license-plugin": "^4.5.1",
101
- "@rstest/tsconfig": "0.0.1",
102
- "@rstest/browser-ui": "0.0.0"
101
+ "@rstest/browser-ui": "0.0.0",
102
+ "@rstest/tsconfig": "0.0.1"
103
103
  },
104
104
  "peerDependencies": {
105
105
  "happy-dom": "*",