vitest 2.2.0-beta.2 → 3.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE.md +75 -0
  2. package/dist/browser.d.ts +3 -3
  3. package/dist/browser.js +1 -1
  4. package/dist/chunks/{RandomSequencer.BPedXEug.js → RandomSequencer.gisBJ77r.js} +11 -4
  5. package/dist/chunks/{base.BS0HhLXd.js → base.CkcgFVQd.js} +8 -3
  6. package/dist/chunks/{cac.Cs06pOqp.js → cac.CWCZimpS.js} +7 -7
  7. package/dist/chunks/{cli-api.CB-jIbYQ.js → cli-api.BKUOv0Nc.js} +186 -83
  8. package/dist/chunks/{config.CPguQ7J1.d.ts → config.BTPBhmK5.d.ts} +1 -1
  9. package/dist/chunks/{creator.IIqd8RWT.js → creator.DcAcUhMD.js} +1 -4
  10. package/dist/chunks/{globals.BCGEw6ON.js → globals.DJTzb7B3.js} +2 -2
  11. package/dist/chunks/{index.DD5eTY2y.js → index.BqHViJW9.js} +1 -1
  12. package/dist/chunks/{index.BjjsHdBb.js → index.CkOJwybT.js} +1 -1
  13. package/dist/chunks/{index.bzFpKeaq.js → index.DKe7vK-G.js} +530 -144
  14. package/dist/chunks/{index.CqYx2Nsr.js → index.DQboAxJm.js} +23 -14
  15. package/dist/chunks/{inspector.70d6emsh.js → inspector.DKLceBVD.js} +1 -1
  16. package/dist/chunks/{reporters.F9D2idOT.d.ts → reporters.BZbwTvrM.d.ts} +249 -258
  17. package/dist/chunks/{resolveConfig.CLnvCvEs.js → resolveConfig.3rGGWga5.js} +81 -49
  18. package/dist/chunks/{runBaseTests.B7hcVT-s.js → runBaseTests.C6huCAng.js} +6 -6
  19. package/dist/chunks/{setup-common.BfGt8K-K.js → setup-common.B5ClyS48.js} +1 -1
  20. package/dist/chunks/{utils.DJONn5B5.js → utils.CMUTX-p8.js} +5 -2
  21. package/dist/chunks/{vi.BlPttogV.js → vi.CZKezqeD.js} +18 -13
  22. package/dist/chunks/{vite.DonA4fvH.d.ts → vite.DIfmneq0.d.ts} +1 -1
  23. package/dist/chunks/{vm.Zr4qWzDJ.js → vm.DGhTouO3.js} +10 -1
  24. package/dist/chunks/{worker.9VY11NZs.d.ts → worker.CmzGeuVD.d.ts} +3 -3
  25. package/dist/chunks/{worker.Qz1UB4Fv.d.ts → worker.umPNbBNk.d.ts} +1 -1
  26. package/dist/cli.js +1 -1
  27. package/dist/config.cjs +1 -10
  28. package/dist/config.d.ts +4 -5
  29. package/dist/config.js +1 -10
  30. package/dist/coverage.d.ts +2 -2
  31. package/dist/coverage.js +4 -4
  32. package/dist/execute.d.ts +3 -3
  33. package/dist/index.d.ts +21 -13
  34. package/dist/index.js +2 -2
  35. package/dist/node.d.ts +9 -22
  36. package/dist/node.js +9 -9
  37. package/dist/reporters.d.ts +2 -2
  38. package/dist/reporters.js +5 -4
  39. package/dist/runners.d.ts +1 -2
  40. package/dist/runners.js +9 -14
  41. package/dist/worker.js +1 -1
  42. package/dist/workers/forks.js +1 -1
  43. package/dist/workers/runVmTests.js +6 -6
  44. package/dist/workers/threads.js +1 -1
  45. package/dist/workers/vmForks.js +1 -1
  46. package/dist/workers/vmThreads.js +1 -1
  47. package/dist/workers.d.ts +3 -3
  48. package/dist/workers.js +3 -3
  49. package/package.json +21 -22
@@ -3,19 +3,20 @@ 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, 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, p as padSummaryTitle, e as getStateString$1, h as formatTime, i as countTestErrors, j as F_TREE_NODE_END, k as F_TREE_NODE_MIDDLE, l as getCols } from './utils.DJONn5B5.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, p as padSummaryTitle, e as getStateString$1, h as formatTime, i as countTestErrors, j as F_TREE_NODE_END, k as F_TREE_NODE_MIDDLE, l as getCols } from './utils.CMUTX-p8.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 as performance$1 } from 'node:perf_hooks';
10
10
  import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
11
- import { a as TypeCheckError, R as RandomSequencer, g as getOutputFile, b as isNode, c as isDeno } from './RandomSequencer.BPedXEug.js';
11
+ import { a as TypeCheckError, R as RandomSequencer, g as getOutputFile, b as isNode, c as isDeno } from './RandomSequencer.gisBJ77r.js';
12
12
  import { isCI } from 'std-env';
13
13
  import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
14
- import restoreCursor from 'restore-cursor';
15
14
  import { Writable } from 'node:stream';
16
15
  import { Console } from 'node:console';
17
16
  import process$1 from 'node:process';
18
- import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
17
+ import { g as getDefaultExportFromCjs, c as commonjsGlobal } from './_commonjsHelpers.BFTU3MAI.js';
18
+ import require$$0 from 'assert';
19
+ import require$$0$1 from 'events';
19
20
  import { createRequire } from 'node:module';
20
21
  import { hostname } from 'node:os';
21
22
 
@@ -626,6 +627,370 @@ ansiEscapes.iTerm = {
626
627
  }
627
628
  };
628
629
 
630
+ var onetime$1 = {exports: {}};
631
+
632
+ var mimicFn = {exports: {}};
633
+
634
+ var hasRequiredMimicFn;
635
+
636
+ function requireMimicFn () {
637
+ if (hasRequiredMimicFn) return mimicFn.exports;
638
+ hasRequiredMimicFn = 1;
639
+
640
+ const mimicFn$1 = (to, from) => {
641
+ for (const prop of Reflect.ownKeys(from)) {
642
+ Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop));
643
+ }
644
+
645
+ return to;
646
+ };
647
+
648
+ mimicFn.exports = mimicFn$1;
649
+ // TODO: Remove this for the next major release
650
+ mimicFn.exports.default = mimicFn$1;
651
+ return mimicFn.exports;
652
+ }
653
+
654
+ var hasRequiredOnetime;
655
+
656
+ function requireOnetime () {
657
+ if (hasRequiredOnetime) return onetime$1.exports;
658
+ hasRequiredOnetime = 1;
659
+ const mimicFn = requireMimicFn();
660
+
661
+ const calledFunctions = new WeakMap();
662
+
663
+ const onetime = (function_, options = {}) => {
664
+ if (typeof function_ !== 'function') {
665
+ throw new TypeError('Expected a function');
666
+ }
667
+
668
+ let returnValue;
669
+ let callCount = 0;
670
+ const functionName = function_.displayName || function_.name || '<anonymous>';
671
+
672
+ const onetime = function (...arguments_) {
673
+ calledFunctions.set(onetime, ++callCount);
674
+
675
+ if (callCount === 1) {
676
+ returnValue = function_.apply(this, arguments_);
677
+ function_ = null;
678
+ } else if (options.throw === true) {
679
+ throw new Error(`Function \`${functionName}\` can only be called once`);
680
+ }
681
+
682
+ return returnValue;
683
+ };
684
+
685
+ mimicFn(onetime, function_);
686
+ calledFunctions.set(onetime, callCount);
687
+
688
+ return onetime;
689
+ };
690
+
691
+ onetime$1.exports = onetime;
692
+ // TODO: Remove this for the next major release
693
+ onetime$1.exports.default = onetime;
694
+
695
+ onetime$1.exports.callCount = function_ => {
696
+ if (!calledFunctions.has(function_)) {
697
+ throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`);
698
+ }
699
+
700
+ return calledFunctions.get(function_);
701
+ };
702
+ return onetime$1.exports;
703
+ }
704
+
705
+ var onetimeExports = requireOnetime();
706
+ var onetime = /*@__PURE__*/getDefaultExportFromCjs(onetimeExports);
707
+
708
+ var signalExit$1 = {exports: {}};
709
+
710
+ var signals = {exports: {}};
711
+
712
+ var hasRequiredSignals;
713
+
714
+ function requireSignals () {
715
+ if (hasRequiredSignals) return signals.exports;
716
+ hasRequiredSignals = 1;
717
+ (function (module) {
718
+ // This is not the set of all possible signals.
719
+ //
720
+ // It IS, however, the set of all signals that trigger
721
+ // an exit on either Linux or BSD systems. Linux is a
722
+ // superset of the signal names supported on BSD, and
723
+ // the unknown signals just fail to register, so we can
724
+ // catch that easily enough.
725
+ //
726
+ // Don't bother with SIGKILL. It's uncatchable, which
727
+ // means that we can't fire any callbacks anyway.
728
+ //
729
+ // If a user does happen to register a handler on a non-
730
+ // fatal signal like SIGWINCH or something, and then
731
+ // exit, it'll end up firing `process.emit('exit')`, so
732
+ // the handler will be fired anyway.
733
+ //
734
+ // SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
735
+ // artificially, inherently leave the process in a
736
+ // state from which it is not safe to try and enter JS
737
+ // listeners.
738
+ module.exports = [
739
+ 'SIGABRT',
740
+ 'SIGALRM',
741
+ 'SIGHUP',
742
+ 'SIGINT',
743
+ 'SIGTERM'
744
+ ];
745
+
746
+ if (process.platform !== 'win32') {
747
+ module.exports.push(
748
+ 'SIGVTALRM',
749
+ 'SIGXCPU',
750
+ 'SIGXFSZ',
751
+ 'SIGUSR2',
752
+ 'SIGTRAP',
753
+ 'SIGSYS',
754
+ 'SIGQUIT',
755
+ 'SIGIOT'
756
+ // should detect profiler and enable/disable accordingly.
757
+ // see #21
758
+ // 'SIGPROF'
759
+ );
760
+ }
761
+
762
+ if (process.platform === 'linux') {
763
+ module.exports.push(
764
+ 'SIGIO',
765
+ 'SIGPOLL',
766
+ 'SIGPWR',
767
+ 'SIGSTKFLT',
768
+ 'SIGUNUSED'
769
+ );
770
+ }
771
+ } (signals));
772
+ return signals.exports;
773
+ }
774
+
775
+ var hasRequiredSignalExit;
776
+
777
+ function requireSignalExit () {
778
+ if (hasRequiredSignalExit) return signalExit$1.exports;
779
+ hasRequiredSignalExit = 1;
780
+ // Note: since nyc uses this module to output coverage, any lines
781
+ // that are in the direct sync flow of nyc's outputCoverage are
782
+ // ignored, since we can never get coverage for them.
783
+ // grab a reference to node's real process object right away
784
+ var process = commonjsGlobal.process;
785
+
786
+ const processOk = function (process) {
787
+ return process &&
788
+ typeof process === 'object' &&
789
+ typeof process.removeListener === 'function' &&
790
+ typeof process.emit === 'function' &&
791
+ typeof process.reallyExit === 'function' &&
792
+ typeof process.listeners === 'function' &&
793
+ typeof process.kill === 'function' &&
794
+ typeof process.pid === 'number' &&
795
+ typeof process.on === 'function'
796
+ };
797
+
798
+ // some kind of non-node environment, just no-op
799
+ /* istanbul ignore if */
800
+ if (!processOk(process)) {
801
+ signalExit$1.exports = function () {
802
+ return function () {}
803
+ };
804
+ } else {
805
+ var assert = require$$0;
806
+ var signals = requireSignals();
807
+ var isWin = /^win/i.test(process.platform);
808
+
809
+ var EE = require$$0$1;
810
+ /* istanbul ignore if */
811
+ if (typeof EE !== 'function') {
812
+ EE = EE.EventEmitter;
813
+ }
814
+
815
+ var emitter;
816
+ if (process.__signal_exit_emitter__) {
817
+ emitter = process.__signal_exit_emitter__;
818
+ } else {
819
+ emitter = process.__signal_exit_emitter__ = new EE();
820
+ emitter.count = 0;
821
+ emitter.emitted = {};
822
+ }
823
+
824
+ // Because this emitter is a global, we have to check to see if a
825
+ // previous version of this library failed to enable infinite listeners.
826
+ // I know what you're about to say. But literally everything about
827
+ // signal-exit is a compromise with evil. Get used to it.
828
+ if (!emitter.infinite) {
829
+ emitter.setMaxListeners(Infinity);
830
+ emitter.infinite = true;
831
+ }
832
+
833
+ signalExit$1.exports = function (cb, opts) {
834
+ /* istanbul ignore if */
835
+ if (!processOk(commonjsGlobal.process)) {
836
+ return function () {}
837
+ }
838
+ assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler');
839
+
840
+ if (loaded === false) {
841
+ load();
842
+ }
843
+
844
+ var ev = 'exit';
845
+ if (opts && opts.alwaysLast) {
846
+ ev = 'afterexit';
847
+ }
848
+
849
+ var remove = function () {
850
+ emitter.removeListener(ev, cb);
851
+ if (emitter.listeners('exit').length === 0 &&
852
+ emitter.listeners('afterexit').length === 0) {
853
+ unload();
854
+ }
855
+ };
856
+ emitter.on(ev, cb);
857
+
858
+ return remove
859
+ };
860
+
861
+ var unload = function unload () {
862
+ if (!loaded || !processOk(commonjsGlobal.process)) {
863
+ return
864
+ }
865
+ loaded = false;
866
+
867
+ signals.forEach(function (sig) {
868
+ try {
869
+ process.removeListener(sig, sigListeners[sig]);
870
+ } catch (er) {}
871
+ });
872
+ process.emit = originalProcessEmit;
873
+ process.reallyExit = originalProcessReallyExit;
874
+ emitter.count -= 1;
875
+ };
876
+ signalExit$1.exports.unload = unload;
877
+
878
+ var emit = function emit (event, code, signal) {
879
+ /* istanbul ignore if */
880
+ if (emitter.emitted[event]) {
881
+ return
882
+ }
883
+ emitter.emitted[event] = true;
884
+ emitter.emit(event, code, signal);
885
+ };
886
+
887
+ // { <signal>: <listener fn>, ... }
888
+ var sigListeners = {};
889
+ signals.forEach(function (sig) {
890
+ sigListeners[sig] = function listener () {
891
+ /* istanbul ignore if */
892
+ if (!processOk(commonjsGlobal.process)) {
893
+ return
894
+ }
895
+ // If there are no other listeners, an exit is coming!
896
+ // Simplest way: remove us and then re-send the signal.
897
+ // We know that this will kill the process, so we can
898
+ // safely emit now.
899
+ var listeners = process.listeners(sig);
900
+ if (listeners.length === emitter.count) {
901
+ unload();
902
+ emit('exit', null, sig);
903
+ /* istanbul ignore next */
904
+ emit('afterexit', null, sig);
905
+ /* istanbul ignore next */
906
+ if (isWin && sig === 'SIGHUP') {
907
+ // "SIGHUP" throws an `ENOSYS` error on Windows,
908
+ // so use a supported signal instead
909
+ sig = 'SIGINT';
910
+ }
911
+ /* istanbul ignore next */
912
+ process.kill(process.pid, sig);
913
+ }
914
+ };
915
+ });
916
+
917
+ signalExit$1.exports.signals = function () {
918
+ return signals
919
+ };
920
+
921
+ var loaded = false;
922
+
923
+ var load = function load () {
924
+ if (loaded || !processOk(commonjsGlobal.process)) {
925
+ return
926
+ }
927
+ loaded = true;
928
+
929
+ // This is the number of onSignalExit's that are in play.
930
+ // It's important so that we can count the correct number of
931
+ // listeners on signals, and don't wait for the other one to
932
+ // handle it instead of us.
933
+ emitter.count += 1;
934
+
935
+ signals = signals.filter(function (sig) {
936
+ try {
937
+ process.on(sig, sigListeners[sig]);
938
+ return true
939
+ } catch (er) {
940
+ return false
941
+ }
942
+ });
943
+
944
+ process.emit = processEmit;
945
+ process.reallyExit = processReallyExit;
946
+ };
947
+ signalExit$1.exports.load = load;
948
+
949
+ var originalProcessReallyExit = process.reallyExit;
950
+ var processReallyExit = function processReallyExit (code) {
951
+ /* istanbul ignore if */
952
+ if (!processOk(commonjsGlobal.process)) {
953
+ return
954
+ }
955
+ process.exitCode = code || /* istanbul ignore next */ 0;
956
+ emit('exit', process.exitCode, null);
957
+ /* istanbul ignore next */
958
+ emit('afterexit', process.exitCode, null);
959
+ /* istanbul ignore next */
960
+ originalProcessReallyExit.call(process, process.exitCode);
961
+ };
962
+
963
+ var originalProcessEmit = process.emit;
964
+ var processEmit = function processEmit (ev, arg) {
965
+ if (ev === 'exit' && processOk(commonjsGlobal.process)) {
966
+ /* istanbul ignore else */
967
+ if (arg !== undefined) {
968
+ process.exitCode = arg;
969
+ }
970
+ var ret = originalProcessEmit.apply(this, arguments);
971
+ /* istanbul ignore next */
972
+ emit('exit', process.exitCode, null);
973
+ /* istanbul ignore next */
974
+ emit('afterexit', process.exitCode, null);
975
+ /* istanbul ignore next */
976
+ return ret
977
+ } else {
978
+ return originalProcessEmit.apply(this, arguments)
979
+ }
980
+ };
981
+ }
982
+ return signalExit$1.exports;
983
+ }
984
+
985
+ var signalExitExports = requireSignalExit();
986
+ var signalExit = /*@__PURE__*/getDefaultExportFromCjs(signalExitExports);
987
+
988
+ const restoreCursor = onetime(() => {
989
+ signalExit(() => {
990
+ process$1.stderr.write('\u001B[?25h');
991
+ }, {alwaysLast: true});
992
+ });
993
+
629
994
  let isHidden = false;
630
995
 
631
996
  const cliCursor = {};
@@ -2804,15 +3169,15 @@ Vitest caught ${errors.length} unhandled error${errors.length > 1 ? "s" : ""} du
2804
3169
  This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.`
2805
3170
  )
2806
3171
  );
2807
- this.log(c.red(divider(c.bold(c.inverse(" Unhandled Errors ")))));
2808
- this.log(errorMessage);
3172
+ this.error(c.red(divider(c.bold(c.inverse(" Unhandled Errors ")))));
3173
+ this.error(errorMessage);
2809
3174
  errors.forEach((err) => {
2810
3175
  this.printError(err, {
2811
3176
  fullStack: true,
2812
3177
  type: err.type || "Unhandled Error"
2813
3178
  });
2814
3179
  });
2815
- this.log(c.red(divider()));
3180
+ this.error(c.red(divider()));
2816
3181
  }
2817
3182
  printSourceTypeErrors(errors) {
2818
3183
  const errorMessage = c.red(
@@ -2866,7 +3231,7 @@ class BlobReporter {
2866
3231
  const modules = this.ctx.projects.map(
2867
3232
  (project) => {
2868
3233
  return [
2869
- project.getName(),
3234
+ project.name,
2870
3235
  [...project.vite.moduleGraph.idToModuleMap.entries()].map((mod) => {
2871
3236
  if (!mod[1].file) {
2872
3237
  return null;
@@ -2934,7 +3299,7 @@ ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
2934
3299
  );
2935
3300
  }
2936
3301
  const projects = Object.fromEntries(
2937
- projectsArray.map((p) => [p.getName(), p])
3302
+ projectsArray.map((p) => [p.name, p])
2938
3303
  );
2939
3304
  blobs.forEach((blob) => {
2940
3305
  blob.moduleKeys.forEach(([projectName, moduleIds]) => {
@@ -3041,21 +3406,27 @@ class BaseReporter {
3041
3406
  this.log(` ${title} ${task.name} ${suffix}`);
3042
3407
  const anyFailed = tests.some((test) => test.result?.state === "fail");
3043
3408
  for (const test of tests) {
3044
- const duration = test.result?.duration;
3409
+ const { duration, retryCount, repeatCount } = test.result || {};
3410
+ let suffix2 = "";
3411
+ if (retryCount != null && retryCount > 0) {
3412
+ suffix2 += c.yellow(` (retry x${retryCount})`);
3413
+ }
3414
+ if (repeatCount != null && repeatCount > 0) {
3415
+ suffix2 += c.yellow(` (repeat x${repeatCount})`);
3416
+ }
3045
3417
  if (test.result?.state === "fail") {
3046
- const suffix2 = this.getDurationPrefix(test);
3047
- this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${suffix2}`));
3418
+ this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${this.getDurationPrefix(test)}`) + suffix2);
3048
3419
  test.result?.errors?.forEach((e) => {
3049
3420
  this.log(c.red(` ${F_RIGHT} ${e?.message}`));
3050
3421
  });
3051
3422
  } else if (duration && duration > this.ctx.config.slowTestThreshold) {
3052
3423
  this.log(
3053
- ` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))} ${c.yellow(Math.round(duration) + c.dim("ms"))}`
3424
+ ` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))} ${c.yellow(Math.round(duration) + c.dim("ms"))}${suffix2}`
3054
3425
  );
3055
- } else if (test.result?.state === "skip" && test.result.note) {
3426
+ } else if (this.ctx.config.hideSkippedTests && (test.mode === "skip" || test.result?.state === "skip")) ; else if (test.result?.state === "skip" && test.result.note) {
3056
3427
  this.log(` ${getStateSymbol(test)} ${getTestName(test)}${c.dim(c.gray(` [${test.result.note}]`))}`);
3057
3428
  } else if (this.renderSucceed || anyFailed) {
3058
- this.log(` ${c.green(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))}`);
3429
+ this.log(` ${c.dim(getStateSymbol(test))} ${getTestName(test, c.dim(" > "))}${suffix2}`);
3059
3430
  }
3060
3431
  }
3061
3432
  }
@@ -3245,12 +3616,14 @@ class BaseReporter {
3245
3616
  const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
3246
3617
  `);
3247
3618
  if (failedSuites.length) {
3248
- this.error(`${errorBanner(`Failed Suites ${failedSuites.length}`)}
3619
+ this.error(`
3620
+ ${errorBanner(`Failed Suites ${failedSuites.length}`)}
3249
3621
  `);
3250
3622
  this.printTaskErrors(failedSuites, errorDivider);
3251
3623
  }
3252
3624
  if (failedTests.length) {
3253
- this.error(`${errorBanner(`Failed Tests ${failedTests.length}`)}
3625
+ this.error(`
3626
+ ${errorBanner(`Failed Tests ${failedTests.length}`)}
3254
3627
  `);
3255
3628
  this.printTaskErrors(failedTests, errorDivider);
3256
3629
  }
@@ -3309,7 +3682,7 @@ class BaseReporter {
3309
3682
  name += c.dim(` [ ${this.relative(filepath)} ]`);
3310
3683
  }
3311
3684
  this.ctx.logger.error(
3312
- `${c.red(c.bold(c.inverse(" FAIL ")))}${formatProjectName(projectName)} ${name}`
3685
+ `${c.red(c.bold(c.inverse(" FAIL ")))} ${formatProjectName(projectName)}${name}`
3313
3686
  );
3314
3687
  }
3315
3688
  const screenshotPaths = tasks2.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
@@ -3378,9 +3751,9 @@ class WindowRenderer {
3378
3751
  };
3379
3752
  this.cleanups.push(
3380
3753
  this.interceptStream(process.stdout, "output"),
3381
- this.interceptStream(process.stderr, "error")
3754
+ this.interceptStream(process.stderr, "error"),
3755
+ this.addProcessExitListeners()
3382
3756
  );
3383
- restoreCursor();
3384
3757
  this.write(HIDE_CURSOR, "output");
3385
3758
  this.start();
3386
3759
  }
@@ -3402,6 +3775,9 @@ class WindowRenderer {
3402
3775
  this.flushBuffer();
3403
3776
  clearInterval(this.renderInterval);
3404
3777
  }
3778
+ getColumns() {
3779
+ return "columns" in this.options.logger.outputStream ? this.options.logger.outputStream.columns : 80;
3780
+ }
3405
3781
  flushBuffer() {
3406
3782
  if (this.buffer.length === 0) {
3407
3783
  return this.render();
@@ -3429,10 +3805,10 @@ class WindowRenderer {
3429
3805
  return this.write(message || "", type);
3430
3806
  }
3431
3807
  const windowContent = this.options.getWindow();
3432
- const rowCount = getRenderedRowCount(windowContent, this.options.logger.outputStream);
3808
+ const rowCount = getRenderedRowCount(windowContent, this.getColumns());
3433
3809
  let padding = this.windowHeight - rowCount;
3434
3810
  if (padding > 0 && message) {
3435
- padding -= getRenderedRowCount([message], this.options.logger.outputStream);
3811
+ padding -= getRenderedRowCount([message], this.getColumns());
3436
3812
  }
3437
3813
  this.write(SYNC_START);
3438
3814
  this.clearWindow();
@@ -3475,10 +3851,27 @@ class WindowRenderer {
3475
3851
  write(message, type = "output") {
3476
3852
  this.streams[type](message);
3477
3853
  }
3854
+ addProcessExitListeners() {
3855
+ const onExit = (signal, exitCode) => {
3856
+ this.flushBuffer();
3857
+ this.stop();
3858
+ if (process.exitCode === void 0) {
3859
+ process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
3860
+ }
3861
+ process.exit();
3862
+ };
3863
+ process.once("SIGINT", onExit);
3864
+ process.once("SIGTERM", onExit);
3865
+ process.once("exit", onExit);
3866
+ return function cleanup() {
3867
+ process.off("SIGINT", onExit);
3868
+ process.off("SIGTERM", onExit);
3869
+ process.off("exit", onExit);
3870
+ };
3871
+ }
3478
3872
  }
3479
- function getRenderedRowCount(rows, stream) {
3873
+ function getRenderedRowCount(rows, columns) {
3480
3874
  let count = 0;
3481
- const columns = "columns" in stream ? stream.columns : 80;
3482
3875
  for (const row of rows) {
3483
3876
  const text = stripVTControlCharacters(row);
3484
3877
  count += Math.max(1, Math.ceil(text.length / columns));
@@ -3524,7 +3917,7 @@ class TaskParser {
3524
3917
  finishedTestFiles.push(task.file);
3525
3918
  }
3526
3919
  }
3527
- if (task?.type === "test" || task?.type === "custom") {
3920
+ if (task?.type === "test") {
3528
3921
  if (task.result?.state === "run") {
3529
3922
  startingTests.push(task);
3530
3923
  } else if (task.result?.hooks?.afterEach !== "run") {
@@ -3855,139 +4248,131 @@ class DefaultReporter extends BaseReporter {
3855
4248
  }
3856
4249
  }
3857
4250
 
3858
- const check = { char: "\xB7", color: c.green };
3859
- const cross = { char: "x", color: c.red };
4251
+ class DotReporter extends BaseReporter {
4252
+ summary;
4253
+ onInit(ctx) {
4254
+ super.onInit(ctx);
4255
+ if (this.isTTY) {
4256
+ this.summary = new DotSummary();
4257
+ this.summary.onInit(ctx);
4258
+ }
4259
+ }
4260
+ onTaskUpdate(packs) {
4261
+ this.summary?.onTaskUpdate(packs);
4262
+ if (!this.isTTY) {
4263
+ super.onTaskUpdate(packs);
4264
+ }
4265
+ }
4266
+ onWatcherRerun(files, trigger) {
4267
+ this.summary?.onWatcherRerun();
4268
+ super.onWatcherRerun(files, trigger);
4269
+ }
4270
+ onFinished(files, errors) {
4271
+ this.summary?.onFinished();
4272
+ super.onFinished(files, errors);
4273
+ }
4274
+ }
4275
+ class DotSummary extends TaskParser {
4276
+ renderer;
4277
+ tests = /* @__PURE__ */ new Map();
4278
+ finishedTests = /* @__PURE__ */ new Set();
4279
+ onInit(ctx) {
4280
+ this.ctx = ctx;
4281
+ this.renderer = new WindowRenderer({
4282
+ logger: ctx.logger,
4283
+ getWindow: () => this.createSummary()
4284
+ });
4285
+ this.ctx.onClose(() => this.renderer.stop());
4286
+ }
4287
+ onWatcherRerun() {
4288
+ this.tests.clear();
4289
+ this.renderer.start();
4290
+ }
4291
+ onFinished() {
4292
+ const finalLog = formatTests(Array.from(this.tests.values()));
4293
+ this.ctx.logger.log(finalLog);
4294
+ this.tests.clear();
4295
+ this.renderer.finish();
4296
+ }
4297
+ onTestFilePrepare(file) {
4298
+ for (const test of getTests(file)) {
4299
+ this.onTestStart(test);
4300
+ }
4301
+ }
4302
+ onTestStart(test) {
4303
+ if (this.finishedTests.has(test.id)) {
4304
+ return;
4305
+ }
4306
+ this.tests.set(test.id, test.mode || "run");
4307
+ }
4308
+ onTestFinished(test) {
4309
+ if (this.finishedTests.has(test.id)) {
4310
+ return;
4311
+ }
4312
+ this.finishedTests.add(test.id);
4313
+ this.tests.set(test.id, test.result?.state || "skip");
4314
+ }
4315
+ onTestFileFinished() {
4316
+ const columns = this.renderer.getColumns();
4317
+ if (this.tests.size < columns) {
4318
+ return;
4319
+ }
4320
+ const finishedTests = Array.from(this.tests).filter((entry) => entry[1] !== "run");
4321
+ if (finishedTests.length < columns) {
4322
+ return;
4323
+ }
4324
+ const states = [];
4325
+ let count = 0;
4326
+ for (const [id, state] of finishedTests) {
4327
+ if (count++ >= columns) {
4328
+ break;
4329
+ }
4330
+ this.tests.delete(id);
4331
+ states.push(state);
4332
+ }
4333
+ this.ctx.logger.log(formatTests(states));
4334
+ }
4335
+ createSummary() {
4336
+ return [
4337
+ formatTests(Array.from(this.tests.values())),
4338
+ ""
4339
+ ];
4340
+ }
4341
+ }
4342
+ const pass = { char: "\xB7", color: c.green };
4343
+ const fail = { char: "x", color: c.red };
3860
4344
  const pending = { char: "*", color: c.yellow };
3861
4345
  const skip = { char: "-", color: (char) => c.dim(c.gray(char)) };
3862
- function getIcon(task) {
3863
- if (task.mode === "skip" || task.mode === "todo") {
3864
- return skip;
3865
- }
3866
- switch (task.result?.state) {
4346
+ function getIcon(state) {
4347
+ switch (state) {
3867
4348
  case "pass":
3868
- return check;
4349
+ return pass;
3869
4350
  case "fail":
3870
- return cross;
4351
+ return fail;
4352
+ case "skip":
4353
+ case "todo":
4354
+ return skip;
3871
4355
  default:
3872
4356
  return pending;
3873
4357
  }
3874
4358
  }
3875
- function render(tasks, width) {
3876
- const all = getTests(tasks);
4359
+ function formatTests(states) {
3877
4360
  let currentIcon = pending;
3878
- let currentTasks = 0;
3879
- let previousLineWidth = 0;
4361
+ let count = 0;
3880
4362
  let output = "";
3881
- const addOutput = () => {
3882
- const { char, color } = currentIcon;
3883
- const availableWidth = width - previousLineWidth;
3884
- if (availableWidth > currentTasks) {
3885
- output += color(char.repeat(currentTasks));
3886
- previousLineWidth += currentTasks;
3887
- } else {
3888
- let buf = `${char.repeat(availableWidth)}
3889
- `;
3890
- const remaining = currentTasks - availableWidth;
3891
- const fullRows = Math.floor(remaining / width);
3892
- buf += `${char.repeat(width)}
3893
- `.repeat(fullRows);
3894
- const partialRow = remaining % width;
3895
- if (partialRow > 0) {
3896
- buf += char.repeat(partialRow);
3897
- previousLineWidth = partialRow;
3898
- } else {
3899
- previousLineWidth = 0;
3900
- }
3901
- output += color(buf);
3902
- }
3903
- };
3904
- for (const task of all) {
3905
- const icon = getIcon(task);
3906
- if (icon === currentIcon) {
3907
- currentTasks++;
4363
+ for (const state of states) {
4364
+ const icon = getIcon(state);
4365
+ if (currentIcon === icon) {
4366
+ count++;
3908
4367
  continue;
3909
4368
  }
3910
- addOutput();
3911
- currentTasks = 1;
4369
+ output += currentIcon.color(currentIcon.char.repeat(count));
4370
+ count = 1;
3912
4371
  currentIcon = icon;
3913
4372
  }
3914
- addOutput();
4373
+ output += currentIcon.color(currentIcon.char.repeat(count));
3915
4374
  return output;
3916
4375
  }
3917
- function createDotRenderer(_tasks, options) {
3918
- let tasks = _tasks;
3919
- let timer;
3920
- const { logUpdate: log, outputStream } = options.logger;
3921
- const columns = "columns" in outputStream ? outputStream.columns : 80;
3922
- function update() {
3923
- log(render(tasks, columns));
3924
- }
3925
- return {
3926
- start() {
3927
- if (timer) {
3928
- return this;
3929
- }
3930
- timer = setInterval(update, 16);
3931
- return this;
3932
- },
3933
- update(_tasks2) {
3934
- tasks = _tasks2;
3935
- return this;
3936
- },
3937
- async stop() {
3938
- if (timer) {
3939
- clearInterval(timer);
3940
- timer = void 0;
3941
- }
3942
- log.clear();
3943
- options.logger.log(render(tasks, columns));
3944
- return this;
3945
- },
3946
- clear() {
3947
- log.clear();
3948
- }
3949
- };
3950
- }
3951
-
3952
- class DotReporter extends BaseReporter {
3953
- renderer;
3954
- onTaskUpdate() {
3955
- }
3956
- onCollected() {
3957
- if (this.isTTY) {
3958
- const files = this.ctx.state.getFiles(this.watchFilters);
3959
- if (!this.renderer) {
3960
- this.renderer = createDotRenderer(files, {
3961
- logger: this.ctx.logger
3962
- }).start();
3963
- } else {
3964
- this.renderer.update(files);
3965
- }
3966
- }
3967
- }
3968
- async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
3969
- await this.stopListRender();
3970
- this.ctx.logger.log();
3971
- super.onFinished(files, errors);
3972
- }
3973
- async onWatcherStart() {
3974
- await this.stopListRender();
3975
- super.onWatcherStart();
3976
- }
3977
- async stopListRender() {
3978
- this.renderer?.stop();
3979
- this.renderer = void 0;
3980
- await new Promise((resolve) => setTimeout(resolve, 10));
3981
- }
3982
- async onWatcherRerun(files, trigger) {
3983
- await this.stopListRender();
3984
- super.onWatcherRerun(files, trigger);
3985
- }
3986
- onUserConsoleLog(log) {
3987
- this.renderer?.clear();
3988
- super.onUserConsoleLog(log);
3989
- }
3990
- }
3991
4376
 
3992
4377
  class GithubActionsReporter {
3993
4378
  ctx = void 0;
@@ -4443,6 +4828,7 @@ class JUnitReporter {
4443
4828
  (stats2, file) => {
4444
4829
  stats2.tests += file.tasks.length;
4445
4830
  stats2.failures += file.stats.failures;
4831
+ stats2.time += file.result?.duration || 0;
4446
4832
  return stats2;
4447
4833
  },
4448
4834
  {
@@ -4451,10 +4837,10 @@ class JUnitReporter {
4451
4837
  failures: 0,
4452
4838
  errors: 0,
4453
4839
  // we cannot detect those
4454
- time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
4840
+ time: 0
4455
4841
  }
4456
4842
  );
4457
- await this.writeElement("testsuites", stats, async () => {
4843
+ await this.writeElement("testsuites", { ...stats, time: executionTime(stats.time) }, async () => {
4458
4844
  for (const file of transformed) {
4459
4845
  const filename = relative(this.ctx.config.root, file.filepath);
4460
4846
  await this.writeElement(