vitest 2.1.4 → 2.1.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.
Files changed (44) hide show
  1. package/dist/browser.d.ts +9 -9
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.C3xNdjV6.js → base.BZZh4cSm.js} +1 -1
  4. package/dist/chunks/{cac.DrfPaMvZ.js → cac.DWAW3Uh5.js} +5 -5
  5. package/dist/chunks/{cli-api.CKrRYkw8.js → cli-api.BtqJwSCh.js} +106 -77
  6. package/dist/chunks/{config.Crbj2GAb.d.ts → config.Cy0C388Z.d.ts} +0 -1
  7. package/dist/chunks/{globals.Bp645TTJ.js → globals.D8ZVAdXd.js} +2 -2
  8. package/dist/chunks/{index.Bn81VaWg.js → index.DsZFoqi9.js} +205 -303
  9. package/dist/chunks/{index.BMoXz_-n.js → index.K90BXFOx.js} +1 -1
  10. package/dist/chunks/{index.Dqe5k2Rk.js → index.ckWaX2gY.js} +1 -1
  11. package/dist/chunks/{index.D3d79vc8.js → index.nEwtF0bu.js} +1 -1
  12. package/dist/chunks/{reporters.anwo7Y6a.d.ts → reporters.D7Jzd9GS.d.ts} +26 -16
  13. package/dist/chunks/{resolveConfig.DPmbhVlP.js → resolveConfig.RxKrDli4.js} +4 -2
  14. package/dist/chunks/{runBaseTests.Dm-659zB.js → runBaseTests.3qpJUEJM.js} +4 -4
  15. package/dist/chunks/{setup-common.DDmVKp6O.js → setup-common.Dj6BZI3u.js} +1 -1
  16. package/dist/chunks/{utils.BB4zjzR8.js → utils.DNoFbBUZ.js} +6 -2
  17. package/dist/chunks/{vi.JMQoNY_Z.js → vi.DgezovHB.js} +85 -3
  18. package/dist/chunks/{vite.BdBj-UWY.d.ts → vite.C-N5BBZe.d.ts} +1 -1
  19. package/dist/chunks/{vm.jpyrB0xy.js → vm.Zr4qWzDJ.js} +3 -0
  20. package/dist/chunks/{worker.DHnGaO2M.d.ts → worker.B9FxPCaC.d.ts} +1 -1
  21. package/dist/chunks/{worker.BAlI9hII.d.ts → worker.tN5KGIih.d.ts} +2 -1
  22. package/dist/cli.js +1 -1
  23. package/dist/config.d.ts +9 -9
  24. package/dist/coverage.d.ts +7 -7
  25. package/dist/coverage.js +1 -1
  26. package/dist/environments.js +1 -1
  27. package/dist/execute.d.ts +2 -2
  28. package/dist/index.d.ts +37 -8
  29. package/dist/index.js +2 -2
  30. package/dist/node.d.ts +8 -8
  31. package/dist/node.js +7 -7
  32. package/dist/reporters.d.ts +9 -9
  33. package/dist/reporters.js +2 -2
  34. package/dist/runners.d.ts +1 -1
  35. package/dist/runners.js +1 -1
  36. package/dist/worker.js +6 -2
  37. package/dist/workers/forks.js +1 -1
  38. package/dist/workers/runVmTests.js +4 -4
  39. package/dist/workers/threads.js +1 -1
  40. package/dist/workers/vmForks.js +1 -1
  41. package/dist/workers/vmThreads.js +1 -1
  42. package/dist/workers.d.ts +3 -3
  43. package/dist/workers.js +4 -3
  44. package/package.json +15 -15
@@ -3,7 +3,7 @@ import { getTests, getTestName, hasFailed, getFullName, getSuites, getTasks } fr
3
3
  import * as pathe from 'pathe';
4
4
  import { extname, relative, normalize, resolve, dirname } from 'pathe';
5
5
  import c from 'tinyrainbow';
6
- import { d as divider, F as F_POINTER, g as getStateSymbol, f as formatProjectName, t as taskFail, a as F_RIGHT, b as F_CHECK, r as renderSnapshotSummary, c as getStateString, e as formatTimeString, h as countTestErrors, i as getCols, j as getHookStateSymbol } from './utils.BB4zjzR8.js';
6
+ import { d as divider, F as F_POINTER, w as withLabel, f as formatProjectName, a as formatTimeString, g as getStateSymbol, t as taskFail, b as F_RIGHT, c as F_CHECK, r as renderSnapshotSummary, e as getStateString, h as countTestErrors, i as getCols, j as getHookStateSymbol } from './utils.DNoFbBUZ.js';
7
7
  import { stripVTControlCharacters } from 'node:util';
8
8
  import { highlight, isPrimitive, inspect, positionToOffset, lineSplitRE, toArray, notNullish } from '@vitest/utils';
9
9
  import { performance } from 'node:perf_hooks';
@@ -31,6 +31,10 @@ class TestProject {
31
31
  * @experimental The public Vitest API is experimental and does not follow semver.
32
32
  */
33
33
  workspaceProject;
34
+ /**
35
+ * Vite's dev server instance. Every workspace project has its own server.
36
+ */
37
+ vite;
34
38
  /**
35
39
  * Resolved project configuration.
36
40
  */
@@ -46,6 +50,7 @@ class TestProject {
46
50
  constructor(workspaceProject) {
47
51
  this.workspaceProject = workspaceProject;
48
52
  this.vitest = workspaceProject.ctx;
53
+ this.vite = workspaceProject.server;
49
54
  this.globalConfig = workspaceProject.ctx.config;
50
55
  this.config = workspaceProject.config;
51
56
  this.name = workspaceProject.getName();
@@ -3005,6 +3010,7 @@ function lineNo(no = "") {
3005
3010
  return c.gray(`${String(no).padStart(3, " ")}| `);
3006
3011
  }
3007
3012
 
3013
+ const PAD = " ";
3008
3014
  const ESC = "\x1B[";
3009
3015
  const ERASE_DOWN = `${ESC}J`;
3010
3016
  const ERASE_SCROLLBACK = `${ESC}3J`;
@@ -3018,6 +3024,7 @@ class Logger {
3018
3024
  this.console = new Console({ stdout: outputStream, stderr: errorStream });
3019
3025
  this.logUpdate = createLogUpdate(this.outputStream);
3020
3026
  this._highlights.clear();
3027
+ this.registerUnhandledRejection();
3021
3028
  }
3022
3029
  logUpdate;
3023
3030
  _clearScreenPending;
@@ -3035,12 +3042,16 @@ class Logger {
3035
3042
  this._clearScreen();
3036
3043
  this.console.warn(...args);
3037
3044
  }
3038
- clearFullScreen(message) {
3045
+ clearFullScreen(message = "") {
3039
3046
  if (!this.ctx.config.clearScreen) {
3040
3047
  this.console.log(message);
3041
3048
  return;
3042
3049
  }
3043
- this.console.log(`${CLEAR_SCREEN}${ERASE_SCROLLBACK}${message}`);
3050
+ if (message) {
3051
+ this.console.log(`${CLEAR_SCREEN}${ERASE_SCROLLBACK}${message}`);
3052
+ } else {
3053
+ this.outputStream.write(`${CLEAR_SCREEN}${ERASE_SCROLLBACK}`);
3054
+ }
3044
3055
  }
3045
3056
  clearScreen(message, force = false) {
3046
3057
  if (!this.ctx.config.clearScreen) {
@@ -3152,68 +3163,53 @@ No ${config.mode} files found, exiting with code 1`)
3152
3163
  }
3153
3164
  printBanner() {
3154
3165
  this.log();
3155
- const versionTest = this.ctx.config.watch ? c.blue(`v${this.ctx.version}`) : c.cyan(`v${this.ctx.version}`);
3156
- const mode = this.ctx.config.watch ? c.blue(" DEV ") : c.cyan(" RUN ");
3157
- this.log(
3158
- `${c.inverse(c.bold(mode))} ${versionTest} ${c.gray(
3159
- this.ctx.config.root
3160
- )}`
3161
- );
3166
+ const color = this.ctx.config.watch ? "blue" : "cyan";
3167
+ const mode = this.ctx.config.watch ? "DEV" : "RUN";
3168
+ this.log(withLabel(color, mode, `v${this.ctx.version} `) + c.gray(this.ctx.config.root));
3162
3169
  if (this.ctx.config.sequence.sequencer === RandomSequencer) {
3163
- this.log(
3164
- c.gray(
3165
- ` Running tests with seed "${this.ctx.config.sequence.seed}"`
3166
- )
3167
- );
3170
+ this.log(PAD + c.gray(`Running tests with seed "${this.ctx.config.sequence.seed}"`));
3168
3171
  }
3169
- this.ctx.projects.forEach((project) => {
3170
- if (!project.browser) {
3171
- return;
3172
- }
3173
- const name = project.getName();
3174
- const output = project.isCore() ? "" : ` [${name}]`;
3175
- const resolvedUrls = project.browser.vite.resolvedUrls;
3176
- const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
3177
- const provider = project.browser.provider.name;
3178
- const providerString = provider === "preview" ? "" : ` by ${provider}`;
3179
- this.log(
3180
- c.dim(
3181
- c.green(
3182
- ` ${output} Browser runner started${providerString} at ${new URL("/", origin)}`
3183
- )
3184
- )
3185
- );
3186
- });
3187
3172
  if (this.ctx.config.ui) {
3188
- this.log(
3189
- c.dim(
3190
- c.green(
3191
- ` UI started at http://${this.ctx.config.api?.host || "localhost"}:${c.bold(`${this.ctx.server.config.server.port}`)}${this.ctx.config.uiBase}`
3192
- )
3193
- )
3194
- );
3173
+ const host = this.ctx.config.api?.host || "localhost";
3174
+ const port = this.ctx.server.config.server.port;
3175
+ const base = this.ctx.config.uiBase;
3176
+ this.log(PAD + c.dim(c.green(`UI started at http://${host}:${c.bold(port)}${base}`)));
3195
3177
  } else if (this.ctx.config.api?.port) {
3196
3178
  const resolvedUrls = this.ctx.server.resolvedUrls;
3197
3179
  const fallbackUrl = `http://${this.ctx.config.api.host || "localhost"}:${this.ctx.config.api.port}`;
3198
3180
  const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0] ?? fallbackUrl;
3199
- this.log(c.dim(c.green(` API started at ${new URL("/", origin)}`)));
3181
+ this.log(PAD + c.dim(c.green(`API started at ${new URL("/", origin)}`)));
3200
3182
  }
3201
3183
  if (this.ctx.coverageProvider) {
3202
- this.log(
3203
- c.dim(" Coverage enabled with ") + c.yellow(this.ctx.coverageProvider.name)
3204
- );
3184
+ this.log(PAD + c.dim("Coverage enabled with ") + c.yellow(this.ctx.coverageProvider.name));
3205
3185
  }
3206
3186
  if (this.ctx.config.standalone) {
3207
- this.log(
3208
- c.yellow(
3209
- `
3210
- Vitest is running in standalone mode. Edit a test file to rerun tests.`
3211
- )
3212
- );
3187
+ this.log(c.yellow(`
3188
+ Vitest is running in standalone mode. Edit a test file to rerun tests.`));
3213
3189
  } else {
3214
3190
  this.log();
3215
3191
  }
3216
3192
  }
3193
+ printBrowserBanner(project) {
3194
+ if (!project.browser) {
3195
+ return;
3196
+ }
3197
+ const resolvedUrls = project.browser.vite.resolvedUrls;
3198
+ const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
3199
+ if (!origin) {
3200
+ return;
3201
+ }
3202
+ const name = project.getName();
3203
+ const output = project.isCore() ? "" : formatProjectName(name);
3204
+ const provider = project.browser.provider.name;
3205
+ const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
3206
+ this.log(
3207
+ c.dim(
3208
+ `${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/", origin))}
3209
+ `
3210
+ )
3211
+ );
3212
+ }
3217
3213
  printUnhandledErrors(errors) {
3218
3214
  const errorMessage = c.red(
3219
3215
  c.bold(
@@ -3246,6 +3242,21 @@ Vitest found ${errors.length} error${errors.length > 1 ? "s" : ""} not related t
3246
3242
  });
3247
3243
  this.log(c.red(divider()));
3248
3244
  }
3245
+ registerUnhandledRejection() {
3246
+ const onUnhandledRejection = (err) => {
3247
+ process.exitCode = 1;
3248
+ this.printError(err, {
3249
+ fullStack: true,
3250
+ type: "Unhandled Rejection"
3251
+ });
3252
+ this.error("\n\n");
3253
+ process.exit();
3254
+ };
3255
+ process.on("unhandledRejection", onUnhandledRejection);
3256
+ this.ctx.onClose(() => {
3257
+ process.off("unhandledRejection", onUnhandledRejection);
3258
+ });
3259
+ }
3249
3260
  }
3250
3261
 
3251
3262
  class BlobReporter {
@@ -3376,21 +3387,6 @@ function hasFailedSnapshot(suite) {
3376
3387
  }
3377
3388
 
3378
3389
  const BADGE_PADDING = " ";
3379
- const HELP_HINT = `${c.dim("press ")}${c.bold("h")}${c.dim(" to show help")}`;
3380
- const HELP_UPDATE_SNAP = c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot");
3381
- const HELP_QUITE = `${c.dim("press ")}${c.bold("q")}${c.dim(" to quit")}`;
3382
- const WAIT_FOR_CHANGE_PASS = `
3383
- ${c.bold(
3384
- c.inverse(c.green(" PASS "))
3385
- )}${c.green(" Waiting for file changes...")}`;
3386
- const WAIT_FOR_CHANGE_FAIL = `
3387
- ${c.bold(c.inverse(c.red(" FAIL ")))}${c.red(
3388
- " Tests failed. Watching for file changes..."
3389
- )}`;
3390
- const WAIT_FOR_CHANGE_CANCELLED = `
3391
- ${c.bold(
3392
- c.inverse(c.red(" CANCELLED "))
3393
- )}${c.red(" Test run cancelled. Watching for file changes...")}`;
3394
3390
  const LAST_RUN_LOG_TIMEOUT = 1500;
3395
3391
  class BaseReporter {
3396
3392
  start = 0;
@@ -3401,37 +3397,30 @@ class BaseReporter {
3401
3397
  ctx = void 0;
3402
3398
  verbose = false;
3403
3399
  _filesInWatchMode = /* @__PURE__ */ new Map();
3400
+ _timeStart = formatTimeString(/* @__PURE__ */ new Date());
3404
3401
  _lastRunTimeout = 0;
3405
3402
  _lastRunTimer;
3406
3403
  _lastRunCount = 0;
3407
- _timeStart = /* @__PURE__ */ new Date();
3408
- _offUnhandledRejection;
3409
3404
  constructor(options = {}) {
3410
3405
  this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI);
3411
- this.registerUnhandledRejection();
3412
- }
3413
- get mode() {
3414
- return this.ctx.config.mode;
3415
3406
  }
3416
3407
  onInit(ctx) {
3417
3408
  this.ctx = ctx;
3418
- ctx.onClose(() => {
3419
- this._offUnhandledRejection?.();
3420
- });
3421
- ctx.logger.printBanner();
3409
+ this.ctx.logger.printBanner();
3422
3410
  this.start = performance.now();
3423
3411
  }
3412
+ log(...messages) {
3413
+ this.ctx.logger.log(...messages);
3414
+ }
3415
+ error(...messages) {
3416
+ this.ctx.logger.error(...messages);
3417
+ }
3424
3418
  relative(path) {
3425
3419
  return relative(this.ctx.config.root, path);
3426
3420
  }
3427
3421
  onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
3428
3422
  this.end = performance.now();
3429
3423
  this.reportSummary(files, errors);
3430
- if (errors.length) {
3431
- if (!this.ctx.config.dangerouslyIgnoreUnhandledErrors) {
3432
- process.exitCode = 1;
3433
- }
3434
- }
3435
3424
  }
3436
3425
  onTaskUpdate(packs) {
3437
3426
  if (this.isTTY) {
@@ -3448,48 +3437,39 @@ class BaseReporter {
3448
3437
  if (!("filepath" in task) || !task.result?.state || task.result?.state === "run") {
3449
3438
  return;
3450
3439
  }
3451
- const logger = this.ctx.logger;
3452
3440
  const tests = getTests(task);
3453
3441
  const failed = tests.filter((t) => t.result?.state === "fail");
3454
- const skipped = tests.filter(
3455
- (t) => t.mode === "skip" || t.mode === "todo"
3456
- );
3442
+ const skipped = tests.filter((t) => t.mode === "skip" || t.mode === "todo");
3457
3443
  let state = c.dim(`${tests.length} test${tests.length > 1 ? "s" : ""}`);
3458
3444
  if (failed.length) {
3459
- state += ` ${c.dim("|")} ${c.red(`${failed.length} failed`)}`;
3445
+ state += c.dim(" | ") + c.red(`${failed.length} failed`);
3460
3446
  }
3461
3447
  if (skipped.length) {
3462
- state += ` ${c.dim("|")} ${c.yellow(`${skipped.length} skipped`)}`;
3448
+ state += c.dim(" | ") + c.yellow(`${skipped.length} skipped`);
3463
3449
  }
3464
- let suffix = c.dim(" (") + state + c.dim(")");
3465
- suffix += this.getDurationPrefix(task);
3450
+ let suffix = c.dim("(") + state + c.dim(")") + this.getDurationPrefix(task);
3466
3451
  if (this.ctx.config.logHeapUsage && task.result.heap != null) {
3467
- suffix += c.magenta(
3468
- ` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`
3469
- );
3452
+ suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
3470
3453
  }
3471
- let title = ` ${getStateSymbol(task)} `;
3454
+ let title = getStateSymbol(task);
3472
3455
  if (task.meta.typecheck) {
3473
- title += `${c.bgBlue(c.bold(" TS "))} `;
3456
+ title += ` ${c.bgBlue(c.bold(" TS "))}`;
3474
3457
  }
3475
3458
  if (task.projectName) {
3476
- title += formatProjectName(task.projectName);
3459
+ title += ` ${formatProjectName(task.projectName, "")}`;
3477
3460
  }
3478
- title += `${task.name} ${suffix}`;
3479
- logger.log(title);
3461
+ this.log(` ${title} ${task.name} ${suffix}`);
3480
3462
  for (const test of tests) {
3481
3463
  const duration = test.result?.duration;
3482
3464
  if (test.result?.state === "fail") {
3483
3465
  const suffix2 = this.getDurationPrefix(test);
3484
- logger.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${suffix2}`));
3466
+ this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${suffix2}`));
3485
3467
  test.result?.errors?.forEach((e) => {
3486
- logger.log(c.red(` ${F_RIGHT} ${e?.message}`));
3468
+ this.log(c.red(` ${F_RIGHT} ${e?.message}`));
3487
3469
  });
3488
3470
  } else if (duration && duration > this.ctx.config.slowTestThreshold) {
3489
- logger.log(
3490
- ` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))}${c.yellow(
3491
- ` ${Math.round(duration)}${c.dim("ms")}`
3492
- )}`
3471
+ this.log(
3472
+ ` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))} ${c.yellow(Math.round(duration) + c.dim("ms"))}`
3493
3473
  );
3494
3474
  }
3495
3475
  }
@@ -3504,23 +3484,20 @@ class BaseReporter {
3504
3484
  onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
3505
3485
  this.resetLastRunLog();
3506
3486
  const failed = errors.length > 0 || hasFailed(files);
3507
- const failedSnap = hasFailedSnapshot(files);
3508
- const cancelled = this.ctx.isCancelling;
3509
3487
  if (failed) {
3510
- this.ctx.logger.log(WAIT_FOR_CHANGE_FAIL);
3511
- } else if (cancelled) {
3512
- this.ctx.logger.log(WAIT_FOR_CHANGE_CANCELLED);
3488
+ this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
3489
+ } else if (this.ctx.isCancelling) {
3490
+ this.log(withLabel("red", "CANCELLED", "Test run cancelled. Watching for file changes..."));
3513
3491
  } else {
3514
- this.ctx.logger.log(WAIT_FOR_CHANGE_PASS);
3492
+ this.log(withLabel("green", "PASS", "Waiting for file changes..."));
3515
3493
  }
3516
- const hints = [];
3517
- hints.push(HELP_HINT);
3518
- if (failedSnap) {
3519
- hints.unshift(HELP_UPDATE_SNAP);
3494
+ const hints = [c.dim("press ") + c.bold("h") + c.dim(" to show help")];
3495
+ if (hasFailedSnapshot(files)) {
3496
+ hints.unshift(c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot"));
3520
3497
  } else {
3521
- hints.push(HELP_QUITE);
3498
+ hints.push(c.dim("press ") + c.bold("q") + c.dim(" to quit"));
3522
3499
  }
3523
- this.ctx.logger.log(BADGE_PADDING + hints.join(c.dim(", ")));
3500
+ this.log(BADGE_PADDING + hints.join(c.dim(", ")));
3524
3501
  if (this._lastRunCount) {
3525
3502
  const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`;
3526
3503
  const LAST_RUN_TEXTS = [
@@ -3550,90 +3527,79 @@ class BaseReporter {
3550
3527
  onWatcherRerun(files, trigger) {
3551
3528
  this.resetLastRunLog();
3552
3529
  this.watchFilters = files;
3553
- this.failedUnwatchedFiles = this.ctx.state.getFiles().filter((file) => {
3554
- return !files.includes(file.filepath) && hasFailed(file);
3555
- });
3530
+ this.failedUnwatchedFiles = this.ctx.state.getFiles().filter(
3531
+ (file) => !files.includes(file.filepath) && hasFailed(file)
3532
+ );
3556
3533
  files.forEach((filepath) => {
3557
3534
  let reruns = this._filesInWatchMode.get(filepath) ?? 0;
3558
3535
  this._filesInWatchMode.set(filepath, ++reruns);
3559
3536
  });
3560
- const BADGE = c.inverse(c.bold(c.blue(" RERUN ")));
3561
- const TRIGGER = trigger ? c.dim(` ${this.relative(trigger)}`) : "";
3562
- const FILENAME_PATTERN = this.ctx.filenamePattern ? `${BADGE_PADDING} ${c.dim("Filename pattern: ")}${c.blue(
3563
- this.ctx.filenamePattern
3564
- )}
3565
- ` : "";
3566
- const TESTNAME_PATTERN = this.ctx.configOverride.testNamePattern ? `${BADGE_PADDING} ${c.dim("Test name pattern: ")}${c.blue(
3567
- String(this.ctx.configOverride.testNamePattern)
3568
- )}
3569
- ` : "";
3570
- const PROJECT_FILTER = this.ctx.configOverride.project ? `${BADGE_PADDING} ${c.dim("Project name: ")}${c.blue(
3571
- toArray(this.ctx.configOverride.project).join(", ")
3572
- )}
3573
- ` : "";
3537
+ let banner = trigger ? c.dim(`${this.relative(trigger)} `) : "";
3574
3538
  if (files.length > 1 || !files.length) {
3575
- this.ctx.logger.clearFullScreen(
3576
- `
3577
- ${BADGE}${TRIGGER}
3578
- ${PROJECT_FILTER}${FILENAME_PATTERN}${TESTNAME_PATTERN}`
3579
- );
3580
3539
  this._lastRunCount = 0;
3581
3540
  } else if (files.length === 1) {
3582
3541
  const rerun = this._filesInWatchMode.get(files[0]) ?? 1;
3583
- this._lastRunCount = rerun;
3584
- this.ctx.logger.clearFullScreen(
3585
- `
3586
- ${BADGE}${TRIGGER} ${c.blue(
3587
- `x${rerun}`
3588
- )}
3589
- ${PROJECT_FILTER}${FILENAME_PATTERN}${TESTNAME_PATTERN}`
3590
- );
3542
+ banner += c.blue(`x${rerun} `);
3543
+ }
3544
+ this.ctx.logger.clearFullScreen();
3545
+ this.log(withLabel("blue", "RERUN", banner));
3546
+ if (this.ctx.configOverride.project) {
3547
+ this.log(BADGE_PADDING + c.dim(" Project name: ") + c.blue(toArray(this.ctx.configOverride.project).join(", ")));
3548
+ }
3549
+ if (this.ctx.filenamePattern) {
3550
+ this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern));
3591
3551
  }
3552
+ if (this.ctx.configOverride.testNamePattern) {
3553
+ this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
3554
+ }
3555
+ this.log("");
3592
3556
  if (!this.isTTY) {
3593
3557
  for (const task of this.failedUnwatchedFiles) {
3594
3558
  this.printTask(task);
3595
3559
  }
3596
3560
  }
3597
- this._timeStart = /* @__PURE__ */ new Date();
3561
+ this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
3598
3562
  this.start = performance.now();
3599
3563
  }
3600
3564
  onUserConsoleLog(log) {
3601
3565
  if (!this.shouldLog(log)) {
3602
3566
  return;
3603
3567
  }
3604
- const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
3605
- const header = c.gray(
3606
- log.type + c.dim(
3607
- ` | ${task ? getFullName(task, c.dim(" > ")) : log.taskId !== "__vitest__unknown_test__" ? log.taskId : "unknown test"}`
3608
- )
3609
- );
3610
3568
  const output = log.type === "stdout" ? this.ctx.logger.outputStream : this.ctx.logger.errorStream;
3611
3569
  const write = (msg) => output.write(msg);
3612
- write(`${header}
3613
- ${log.content}`);
3570
+ let headerText = "unknown test";
3571
+ const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
3572
+ if (task) {
3573
+ headerText = getFullName(task, c.dim(" > "));
3574
+ } else if (log.taskId && log.taskId !== "__vitest__unknown_test__") {
3575
+ headerText = log.taskId;
3576
+ }
3577
+ write(c.gray(log.type + c.dim(` | ${headerText}
3578
+ `)) + log.content);
3614
3579
  if (log.origin) {
3615
3580
  if (log.browser) {
3616
3581
  write("\n");
3617
3582
  }
3618
3583
  const project = log.taskId ? this.ctx.getProjectByTaskId(log.taskId) : this.ctx.getCoreWorkspaceProject();
3619
3584
  const stack = log.browser ? project.browser?.parseStacktrace(log.origin) || [] : parseStacktrace(log.origin);
3620
- const highlight = task ? stack.find((i) => i.file === task.file.filepath) : null;
3585
+ const highlight = task && stack.find((i) => i.file === task.file.filepath);
3621
3586
  for (const frame of stack) {
3622
3587
  const color = frame === highlight ? c.cyan : c.gray;
3623
3588
  const path = relative(project.config.root, frame.file);
3624
- write(
3625
- color(
3626
- ` ${c.dim(F_POINTER)} ${[
3627
- frame.method,
3628
- `${path}:${c.dim(`${frame.line}:${frame.column}`)}`
3629
- ].filter(Boolean).join(" ")}
3630
- `
3631
- )
3632
- );
3589
+ const positions = [
3590
+ frame.method,
3591
+ `${path}:${c.dim(`${frame.line}:${frame.column}`)}`
3592
+ ].filter(Boolean).join(" ");
3593
+ write(color(` ${c.dim(F_POINTER)} ${positions}
3594
+ `));
3633
3595
  }
3634
3596
  }
3635
3597
  write("\n");
3636
3598
  }
3599
+ onTestRemoved(trigger) {
3600
+ this.log(c.yellow("Test removed...") + (trigger ? c.dim(` [ ${this.relative(trigger)} ]
3601
+ `) : ""));
3602
+ }
3637
3603
  shouldLog(log) {
3638
3604
  if (this.ctx.config.silent) {
3639
3605
  return false;
@@ -3645,17 +3611,13 @@ ${log.content}`);
3645
3611
  return true;
3646
3612
  }
3647
3613
  onServerRestart(reason) {
3648
- this.ctx.logger.log(
3649
- c.bold(
3650
- c.magenta(
3651
- reason === "config" ? "\nRestarting due to config changes..." : "\nRestarting Vitest..."
3652
- )
3653
- )
3654
- );
3614
+ this.log(c.bold(c.magenta(
3615
+ reason === "config" ? "\nRestarting due to config changes..." : "\nRestarting Vitest..."
3616
+ )));
3655
3617
  }
3656
3618
  reportSummary(files, errors) {
3657
3619
  this.printErrorsSummary(files, errors);
3658
- if (this.mode === "benchmark") {
3620
+ if (this.ctx.config.mode === "benchmark") {
3659
3621
  this.reportBenchmarkSummary(files);
3660
3622
  } else {
3661
3623
  this.reportTestSummary(files, errors);
@@ -3667,167 +3629,117 @@ ${log.content}`);
3667
3629
  ...files
3668
3630
  ];
3669
3631
  const tests = getTests(affectedFiles);
3670
- const logger = this.ctx.logger;
3671
- const executionTime = this.end - this.start;
3672
- const collectTime = files.reduce(
3673
- (acc, test) => acc + Math.max(0, test.collectDuration || 0),
3674
- 0
3675
- );
3676
- const setupTime = files.reduce(
3677
- (acc, test) => acc + Math.max(0, test.setupDuration || 0),
3678
- 0
3679
- );
3680
- const testsTime = files.reduce(
3681
- (acc, test) => acc + Math.max(0, test.result?.duration || 0),
3682
- 0
3683
- );
3684
- const transformTime = this.ctx.projects.flatMap((w) => w.vitenode.getTotalDuration()).reduce((a, b) => a + b, 0);
3685
- const environmentTime = files.reduce(
3686
- (acc, file) => acc + Math.max(0, file.environmentLoad || 0),
3687
- 0
3688
- );
3689
- const prepareTime = files.reduce(
3690
- (acc, file) => acc + Math.max(0, file.prepareDuration || 0),
3691
- 0
3692
- );
3693
- const threadTime = collectTime + testsTime + setupTime;
3694
3632
  const snapshotOutput = renderSnapshotSummary(
3695
3633
  this.ctx.config.root,
3696
3634
  this.ctx.snapshot.summary
3697
3635
  );
3698
- if (snapshotOutput.length) {
3699
- logger.log(
3700
- snapshotOutput.map(
3701
- (t, i) => i === 0 ? `${padTitle("Snapshots")} ${t}` : `${padTitle("")} ${t}`
3702
- ).join("\n")
3703
- );
3704
- if (snapshotOutput.length > 1) {
3705
- logger.log();
3706
- }
3636
+ for (const [index, snapshot] of snapshotOutput.entries()) {
3637
+ const title = index === 0 ? "Snapshots" : "";
3638
+ this.log(`${padTitle(title)} ${snapshot}`);
3639
+ }
3640
+ if (snapshotOutput.length > 1) {
3641
+ this.log();
3707
3642
  }
3708
- logger.log(padTitle("Test Files"), getStateString(affectedFiles));
3709
- logger.log(padTitle("Tests"), getStateString(tests));
3643
+ this.log(padTitle("Test Files"), getStateString(affectedFiles));
3644
+ this.log(padTitle("Tests"), getStateString(tests));
3710
3645
  if (this.ctx.projects.some((c2) => c2.config.typecheck.enabled)) {
3711
- const failed = tests.filter(
3712
- (t) => t.meta?.typecheck && t.result?.errors?.length
3713
- );
3714
- logger.log(
3646
+ const failed = tests.filter((t) => t.meta?.typecheck && t.result?.errors?.length);
3647
+ this.log(
3715
3648
  padTitle("Type Errors"),
3716
3649
  failed.length ? c.bold(c.red(`${failed.length} failed`)) : c.dim("no errors")
3717
3650
  );
3718
3651
  }
3719
3652
  if (errors.length) {
3720
- logger.log(
3653
+ this.log(
3721
3654
  padTitle("Errors"),
3722
3655
  c.bold(c.red(`${errors.length} error${errors.length > 1 ? "s" : ""}`))
3723
3656
  );
3724
3657
  }
3725
- logger.log(padTitle("Start at"), formatTimeString(this._timeStart));
3658
+ this.log(padTitle("Start at"), this._timeStart);
3659
+ const collectTime = sum(files, (file) => file.collectDuration);
3660
+ const testsTime = sum(files, (file) => file.result?.duration);
3661
+ const setupTime = sum(files, (file) => file.setupDuration);
3726
3662
  if (this.watchFilters) {
3727
- logger.log(padTitle("Duration"), time(threadTime));
3663
+ this.log(padTitle("Duration"), time(collectTime + testsTime + setupTime));
3728
3664
  } else {
3729
- let timers = `transform ${time(transformTime)}, setup ${time(
3730
- setupTime
3731
- )}, collect ${time(collectTime)}, tests ${time(
3732
- testsTime
3733
- )}, environment ${time(environmentTime)}, prepare ${time(prepareTime)}`;
3734
- const typecheck = this.ctx.projects.reduce(
3735
- (acc, c2) => acc + (c2.typechecker?.getResult().time || 0),
3736
- 0
3737
- );
3738
- if (typecheck) {
3739
- timers += `, typecheck ${time(typecheck)}`;
3740
- }
3741
- logger.log(
3742
- padTitle("Duration"),
3743
- time(executionTime) + c.dim(` (${timers})`)
3744
- );
3665
+ const executionTime = this.end - this.start;
3666
+ const environmentTime = sum(files, (file) => file.environmentLoad);
3667
+ const prepareTime = sum(files, (file) => file.prepareDuration);
3668
+ const transformTime = sum(this.ctx.projects, (project) => project.vitenode.getTotalDuration());
3669
+ const typecheck = sum(this.ctx.projects, (project) => project.typechecker?.getResult().time);
3670
+ const timers = [
3671
+ `transform ${time(transformTime)}`,
3672
+ `setup ${time(setupTime)}`,
3673
+ `collect ${time(collectTime)}`,
3674
+ `tests ${time(testsTime)}`,
3675
+ `environment ${time(environmentTime)}`,
3676
+ `prepare ${time(prepareTime)}`,
3677
+ typecheck && `typecheck ${time(typecheck)}`
3678
+ ].filter(Boolean).join(", ");
3679
+ this.log(padTitle("Duration"), time(executionTime) + c.dim(` (${timers})`));
3745
3680
  }
3746
- logger.log();
3681
+ this.log();
3747
3682
  }
3748
3683
  printErrorsSummary(files, errors) {
3749
- const logger = this.ctx.logger;
3750
3684
  const suites = getSuites(files);
3751
3685
  const tests = getTests(files);
3752
3686
  const failedSuites = suites.filter((i) => i.result?.errors);
3753
3687
  const failedTests = tests.filter((i) => i.result?.state === "fail");
3754
3688
  const failedTotal = countTestErrors(failedSuites) + countTestErrors(failedTests);
3755
3689
  let current = 1;
3756
- const errorDivider = () => logger.error(
3757
- `${c.red(
3758
- c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1))
3759
- )}
3760
- `
3761
- );
3690
+ const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
3691
+ `);
3762
3692
  if (failedSuites.length) {
3763
- logger.error(
3764
- c.red(
3765
- divider(c.bold(c.inverse(` Failed Suites ${failedSuites.length} `)))
3766
- )
3767
- );
3768
- logger.error();
3693
+ this.error(`${errorBanner(`Failed Suites ${failedSuites.length}`)}
3694
+ `);
3769
3695
  this.printTaskErrors(failedSuites, errorDivider);
3770
3696
  }
3771
3697
  if (failedTests.length) {
3772
- logger.error(
3773
- c.red(
3774
- divider(c.bold(c.inverse(` Failed Tests ${failedTests.length} `)))
3775
- )
3776
- );
3777
- logger.error();
3698
+ this.error(`${errorBanner(`Failed Tests ${failedTests.length}`)}
3699
+ `);
3778
3700
  this.printTaskErrors(failedTests, errorDivider);
3779
3701
  }
3780
3702
  if (errors.length) {
3781
- logger.printUnhandledErrors(errors);
3782
- logger.error();
3703
+ this.ctx.logger.printUnhandledErrors(errors);
3704
+ this.error();
3783
3705
  }
3784
- return tests;
3785
3706
  }
3786
3707
  reportBenchmarkSummary(files) {
3787
- const logger = this.ctx.logger;
3788
3708
  const benches = getTests(files);
3789
3709
  const topBenches = benches.filter((i) => i.result?.benchmark?.rank === 1);
3790
- logger.log(
3791
- `
3792
- ${c.cyan(c.inverse(c.bold(" BENCH ")))} ${c.cyan("Summary")}
3793
- `
3794
- );
3710
+ this.log(withLabel("cyan", "BENCH", "Summary\n"));
3795
3711
  for (const bench of topBenches) {
3796
3712
  const group = bench.suite || bench.file;
3797
3713
  if (!group) {
3798
3714
  continue;
3799
3715
  }
3800
3716
  const groupName = getFullName(group, c.dim(" > "));
3801
- logger.log(` ${bench.name}${c.dim(` - ${groupName}`)}`);
3717
+ this.log(` ${bench.name}${c.dim(` - ${groupName}`)}`);
3802
3718
  const siblings = group.tasks.filter((i) => i.meta.benchmark && i.result?.benchmark && i !== bench).sort((a, b) => a.result.benchmark.rank - b.result.benchmark.rank);
3803
- if (siblings.length === 0) {
3804
- logger.log("");
3805
- continue;
3806
- }
3807
3719
  for (const sibling of siblings) {
3808
- const number = `${(sibling.result.benchmark.mean / bench.result.benchmark.mean).toFixed(2)}x`;
3809
- logger.log(
3810
- ` ${c.green(number)} ${c.gray("faster than")} ${sibling.name}`
3811
- );
3720
+ const number = (sibling.result.benchmark.mean / bench.result.benchmark.mean).toFixed(2);
3721
+ this.log(c.green(` ${number}x `) + c.gray("faster than ") + sibling.name);
3812
3722
  }
3813
- logger.log("");
3723
+ this.log("");
3814
3724
  }
3815
3725
  }
3816
3726
  printTaskErrors(tasks, errorDivider) {
3817
3727
  const errorsQueue = [];
3818
3728
  for (const task of tasks) {
3819
3729
  task.result?.errors?.forEach((error) => {
3820
- const errorItem = error?.stackStr && errorsQueue.find((i) => {
3821
- const hasStr = i[0]?.stackStr === error.stackStr;
3822
- if (!hasStr) {
3823
- return false;
3824
- }
3825
- const currentProjectName = task?.projectName || task.file?.projectName || "";
3826
- const projectName = i[1][0]?.projectName || i[1][0].file?.projectName || "";
3827
- return projectName === currentProjectName;
3828
- });
3829
- if (errorItem) {
3830
- errorItem[1].push(task);
3730
+ let previous;
3731
+ if (error?.stackStr) {
3732
+ previous = errorsQueue.find((i) => {
3733
+ if (i[0]?.stackStr !== error.stackStr) {
3734
+ return false;
3735
+ }
3736
+ const currentProjectName = task?.projectName || task.file?.projectName || "";
3737
+ const projectName = i[1][0]?.projectName || i[1][0].file?.projectName || "";
3738
+ return projectName === currentProjectName;
3739
+ });
3740
+ }
3741
+ if (previous) {
3742
+ previous[1].push(task);
3831
3743
  } else {
3832
3744
  errorsQueue.push([error, [task]]);
3833
3745
  }
@@ -3839,40 +3751,25 @@ ${c.cyan(c.inverse(c.bold(" BENCH ")))} ${c.cyan("Summary")}
3839
3751
  const projectName = task?.projectName || task.file?.projectName || "";
3840
3752
  let name = getFullName(task, c.dim(" > "));
3841
3753
  if (filepath) {
3842
- name = `${name} ${c.dim(`[ ${this.relative(filepath)} ]`)}`;
3754
+ name += c.dim(` [ ${this.relative(filepath)} ]`);
3843
3755
  }
3844
3756
  this.ctx.logger.error(
3845
- `${c.red(c.bold(c.inverse(" FAIL ")))} ${formatProjectName(
3846
- projectName
3847
- )}${name}`
3757
+ `${c.red(c.bold(c.inverse(" FAIL ")))}${formatProjectName(projectName)} ${name}`
3848
3758
  );
3849
3759
  }
3850
- const screenshots = tasks2.filter((t) => t.meta?.failScreenshotPath).map((t) => t.meta?.failScreenshotPath);
3851
- const project = this.ctx.getProjectByTaskId(tasks2[0].id);
3760
+ const screenshotPaths = tasks2.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
3852
3761
  this.ctx.logger.printError(error, {
3853
- project,
3762
+ project: this.ctx.getProjectByTaskId(tasks2[0].id),
3854
3763
  verbose: this.verbose,
3855
- screenshotPaths: screenshots,
3764
+ screenshotPaths,
3856
3765
  task: tasks2[0]
3857
3766
  });
3858
3767
  errorDivider();
3859
3768
  }
3860
3769
  }
3861
- registerUnhandledRejection() {
3862
- const onUnhandledRejection = async (err) => {
3863
- process.exitCode = 1;
3864
- this.ctx.logger.printError(err, {
3865
- fullStack: true,
3866
- type: "Unhandled Rejection"
3867
- });
3868
- this.ctx.logger.error("\n\n");
3869
- process.exit();
3870
- };
3871
- process.on("unhandledRejection", onUnhandledRejection);
3872
- this._offUnhandledRejection = () => {
3873
- process.off("unhandledRejection", onUnhandledRejection);
3874
- };
3875
- }
3770
+ }
3771
+ function errorBanner(message) {
3772
+ return c.red(divider(c.bold(c.inverse(` ${message} `))));
3876
3773
  }
3877
3774
  function padTitle(str) {
3878
3775
  return c.dim(`${str.padStart(11)} `);
@@ -3883,6 +3780,11 @@ function time(time2) {
3883
3780
  }
3884
3781
  return `${Math.round(time2)}ms`;
3885
3782
  }
3783
+ function sum(items, cb) {
3784
+ return items.reduce((total, next) => {
3785
+ return total + Math.max(cb(next) || 0, 0);
3786
+ }, 0);
3787
+ }
3886
3788
 
3887
3789
  class BasicReporter extends BaseReporter {
3888
3790
  constructor() {
@@ -4135,7 +4037,7 @@ class DefaultReporter extends BaseReporter {
4135
4037
  this.rendererOptions.logger = this.ctx.logger;
4136
4038
  this.rendererOptions.showHeap = this.ctx.config.logHeapUsage;
4137
4039
  this.rendererOptions.slowTestThreshold = this.ctx.config.slowTestThreshold;
4138
- this.rendererOptions.mode = this.mode;
4040
+ this.rendererOptions.mode = this.ctx.config.mode;
4139
4041
  const files = this.ctx.state.getFiles(this.watchFilters);
4140
4042
  if (!this.renderer) {
4141
4043
  this.renderer = createListRenderer(files, this.rendererOptions).start();