include stdout into the detail-float
All checks were successful
tests / test (push) Successful in 7s

This commit is contained in:
2026-01-06 12:27:19 +01:00
parent aa343f67ca
commit e4b4097999
4 changed files with 143 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ Main plugin: https://gitea.mschirmer.com/m13r/test-samurai.nvim
- Builds `npx jest` commands for nearest, file, all, and failed-only runs.
- Streams results via a custom Jest reporter using `onTestCaseResult`.
- Uses `--testLocationInResults` for Quickfix and `<leader>o` support.
- Captures `console.*` output per test case and shows it in the detail float.
## Installation (lazy.nvim)
@@ -60,3 +61,4 @@ Use the standard `test-samurai.nvim` commands (e.g. `TSamNearest`, `TSamFile`, `
- The reporter lives at `reporter/test_samurai_jest_reporter.js` and is loaded via `--reporters`.
- Test names are reported as `Describe/It` for grouping in the listing.
- Stdout capture uses `reporter/test_samurai_jest_stdout.js` via `--setupFilesAfterEnv` and tags output as `TSAMURAI_STDOUT`.

View File

@@ -4,6 +4,7 @@ local runner = {
}
local RESULT_PREFIX = "TSAMURAI_RESULT "
local STDOUT_PREFIX = "TSAMURAI_STDOUT "
local STATUS_MAP = {
passed = "passes",
failed = "failures",
@@ -341,6 +342,15 @@ local function reporter_path()
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_jest_reporter.js")
end
local function setup_path()
local source = debug.getinfo(1, "S").source
if source:sub(1, 1) == "@" then
source = source:sub(2)
end
local dir = vim.fs.dirname(source)
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_jest_stdout.js")
end
local function base_cmd()
return {
"npx",
@@ -348,6 +358,8 @@ local function base_cmd()
"--testLocationInResults",
"--reporters",
reporter_path(),
"--setupFilesAfterEnv",
setup_path(),
}
end
@@ -366,6 +378,21 @@ local function parse_result_line(line)
return data
end
local function parse_stdout_line(line)
if not line or line == "" then
return nil
end
if line:sub(1, #STDOUT_PREFIX) ~= STDOUT_PREFIX then
return nil
end
local payload = line:sub(#STDOUT_PREFIX + 1)
local ok, data = pcall(vim.json.decode, payload)
if not ok or type(data) ~= "table" then
return nil
end
return data
end
local function update_location_cache(name, data)
if not name or name == "" then
return
@@ -675,9 +702,14 @@ function runner.parse_test_output(output)
if not output or output == "" then
return out
end
local jest_to_listing = {}
local pending = {}
for line in output:gmatch("[^\n]+") do
local data = parse_result_line(line)
if data and data.name and data.output then
if data.jestName and data.jestName ~= "" then
jest_to_listing[data.jestName] = data.name
end
out[data.name] = out[data.name] or {}
if type(data.output) == "string" then
for _, item in ipairs(split_output_lines(data.output)) do
@@ -691,6 +723,49 @@ function runner.parse_test_output(output)
end
end
end
local stdout = parse_stdout_line(line)
if stdout and (stdout.name or stdout.jestName) and stdout.output then
local stdout_name = stdout.name or stdout.jestName
local target = jest_to_listing[stdout_name] or stdout_name
if not jest_to_listing[stdout_name] then
pending[stdout_name] = pending[stdout_name] or {}
table.insert(pending[stdout_name], stdout.output)
else
out[target] = out[target] or {}
if type(stdout.output) == "string" then
for _, item in ipairs(split_output_lines(stdout.output)) do
if item ~= "" then
table.insert(out[target], item)
end
end
elseif type(stdout.output) == "table" then
for _, item in ipairs(stdout.output) do
if item and item ~= "" then
table.insert(out[target], item)
end
end
end
end
end
end
for jest_name, outputs in pairs(pending) do
local target = jest_to_listing[jest_name] or jest_name
out[target] = out[target] or {}
for _, items in ipairs(outputs) do
if type(items) == "string" then
for _, item in ipairs(split_output_lines(items)) do
if item ~= "" then
table.insert(out[target], item)
end
end
elseif type(items) == "table" then
for _, item in ipairs(items) do
if item and item ~= "" then
table.insert(out[target], item)
end
end
end
end
end
return out
end

View File

@@ -0,0 +1,36 @@
'use strict';
const util = require('util');
const STDOUT_PREFIX = 'TSAMURAI_STDOUT ';
function getCurrentTestName() {
if (typeof expect !== 'function') return null;
try {
const state = expect.getState();
if (state && state.currentTestName) {
return String(state.currentTestName);
}
} catch (_err) {
return null;
}
return null;
}
function emitStdout(name, args) {
if (!name) return;
const message = util.format(...args);
if (message === '') return;
process.stdout.write(`${STDOUT_PREFIX}${JSON.stringify({ jestName: name, output: message })}\n`);
}
function wrapConsoleMethod(method) {
if (typeof console[method] !== 'function') return;
const original = console[method].bind(console);
console[method] = (...args) => {
emitStdout(getCurrentTestName(), args);
return original(...args);
};
}
['log', 'info', 'warn', 'error', 'debug'].forEach(wrapConsoleMethod);

View File

@@ -41,6 +41,15 @@ local function get_reporter_path()
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_jest_reporter.js")
end
local function get_setup_path()
local source = debug.getinfo(runner.build_command, "S").source
if source:sub(1, 1) == "@" then
source = source:sub(2)
end
local dir = vim.fs.dirname(source)
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_jest_stdout.js")
end
describe("test-samurai-jest-runner", function()
it("detects Jest test files by suffix and package.json", function()
with_project(JEST_PACKAGE, function(root)
@@ -176,6 +185,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
cmd_spec.cmd[#cmd_spec.cmd],
},
@@ -200,6 +211,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
"/tmp/math.test.js",
"--testNamePattern",
@@ -226,6 +239,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
"/tmp/math.test.js",
"--testNamePattern",
@@ -253,6 +268,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
root .. "/foo.test.ts",
},
@@ -276,6 +293,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
},
cmd_spec.cmd
)
@@ -291,6 +310,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
"/tmp/math.test.js",
"--testNamePattern",
@@ -310,6 +331,8 @@ describe("test-samurai-jest-runner", function()
"--testLocationInResults",
"--reporters",
get_reporter_path(),
"--setupFilesAfterEnv",
get_setup_path(),
"--runTestsByPath",
"/tmp/math.test.js",
"--testNamePattern",
@@ -395,18 +418,24 @@ describe("test-samurai-jest-runner", function()
local output = table.concat({
"TSAMURAI_RESULT " .. vim.json.encode({
name = "Math/adds",
jestName = "Math adds",
status = "failed",
output = { "line1", "line2" },
}),
"TSAMURAI_STDOUT " .. vim.json.encode({
jestName = "Math adds",
output = "log line",
}),
"TSAMURAI_RESULT " .. vim.json.encode({
name = "Math/adds",
jestName = "Math adds",
status = "failed",
output = { "line3" },
}),
}, "\n")
local results = runner.parse_test_output(output)
assert.are.same({ "line1", "line2", "line3" }, results["Math/adds"])
assert.are.same({ "line1", "line2", "log line", "line3" }, results["Math/adds"])
end)
it("collect_failed_locations uses cached locations", function()