fix false detecting skipped tests
tests / test (push) Failing after 6s

This commit is contained in:
2026-06-12 12:13:29 +02:00
parent 770e49d98a
commit 279fc56ee5
3 changed files with 156 additions and 47 deletions
+52 -32
View File
@@ -268,7 +268,30 @@ local function reporter_path()
source = source:sub(2) source = source:sub(2)
end end
local dir = vim.fs.dirname(source) local dir = vim.fs.dirname(source)
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_vitest_reporter.js") return vim.fs.normalize(dir .. "/../../reporter/test_samurai_vitest_reporter.mjs")
end
local function vitest_bin(cwd)
if cwd and cwd ~= "" then
local bin = vim.fs.normalize(cwd .. "/node_modules/.bin/vitest")
if vim.fn.executable(bin) == 1 then
return { bin }
end
end
return { "npx", "vitest" }
end
local function find_vitest_config(cwd)
if not cwd or cwd == "" then
return nil
end
for _, name in ipairs({ "vitest.config.ts", "vitest.config.js" }) do
local candidate = cwd .. "/" .. name
if vim.fn.filereadable(candidate) == 1 then
return candidate
end
end
return nil
end end
local function find_vitest_setup(cwd) local function find_vitest_setup(cwd)
@@ -298,17 +321,15 @@ local VITEST_CONFIG_MARKERS = {
} }
local function base_cmd(cwd) local function base_cmd(cwd)
local cmd = { local bin = vitest_bin(cwd)
"npx", local cmd = vim.list_extend(vim.deepcopy(bin), { "run", "--reporter", reporter_path() })
"vitest", local cfg = find_vitest_config(cwd)
"run", if cfg then
"--reporter", vim.list_extend(cmd, { "--config", cfg })
reporter_path(), end
}
local setup = find_vitest_setup(cwd) local setup = find_vitest_setup(cwd)
if setup then if setup then
table.insert(cmd, "--setupFiles") vim.list_extend(cmd, { "--setupFiles", setup })
table.insert(cmd, setup)
end end
return cmd return cmd
end end
@@ -584,35 +605,34 @@ function runner.parse_results(output)
if not output or output == "" then if not output or output == "" then
return { passes = {}, failures = {}, skips = {}, display = { passes = {}, failures = {}, skips = {} } } return { passes = {}, failures = {}, skips = {}, display = { passes = {}, failures = {}, skips = {} } }
end end
local results_by_name = {}
for line in output:gmatch("[^\n]+") do
local data = parse_result_line(line)
if data and data.name and data.status then
results_by_name[data.name] = data
end
end
local passes = {} local passes = {}
local failures = {} local failures = {}
local skips = {} local skips = {}
local pass_display = {} local pass_display = {}
local fail_display = {} local fail_display = {}
local skip_display = {} local skip_display = {}
local seen = { for name, data in pairs(results_by_name) do
passes = {}, local kind = STATUS_MAP[data.status]
failures = {}, if kind then
skips = {}, if kind == "passes" then
} table.insert(passes, name)
for line in output:gmatch("[^\n]+") do pass_display[name] = name
local data = parse_result_line(line) elseif kind == "failures" then
if data and data.name and data.status then table.insert(failures, name)
local kind = STATUS_MAP[data.status] fail_display[name] = name
if kind and not seen[kind][data.name] then else
seen[kind][data.name] = true table.insert(skips, name)
if kind == "passes" then skip_display[name] = name
table.insert(passes, data.name)
pass_display[data.name] = data.name
elseif kind == "failures" then
table.insert(failures, data.name)
fail_display[data.name] = data.name
else
table.insert(skips, data.name)
skip_display[data.name] = data.name
end
update_location_cache(data.name, data)
end end
update_location_cache(name, data)
end end
end end
@@ -1,5 +1,5 @@
/** /**
* test_samurai_vitest_reporter.js * test_samurai_vitest_reporter.mjs
* *
* Custom Vitest 2.x reporter for test-samurai. * Custom Vitest 2.x reporter for test-samurai.
* *
@@ -21,8 +21,10 @@ const RESULT_PREFIX = 'TSAMURAI_RESULT ';
export default class TestSamuraiVitestReporter { export default class TestSamuraiVitestReporter {
onTestCaseResult(testCase) { onTestCaseResult(testCase) {
const name = buildListingName(testCase); const name = buildListingName(testCase);
const status = mapStatus(testCase.result?.state); const status = mapStatus(testCase.task);
const output = buildOutput(testCase); if (!status) return;
const output = buildOutput(testCase.task);
const payload = { const payload = {
name, name,
@@ -52,24 +54,27 @@ function buildListingName(testCase) {
} }
/** /**
* Maps Vitest result states to test-samurai status strings. * Maps Vitest task result states to test-samurai status strings.
* Falls back to task.mode if result.state is missing.
*/ */
function mapStatus(state) { function mapStatus(task) {
const map = { if (!task) return null;
pass: 'passed', const state = task.result?.state;
fail: 'failed', if (state === 'pass') return 'passed';
skip: 'skipped', if (state === 'fail') return 'failed';
todo: 'skipped', if (state === 'skip') return 'skipped';
}; const mode = task.mode;
return map[state] ?? 'skipped'; if (mode === 'skip' || mode === 'todo') return 'skipped';
return null;
} }
/** /**
* Collects error messages and stack traces for failed tests. * Collects error messages and stack traces for failed tests.
*/ */
function buildOutput(testCase) { function buildOutput(task) {
const lines = []; const lines = [];
const errors = testCase.result?.errors ?? []; if (!task) return lines;
const errors = task.result?.errors ?? [];
for (const err of errors) { for (const err of errors) {
if (typeof err.message === 'string' && err.message.length > 0) { if (typeof err.message === 'string' && err.message.length > 0) {
err.message.split('\n').forEach((line) => { err.message.split('\n').forEach((line) => {
+85 -1
View File
@@ -38,7 +38,7 @@ local function get_reporter_path()
source = source:sub(2) source = source:sub(2)
end end
local dir = vim.fs.dirname(source) local dir = vim.fs.dirname(source)
return vim.fs.normalize(dir .. "/../../reporter/test_samurai_vitest_reporter.js") return vim.fs.normalize(dir .. "/../../reporter/test_samurai_vitest_reporter.mjs")
end end
describe("test-samurai-vitest-runner", function() describe("test-samurai-vitest-runner", function()
@@ -539,4 +539,88 @@ describe("test-samurai-vitest-runner", function()
local result = parser.on_line("some random vitest output line", state) local result = parser.on_line("some random vitest output line", state)
assert.is_nil(result) assert.is_nil(result)
end) end)
it("parse_results deduplicates across buckets (first skipped, then passed)", function()
local output = "TSAMURAI_RESULT " .. vim.json.encode({
name = "footer/evergreen",
status = "skipped",
file = "/tmp/footer.test.ts",
location = { line = 5, column = 1 },
}) .. "\n"
.. "TSAMURAI_RESULT " .. vim.json.encode({
name = "footer/evergreen",
status = "passed",
file = "/tmp/footer.test.ts",
location = { line = 5, column = 1 },
})
local results = runner.parse_results(output)
assert.equals(1, #results.passes)
assert.equals(0, #results.skips)
assert.equals("footer/evergreen", results.passes[1])
end)
it("build_command uses local node_modules/.bin/vitest if available", function()
with_project(VITEST_PACKAGE, function(root)
local bin_dir = root .. "/node_modules/.bin"
vim.fn.mkdir(bin_dir, "p")
write_file(bin_dir .. "/vitest", "#!/bin/sh\necho mock")
vim.fn.setfperm(bin_dir .. "/vitest", "rwxr-xr-x")
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, root .. "/example.test.ts")
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
'describe("footer", function()',
' it("evergreen", function()',
' assert.true(true)',
' end)',
'end)',
})
local spec = runner.find_nearest(bufnr, 2, 0)
local cmd = runner.build_command(spec).cmd
assert.equals(root .. "/node_modules/.bin/vitest", cmd[1])
end)
end)
it("base_cmd includes --config for vitest.config.ts", function()
with_project(VITEST_PACKAGE, function(root)
write_file(root .. "/vitest.config.ts", "export default { }")
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, root .. "/example.test.ts")
local cmd = runner.build_file_command(bufnr).cmd
local has_config = false
for i, arg in ipairs(cmd) do
if arg == "--config" and cmd[i + 1] and cmd[i + 1]:match("vitest%.config%.ts$") then
has_config = true
break
end
end
assert.is_true(has_config)
end)
end)
it("reporter_path() returns .mjs file", function()
local path = get_reporter_path()
assert.is_true(path:match("test_samurai_vitest_reporter%.mjs$") ~= nil)
end)
it("build_file_command includes .mjs reporter path", function()
with_project(VITEST_PACKAGE, function(root)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, root .. "/example.test.ts")
local cmd = runner.build_file_command(bufnr).cmd
local has_mjs_reporter = false
for _, arg in ipairs(cmd) do
if arg:match("test_samurai_vitest_reporter%.mjs$") then
has_mjs_reporter = true
break
end
end
assert.is_true(has_mjs_reporter)
end)
end)
end) end)