include stdout into the detail-float
All checks were successful
tests / test (push) Successful in 7s
All checks were successful
tests / test (push) Successful in 7s
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
36
reporter/test_samurai_jest_stdout.js
Normal file
36
reporter/test_samurai_jest_stdout.js
Normal 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);
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user