vitest 2.2.0-beta.1 → 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.
- package/dist/browser.d.ts +8 -8
- package/dist/browser.js +1 -1
- package/dist/chunks/{RandomSequencer.CMRlh2v4.js → RandomSequencer.gisBJ77r.js} +11 -3
- package/dist/chunks/{base.BS0HhLXd.js → base.CkcgFVQd.js} +8 -3
- package/dist/chunks/{benchmark.geERunq4.d.ts → benchmark.CFFwLv-O.d.ts} +2 -2
- package/dist/chunks/{cac.Z91LBqmg.js → cac.CWCZimpS.js} +7 -7
- package/dist/chunks/{cli-api.DVJJMJHj.js → cli-api.BKUOv0Nc.js} +928 -661
- package/dist/chunks/{config.CPguQ7J1.d.ts → config.BTPBhmK5.d.ts} +1 -1
- package/dist/chunks/{creator.IIqd8RWT.js → creator.DcAcUhMD.js} +1 -4
- package/dist/chunks/{globals.BCGEw6ON.js → globals.DJTzb7B3.js} +2 -2
- package/dist/chunks/{index.DD5eTY2y.js → index.BqHViJW9.js} +1 -1
- package/dist/chunks/{index.BjjsHdBb.js → index.CkOJwybT.js} +1 -1
- package/dist/chunks/{index.DLRzErGF.js → index.DKe7vK-G.js} +708 -548
- package/dist/chunks/{index.CqYx2Nsr.js → index.DQboAxJm.js} +23 -14
- package/dist/chunks/{inspector.70d6emsh.js → inspector.DKLceBVD.js} +1 -1
- package/dist/chunks/{reporters.B_9uUTGW.d.ts → reporters.BZbwTvrM.d.ts} +1252 -1234
- package/dist/chunks/{resolveConfig.CQIc6fe7.js → resolveConfig.3rGGWga5.js} +88 -56
- package/dist/chunks/{runBaseTests.B7hcVT-s.js → runBaseTests.C6huCAng.js} +6 -6
- package/dist/chunks/{setup-common.BfGt8K-K.js → setup-common.B5ClyS48.js} +1 -1
- package/dist/chunks/{suite.B2jumIFP.d.ts → suite.BJU7kdY9.d.ts} +4 -4
- package/dist/chunks/{utils.DNoFbBUZ.js → utils.CMUTX-p8.js} +20 -23
- package/dist/chunks/{vi.BlPttogV.js → vi.CZKezqeD.js} +18 -13
- package/dist/chunks/{vite.Bvms8Xir.d.ts → vite.DIfmneq0.d.ts} +1 -1
- package/dist/chunks/{vm.Zr4qWzDJ.js → vm.DGhTouO3.js} +10 -1
- package/dist/chunks/{worker.9VY11NZs.d.ts → worker.CmzGeuVD.d.ts} +3 -3
- package/dist/chunks/{worker.Qz1UB4Fv.d.ts → worker.umPNbBNk.d.ts} +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.cjs +1 -10
- package/dist/config.d.ts +11 -11
- package/dist/config.js +1 -10
- package/dist/coverage.d.ts +7 -7
- package/dist/coverage.js +4 -4
- package/dist/execute.d.ts +3 -3
- package/dist/index.d.ts +26 -17
- package/dist/index.js +2 -2
- package/dist/node.d.ts +16 -20
- package/dist/node.js +7 -7
- package/dist/reporters.d.ts +7 -7
- package/dist/reporters.js +3 -3
- package/dist/runners.d.ts +3 -4
- package/dist/runners.js +9 -14
- package/dist/suite.d.ts +2 -2
- package/dist/worker.js +1 -1
- package/dist/workers/forks.js +1 -1
- package/dist/workers/runVmTests.js +6 -6
- package/dist/workers/threads.js +1 -1
- package/dist/workers/vmForks.js +1 -1
- package/dist/workers/vmThreads.js +1 -1
- package/dist/workers.d.ts +3 -3
- package/dist/workers.js +3 -3
- package/package.json +22 -22
|
@@ -3,12 +3,12 @@ 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, e as getStateString, h as
|
|
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
|
-
import { performance } from 'node:perf_hooks';
|
|
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.
|
|
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
14
|
import { Writable } from 'node:stream';
|
|
@@ -20,68 +20,6 @@ import require$$0$1 from 'events';
|
|
|
20
20
|
import { createRequire } from 'node:module';
|
|
21
21
|
import { hostname } from 'node:os';
|
|
22
22
|
|
|
23
|
-
class TestProject {
|
|
24
|
-
/**
|
|
25
|
-
* The global vitest instance.
|
|
26
|
-
* @experimental The public Vitest API is experimental and does not follow semver.
|
|
27
|
-
*/
|
|
28
|
-
vitest;
|
|
29
|
-
/**
|
|
30
|
-
* The workspace project this test project is associated with.
|
|
31
|
-
* @experimental The public Vitest API is experimental and does not follow semver.
|
|
32
|
-
*/
|
|
33
|
-
workspaceProject;
|
|
34
|
-
/**
|
|
35
|
-
* Vite's dev server instance. Every workspace project has its own server.
|
|
36
|
-
*/
|
|
37
|
-
vite;
|
|
38
|
-
/**
|
|
39
|
-
* Resolved project configuration.
|
|
40
|
-
*/
|
|
41
|
-
config;
|
|
42
|
-
/**
|
|
43
|
-
* Resolved global configuration. If there are no workspace projects, this will be the same as `config`.
|
|
44
|
-
*/
|
|
45
|
-
globalConfig;
|
|
46
|
-
/**
|
|
47
|
-
* The name of the project or an empty string if not set.
|
|
48
|
-
*/
|
|
49
|
-
name;
|
|
50
|
-
constructor(workspaceProject) {
|
|
51
|
-
this.workspaceProject = workspaceProject;
|
|
52
|
-
this.vitest = workspaceProject.ctx;
|
|
53
|
-
this.vite = workspaceProject.server;
|
|
54
|
-
this.globalConfig = workspaceProject.ctx.config;
|
|
55
|
-
this.config = workspaceProject.config;
|
|
56
|
-
this.name = workspaceProject.getName();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Serialized project configuration. This is the config that tests receive.
|
|
60
|
-
*/
|
|
61
|
-
get serializedConfig() {
|
|
62
|
-
return this.workspaceProject.getSerializableConfig();
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Custom context provided to the project.
|
|
66
|
-
*/
|
|
67
|
-
context() {
|
|
68
|
-
return this.workspaceProject.getProvidedContext();
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Provide a custom serializable context to the project. This context will be available for tests once they run.
|
|
72
|
-
*/
|
|
73
|
-
provide(key, value) {
|
|
74
|
-
this.workspaceProject.provide(key, value);
|
|
75
|
-
}
|
|
76
|
-
toJSON() {
|
|
77
|
-
return {
|
|
78
|
-
name: this.name,
|
|
79
|
-
serializedConfig: this.serializedConfig,
|
|
80
|
-
context: this.context()
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
23
|
class ReportedTaskImplementation {
|
|
86
24
|
/**
|
|
87
25
|
* Task instance.
|
|
@@ -104,7 +42,7 @@ class ReportedTaskImplementation {
|
|
|
104
42
|
location;
|
|
105
43
|
constructor(task, project) {
|
|
106
44
|
this.task = task;
|
|
107
|
-
this.project = project
|
|
45
|
+
this.project = project;
|
|
108
46
|
this.id = task.id;
|
|
109
47
|
this.location = task.location;
|
|
110
48
|
}
|
|
@@ -415,10 +353,10 @@ function getTestState(test) {
|
|
|
415
353
|
return result ? result.state : "running";
|
|
416
354
|
}
|
|
417
355
|
function storeTask(project, runnerTask, reportedTask) {
|
|
418
|
-
project.
|
|
356
|
+
project.vitest.state.reportedTasksMap.set(runnerTask, reportedTask);
|
|
419
357
|
}
|
|
420
358
|
function getReportedTask(project, runnerTask) {
|
|
421
|
-
const reportedTask = project.
|
|
359
|
+
const reportedTask = project.vitest.state.getReportedEntity(runnerTask);
|
|
422
360
|
if (!reportedTask) {
|
|
423
361
|
throw new Error(
|
|
424
362
|
`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
|
|
@@ -534,7 +472,7 @@ const stringify = (value, replacer, space) => {
|
|
|
534
472
|
}
|
|
535
473
|
};
|
|
536
474
|
|
|
537
|
-
const ESC$
|
|
475
|
+
const ESC$2 = '\u001B[';
|
|
538
476
|
const OSC = '\u001B]';
|
|
539
477
|
const BEL = '\u0007';
|
|
540
478
|
const SEP = ';';
|
|
@@ -548,10 +486,10 @@ ansiEscapes.cursorTo = (x, y) => {
|
|
|
548
486
|
}
|
|
549
487
|
|
|
550
488
|
if (typeof y !== 'number') {
|
|
551
|
-
return ESC$
|
|
489
|
+
return ESC$2 + (x + 1) + 'G';
|
|
552
490
|
}
|
|
553
491
|
|
|
554
|
-
return ESC$
|
|
492
|
+
return ESC$2 + (y + 1) + ';' + (x + 1) + 'H';
|
|
555
493
|
};
|
|
556
494
|
|
|
557
495
|
ansiEscapes.cursorMove = (x, y) => {
|
|
@@ -562,33 +500,33 @@ ansiEscapes.cursorMove = (x, y) => {
|
|
|
562
500
|
let returnValue = '';
|
|
563
501
|
|
|
564
502
|
if (x < 0) {
|
|
565
|
-
returnValue += ESC$
|
|
503
|
+
returnValue += ESC$2 + (-x) + 'D';
|
|
566
504
|
} else if (x > 0) {
|
|
567
|
-
returnValue += ESC$
|
|
505
|
+
returnValue += ESC$2 + x + 'C';
|
|
568
506
|
}
|
|
569
507
|
|
|
570
508
|
if (y < 0) {
|
|
571
|
-
returnValue += ESC$
|
|
509
|
+
returnValue += ESC$2 + (-y) + 'A';
|
|
572
510
|
} else if (y > 0) {
|
|
573
|
-
returnValue += ESC$
|
|
511
|
+
returnValue += ESC$2 + y + 'B';
|
|
574
512
|
}
|
|
575
513
|
|
|
576
514
|
return returnValue;
|
|
577
515
|
};
|
|
578
516
|
|
|
579
|
-
ansiEscapes.cursorUp = (count = 1) => ESC$
|
|
580
|
-
ansiEscapes.cursorDown = (count = 1) => ESC$
|
|
581
|
-
ansiEscapes.cursorForward = (count = 1) => ESC$
|
|
582
|
-
ansiEscapes.cursorBackward = (count = 1) => ESC$
|
|
517
|
+
ansiEscapes.cursorUp = (count = 1) => ESC$2 + count + 'A';
|
|
518
|
+
ansiEscapes.cursorDown = (count = 1) => ESC$2 + count + 'B';
|
|
519
|
+
ansiEscapes.cursorForward = (count = 1) => ESC$2 + count + 'C';
|
|
520
|
+
ansiEscapes.cursorBackward = (count = 1) => ESC$2 + count + 'D';
|
|
583
521
|
|
|
584
|
-
ansiEscapes.cursorLeft = ESC$
|
|
585
|
-
ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC$
|
|
586
|
-
ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC$
|
|
587
|
-
ansiEscapes.cursorGetPosition = ESC$
|
|
588
|
-
ansiEscapes.cursorNextLine = ESC$
|
|
589
|
-
ansiEscapes.cursorPrevLine = ESC$
|
|
590
|
-
ansiEscapes.cursorHide = ESC$
|
|
591
|
-
ansiEscapes.cursorShow = ESC$
|
|
522
|
+
ansiEscapes.cursorLeft = ESC$2 + 'G';
|
|
523
|
+
ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC$2 + 's';
|
|
524
|
+
ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC$2 + 'u';
|
|
525
|
+
ansiEscapes.cursorGetPosition = ESC$2 + '6n';
|
|
526
|
+
ansiEscapes.cursorNextLine = ESC$2 + 'E';
|
|
527
|
+
ansiEscapes.cursorPrevLine = ESC$2 + 'F';
|
|
528
|
+
ansiEscapes.cursorHide = ESC$2 + '?25l';
|
|
529
|
+
ansiEscapes.cursorShow = ESC$2 + '?25h';
|
|
592
530
|
|
|
593
531
|
ansiEscapes.eraseLines = count => {
|
|
594
532
|
let clear = '';
|
|
@@ -604,24 +542,24 @@ ansiEscapes.eraseLines = count => {
|
|
|
604
542
|
return clear;
|
|
605
543
|
};
|
|
606
544
|
|
|
607
|
-
ansiEscapes.eraseEndLine = ESC$
|
|
608
|
-
ansiEscapes.eraseStartLine = ESC$
|
|
609
|
-
ansiEscapes.eraseLine = ESC$
|
|
610
|
-
ansiEscapes.eraseDown = ESC$
|
|
611
|
-
ansiEscapes.eraseUp = ESC$
|
|
612
|
-
ansiEscapes.eraseScreen = ESC$
|
|
613
|
-
ansiEscapes.scrollUp = ESC$
|
|
614
|
-
ansiEscapes.scrollDown = ESC$
|
|
545
|
+
ansiEscapes.eraseEndLine = ESC$2 + 'K';
|
|
546
|
+
ansiEscapes.eraseStartLine = ESC$2 + '1K';
|
|
547
|
+
ansiEscapes.eraseLine = ESC$2 + '2K';
|
|
548
|
+
ansiEscapes.eraseDown = ESC$2 + 'J';
|
|
549
|
+
ansiEscapes.eraseUp = ESC$2 + '1J';
|
|
550
|
+
ansiEscapes.eraseScreen = ESC$2 + '2J';
|
|
551
|
+
ansiEscapes.scrollUp = ESC$2 + 'S';
|
|
552
|
+
ansiEscapes.scrollDown = ESC$2 + 'T';
|
|
615
553
|
|
|
616
554
|
ansiEscapes.clearScreen = '\u001Bc';
|
|
617
555
|
|
|
618
556
|
ansiEscapes.clearTerminal = process.platform === 'win32' ?
|
|
619
|
-
`${ansiEscapes.eraseScreen}${ESC$
|
|
557
|
+
`${ansiEscapes.eraseScreen}${ESC$2}0f` :
|
|
620
558
|
// 1. Erases the screen (Only done in case `2` is not supported)
|
|
621
559
|
// 2. Erases the whole screen including scrollback buffer
|
|
622
560
|
// 3. Moves cursor to the top-left position
|
|
623
561
|
// More info: https://www.real-world-systems.com/docs/ANSIcode.html
|
|
624
|
-
`${ansiEscapes.eraseScreen}${ESC$
|
|
562
|
+
`${ansiEscapes.eraseScreen}${ESC$2}3J${ESC$2}H`;
|
|
625
563
|
|
|
626
564
|
ansiEscapes.beep = BEL;
|
|
627
565
|
|
|
@@ -3026,10 +2964,10 @@ function lineNo(no = "") {
|
|
|
3026
2964
|
}
|
|
3027
2965
|
|
|
3028
2966
|
const PAD = " ";
|
|
3029
|
-
const ESC = "\x1B[";
|
|
3030
|
-
const ERASE_DOWN = `${ESC}J`;
|
|
3031
|
-
const ERASE_SCROLLBACK = `${ESC}3J`;
|
|
3032
|
-
const CURSOR_TO_START = `${ESC}1;1H`;
|
|
2967
|
+
const ESC$1 = "\x1B[";
|
|
2968
|
+
const ERASE_DOWN = `${ESC$1}J`;
|
|
2969
|
+
const ERASE_SCROLLBACK = `${ESC$1}3J`;
|
|
2970
|
+
const CURSOR_TO_START = `${ESC$1}1;1H`;
|
|
3033
2971
|
const CLEAR_SCREEN = "\x1Bc";
|
|
3034
2972
|
class Logger {
|
|
3035
2973
|
constructor(ctx, outputStream = process.stdout, errorStream = process.stderr) {
|
|
@@ -3088,7 +3026,7 @@ class Logger {
|
|
|
3088
3026
|
}
|
|
3089
3027
|
printError(err, options = {}) {
|
|
3090
3028
|
const { fullStack = false, type } = options;
|
|
3091
|
-
const project = options.project ?? this.ctx.
|
|
3029
|
+
const project = options.project ?? this.ctx.coreWorkspaceProject ?? this.ctx.projects[0];
|
|
3092
3030
|
return printError(err, project, {
|
|
3093
3031
|
type,
|
|
3094
3032
|
showCodeFrame: options.showCodeFrame ?? true,
|
|
@@ -3137,8 +3075,7 @@ class Logger {
|
|
|
3137
3075
|
}
|
|
3138
3076
|
this.ctx.projects.forEach((project) => {
|
|
3139
3077
|
const config2 = project.config;
|
|
3140
|
-
const
|
|
3141
|
-
const output = project.isCore() || !name ? "" : `[${name}]`;
|
|
3078
|
+
const output = project.isRootProject() || !project.name ? "" : `[${project.name}]`;
|
|
3142
3079
|
if (output) {
|
|
3143
3080
|
this.console.error(c.bgCyan(`${output} Config`));
|
|
3144
3081
|
}
|
|
@@ -3214,8 +3151,7 @@ Vitest is running in standalone mode. Edit a test file to rerun tests.`));
|
|
|
3214
3151
|
if (!origin) {
|
|
3215
3152
|
return;
|
|
3216
3153
|
}
|
|
3217
|
-
const
|
|
3218
|
-
const output = project.isCore() ? "" : formatProjectName(name);
|
|
3154
|
+
const output = project.isRootProject() ? "" : formatProjectName(project.name);
|
|
3219
3155
|
const provider = project.browser.provider.name;
|
|
3220
3156
|
const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
|
|
3221
3157
|
this.log(
|
|
@@ -3233,15 +3169,15 @@ Vitest caught ${errors.length} unhandled error${errors.length > 1 ? "s" : ""} du
|
|
|
3233
3169
|
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.`
|
|
3234
3170
|
)
|
|
3235
3171
|
);
|
|
3236
|
-
this.
|
|
3237
|
-
this.
|
|
3172
|
+
this.error(c.red(divider(c.bold(c.inverse(" Unhandled Errors ")))));
|
|
3173
|
+
this.error(errorMessage);
|
|
3238
3174
|
errors.forEach((err) => {
|
|
3239
3175
|
this.printError(err, {
|
|
3240
3176
|
fullStack: true,
|
|
3241
3177
|
type: err.type || "Unhandled Error"
|
|
3242
3178
|
});
|
|
3243
3179
|
});
|
|
3244
|
-
this.
|
|
3180
|
+
this.error(c.red(divider()));
|
|
3245
3181
|
}
|
|
3246
3182
|
printSourceTypeErrors(errors) {
|
|
3247
3183
|
const errorMessage = c.red(
|
|
@@ -3295,8 +3231,8 @@ class BlobReporter {
|
|
|
3295
3231
|
const modules = this.ctx.projects.map(
|
|
3296
3232
|
(project) => {
|
|
3297
3233
|
return [
|
|
3298
|
-
project.
|
|
3299
|
-
[...project.
|
|
3234
|
+
project.name,
|
|
3235
|
+
[...project.vite.moduleGraph.idToModuleMap.entries()].map((mod) => {
|
|
3300
3236
|
if (!mod[1].file) {
|
|
3301
3237
|
return null;
|
|
3302
3238
|
}
|
|
@@ -3363,7 +3299,7 @@ ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
|
|
|
3363
3299
|
);
|
|
3364
3300
|
}
|
|
3365
3301
|
const projects = Object.fromEntries(
|
|
3366
|
-
projectsArray.map((p) => [p.
|
|
3302
|
+
projectsArray.map((p) => [p.name, p])
|
|
3367
3303
|
);
|
|
3368
3304
|
blobs.forEach((blob) => {
|
|
3369
3305
|
blob.moduleKeys.forEach(([projectName, moduleIds]) => {
|
|
@@ -3372,10 +3308,10 @@ ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
|
|
|
3372
3308
|
return;
|
|
3373
3309
|
}
|
|
3374
3310
|
moduleIds.forEach(([moduleId, file, url]) => {
|
|
3375
|
-
const moduleNode = project.
|
|
3311
|
+
const moduleNode = project.vite.moduleGraph.createFileOnlyEntry(file);
|
|
3376
3312
|
moduleNode.url = url;
|
|
3377
3313
|
moduleNode.id = moduleId;
|
|
3378
|
-
project.
|
|
3314
|
+
project.vite.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
|
|
3379
3315
|
});
|
|
3380
3316
|
});
|
|
3381
3317
|
});
|
|
@@ -3402,7 +3338,6 @@ function hasFailedSnapshot(suite) {
|
|
|
3402
3338
|
}
|
|
3403
3339
|
|
|
3404
3340
|
const BADGE_PADDING = " ";
|
|
3405
|
-
const LAST_RUN_LOG_TIMEOUT = 1500;
|
|
3406
3341
|
class BaseReporter {
|
|
3407
3342
|
start = 0;
|
|
3408
3343
|
end = 0;
|
|
@@ -3410,19 +3345,17 @@ class BaseReporter {
|
|
|
3410
3345
|
failedUnwatchedFiles = [];
|
|
3411
3346
|
isTTY;
|
|
3412
3347
|
ctx = void 0;
|
|
3348
|
+
renderSucceed = false;
|
|
3413
3349
|
verbose = false;
|
|
3414
3350
|
_filesInWatchMode = /* @__PURE__ */ new Map();
|
|
3415
3351
|
_timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
3416
|
-
_lastRunTimeout = 0;
|
|
3417
|
-
_lastRunTimer;
|
|
3418
|
-
_lastRunCount = 0;
|
|
3419
3352
|
constructor(options = {}) {
|
|
3420
3353
|
this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI);
|
|
3421
3354
|
}
|
|
3422
3355
|
onInit(ctx) {
|
|
3423
3356
|
this.ctx = ctx;
|
|
3424
3357
|
this.ctx.logger.printBanner();
|
|
3425
|
-
this.start = performance.now();
|
|
3358
|
+
this.start = performance$1.now();
|
|
3426
3359
|
}
|
|
3427
3360
|
log(...messages) {
|
|
3428
3361
|
this.ctx.logger.log(...messages);
|
|
@@ -3434,13 +3367,10 @@ class BaseReporter {
|
|
|
3434
3367
|
return relative(this.ctx.config.root, path);
|
|
3435
3368
|
}
|
|
3436
3369
|
onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
3437
|
-
this.end = performance.now();
|
|
3370
|
+
this.end = performance$1.now();
|
|
3438
3371
|
this.reportSummary(files, errors);
|
|
3439
3372
|
}
|
|
3440
3373
|
onTaskUpdate(packs) {
|
|
3441
|
-
if (this.isTTY) {
|
|
3442
|
-
return;
|
|
3443
|
-
}
|
|
3444
3374
|
for (const pack of packs) {
|
|
3445
3375
|
const task = this.ctx.state.idMap.get(pack[0]);
|
|
3446
3376
|
if (task) {
|
|
@@ -3474,18 +3404,29 @@ class BaseReporter {
|
|
|
3474
3404
|
title += ` ${formatProjectName(task.projectName, "")}`;
|
|
3475
3405
|
}
|
|
3476
3406
|
this.log(` ${title} ${task.name} ${suffix}`);
|
|
3407
|
+
const anyFailed = tests.some((test) => test.result?.state === "fail");
|
|
3477
3408
|
for (const test of tests) {
|
|
3478
|
-
const duration = test.result
|
|
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
|
+
}
|
|
3479
3417
|
if (test.result?.state === "fail") {
|
|
3480
|
-
|
|
3481
|
-
this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${suffix2}`));
|
|
3418
|
+
this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${this.getDurationPrefix(test)}`) + suffix2);
|
|
3482
3419
|
test.result?.errors?.forEach((e) => {
|
|
3483
3420
|
this.log(c.red(` ${F_RIGHT} ${e?.message}`));
|
|
3484
3421
|
});
|
|
3485
3422
|
} else if (duration && duration > this.ctx.config.slowTestThreshold) {
|
|
3486
3423
|
this.log(
|
|
3487
|
-
` ${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}`
|
|
3488
3425
|
);
|
|
3426
|
+
} else if (this.ctx.config.hideSkippedTests && (test.mode === "skip" || test.result?.state === "skip")) ; else if (test.result?.state === "skip" && test.result.note) {
|
|
3427
|
+
this.log(` ${getStateSymbol(test)} ${getTestName(test)}${c.dim(c.gray(` [${test.result.note}]`))}`);
|
|
3428
|
+
} else if (this.renderSucceed || anyFailed) {
|
|
3429
|
+
this.log(` ${c.dim(getStateSymbol(test))} ${getTestName(test, c.dim(" > "))}${suffix2}`);
|
|
3489
3430
|
}
|
|
3490
3431
|
}
|
|
3491
3432
|
}
|
|
@@ -3497,7 +3438,6 @@ class BaseReporter {
|
|
|
3497
3438
|
return color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
|
|
3498
3439
|
}
|
|
3499
3440
|
onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
3500
|
-
this.resetLastRunLog();
|
|
3501
3441
|
const failed = errors.length > 0 || hasFailed(files);
|
|
3502
3442
|
if (failed) {
|
|
3503
3443
|
this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
|
|
@@ -3513,34 +3453,8 @@ class BaseReporter {
|
|
|
3513
3453
|
hints.push(c.dim("press ") + c.bold("q") + c.dim(" to quit"));
|
|
3514
3454
|
}
|
|
3515
3455
|
this.log(BADGE_PADDING + hints.join(c.dim(", ")));
|
|
3516
|
-
if (this._lastRunCount) {
|
|
3517
|
-
const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`;
|
|
3518
|
-
const LAST_RUN_TEXTS = [
|
|
3519
|
-
c.blue(LAST_RUN_TEXT),
|
|
3520
|
-
c.gray(LAST_RUN_TEXT),
|
|
3521
|
-
c.dim(c.gray(LAST_RUN_TEXT))
|
|
3522
|
-
];
|
|
3523
|
-
this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]);
|
|
3524
|
-
this._lastRunTimeout = 0;
|
|
3525
|
-
this._lastRunTimer = setInterval(() => {
|
|
3526
|
-
this._lastRunTimeout += 1;
|
|
3527
|
-
if (this._lastRunTimeout >= LAST_RUN_TEXTS.length) {
|
|
3528
|
-
this.resetLastRunLog();
|
|
3529
|
-
} else {
|
|
3530
|
-
this.ctx.logger.logUpdate(
|
|
3531
|
-
BADGE_PADDING + LAST_RUN_TEXTS[this._lastRunTimeout]
|
|
3532
|
-
);
|
|
3533
|
-
}
|
|
3534
|
-
}, LAST_RUN_LOG_TIMEOUT / LAST_RUN_TEXTS.length);
|
|
3535
|
-
}
|
|
3536
|
-
}
|
|
3537
|
-
resetLastRunLog() {
|
|
3538
|
-
clearInterval(this._lastRunTimer);
|
|
3539
|
-
this._lastRunTimer = void 0;
|
|
3540
|
-
this.ctx.logger.logUpdate.clear();
|
|
3541
3456
|
}
|
|
3542
3457
|
onWatcherRerun(files, trigger) {
|
|
3543
|
-
this.resetLastRunLog();
|
|
3544
3458
|
this.watchFilters = files;
|
|
3545
3459
|
this.failedUnwatchedFiles = this.ctx.state.getFiles().filter(
|
|
3546
3460
|
(file) => !files.includes(file.filepath) && hasFailed(file)
|
|
@@ -3550,9 +3464,7 @@ class BaseReporter {
|
|
|
3550
3464
|
this._filesInWatchMode.set(filepath, ++reruns);
|
|
3551
3465
|
});
|
|
3552
3466
|
let banner = trigger ? c.dim(`${this.relative(trigger)} `) : "";
|
|
3553
|
-
if (files.length
|
|
3554
|
-
this._lastRunCount = 0;
|
|
3555
|
-
} else if (files.length === 1) {
|
|
3467
|
+
if (files.length === 1) {
|
|
3556
3468
|
const rerun = this._filesInWatchMode.get(files[0]) ?? 1;
|
|
3557
3469
|
banner += c.blue(`x${rerun} `);
|
|
3558
3470
|
}
|
|
@@ -3568,13 +3480,11 @@ class BaseReporter {
|
|
|
3568
3480
|
this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
|
|
3569
3481
|
}
|
|
3570
3482
|
this.log("");
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
this.printTask(task);
|
|
3574
|
-
}
|
|
3483
|
+
for (const task of this.failedUnwatchedFiles) {
|
|
3484
|
+
this.printTask(task);
|
|
3575
3485
|
}
|
|
3576
3486
|
this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
3577
|
-
this.start = performance.now();
|
|
3487
|
+
this.start = performance$1.now();
|
|
3578
3488
|
}
|
|
3579
3489
|
onUserConsoleLog(log) {
|
|
3580
3490
|
if (!this.shouldLog(log)) {
|
|
@@ -3595,7 +3505,7 @@ class BaseReporter {
|
|
|
3595
3505
|
if (log.browser) {
|
|
3596
3506
|
write("\n");
|
|
3597
3507
|
}
|
|
3598
|
-
const project = log.taskId ? this.ctx.getProjectByTaskId(log.taskId) : this.ctx.
|
|
3508
|
+
const project = log.taskId ? this.ctx.getProjectByTaskId(log.taskId) : this.ctx.getRootTestProject();
|
|
3599
3509
|
const stack = log.browser ? project.browser?.parseStacktrace(log.origin) || [] : parseStacktrace(log.origin);
|
|
3600
3510
|
const highlight = task && stack.find((i) => i.file === task.file.filepath);
|
|
3601
3511
|
for (const frame of stack) {
|
|
@@ -3639,6 +3549,7 @@ class BaseReporter {
|
|
|
3639
3549
|
}
|
|
3640
3550
|
}
|
|
3641
3551
|
reportTestSummary(files, errors) {
|
|
3552
|
+
this.log();
|
|
3642
3553
|
const affectedFiles = [
|
|
3643
3554
|
...this.failedUnwatchedFiles,
|
|
3644
3555
|
...files
|
|
@@ -3650,32 +3561,32 @@ class BaseReporter {
|
|
|
3650
3561
|
);
|
|
3651
3562
|
for (const [index, snapshot] of snapshotOutput.entries()) {
|
|
3652
3563
|
const title = index === 0 ? "Snapshots" : "";
|
|
3653
|
-
this.log(`${
|
|
3564
|
+
this.log(`${padSummaryTitle(title)} ${snapshot}`);
|
|
3654
3565
|
}
|
|
3655
3566
|
if (snapshotOutput.length > 1) {
|
|
3656
3567
|
this.log();
|
|
3657
3568
|
}
|
|
3658
|
-
this.log(
|
|
3659
|
-
this.log(
|
|
3569
|
+
this.log(padSummaryTitle("Test Files"), getStateString$1(affectedFiles));
|
|
3570
|
+
this.log(padSummaryTitle("Tests"), getStateString$1(tests));
|
|
3660
3571
|
if (this.ctx.projects.some((c2) => c2.config.typecheck.enabled)) {
|
|
3661
3572
|
const failed = tests.filter((t) => t.meta?.typecheck && t.result?.errors?.length);
|
|
3662
3573
|
this.log(
|
|
3663
|
-
|
|
3574
|
+
padSummaryTitle("Type Errors"),
|
|
3664
3575
|
failed.length ? c.bold(c.red(`${failed.length} failed`)) : c.dim("no errors")
|
|
3665
3576
|
);
|
|
3666
3577
|
}
|
|
3667
3578
|
if (errors.length) {
|
|
3668
3579
|
this.log(
|
|
3669
|
-
|
|
3580
|
+
padSummaryTitle("Errors"),
|
|
3670
3581
|
c.bold(c.red(`${errors.length} error${errors.length > 1 ? "s" : ""}`))
|
|
3671
3582
|
);
|
|
3672
3583
|
}
|
|
3673
|
-
this.log(
|
|
3584
|
+
this.log(padSummaryTitle("Start at"), this._timeStart);
|
|
3674
3585
|
const collectTime = sum(files, (file) => file.collectDuration);
|
|
3675
3586
|
const testsTime = sum(files, (file) => file.result?.duration);
|
|
3676
3587
|
const setupTime = sum(files, (file) => file.setupDuration);
|
|
3677
3588
|
if (this.watchFilters) {
|
|
3678
|
-
this.log(
|
|
3589
|
+
this.log(padSummaryTitle("Duration"), formatTime(collectTime + testsTime + setupTime));
|
|
3679
3590
|
} else {
|
|
3680
3591
|
const executionTime = this.end - this.start;
|
|
3681
3592
|
const environmentTime = sum(files, (file) => file.environmentLoad);
|
|
@@ -3683,15 +3594,15 @@ class BaseReporter {
|
|
|
3683
3594
|
const transformTime = sum(this.ctx.projects, (project) => project.vitenode.getTotalDuration());
|
|
3684
3595
|
const typecheck = sum(this.ctx.projects, (project) => project.typechecker?.getResult().time);
|
|
3685
3596
|
const timers = [
|
|
3686
|
-
`transform ${
|
|
3687
|
-
`setup ${
|
|
3688
|
-
`collect ${
|
|
3689
|
-
`tests ${
|
|
3690
|
-
`environment ${
|
|
3691
|
-
`prepare ${
|
|
3692
|
-
typecheck && `typecheck ${
|
|
3597
|
+
`transform ${formatTime(transformTime)}`,
|
|
3598
|
+
`setup ${formatTime(setupTime)}`,
|
|
3599
|
+
`collect ${formatTime(collectTime)}`,
|
|
3600
|
+
`tests ${formatTime(testsTime)}`,
|
|
3601
|
+
`environment ${formatTime(environmentTime)}`,
|
|
3602
|
+
`prepare ${formatTime(prepareTime)}`,
|
|
3603
|
+
typecheck && `typecheck ${formatTime(typecheck)}`
|
|
3693
3604
|
].filter(Boolean).join(", ");
|
|
3694
|
-
this.log(
|
|
3605
|
+
this.log(padSummaryTitle("Duration"), formatTime(executionTime) + c.dim(` (${timers})`));
|
|
3695
3606
|
}
|
|
3696
3607
|
this.log();
|
|
3697
3608
|
}
|
|
@@ -3705,12 +3616,14 @@ class BaseReporter {
|
|
|
3705
3616
|
const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
|
|
3706
3617
|
`);
|
|
3707
3618
|
if (failedSuites.length) {
|
|
3708
|
-
this.error(
|
|
3619
|
+
this.error(`
|
|
3620
|
+
${errorBanner(`Failed Suites ${failedSuites.length}`)}
|
|
3709
3621
|
`);
|
|
3710
3622
|
this.printTaskErrors(failedSuites, errorDivider);
|
|
3711
3623
|
}
|
|
3712
3624
|
if (failedTests.length) {
|
|
3713
|
-
this.error(
|
|
3625
|
+
this.error(`
|
|
3626
|
+
${errorBanner(`Failed Tests ${failedTests.length}`)}
|
|
3714
3627
|
`);
|
|
3715
3628
|
this.printTaskErrors(failedTests, errorDivider);
|
|
3716
3629
|
}
|
|
@@ -3769,7 +3682,7 @@ class BaseReporter {
|
|
|
3769
3682
|
name += c.dim(` [ ${this.relative(filepath)} ]`);
|
|
3770
3683
|
}
|
|
3771
3684
|
this.ctx.logger.error(
|
|
3772
|
-
`${c.red(c.bold(c.inverse(" FAIL ")))}${formatProjectName(projectName)}
|
|
3685
|
+
`${c.red(c.bold(c.inverse(" FAIL ")))} ${formatProjectName(projectName)}${name}`
|
|
3773
3686
|
);
|
|
3774
3687
|
}
|
|
3775
3688
|
const screenshotPaths = tasks2.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
|
|
@@ -3786,15 +3699,6 @@ class BaseReporter {
|
|
|
3786
3699
|
function errorBanner(message) {
|
|
3787
3700
|
return c.red(divider(c.bold(c.inverse(` ${message} `))));
|
|
3788
3701
|
}
|
|
3789
|
-
function padTitle(str) {
|
|
3790
|
-
return c.dim(`${str.padStart(11)} `);
|
|
3791
|
-
}
|
|
3792
|
-
function time(time2) {
|
|
3793
|
-
if (time2 > 1e3) {
|
|
3794
|
-
return `${(time2 / 1e3).toFixed(2)}s`;
|
|
3795
|
-
}
|
|
3796
|
-
return `${Math.round(time2)}ms`;
|
|
3797
|
-
}
|
|
3798
3702
|
function sum(items, cb) {
|
|
3799
3703
|
return items.reduce((total, next) => {
|
|
3800
3704
|
return total + Math.max(cb(next) || 0, 0);
|
|
@@ -3806,423 +3710,669 @@ class BasicReporter extends BaseReporter {
|
|
|
3806
3710
|
super();
|
|
3807
3711
|
this.isTTY = false;
|
|
3808
3712
|
}
|
|
3713
|
+
onInit(ctx) {
|
|
3714
|
+
super.onInit(ctx);
|
|
3715
|
+
ctx.logger.log(c.inverse(c.bold(c.yellow(" DEPRECATED "))), c.yellow(
|
|
3716
|
+
`'basic' reporter is deprecated and will be removed in Vitest v3.
|
|
3717
|
+
Remove 'basic' from 'reporters' option. To match 'basic' reporter 100%, use configuration:
|
|
3718
|
+
${JSON.stringify({ test: { reporters: [["default", { summary: false }]] } }, null, 2)}`
|
|
3719
|
+
));
|
|
3720
|
+
}
|
|
3809
3721
|
reportSummary(files, errors) {
|
|
3810
3722
|
this.ctx.logger.log();
|
|
3811
3723
|
return super.reportSummary(files, errors);
|
|
3812
3724
|
}
|
|
3813
3725
|
}
|
|
3814
3726
|
|
|
3815
|
-
const
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3727
|
+
const DEFAULT_RENDER_INTERVAL = 16;
|
|
3728
|
+
const ESC = "\x1B[";
|
|
3729
|
+
const CLEAR_LINE = `${ESC}K`;
|
|
3730
|
+
const MOVE_CURSOR_ONE_ROW_UP = `${ESC}1A`;
|
|
3731
|
+
const HIDE_CURSOR = `${ESC}?25l`;
|
|
3732
|
+
const SHOW_CURSOR = `${ESC}?25h`;
|
|
3733
|
+
const SYNC_START = `${ESC}?2026h`;
|
|
3734
|
+
const SYNC_END = `${ESC}?2026l`;
|
|
3735
|
+
class WindowRenderer {
|
|
3736
|
+
options;
|
|
3737
|
+
streams;
|
|
3738
|
+
buffer = [];
|
|
3739
|
+
renderInterval = void 0;
|
|
3740
|
+
windowHeight = 0;
|
|
3741
|
+
finished = false;
|
|
3742
|
+
cleanups = [];
|
|
3743
|
+
constructor(options) {
|
|
3744
|
+
this.options = {
|
|
3745
|
+
interval: DEFAULT_RENDER_INTERVAL,
|
|
3746
|
+
...options
|
|
3747
|
+
};
|
|
3748
|
+
this.streams = {
|
|
3749
|
+
output: options.logger.outputStream.write.bind(options.logger.outputStream),
|
|
3750
|
+
error: options.logger.errorStream.write.bind(options.logger.errorStream)
|
|
3751
|
+
};
|
|
3752
|
+
this.cleanups.push(
|
|
3753
|
+
this.interceptStream(process.stdout, "output"),
|
|
3754
|
+
this.interceptStream(process.stderr, "error"),
|
|
3755
|
+
this.addProcessExitListeners()
|
|
3756
|
+
);
|
|
3757
|
+
this.write(HIDE_CURSOR, "output");
|
|
3758
|
+
this.start();
|
|
3822
3759
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
}
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
return [
|
|
3861
|
-
padded[0],
|
|
3862
|
-
// name
|
|
3863
|
-
c.dim(" "),
|
|
3864
|
-
c.blue(padded[1]),
|
|
3865
|
-
c.dim(" ops/sec "),
|
|
3866
|
-
c.cyan(padded[3]),
|
|
3867
|
-
c.dim(` (${padded[4]} samples)`),
|
|
3868
|
-
result.rank === 1 ? c.bold(c.green(" fastest")) : result.rank === benches.length && benches.length > 2 ? c.bold(c.gray(" slowest")) : ""
|
|
3869
|
-
].join("");
|
|
3870
|
-
}
|
|
3871
|
-
function renderTree$1(tasks, options, level = 0, maxRows) {
|
|
3872
|
-
const output = [];
|
|
3873
|
-
let currentRowCount = 0;
|
|
3874
|
-
for (const task of [...tasks].reverse()) {
|
|
3875
|
-
const taskOutput = [];
|
|
3876
|
-
let suffix = "";
|
|
3877
|
-
let prefix = ` ${getStateSymbol(task)} `;
|
|
3878
|
-
if (level === 0 && task.type === "suite" && "projectName" in task) {
|
|
3879
|
-
prefix += formatProjectName(task.projectName);
|
|
3880
|
-
}
|
|
3881
|
-
if (level === 0 && task.type === "suite" && task.meta.typecheck) {
|
|
3882
|
-
prefix += c.bgBlue(c.bold(" TS "));
|
|
3883
|
-
prefix += " ";
|
|
3760
|
+
start() {
|
|
3761
|
+
this.finished = false;
|
|
3762
|
+
this.renderInterval = setInterval(() => this.flushBuffer(), this.options.interval);
|
|
3763
|
+
}
|
|
3764
|
+
stop() {
|
|
3765
|
+
this.write(SHOW_CURSOR, "output");
|
|
3766
|
+
this.cleanups.splice(0).map((fn) => fn());
|
|
3767
|
+
clearInterval(this.renderInterval);
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Write all buffered output and stop buffering.
|
|
3771
|
+
* All intercepted writes are forwarded to actual write after this.
|
|
3772
|
+
*/
|
|
3773
|
+
finish() {
|
|
3774
|
+
this.finished = true;
|
|
3775
|
+
this.flushBuffer();
|
|
3776
|
+
clearInterval(this.renderInterval);
|
|
3777
|
+
}
|
|
3778
|
+
getColumns() {
|
|
3779
|
+
return "columns" in this.options.logger.outputStream ? this.options.logger.outputStream.columns : 80;
|
|
3780
|
+
}
|
|
3781
|
+
flushBuffer() {
|
|
3782
|
+
if (this.buffer.length === 0) {
|
|
3783
|
+
return this.render();
|
|
3784
|
+
}
|
|
3785
|
+
let current;
|
|
3786
|
+
for (const next of this.buffer.splice(0)) {
|
|
3787
|
+
if (!current) {
|
|
3788
|
+
current = next;
|
|
3789
|
+
continue;
|
|
3790
|
+
}
|
|
3791
|
+
if (current.type !== next.type) {
|
|
3792
|
+
this.render(current.message, current.type);
|
|
3793
|
+
current = next;
|
|
3794
|
+
continue;
|
|
3795
|
+
}
|
|
3796
|
+
current.message += next.message;
|
|
3884
3797
|
}
|
|
3885
|
-
if (
|
|
3886
|
-
|
|
3798
|
+
if (current) {
|
|
3799
|
+
this.render(current?.message, current?.type);
|
|
3887
3800
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3801
|
+
}
|
|
3802
|
+
render(message, type = "output") {
|
|
3803
|
+
if (this.finished) {
|
|
3804
|
+
this.clearWindow();
|
|
3805
|
+
return this.write(message || "", type);
|
|
3891
3806
|
}
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3807
|
+
const windowContent = this.options.getWindow();
|
|
3808
|
+
const rowCount = getRenderedRowCount(windowContent, this.getColumns());
|
|
3809
|
+
let padding = this.windowHeight - rowCount;
|
|
3810
|
+
if (padding > 0 && message) {
|
|
3811
|
+
padding -= getRenderedRowCount([message], this.getColumns());
|
|
3895
3812
|
}
|
|
3896
|
-
|
|
3897
|
-
|
|
3813
|
+
this.write(SYNC_START);
|
|
3814
|
+
this.clearWindow();
|
|
3815
|
+
if (message) {
|
|
3816
|
+
this.write(message, type);
|
|
3898
3817
|
}
|
|
3899
|
-
if (
|
|
3900
|
-
|
|
3901
|
-
suffix += c.yellow(
|
|
3902
|
-
` ${Math.round(task.result.duration)}${c.dim("ms")}`
|
|
3903
|
-
);
|
|
3904
|
-
}
|
|
3818
|
+
if (padding > 0) {
|
|
3819
|
+
this.write("\n".repeat(padding));
|
|
3905
3820
|
}
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3821
|
+
this.write(windowContent.join("\n"));
|
|
3822
|
+
this.write(SYNC_END);
|
|
3823
|
+
this.windowHeight = rowCount + Math.max(0, padding);
|
|
3824
|
+
}
|
|
3825
|
+
clearWindow() {
|
|
3826
|
+
if (this.windowHeight === 0) {
|
|
3827
|
+
return;
|
|
3910
3828
|
}
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3829
|
+
this.write(CLEAR_LINE);
|
|
3830
|
+
for (let i = 1; i < this.windowHeight; i++) {
|
|
3831
|
+
this.write(`${MOVE_CURSOR_ONE_ROW_UP}${CLEAR_LINE}`);
|
|
3914
3832
|
}
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
if (
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3833
|
+
this.windowHeight = 0;
|
|
3834
|
+
}
|
|
3835
|
+
interceptStream(stream, type) {
|
|
3836
|
+
const original = stream.write;
|
|
3837
|
+
stream.write = (chunk, _, callback) => {
|
|
3838
|
+
if (chunk) {
|
|
3839
|
+
if (this.finished) {
|
|
3840
|
+
this.write(chunk.toString(), type);
|
|
3841
|
+
} else {
|
|
3842
|
+
this.buffer.push({ type, message: chunk.toString() });
|
|
3924
3843
|
}
|
|
3925
3844
|
}
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3845
|
+
callback?.();
|
|
3846
|
+
};
|
|
3847
|
+
return function restore() {
|
|
3848
|
+
stream.write = original;
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
write(message, type = "output") {
|
|
3852
|
+
this.streams[type](message);
|
|
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);
|
|
3929
3860
|
}
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
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
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
function getRenderedRowCount(rows, columns) {
|
|
3874
|
+
let count = 0;
|
|
3875
|
+
for (const row of rows) {
|
|
3876
|
+
const text = stripVTControlCharacters(row);
|
|
3877
|
+
count += Math.max(1, Math.ceil(text.length / columns));
|
|
3878
|
+
}
|
|
3879
|
+
return count;
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3882
|
+
class TaskParser {
|
|
3883
|
+
ctx;
|
|
3884
|
+
onInit(ctx) {
|
|
3885
|
+
this.ctx = ctx;
|
|
3886
|
+
}
|
|
3887
|
+
onHookStart(_options) {
|
|
3888
|
+
}
|
|
3889
|
+
onHookEnd(_options) {
|
|
3890
|
+
}
|
|
3891
|
+
onTestStart(_test) {
|
|
3892
|
+
}
|
|
3893
|
+
onTestFinished(_test) {
|
|
3894
|
+
}
|
|
3895
|
+
onTestFilePrepare(_file) {
|
|
3896
|
+
}
|
|
3897
|
+
onTestFileFinished(_file) {
|
|
3898
|
+
}
|
|
3899
|
+
onTaskUpdate(packs) {
|
|
3900
|
+
const startingTestFiles = [];
|
|
3901
|
+
const finishedTestFiles = [];
|
|
3902
|
+
const startingTests = [];
|
|
3903
|
+
const finishedTests = [];
|
|
3904
|
+
const startingHooks = [];
|
|
3905
|
+
const endingHooks = [];
|
|
3906
|
+
for (const pack of packs) {
|
|
3907
|
+
const task = this.ctx.state.idMap.get(pack[0]);
|
|
3908
|
+
if (task?.type === "suite" && "filepath" in task && task.result?.state) {
|
|
3909
|
+
if (task?.result?.state === "run") {
|
|
3910
|
+
startingTestFiles.push(task);
|
|
3942
3911
|
} else {
|
|
3943
|
-
|
|
3912
|
+
for (const test of getTests(task)) {
|
|
3913
|
+
if (!test.result || test.result?.state === "skip") {
|
|
3914
|
+
finishedTests.push(test);
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
finishedTestFiles.push(task.file);
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
if (task?.type === "test") {
|
|
3921
|
+
if (task.result?.state === "run") {
|
|
3922
|
+
startingTests.push(task);
|
|
3923
|
+
} else if (task.result?.hooks?.afterEach !== "run") {
|
|
3924
|
+
finishedTests.push(task);
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
if (task?.result?.hooks) {
|
|
3928
|
+
for (const [hook, state] of Object.entries(task.result.hooks)) {
|
|
3929
|
+
if (state === "run") {
|
|
3930
|
+
startingHooks.push({ name: hook, file: task.file, id: task.id, type: task.type });
|
|
3931
|
+
} else {
|
|
3932
|
+
endingHooks.push({ name: hook, file: task.file, id: task.id, type: task.type });
|
|
3933
|
+
}
|
|
3944
3934
|
}
|
|
3945
3935
|
}
|
|
3946
3936
|
}
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3937
|
+
endingHooks.forEach((hook) => this.onHookEnd(hook));
|
|
3938
|
+
finishedTests.forEach((test) => this.onTestFinished(test));
|
|
3939
|
+
finishedTestFiles.forEach((file) => this.onTestFileFinished(file));
|
|
3940
|
+
startingTestFiles.forEach((file) => this.onTestFilePrepare(file));
|
|
3941
|
+
startingTests.forEach((test) => this.onTestStart(test));
|
|
3942
|
+
startingHooks.forEach(
|
|
3943
|
+
(hook) => this.onHookStart(hook)
|
|
3944
|
+
);
|
|
3955
3945
|
}
|
|
3956
|
-
return output.reverse().join("\n");
|
|
3957
3946
|
}
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3947
|
+
|
|
3948
|
+
const DURATION_UPDATE_INTERVAL_MS = 100;
|
|
3949
|
+
const FINISHED_TEST_CLEANUP_TIME_MS = 1e3;
|
|
3950
|
+
class SummaryReporter extends TaskParser {
|
|
3951
|
+
options;
|
|
3952
|
+
renderer;
|
|
3953
|
+
suites = emptyCounters();
|
|
3954
|
+
tests = emptyCounters();
|
|
3955
|
+
maxParallelTests = 0;
|
|
3956
|
+
/** Currently running tests, may include finished tests too */
|
|
3957
|
+
runningTests = /* @__PURE__ */ new Map();
|
|
3958
|
+
/** ID of finished `this.runningTests` that are currently being shown */
|
|
3959
|
+
finishedTests = /* @__PURE__ */ new Map();
|
|
3960
|
+
/** IDs of all finished tests */
|
|
3961
|
+
allFinishedTests = /* @__PURE__ */ new Set();
|
|
3962
|
+
startTime = "";
|
|
3963
|
+
currentTime = 0;
|
|
3964
|
+
duration = 0;
|
|
3965
|
+
durationInterval = void 0;
|
|
3966
|
+
onInit(ctx, options = {}) {
|
|
3967
|
+
this.ctx = ctx;
|
|
3968
|
+
this.options = {
|
|
3969
|
+
verbose: false,
|
|
3970
|
+
...options
|
|
3971
|
+
};
|
|
3972
|
+
this.renderer = new WindowRenderer({
|
|
3973
|
+
logger: ctx.logger,
|
|
3974
|
+
getWindow: () => this.createSummary()
|
|
3975
|
+
});
|
|
3976
|
+
this.startTimers();
|
|
3977
|
+
this.ctx.onClose(() => {
|
|
3978
|
+
clearInterval(this.durationInterval);
|
|
3979
|
+
this.renderer.stop();
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3982
|
+
onPathsCollected(paths) {
|
|
3983
|
+
this.suites.total = (paths || []).length;
|
|
3984
|
+
}
|
|
3985
|
+
onWatcherRerun() {
|
|
3986
|
+
this.runningTests.clear();
|
|
3987
|
+
this.finishedTests.clear();
|
|
3988
|
+
this.allFinishedTests.clear();
|
|
3989
|
+
this.suites = emptyCounters();
|
|
3990
|
+
this.tests = emptyCounters();
|
|
3991
|
+
this.startTimers();
|
|
3992
|
+
this.renderer.start();
|
|
3993
|
+
}
|
|
3994
|
+
onFinished() {
|
|
3995
|
+
this.runningTests.clear();
|
|
3996
|
+
this.finishedTests.clear();
|
|
3997
|
+
this.allFinishedTests.clear();
|
|
3998
|
+
this.renderer.finish();
|
|
3999
|
+
clearInterval(this.durationInterval);
|
|
4000
|
+
}
|
|
4001
|
+
onTestFilePrepare(file) {
|
|
4002
|
+
if (this.allFinishedTests.has(file.id) || this.runningTests.has(file.id)) {
|
|
4003
|
+
return;
|
|
3988
4004
|
}
|
|
4005
|
+
const total = getTests(file).length;
|
|
4006
|
+
this.tests.total += total;
|
|
4007
|
+
if (this.finishedTests.size) {
|
|
4008
|
+
const finished = this.finishedTests.keys().next().value;
|
|
4009
|
+
this.removeTestFile(finished);
|
|
4010
|
+
}
|
|
4011
|
+
this.runningTests.set(file.id, {
|
|
4012
|
+
total,
|
|
4013
|
+
completed: 0,
|
|
4014
|
+
filename: file.name,
|
|
4015
|
+
projectName: file.projectName,
|
|
4016
|
+
tests: /* @__PURE__ */ new Map()
|
|
4017
|
+
});
|
|
4018
|
+
this.maxParallelTests = Math.max(this.maxParallelTests, this.runningTests.size);
|
|
3989
4019
|
}
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4020
|
+
onHookStart(options) {
|
|
4021
|
+
const stats = this.getHookStats(options);
|
|
4022
|
+
if (!stats) {
|
|
4023
|
+
return;
|
|
4024
|
+
}
|
|
4025
|
+
const hook = {
|
|
4026
|
+
name: options.name,
|
|
4027
|
+
visible: false,
|
|
4028
|
+
startTime: performance.now(),
|
|
4029
|
+
onFinish: () => {
|
|
3994
4030
|
}
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4031
|
+
};
|
|
4032
|
+
stats.hook?.onFinish?.();
|
|
4033
|
+
stats.hook = hook;
|
|
4034
|
+
const timeout = setTimeout(() => {
|
|
4035
|
+
hook.visible = true;
|
|
4036
|
+
}, this.ctx.config.slowTestThreshold).unref();
|
|
4037
|
+
hook.onFinish = () => clearTimeout(timeout);
|
|
4038
|
+
}
|
|
4039
|
+
onHookEnd(options) {
|
|
4040
|
+
const stats = this.getHookStats(options);
|
|
4041
|
+
if (stats?.hook?.name !== options.name) {
|
|
4042
|
+
return;
|
|
4043
|
+
}
|
|
4044
|
+
stats.hook.onFinish();
|
|
4045
|
+
stats.hook.visible = false;
|
|
4046
|
+
}
|
|
4047
|
+
onTestStart(test) {
|
|
4048
|
+
if (!this.options.verbose) {
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
const stats = this.getTestStats(test);
|
|
4052
|
+
if (!stats || stats.tests.has(test.id)) {
|
|
4053
|
+
return;
|
|
4054
|
+
}
|
|
4055
|
+
const slowTest = {
|
|
4056
|
+
name: test.name,
|
|
4057
|
+
visible: false,
|
|
4058
|
+
startTime: performance.now(),
|
|
4059
|
+
onFinish: () => {
|
|
4006
4060
|
}
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4061
|
+
};
|
|
4062
|
+
const timeout = setTimeout(() => {
|
|
4063
|
+
slowTest.visible = true;
|
|
4064
|
+
}, this.ctx.config.slowTestThreshold).unref();
|
|
4065
|
+
slowTest.onFinish = () => {
|
|
4066
|
+
slowTest.hook?.onFinish();
|
|
4067
|
+
clearTimeout(timeout);
|
|
4068
|
+
};
|
|
4069
|
+
stats.tests.set(test.id, slowTest);
|
|
4070
|
+
}
|
|
4071
|
+
onTestFinished(test) {
|
|
4072
|
+
const stats = this.getTestStats(test);
|
|
4073
|
+
if (!stats) {
|
|
4074
|
+
return;
|
|
4075
|
+
}
|
|
4076
|
+
stats.tests.get(test.id)?.onFinish();
|
|
4077
|
+
stats.tests.delete(test.id);
|
|
4078
|
+
stats.completed++;
|
|
4079
|
+
const result = test.result;
|
|
4080
|
+
if (result?.state === "pass") {
|
|
4081
|
+
this.tests.passed++;
|
|
4082
|
+
} else if (result?.state === "fail") {
|
|
4083
|
+
this.tests.failed++;
|
|
4084
|
+
} else if (!result?.state || result?.state === "skip" || result?.state === "todo") {
|
|
4085
|
+
this.tests.skipped++;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
onTestFileFinished(file) {
|
|
4089
|
+
if (this.allFinishedTests.has(file.id)) {
|
|
4090
|
+
return;
|
|
4091
|
+
}
|
|
4092
|
+
this.allFinishedTests.add(file.id);
|
|
4093
|
+
this.suites.completed++;
|
|
4094
|
+
if (file.result?.state === "pass") {
|
|
4095
|
+
this.suites.passed++;
|
|
4096
|
+
} else if (file.result?.state === "fail") {
|
|
4097
|
+
this.suites.failed++;
|
|
4098
|
+
} else if (file.result?.state === "skip") {
|
|
4099
|
+
this.suites.skipped++;
|
|
4100
|
+
} else if (file.result?.state === "todo") {
|
|
4101
|
+
this.suites.todo++;
|
|
4102
|
+
}
|
|
4103
|
+
const left = this.suites.total - this.suites.completed;
|
|
4104
|
+
if (left > this.maxParallelTests) {
|
|
4105
|
+
this.finishedTests.set(file.id, setTimeout(() => {
|
|
4106
|
+
this.removeTestFile(file.id);
|
|
4107
|
+
}, FINISHED_TEST_CLEANUP_TIME_MS).unref());
|
|
4108
|
+
} else {
|
|
4109
|
+
this.removeTestFile(file.id);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
getTestStats(test) {
|
|
4113
|
+
const file = test.file;
|
|
4114
|
+
let stats = this.runningTests.get(file.id);
|
|
4115
|
+
if (!stats) {
|
|
4116
|
+
this.onTestFilePrepare(test.file);
|
|
4117
|
+
stats = this.runningTests.get(file.id);
|
|
4118
|
+
if (!stats) {
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
return stats;
|
|
4123
|
+
}
|
|
4124
|
+
getHookStats({ file, id, type }) {
|
|
4125
|
+
if (!this.options.verbose) {
|
|
4126
|
+
return;
|
|
4127
|
+
}
|
|
4128
|
+
const stats = this.runningTests.get(file.id);
|
|
4129
|
+
if (!stats) {
|
|
4130
|
+
return;
|
|
4131
|
+
}
|
|
4132
|
+
return type === "suite" ? stats : stats?.tests.get(id);
|
|
4133
|
+
}
|
|
4134
|
+
createSummary() {
|
|
4135
|
+
const summary = [""];
|
|
4136
|
+
for (const testFile of Array.from(this.runningTests.values()).sort(sortRunningTests)) {
|
|
4137
|
+
summary.push(
|
|
4138
|
+
c.bold(c.yellow(` ${F_POINTER} `)) + formatProjectName(testFile.projectName) + testFile.filename + c.dim(` ${testFile.completed}/${testFile.total}`)
|
|
4139
|
+
);
|
|
4140
|
+
const slowTasks = [
|
|
4141
|
+
testFile.hook,
|
|
4142
|
+
...Array.from(testFile.tests.values())
|
|
4143
|
+
].filter((t) => t != null && t.visible);
|
|
4144
|
+
for (const [index, task] of slowTasks.entries()) {
|
|
4145
|
+
const elapsed = this.currentTime - task.startTime;
|
|
4146
|
+
const icon = index === slowTasks.length - 1 ? F_TREE_NODE_END : F_TREE_NODE_MIDDLE;
|
|
4147
|
+
summary.push(
|
|
4148
|
+
c.bold(c.yellow(` ${icon} `)) + task.name + c.bold(c.yellow(` ${formatTime(Math.max(0, elapsed))}`))
|
|
4011
4149
|
);
|
|
4012
|
-
|
|
4013
|
-
}
|
|
4014
|
-
|
|
4150
|
+
if (task.hook?.visible) {
|
|
4151
|
+
summary.push(c.bold(c.yellow(` ${F_TREE_NODE_END} `)) + task.hook.name);
|
|
4152
|
+
}
|
|
4015
4153
|
}
|
|
4016
|
-
return this;
|
|
4017
|
-
},
|
|
4018
|
-
clear() {
|
|
4019
|
-
log.clear();
|
|
4020
4154
|
}
|
|
4021
|
-
|
|
4155
|
+
if (this.runningTests.size > 0) {
|
|
4156
|
+
summary.push("");
|
|
4157
|
+
}
|
|
4158
|
+
summary.push(padSummaryTitle("Test Files") + getStateString(this.suites));
|
|
4159
|
+
summary.push(padSummaryTitle("Tests") + getStateString(this.tests));
|
|
4160
|
+
summary.push(padSummaryTitle("Start at") + this.startTime);
|
|
4161
|
+
summary.push(padSummaryTitle("Duration") + formatTime(this.duration));
|
|
4162
|
+
summary.push("");
|
|
4163
|
+
return summary;
|
|
4164
|
+
}
|
|
4165
|
+
startTimers() {
|
|
4166
|
+
const start = performance.now();
|
|
4167
|
+
this.startTime = formatTimeString(/* @__PURE__ */ new Date());
|
|
4168
|
+
this.durationInterval = setInterval(() => {
|
|
4169
|
+
this.currentTime = performance.now();
|
|
4170
|
+
this.duration = this.currentTime - start;
|
|
4171
|
+
}, DURATION_UPDATE_INTERVAL_MS).unref();
|
|
4172
|
+
}
|
|
4173
|
+
removeTestFile(id) {
|
|
4174
|
+
if (!id) {
|
|
4175
|
+
return;
|
|
4176
|
+
}
|
|
4177
|
+
const testFile = this.runningTests.get(id);
|
|
4178
|
+
testFile?.hook?.onFinish();
|
|
4179
|
+
testFile?.tests?.forEach((test) => test.onFinish());
|
|
4180
|
+
this.runningTests.delete(id);
|
|
4181
|
+
clearTimeout(this.finishedTests.get(id));
|
|
4182
|
+
this.finishedTests.delete(id);
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
function emptyCounters() {
|
|
4186
|
+
return { completed: 0, passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
|
|
4187
|
+
}
|
|
4188
|
+
function getStateString(entry) {
|
|
4189
|
+
return [
|
|
4190
|
+
entry.failed ? c.bold(c.red(`${entry.failed} failed`)) : null,
|
|
4191
|
+
c.bold(c.green(`${entry.passed} passed`)),
|
|
4192
|
+
entry.skipped ? c.yellow(`${entry.skipped} skipped`) : null,
|
|
4193
|
+
entry.todo ? c.gray(`${entry.todo} todo`) : null
|
|
4194
|
+
].filter(Boolean).join(c.dim(" | ")) + c.gray(` (${entry.total})`);
|
|
4195
|
+
}
|
|
4196
|
+
function sortRunningTests(a, b) {
|
|
4197
|
+
if ((a.projectName || "") > (b.projectName || "")) {
|
|
4198
|
+
return 1;
|
|
4199
|
+
}
|
|
4200
|
+
if ((a.projectName || "") < (b.projectName || "")) {
|
|
4201
|
+
return -1;
|
|
4202
|
+
}
|
|
4203
|
+
return a.filename.localeCompare(b.filename);
|
|
4022
4204
|
}
|
|
4023
4205
|
|
|
4024
4206
|
class DefaultReporter extends BaseReporter {
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4207
|
+
options;
|
|
4208
|
+
summary;
|
|
4209
|
+
constructor(options = {}) {
|
|
4210
|
+
super(options);
|
|
4211
|
+
this.options = {
|
|
4212
|
+
summary: true,
|
|
4213
|
+
...options
|
|
4214
|
+
};
|
|
4215
|
+
if (!this.isTTY) {
|
|
4216
|
+
this.options.summary = false;
|
|
4217
|
+
}
|
|
4218
|
+
if (this.options.summary) {
|
|
4219
|
+
this.summary = new SummaryReporter();
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
onInit(ctx) {
|
|
4223
|
+
super.onInit(ctx);
|
|
4224
|
+
this.summary?.onInit(ctx, { verbose: this.verbose });
|
|
4225
|
+
}
|
|
4028
4226
|
onPathsCollected(paths = []) {
|
|
4029
4227
|
if (this.isTTY) {
|
|
4030
|
-
if (this.
|
|
4031
|
-
this.
|
|
4228
|
+
if (this.renderSucceed === void 0) {
|
|
4229
|
+
this.renderSucceed = !!this.renderSucceed;
|
|
4032
4230
|
}
|
|
4033
|
-
if (this.
|
|
4034
|
-
this.
|
|
4231
|
+
if (this.renderSucceed !== true) {
|
|
4232
|
+
this.renderSucceed = paths.length <= 1;
|
|
4035
4233
|
}
|
|
4036
4234
|
}
|
|
4235
|
+
this.summary?.onPathsCollected(paths);
|
|
4037
4236
|
}
|
|
4038
|
-
|
|
4039
|
-
this.
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
);
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
this.
|
|
4048
|
-
super.
|
|
4049
|
-
super.onWatcherStart();
|
|
4237
|
+
onTaskUpdate(packs) {
|
|
4238
|
+
this.summary?.onTaskUpdate(packs);
|
|
4239
|
+
super.onTaskUpdate(packs);
|
|
4240
|
+
}
|
|
4241
|
+
onWatcherRerun(files, trigger) {
|
|
4242
|
+
this.summary?.onWatcherRerun();
|
|
4243
|
+
super.onWatcherRerun(files, trigger);
|
|
4244
|
+
}
|
|
4245
|
+
onFinished(files, errors) {
|
|
4246
|
+
this.summary?.onFinished();
|
|
4247
|
+
super.onFinished(files, errors);
|
|
4050
4248
|
}
|
|
4051
|
-
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
class DotReporter extends BaseReporter {
|
|
4252
|
+
summary;
|
|
4253
|
+
onInit(ctx) {
|
|
4254
|
+
super.onInit(ctx);
|
|
4052
4255
|
if (this.isTTY) {
|
|
4053
|
-
this.
|
|
4054
|
-
this.
|
|
4055
|
-
this.rendererOptions.slowTestThreshold = this.ctx.config.slowTestThreshold;
|
|
4056
|
-
this.rendererOptions.mode = this.ctx.config.mode;
|
|
4057
|
-
const files = this.ctx.state.getFiles(this.watchFilters);
|
|
4058
|
-
if (!this.renderer) {
|
|
4059
|
-
this.renderer = createListRenderer(files, this.rendererOptions).start();
|
|
4060
|
-
} else {
|
|
4061
|
-
this.renderer.update(files);
|
|
4062
|
-
}
|
|
4256
|
+
this.summary = new DotSummary();
|
|
4257
|
+
this.summary.onInit(ctx);
|
|
4063
4258
|
}
|
|
4064
4259
|
}
|
|
4065
|
-
|
|
4066
|
-
this.
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
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();
|
|
4072
4272
|
super.onFinished(files, errors);
|
|
4073
4273
|
}
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
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());
|
|
4077
4286
|
}
|
|
4078
|
-
|
|
4079
|
-
this.
|
|
4080
|
-
this.renderer
|
|
4287
|
+
onWatcherRerun() {
|
|
4288
|
+
this.tests.clear();
|
|
4289
|
+
this.renderer.start();
|
|
4081
4290
|
}
|
|
4082
|
-
|
|
4083
|
-
this.
|
|
4084
|
-
|
|
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();
|
|
4085
4296
|
}
|
|
4086
|
-
|
|
4087
|
-
|
|
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)) {
|
|
4088
4304
|
return;
|
|
4089
4305
|
}
|
|
4090
|
-
this.
|
|
4091
|
-
|
|
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
|
+
];
|
|
4092
4340
|
}
|
|
4093
4341
|
}
|
|
4094
|
-
|
|
4095
|
-
const
|
|
4096
|
-
const cross = { char: "x", color: c.red };
|
|
4342
|
+
const pass = { char: "\xB7", color: c.green };
|
|
4343
|
+
const fail = { char: "x", color: c.red };
|
|
4097
4344
|
const pending = { char: "*", color: c.yellow };
|
|
4098
4345
|
const skip = { char: "-", color: (char) => c.dim(c.gray(char)) };
|
|
4099
|
-
function getIcon(
|
|
4100
|
-
|
|
4101
|
-
return skip;
|
|
4102
|
-
}
|
|
4103
|
-
switch (task.result?.state) {
|
|
4346
|
+
function getIcon(state) {
|
|
4347
|
+
switch (state) {
|
|
4104
4348
|
case "pass":
|
|
4105
|
-
return
|
|
4349
|
+
return pass;
|
|
4106
4350
|
case "fail":
|
|
4107
|
-
return
|
|
4351
|
+
return fail;
|
|
4352
|
+
case "skip":
|
|
4353
|
+
case "todo":
|
|
4354
|
+
return skip;
|
|
4108
4355
|
default:
|
|
4109
4356
|
return pending;
|
|
4110
4357
|
}
|
|
4111
4358
|
}
|
|
4112
|
-
function
|
|
4113
|
-
const all = getTests(tasks);
|
|
4359
|
+
function formatTests(states) {
|
|
4114
4360
|
let currentIcon = pending;
|
|
4115
|
-
let
|
|
4116
|
-
let previousLineWidth = 0;
|
|
4361
|
+
let count = 0;
|
|
4117
4362
|
let output = "";
|
|
4118
|
-
const
|
|
4119
|
-
const
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
output += color(char.repeat(currentTasks));
|
|
4123
|
-
previousLineWidth += currentTasks;
|
|
4124
|
-
} else {
|
|
4125
|
-
let buf = `${char.repeat(availableWidth)}
|
|
4126
|
-
`;
|
|
4127
|
-
const remaining = currentTasks - availableWidth;
|
|
4128
|
-
const fullRows = Math.floor(remaining / width);
|
|
4129
|
-
buf += `${char.repeat(width)}
|
|
4130
|
-
`.repeat(fullRows);
|
|
4131
|
-
const partialRow = remaining % width;
|
|
4132
|
-
if (partialRow > 0) {
|
|
4133
|
-
buf += char.repeat(partialRow);
|
|
4134
|
-
previousLineWidth = partialRow;
|
|
4135
|
-
} else {
|
|
4136
|
-
previousLineWidth = 0;
|
|
4137
|
-
}
|
|
4138
|
-
output += color(buf);
|
|
4139
|
-
}
|
|
4140
|
-
};
|
|
4141
|
-
for (const task of all) {
|
|
4142
|
-
const icon = getIcon(task);
|
|
4143
|
-
if (icon === currentIcon) {
|
|
4144
|
-
currentTasks++;
|
|
4363
|
+
for (const state of states) {
|
|
4364
|
+
const icon = getIcon(state);
|
|
4365
|
+
if (currentIcon === icon) {
|
|
4366
|
+
count++;
|
|
4145
4367
|
continue;
|
|
4146
4368
|
}
|
|
4147
|
-
|
|
4148
|
-
|
|
4369
|
+
output += currentIcon.color(currentIcon.char.repeat(count));
|
|
4370
|
+
count = 1;
|
|
4149
4371
|
currentIcon = icon;
|
|
4150
4372
|
}
|
|
4151
|
-
|
|
4373
|
+
output += currentIcon.color(currentIcon.char.repeat(count));
|
|
4152
4374
|
return output;
|
|
4153
4375
|
}
|
|
4154
|
-
function createDotRenderer(_tasks, options) {
|
|
4155
|
-
let tasks = _tasks;
|
|
4156
|
-
let timer;
|
|
4157
|
-
const { logUpdate: log, outputStream } = options.logger;
|
|
4158
|
-
const columns = "columns" in outputStream ? outputStream.columns : 80;
|
|
4159
|
-
function update() {
|
|
4160
|
-
log(render(tasks, columns));
|
|
4161
|
-
}
|
|
4162
|
-
return {
|
|
4163
|
-
start() {
|
|
4164
|
-
if (timer) {
|
|
4165
|
-
return this;
|
|
4166
|
-
}
|
|
4167
|
-
timer = setInterval(update, 16);
|
|
4168
|
-
return this;
|
|
4169
|
-
},
|
|
4170
|
-
update(_tasks2) {
|
|
4171
|
-
tasks = _tasks2;
|
|
4172
|
-
return this;
|
|
4173
|
-
},
|
|
4174
|
-
async stop() {
|
|
4175
|
-
if (timer) {
|
|
4176
|
-
clearInterval(timer);
|
|
4177
|
-
timer = void 0;
|
|
4178
|
-
}
|
|
4179
|
-
log.clear();
|
|
4180
|
-
options.logger.log(render(tasks, columns));
|
|
4181
|
-
return this;
|
|
4182
|
-
},
|
|
4183
|
-
clear() {
|
|
4184
|
-
log.clear();
|
|
4185
|
-
}
|
|
4186
|
-
};
|
|
4187
|
-
}
|
|
4188
|
-
|
|
4189
|
-
class DotReporter extends BaseReporter {
|
|
4190
|
-
renderer;
|
|
4191
|
-
onCollected() {
|
|
4192
|
-
if (this.isTTY) {
|
|
4193
|
-
const files = this.ctx.state.getFiles(this.watchFilters);
|
|
4194
|
-
if (!this.renderer) {
|
|
4195
|
-
this.renderer = createDotRenderer(files, {
|
|
4196
|
-
logger: this.ctx.logger
|
|
4197
|
-
}).start();
|
|
4198
|
-
} else {
|
|
4199
|
-
this.renderer.update(files);
|
|
4200
|
-
}
|
|
4201
|
-
}
|
|
4202
|
-
}
|
|
4203
|
-
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
4204
|
-
await this.stopListRender();
|
|
4205
|
-
this.ctx.logger.log();
|
|
4206
|
-
super.onFinished(files, errors);
|
|
4207
|
-
}
|
|
4208
|
-
async onWatcherStart() {
|
|
4209
|
-
await this.stopListRender();
|
|
4210
|
-
super.onWatcherStart();
|
|
4211
|
-
}
|
|
4212
|
-
async stopListRender() {
|
|
4213
|
-
this.renderer?.stop();
|
|
4214
|
-
this.renderer = void 0;
|
|
4215
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
4216
|
-
}
|
|
4217
|
-
async onWatcherRerun(files, trigger) {
|
|
4218
|
-
await this.stopListRender();
|
|
4219
|
-
super.onWatcherRerun(files, trigger);
|
|
4220
|
-
}
|
|
4221
|
-
onUserConsoleLog(log) {
|
|
4222
|
-
this.renderer?.clear();
|
|
4223
|
-
super.onUserConsoleLog(log);
|
|
4224
|
-
}
|
|
4225
|
-
}
|
|
4226
4376
|
|
|
4227
4377
|
class GithubActionsReporter {
|
|
4228
4378
|
ctx = void 0;
|
|
@@ -4233,7 +4383,7 @@ class GithubActionsReporter {
|
|
|
4233
4383
|
const projectErrors = new Array();
|
|
4234
4384
|
for (const error of errors) {
|
|
4235
4385
|
projectErrors.push({
|
|
4236
|
-
project: this.ctx.
|
|
4386
|
+
project: this.ctx.getRootTestProject(),
|
|
4237
4387
|
title: "Unhandled error",
|
|
4238
4388
|
error
|
|
4239
4389
|
});
|
|
@@ -4571,10 +4721,22 @@ class JUnitReporter {
|
|
|
4571
4721
|
}
|
|
4572
4722
|
async writeTasks(tasks, filename) {
|
|
4573
4723
|
for (const task of tasks) {
|
|
4724
|
+
let classname = filename;
|
|
4725
|
+
const templateVars = {
|
|
4726
|
+
filename: task.file.name,
|
|
4727
|
+
filepath: task.file.filepath
|
|
4728
|
+
};
|
|
4729
|
+
if (typeof this.options.classnameTemplate === "function") {
|
|
4730
|
+
classname = this.options.classnameTemplate(templateVars);
|
|
4731
|
+
} else if (typeof this.options.classnameTemplate === "string") {
|
|
4732
|
+
classname = this.options.classnameTemplate.replace(/\{filename\}/g, templateVars.filename).replace(/\{filepath\}/g, templateVars.filepath);
|
|
4733
|
+
} else if (typeof this.options.classname === "string") {
|
|
4734
|
+
classname = this.options.classname;
|
|
4735
|
+
}
|
|
4574
4736
|
await this.writeElement(
|
|
4575
4737
|
"testcase",
|
|
4576
4738
|
{
|
|
4577
|
-
classname
|
|
4739
|
+
classname,
|
|
4578
4740
|
file: this.options.addFileAttribute ? filename : void 0,
|
|
4579
4741
|
name: task.name,
|
|
4580
4742
|
time: getDuration(task)
|
|
@@ -4666,6 +4828,7 @@ class JUnitReporter {
|
|
|
4666
4828
|
(stats2, file) => {
|
|
4667
4829
|
stats2.tests += file.tasks.length;
|
|
4668
4830
|
stats2.failures += file.stats.failures;
|
|
4831
|
+
stats2.time += file.result?.duration || 0;
|
|
4669
4832
|
return stats2;
|
|
4670
4833
|
},
|
|
4671
4834
|
{
|
|
@@ -4674,10 +4837,10 @@ class JUnitReporter {
|
|
|
4674
4837
|
failures: 0,
|
|
4675
4838
|
errors: 0,
|
|
4676
4839
|
// we cannot detect those
|
|
4677
|
-
time:
|
|
4840
|
+
time: 0
|
|
4678
4841
|
}
|
|
4679
4842
|
);
|
|
4680
|
-
await this.writeElement("testsuites", stats, async () => {
|
|
4843
|
+
await this.writeElement("testsuites", { ...stats, time: executionTime(stats.time) }, async () => {
|
|
4681
4844
|
for (const file of transformed) {
|
|
4682
4845
|
const filename = relative(this.ctx.config.root, file.filepath);
|
|
4683
4846
|
await this.writeElement(
|
|
@@ -4820,13 +4983,10 @@ class TapFlatReporter extends TapReporter {
|
|
|
4820
4983
|
|
|
4821
4984
|
class VerboseReporter extends DefaultReporter {
|
|
4822
4985
|
verbose = true;
|
|
4823
|
-
|
|
4824
|
-
super();
|
|
4825
|
-
this.rendererOptions.renderSucceed = true;
|
|
4826
|
-
}
|
|
4986
|
+
renderSucceed = true;
|
|
4827
4987
|
onTaskUpdate(packs) {
|
|
4828
4988
|
if (this.isTTY) {
|
|
4829
|
-
return;
|
|
4989
|
+
return super.onTaskUpdate(packs);
|
|
4830
4990
|
}
|
|
4831
4991
|
for (const pack of packs) {
|
|
4832
4992
|
const task = this.ctx.state.idMap.get(pack[0]);
|
|
@@ -5250,4 +5410,4 @@ const ReportersMap = {
|
|
|
5250
5410
|
"github-actions": GithubActionsReporter
|
|
5251
5411
|
};
|
|
5252
5412
|
|
|
5253
|
-
export { BasicReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, Logger as L, ReportersMap as R, TapFlatReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapReporter as c, TestFile as d, TestCase as e, TestModule as f, TestSuite as g, BenchmarkReportsMap as h,
|
|
5413
|
+
export { BasicReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, Logger as L, ReportersMap as R, TapFlatReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapReporter as c, TestFile as d, TestCase as e, TestModule as f, TestSuite as g, BenchmarkReportsMap as h, generateCodeFrame as i, BlobReporter as j, parse as p, readBlobs as r, stringify as s };
|