factorio-test-cli 3.3.0 → 3.3.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/factorio-process.js +3 -0
- package/package.json +1 -1
- package/test-output.js +12 -5
- package/test-output.test.js +57 -3
- package/test-results.js +19 -0
- package/test-results.test.js +58 -1
package/factorio-process.js
CHANGED
|
@@ -132,6 +132,9 @@ function createOutputComponents(options) {
|
|
|
132
132
|
progress.handleTestFinished(test);
|
|
133
133
|
progress.withPermanentOutput(() => printer.printTestResult(test));
|
|
134
134
|
});
|
|
135
|
+
collector.on("describeBlockFailed", (block) => {
|
|
136
|
+
progress.withPermanentOutput(() => printer.printTestResult(block));
|
|
137
|
+
});
|
|
135
138
|
handler.on("result", () => {
|
|
136
139
|
progress.finish();
|
|
137
140
|
printer.resetMessage();
|
package/package.json
CHANGED
package/test-output.js
CHANGED
|
@@ -87,16 +87,18 @@ export class OutputFormatter {
|
|
|
87
87
|
formatTestResult(test) {
|
|
88
88
|
if (this.options.quiet)
|
|
89
89
|
return;
|
|
90
|
-
const
|
|
90
|
+
const prefix = this.getPrefix(test.result);
|
|
91
|
+
const duration = test.durationMs !== undefined ? ` (${formatDuration(test.durationMs)})` : "";
|
|
92
|
+
console.log(`${prefix} ${test.path}${duration}`);
|
|
93
|
+
const showLogs = test.result === "failed" || test.result === "error" || this.options.showPassedLogs;
|
|
91
94
|
if (showLogs && test.logs.length > 0) {
|
|
95
|
+
console.log("Log messages:");
|
|
92
96
|
for (const log of test.logs) {
|
|
93
97
|
console.log(" " + log);
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
console.log(`${prefix} ${test.path}${duration}`);
|
|
99
|
-
if (test.result === "failed") {
|
|
100
|
+
if ((test.result === "failed" || test.result === "error") && test.errors.length > 0) {
|
|
101
|
+
console.log("Errors:");
|
|
100
102
|
for (const error of test.errors) {
|
|
101
103
|
console.log(" " + error);
|
|
102
104
|
}
|
|
@@ -107,6 +109,7 @@ export class OutputFormatter {
|
|
|
107
109
|
return;
|
|
108
110
|
if (!this.options.quiet) {
|
|
109
111
|
this.printRecapSection(data.tests, "failed", "Failures:");
|
|
112
|
+
this.printRecapSection(data.tests, "error", "Describe block errors:");
|
|
110
113
|
this.printRecapSection(data.tests, "todo", "Todo:");
|
|
111
114
|
}
|
|
112
115
|
this.printCountsLine(data.summary);
|
|
@@ -126,6 +129,8 @@ export class OutputFormatter {
|
|
|
126
129
|
const segments = [];
|
|
127
130
|
if (summary.failed > 0)
|
|
128
131
|
segments.push(chalk.red(`${summary.failed} failed`));
|
|
132
|
+
if (summary.describeBlockErrors > 0)
|
|
133
|
+
segments.push(chalk.red(`${summary.describeBlockErrors} errors`));
|
|
129
134
|
if (summary.todo > 0)
|
|
130
135
|
segments.push(chalk.magenta(`${summary.todo} todo`));
|
|
131
136
|
if (summary.skipped > 0)
|
|
@@ -140,6 +145,8 @@ export class OutputFormatter {
|
|
|
140
145
|
return chalk.green("PASS");
|
|
141
146
|
case "failed":
|
|
142
147
|
return chalk.red("FAIL");
|
|
148
|
+
case "error":
|
|
149
|
+
return chalk.red("ERROR");
|
|
143
150
|
case "skipped":
|
|
144
151
|
return chalk.yellow("SKIP");
|
|
145
152
|
case "todo":
|
package/test-output.test.js
CHANGED
|
@@ -150,12 +150,19 @@ describe("OutputFormatter", () => {
|
|
|
150
150
|
expect(output.some((line) => line.includes("assertion failed"))).toBe(true);
|
|
151
151
|
expect(output.some((line) => line.includes("at test.ts:10"))).toBe(true);
|
|
152
152
|
});
|
|
153
|
-
it("shows logs
|
|
153
|
+
it("shows status line first, then logs and errors with headers", () => {
|
|
154
154
|
const formatter = new OutputFormatter({});
|
|
155
155
|
formatter.formatTestResult(failedTest);
|
|
156
|
-
const logIndex = output.findIndex((line) => line.includes("debug output"));
|
|
157
156
|
const failIndex = output.findIndex((line) => line.includes("FAIL"));
|
|
158
|
-
|
|
157
|
+
const logHeaderIndex = output.findIndex((line) => line.includes("Log messages:"));
|
|
158
|
+
const logIndex = output.findIndex((line) => line.includes("debug output"));
|
|
159
|
+
const errorHeaderIndex = output.findIndex((line) => line.includes("Errors:"));
|
|
160
|
+
const errorIndex = output.findIndex((line) => line.includes("assertion failed"));
|
|
161
|
+
expect(failIndex).toBe(0);
|
|
162
|
+
expect(logHeaderIndex).toBeGreaterThan(failIndex);
|
|
163
|
+
expect(logIndex).toBeGreaterThan(logHeaderIndex);
|
|
164
|
+
expect(errorHeaderIndex).toBeGreaterThan(logIndex);
|
|
165
|
+
expect(errorIndex).toBeGreaterThan(errorHeaderIndex);
|
|
159
166
|
});
|
|
160
167
|
it("hides logs for passed tests by default", () => {
|
|
161
168
|
const formatter = new OutputFormatter({});
|
|
@@ -199,6 +206,22 @@ describe("OutputFormatter", () => {
|
|
|
199
206
|
expect(output[0]).toContain("TODO");
|
|
200
207
|
expect(output[0]).toContain("todo test");
|
|
201
208
|
});
|
|
209
|
+
it("formats describe block error with ERROR prefix", () => {
|
|
210
|
+
const formatter = new OutputFormatter({});
|
|
211
|
+
const errorTest = {
|
|
212
|
+
path: "root > block",
|
|
213
|
+
result: "error",
|
|
214
|
+
errors: ["Error running afterAll: Oh no"],
|
|
215
|
+
logs: ["hook log"],
|
|
216
|
+
};
|
|
217
|
+
formatter.formatTestResult(errorTest);
|
|
218
|
+
expect(output[0]).toContain("ERROR");
|
|
219
|
+
expect(output[0]).toContain("root > block");
|
|
220
|
+
expect(output.some((line) => line.includes("Log messages:"))).toBe(true);
|
|
221
|
+
expect(output.some((line) => line.includes("hook log"))).toBe(true);
|
|
222
|
+
expect(output.some((line) => line.includes("Errors:"))).toBe(true);
|
|
223
|
+
expect(output.some((line) => line.includes("Oh no"))).toBe(true);
|
|
224
|
+
});
|
|
202
225
|
});
|
|
203
226
|
describe("OutputFormatter.formatSummary", () => {
|
|
204
227
|
let consoleSpy;
|
|
@@ -298,6 +321,37 @@ describe("OutputFormatter.formatSummary", () => {
|
|
|
298
321
|
expect(countsLine).toContain("1 passed");
|
|
299
322
|
expect(countsLine).toContain("(4 total)");
|
|
300
323
|
});
|
|
324
|
+
it("shows describe block errors in separate section from failures", () => {
|
|
325
|
+
const formatter = new OutputFormatter({});
|
|
326
|
+
const data = {
|
|
327
|
+
tests: [
|
|
328
|
+
{ path: "test", result: "failed", errors: ["test err"], logs: [] },
|
|
329
|
+
{ path: "block", result: "error", errors: ["block err"], logs: [] },
|
|
330
|
+
],
|
|
331
|
+
summary: makeSummary({ ran: 1, failed: 1, describeBlockErrors: 1, status: "failed" }),
|
|
332
|
+
};
|
|
333
|
+
formatter.formatSummary(data);
|
|
334
|
+
expect(output.some((l) => l.includes("Failures:"))).toBe(true);
|
|
335
|
+
expect(output.some((l) => l.includes("Describe block errors:"))).toBe(true);
|
|
336
|
+
const failuresIdx = output.findIndex((l) => l.includes("Failures:"));
|
|
337
|
+
const errorsIdx = output.findIndex((l) => l.includes("Describe block errors:"));
|
|
338
|
+
const failLine = output.findIndex((l) => l.includes("FAIL"));
|
|
339
|
+
const errorLine = output.findIndex((l) => l.includes("ERROR"));
|
|
340
|
+
expect(failLine).toBeGreaterThan(failuresIdx);
|
|
341
|
+
expect(failLine).toBeLessThan(errorsIdx);
|
|
342
|
+
expect(errorLine).toBeGreaterThan(errorsIdx);
|
|
343
|
+
});
|
|
344
|
+
it("includes describe block errors count in summary line", () => {
|
|
345
|
+
const formatter = new OutputFormatter({});
|
|
346
|
+
const data = {
|
|
347
|
+
tests: [{ path: "block", result: "error", errors: ["err"], logs: [] }],
|
|
348
|
+
summary: makeSummary({ passed: 1, describeBlockErrors: 2, status: "failed" }),
|
|
349
|
+
};
|
|
350
|
+
formatter.formatSummary(data);
|
|
351
|
+
const countsLine = output.find((l) => l.includes("Tests:"));
|
|
352
|
+
expect(countsLine).toContain("2 errors");
|
|
353
|
+
expect(countsLine).toContain("1 passed");
|
|
354
|
+
});
|
|
301
355
|
it("does nothing when summary is undefined", () => {
|
|
302
356
|
const formatter = new OutputFormatter({});
|
|
303
357
|
formatter.formatSummary({ tests: [] });
|
package/test-results.js
CHANGED
|
@@ -5,11 +5,13 @@ export class TestRunCollector extends EventEmitter {
|
|
|
5
5
|
data = { tests: [] };
|
|
6
6
|
currentTest;
|
|
7
7
|
currentLogs = [];
|
|
8
|
+
pendingLogs = [];
|
|
8
9
|
testStartTime;
|
|
9
10
|
handleEvent(event) {
|
|
10
11
|
switch (event.type) {
|
|
11
12
|
case "testStarted":
|
|
12
13
|
this.flushCurrentTest();
|
|
14
|
+
this.pendingLogs = [];
|
|
13
15
|
this.testStartTime = performance.now();
|
|
14
16
|
this.currentTest = {
|
|
15
17
|
path: event.test.path,
|
|
@@ -61,6 +63,20 @@ export class TestRunCollector extends EventEmitter {
|
|
|
61
63
|
logs: [],
|
|
62
64
|
});
|
|
63
65
|
break;
|
|
66
|
+
case "describeBlockFailed": {
|
|
67
|
+
this.flushCurrentTest();
|
|
68
|
+
const captured = {
|
|
69
|
+
path: event.block.path,
|
|
70
|
+
source: event.block.source,
|
|
71
|
+
result: "error",
|
|
72
|
+
errors: event.errors,
|
|
73
|
+
logs: [...this.pendingLogs],
|
|
74
|
+
};
|
|
75
|
+
this.data.tests.push(captured);
|
|
76
|
+
this.emit("describeBlockFailed", captured);
|
|
77
|
+
this.pendingLogs = [];
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
64
80
|
case "testRunFinished":
|
|
65
81
|
this.flushCurrentTest();
|
|
66
82
|
this.data.summary = event.results;
|
|
@@ -75,6 +91,9 @@ export class TestRunCollector extends EventEmitter {
|
|
|
75
91
|
if (this.currentTest) {
|
|
76
92
|
this.currentLogs.push(line);
|
|
77
93
|
}
|
|
94
|
+
else {
|
|
95
|
+
this.pendingLogs.push(line);
|
|
96
|
+
}
|
|
78
97
|
}
|
|
79
98
|
getData() {
|
|
80
99
|
return this.data;
|
package/test-results.test.js
CHANGED
|
@@ -38,12 +38,69 @@ describe("TestRunCollector", () => {
|
|
|
38
38
|
const data = collector.getData();
|
|
39
39
|
expect(data.tests[0].logs).toEqual(["log line 1", "log line 2"]);
|
|
40
40
|
});
|
|
41
|
-
it("does not
|
|
41
|
+
it("does not attach pending logs to skipped tests", () => {
|
|
42
42
|
collector.captureLog("orphan log");
|
|
43
43
|
collector.handleEvent({ type: "testSkipped", test: { path: "test" } });
|
|
44
44
|
const data = collector.getData();
|
|
45
45
|
expect(data.tests[0].logs).toEqual([]);
|
|
46
46
|
});
|
|
47
|
+
it("handles describeBlockFailed with errors and pending logs", () => {
|
|
48
|
+
collector.captureLog("hook output");
|
|
49
|
+
collector.handleEvent({
|
|
50
|
+
type: "describeBlockFailed",
|
|
51
|
+
block: { path: "root > block", source: { file: "test.ts", line: 5 } },
|
|
52
|
+
errors: ["Error running afterAll: Oh no"],
|
|
53
|
+
});
|
|
54
|
+
const data = collector.getData();
|
|
55
|
+
expect(data.tests).toHaveLength(1);
|
|
56
|
+
expect(data.tests[0]).toMatchObject({
|
|
57
|
+
path: "root > block",
|
|
58
|
+
result: "error",
|
|
59
|
+
errors: ["Error running afterAll: Oh no"],
|
|
60
|
+
logs: ["hook output"],
|
|
61
|
+
source: { file: "test.ts", line: 5 },
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it("emits describeBlockFailed event", () => {
|
|
65
|
+
const handler = vi.fn();
|
|
66
|
+
collector.on("describeBlockFailed", handler);
|
|
67
|
+
collector.handleEvent({
|
|
68
|
+
type: "describeBlockFailed",
|
|
69
|
+
block: { path: "block" },
|
|
70
|
+
errors: ["err"],
|
|
71
|
+
});
|
|
72
|
+
expect(handler).toHaveBeenCalledOnce();
|
|
73
|
+
expect(handler.mock.calls[0][0].path).toBe("block");
|
|
74
|
+
});
|
|
75
|
+
it("clears pending logs when a new test starts", () => {
|
|
76
|
+
collector.captureLog("before test");
|
|
77
|
+
collector.handleEvent({ type: "testStarted", test: { path: "test" } });
|
|
78
|
+
collector.handleEvent({ type: "testPassed", test: { path: "test" } });
|
|
79
|
+
collector.handleEvent({
|
|
80
|
+
type: "describeBlockFailed",
|
|
81
|
+
block: { path: "block" },
|
|
82
|
+
errors: ["err"],
|
|
83
|
+
});
|
|
84
|
+
const data = collector.getData();
|
|
85
|
+
const block = data.tests.find((t) => t.path === "block");
|
|
86
|
+
expect(block.logs).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
it("captures logs after test finishes for describe block failure", () => {
|
|
89
|
+
collector.handleEvent({ type: "testStarted", test: { path: "test" } });
|
|
90
|
+
collector.captureLog("test log");
|
|
91
|
+
collector.handleEvent({ type: "testPassed", test: { path: "test" } });
|
|
92
|
+
collector.captureLog("after_all log");
|
|
93
|
+
collector.handleEvent({
|
|
94
|
+
type: "describeBlockFailed",
|
|
95
|
+
block: { path: "block" },
|
|
96
|
+
errors: ["hook error"],
|
|
97
|
+
});
|
|
98
|
+
const data = collector.getData();
|
|
99
|
+
expect(data.tests[0].logs).toEqual(["test log"]);
|
|
100
|
+
const block = data.tests[1];
|
|
101
|
+
expect(block.logs).toEqual(["after_all log"]);
|
|
102
|
+
expect(block.errors).toEqual(["hook error"]);
|
|
103
|
+
});
|
|
47
104
|
it("handles testSkipped without prior testStarted", () => {
|
|
48
105
|
collector.handleEvent({ type: "testSkipped", test: { path: "skipped test" } });
|
|
49
106
|
const data = collector.getData();
|