include stdout into the detail-float
All checks were successful
tests / test (push) Successful in 10s
All checks were successful
tests / test (push) Successful in 10s
This commit is contained in:
@@ -42,6 +42,7 @@ which emits one JSON object per line:
|
|||||||
"titlePath": ["Parent", "Child", "Test"],
|
"titlePath": ["Parent", "Child", "Test"],
|
||||||
"file": "/path/to/test.js",
|
"file": "/path/to/test.js",
|
||||||
"location": { "file": "/path/to/test.js", "line": 10, "column": 5 },
|
"location": { "file": "/path/to/test.js", "line": 10, "column": 5 },
|
||||||
|
"output": ["stdout line 1", "stdout line 2"],
|
||||||
"error": { "message": "string", "stack": "string" }
|
"error": { "message": "string", "stack": "string" }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -248,6 +248,35 @@ local function build_error_lines(error_obj)
|
|||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function output_lines_from_payload(payload)
|
||||||
|
local output = payload and payload.output
|
||||||
|
if type(output) == "string" then
|
||||||
|
return vim.split(output, "\n", { plain = true, trimempty = false })
|
||||||
|
end
|
||||||
|
if type(output) == "table" then
|
||||||
|
local lines = {}
|
||||||
|
for _, line in ipairs(output) do
|
||||||
|
if type(line) == "string" then
|
||||||
|
if line:find("\n", 1, true) then
|
||||||
|
for _, split_line in ipairs(vim.split(line, "\n", { plain = true, trimempty = false })) do
|
||||||
|
table.insert(lines, split_line)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function append_lines(target, lines)
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
table.insert(target, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function extract_location(stack)
|
local function extract_location(stack)
|
||||||
if not stack then
|
if not stack then
|
||||||
return nil
|
return nil
|
||||||
@@ -356,8 +385,11 @@ local function record_ndjson_result(state, status, payload)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local location = normalize_location(payload.location)
|
local location = normalize_location(payload.location)
|
||||||
|
local output_lines = output_lines_from_payload(payload)
|
||||||
if status == "failed" then
|
if status == "failed" then
|
||||||
local lines = build_error_lines(payload.error)
|
local lines = {}
|
||||||
|
append_lines(lines, output_lines)
|
||||||
|
append_lines(lines, build_error_lines(payload.error))
|
||||||
if #lines > 0 then
|
if #lines > 0 then
|
||||||
state.outputs[full_name] = lines
|
state.outputs[full_name] = lines
|
||||||
end
|
end
|
||||||
@@ -377,6 +409,13 @@ local function record_ndjson_result(state, status, payload)
|
|||||||
location.text = status
|
location.text = status
|
||||||
state.locations[full_name] = location
|
state.locations[full_name] = location
|
||||||
end
|
end
|
||||||
|
if #output_lines > 0 then
|
||||||
|
state.outputs[full_name] = output_lines
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not state.outputs[full_name] or #state.outputs[full_name] == 0) and status ~= "failed" then
|
||||||
|
state.outputs[full_name] = { status }
|
||||||
end
|
end
|
||||||
|
|
||||||
if added then
|
if added then
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const {
|
|||||||
EVENT_RUN_BEGIN,
|
EVENT_RUN_BEGIN,
|
||||||
EVENT_RUN_END,
|
EVENT_RUN_END,
|
||||||
EVENT_SUITE_BEGIN,
|
EVENT_SUITE_BEGIN,
|
||||||
|
EVENT_TEST_BEGIN,
|
||||||
|
EVENT_TEST_END,
|
||||||
EVENT_TEST_PASS,
|
EVENT_TEST_PASS,
|
||||||
EVENT_TEST_FAIL,
|
EVENT_TEST_FAIL,
|
||||||
EVENT_TEST_PENDING,
|
EVENT_TEST_PENDING,
|
||||||
@@ -32,11 +34,29 @@ function safeTitlePath(entity) {
|
|||||||
|
|
||||||
class NdjsonReporter {
|
class NdjsonReporter {
|
||||||
constructor(runner /*, options */) {
|
constructor(runner /*, options */) {
|
||||||
|
this.currentTest = null;
|
||||||
|
this.stdoutByTest = new WeakMap();
|
||||||
|
this.originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
||||||
|
process.stdout.write = (...args) => {
|
||||||
|
this.captureStdout(args[0]);
|
||||||
|
return this.originalStdoutWrite(...args);
|
||||||
|
};
|
||||||
|
|
||||||
runner
|
runner
|
||||||
.once(EVENT_RUN_BEGIN, () => {
|
.once(EVENT_RUN_BEGIN, () => {
|
||||||
this.emit({ event: 'run-begin', total: runner.total });
|
this.emit({ event: 'run-begin', total: runner.total });
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.on(EVENT_TEST_BEGIN, (test) => {
|
||||||
|
this.currentTest = test;
|
||||||
|
})
|
||||||
|
|
||||||
|
.on(EVENT_TEST_END, (test) => {
|
||||||
|
if (this.currentTest === test) {
|
||||||
|
this.currentTest = null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Optional suite-level event for describe.skip (helps jump to suite definition)
|
// Optional suite-level event for describe.skip (helps jump to suite definition)
|
||||||
.on(EVENT_SUITE_BEGIN, (suite) => {
|
.on(EVENT_SUITE_BEGIN, (suite) => {
|
||||||
if (suite && suite.pending && suite.title) {
|
if (suite && suite.pending && suite.title) {
|
||||||
@@ -78,10 +98,33 @@ class NdjsonReporter {
|
|||||||
})
|
})
|
||||||
|
|
||||||
.once(EVENT_RUN_END, () => {
|
.once(EVENT_RUN_END, () => {
|
||||||
|
process.stdout.write = this.originalStdoutWrite;
|
||||||
this.emit({ event: 'run-end', stats: runner.stats || null });
|
this.emit({ event: 'run-end', stats: runner.stats || null });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
captureStdout(chunk) {
|
||||||
|
if (!this.currentTest) return;
|
||||||
|
const text = Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);
|
||||||
|
if (!this.stdoutByTest.has(this.currentTest)) {
|
||||||
|
this.stdoutByTest.set(this.currentTest, []);
|
||||||
|
}
|
||||||
|
this.stdoutByTest.get(this.currentTest).push(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeStdoutLines(test) {
|
||||||
|
const chunks = this.stdoutByTest.get(test);
|
||||||
|
if (!chunks) return null;
|
||||||
|
this.stdoutByTest.delete(test);
|
||||||
|
const text = chunks.join('');
|
||||||
|
if (!text) return null;
|
||||||
|
const lines = text.split(/\r?\n/);
|
||||||
|
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
||||||
|
lines.pop();
|
||||||
|
}
|
||||||
|
return lines.length > 0 ? lines : null;
|
||||||
|
}
|
||||||
|
|
||||||
inferPendingType(test) {
|
inferPendingType(test) {
|
||||||
// Priority: explicit marker from UI wrappers
|
// Priority: explicit marker from UI wrappers
|
||||||
if (test && test._akPendingType) return test._akPendingType;
|
if (test && test._akPendingType) return test._akPendingType;
|
||||||
@@ -118,13 +161,16 @@ class NdjsonReporter {
|
|||||||
currentRetry: test && typeof test.currentRetry === 'function' ? test.currentRetry() : null,
|
currentRetry: test && typeof test.currentRetry === 'function' ? test.currentRetry() : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const output = this.consumeStdoutLines(test);
|
||||||
|
if (output && output.length > 0) payload.output = output;
|
||||||
|
|
||||||
if (status === 'failed') payload.error = errorObj;
|
if (status === 'failed') payload.error = errorObj;
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(obj) {
|
emit(obj) {
|
||||||
process.stdout.write(JSON.stringify(obj) + '\n');
|
this.originalStdoutWrite(JSON.stringify(obj) + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ describe("test-samurai-mocha-runner", function()
|
|||||||
titlePath = { "Math", "subs" },
|
titlePath = { "Math", "subs" },
|
||||||
file = "/tmp/math.spec.js",
|
file = "/tmp/math.spec.js",
|
||||||
location = { file = "/tmp/math.spec.js", line = 10, column = 5 },
|
location = { file = "/tmp/math.spec.js", line = 10, column = 5 },
|
||||||
|
output = { "log: before", "log: after" },
|
||||||
error = {
|
error = {
|
||||||
message = "oops",
|
message = "oops",
|
||||||
stack = "Error: oops\n at Context.<anonymous> (/tmp/math.spec.js:10:5)\n at processImmediate",
|
stack = "Error: oops\n at Context.<anonymous> (/tmp/math.spec.js:10:5)\n at processImmediate",
|
||||||
@@ -293,6 +294,7 @@ describe("test-samurai-mocha-runner", function()
|
|||||||
titlePath = { "Math", "adds" },
|
titlePath = { "Math", "adds" },
|
||||||
file = "/tmp/math.spec.js",
|
file = "/tmp/math.spec.js",
|
||||||
location = { file = "/tmp/math.spec.js", line = 4, column = 2 },
|
location = { file = "/tmp/math.spec.js", line = 4, column = 2 },
|
||||||
|
output = { "adds log" },
|
||||||
}),
|
}),
|
||||||
vim.json.encode({
|
vim.json.encode({
|
||||||
event = "test",
|
event = "test",
|
||||||
@@ -311,6 +313,10 @@ describe("test-samurai-mocha-runner", function()
|
|||||||
assert.is_true(outputs["Math/subs"] ~= nil)
|
assert.is_true(outputs["Math/subs"] ~= nil)
|
||||||
assert.is_true(outputs["Math/adds"] ~= nil)
|
assert.is_true(outputs["Math/adds"] ~= nil)
|
||||||
assert.is_true(outputs["Math/skips"] ~= nil)
|
assert.is_true(outputs["Math/skips"] ~= nil)
|
||||||
|
assert.equals("log: before", outputs["Math/subs"][1])
|
||||||
|
assert.equals("log: after", outputs["Math/subs"][2])
|
||||||
|
assert.equals("adds log", outputs["Math/adds"][1])
|
||||||
|
assert.equals("skipped", outputs["Math/skips"][1])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("does not return results on complete", function()
|
it("does not return results on complete", function()
|
||||||
|
|||||||
Reference in New Issue
Block a user