create colored summary output for all commands
This commit is contained in:
@@ -14,9 +14,47 @@ local state = {
|
|||||||
last_scope_kind = nil,
|
last_scope_kind = nil,
|
||||||
last_scope_exit_code = nil,
|
last_scope_exit_code = nil,
|
||||||
last_scope_failures = nil,
|
last_scope_failures = nil,
|
||||||
|
last_border_kind = "default",
|
||||||
autocmds_set = false,
|
autocmds_set = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local summary_ns = vim.api.nvim_create_namespace("TestSamuraiSummary")
|
||||||
|
local result_ns = vim.api.nvim_create_namespace("TestSamuraiResult")
|
||||||
|
|
||||||
|
local function get_hl_fg(name)
|
||||||
|
local ok, hl = pcall(vim.api.nvim_get_hl, 0, { name = name, link = true })
|
||||||
|
if ok and type(hl) == "table" and hl.fg then
|
||||||
|
return hl.fg
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolve_hl_fg(names, fallback)
|
||||||
|
for _, name in ipairs(names or {}) do
|
||||||
|
local fg = get_hl_fg(name)
|
||||||
|
if fg then
|
||||||
|
return fg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return fallback
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_summary_highlights()
|
||||||
|
local normal_fg = get_hl_fg("Normal")
|
||||||
|
local pass_fg = resolve_hl_fg({ "DiffAdd", "DiagnosticOk" }, normal_fg)
|
||||||
|
local fail_fg = resolve_hl_fg({ "DiffDelete", "DiagnosticError" }, normal_fg)
|
||||||
|
local skip_fg = resolve_hl_fg({ "DiagnosticInfo", "DiagnosticHint" }, normal_fg)
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiSummaryBold", { fg = normal_fg, bold = true })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiSummaryPass", { fg = pass_fg, bold = true })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiSummaryFail", { fg = fail_fg, bold = true })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiSummarySkip", { fg = skip_fg, bold = true })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiResultPass", { fg = pass_fg })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiResultFail", { fg = fail_fg })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiResultSkip", { fg = skip_fg })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiBorderPass", { fg = pass_fg, bold = true })
|
||||||
|
pcall(vim.api.nvim_set_hl, 0, "TestSamuraiBorderFail", { fg = fail_fg, bold = true })
|
||||||
|
end
|
||||||
|
|
||||||
local function load_runners()
|
local function load_runners()
|
||||||
state.runners = {}
|
state.runners = {}
|
||||||
local opts = config.get()
|
local opts = config.get()
|
||||||
@@ -51,12 +89,20 @@ local function ensure_output_autocmds()
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("ColorScheme", {
|
||||||
|
group = group,
|
||||||
|
callback = function()
|
||||||
|
setup_summary_highlights()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
state.autocmds_set = true
|
state.autocmds_set = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.setup()
|
function M.setup()
|
||||||
load_runners()
|
load_runners()
|
||||||
ensure_output_autocmds()
|
ensure_output_autocmds()
|
||||||
|
setup_summary_highlights()
|
||||||
state.last_command = nil
|
state.last_command = nil
|
||||||
state.last_runner = nil
|
state.last_runner = nil
|
||||||
state.last_scope_command = nil
|
state.last_scope_command = nil
|
||||||
@@ -64,6 +110,7 @@ function M.setup()
|
|||||||
state.last_scope_kind = nil
|
state.last_scope_kind = nil
|
||||||
state.last_scope_exit_code = nil
|
state.last_scope_exit_code = nil
|
||||||
state.last_scope_failures = nil
|
state.last_scope_failures = nil
|
||||||
|
state.last_border_kind = "default"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.reload_runners()
|
function M.reload_runners()
|
||||||
@@ -176,6 +223,19 @@ local function float_geometry()
|
|||||||
return width, height, row, col
|
return width, height, row, col
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function apply_border_kind(win, kind)
|
||||||
|
if not (win and vim.api.nvim_win_is_valid(win)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if kind == "pass" then
|
||||||
|
vim.api.nvim_win_set_option(win, "winhighlight", "FloatBorder:TestSamuraiBorderPass")
|
||||||
|
elseif kind == "fail" then
|
||||||
|
vim.api.nvim_win_set_option(win, "winhighlight", "FloatBorder:TestSamuraiBorderFail")
|
||||||
|
else
|
||||||
|
vim.api.nvim_win_set_option(win, "winhighlight", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function create_output_win(initial_lines)
|
local function create_output_win(initial_lines)
|
||||||
if state.last_win and vim.api.nvim_win_is_valid(state.last_win) then
|
if state.last_win and vim.api.nvim_win_is_valid(state.last_win) then
|
||||||
pcall(vim.api.nvim_win_close, state.last_win, true)
|
pcall(vim.api.nvim_win_close, state.last_win, true)
|
||||||
@@ -215,6 +275,7 @@ local function create_output_win(initial_lines)
|
|||||||
|
|
||||||
state.last_win = win
|
state.last_win = win
|
||||||
state.last_buf = buf
|
state.last_buf = buf
|
||||||
|
apply_border_kind(win, state.last_border_kind)
|
||||||
|
|
||||||
return buf, win
|
return buf, win
|
||||||
end
|
end
|
||||||
@@ -251,6 +312,7 @@ local function reopen_output_win()
|
|||||||
end, { buffer = state.last_buf, nowait = true, silent = true })
|
end, { buffer = state.last_buf, nowait = true, silent = true })
|
||||||
|
|
||||||
state.last_win = win
|
state.last_win = win
|
||||||
|
apply_border_kind(win, state.last_border_kind)
|
||||||
|
|
||||||
return state.last_buf, win
|
return state.last_buf, win
|
||||||
end
|
end
|
||||||
@@ -344,6 +406,137 @@ local function format_results(results, scope_kind)
|
|||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function make_summary_tracker(enabled)
|
||||||
|
if not enabled then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
passes = {},
|
||||||
|
failures = {},
|
||||||
|
skips = {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_unique_items(target, items)
|
||||||
|
if type(items) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
if item and item ~= "" then
|
||||||
|
target[item] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_summary(summary, results)
|
||||||
|
if not summary or not results then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
add_unique_items(summary.passes, results.passes)
|
||||||
|
add_unique_items(summary.failures, results.failures)
|
||||||
|
add_unique_items(summary.skips, results.skips)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function count_summary(summary)
|
||||||
|
if not summary then
|
||||||
|
return 0, 0, 0, 0
|
||||||
|
end
|
||||||
|
local function count(set)
|
||||||
|
local total = 0
|
||||||
|
for _ in pairs(set) do
|
||||||
|
total = total + 1
|
||||||
|
end
|
||||||
|
return total
|
||||||
|
end
|
||||||
|
local pass_count = count(summary.passes)
|
||||||
|
local fail_count = count(summary.failures)
|
||||||
|
local skip_count = count(summary.skips)
|
||||||
|
return pass_count, fail_count, skip_count, pass_count + fail_count + skip_count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function format_summary_lines(summary, elapsed_seconds)
|
||||||
|
local pass_count, fail_count, skip_count, total = count_summary(summary)
|
||||||
|
local minutes = 0
|
||||||
|
local seconds = 0
|
||||||
|
if type(elapsed_seconds) == "number" and elapsed_seconds >= 0 then
|
||||||
|
local total_seconds = math.floor(elapsed_seconds + 0.0000001)
|
||||||
|
minutes = math.floor(total_seconds / 60)
|
||||||
|
seconds = total_seconds % 60
|
||||||
|
end
|
||||||
|
local duration = string.format("%02dm %02ds", minutes, seconds)
|
||||||
|
return {
|
||||||
|
"",
|
||||||
|
string.format("TOTAL %d", total),
|
||||||
|
string.format("DURATION %s", duration),
|
||||||
|
"",
|
||||||
|
string.format(" PASS %d - SKIPPED %d - FAILED %d", pass_count, skip_count, fail_count),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function highlight_label_number(buf, ns, lnum, line, label, hl_group)
|
||||||
|
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local label_start = line:find(label, 1, true)
|
||||||
|
if not label_start then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local num_start, num_end = line:find("%d+", label_start + #label)
|
||||||
|
if not num_start then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.api.nvim_buf_add_highlight(buf, ns, hl_group, lnum, num_start - 1, num_end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function highlight_label_word(buf, ns, lnum, line, label, hl_group)
|
||||||
|
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local label_start = line:find(label, 1, true)
|
||||||
|
if not label_start then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.api.nvim_buf_add_highlight(buf, ns, hl_group, lnum, label_start - 1, label_start - 1 + #label)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_summary_highlights(buf, start_line, lines)
|
||||||
|
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i, line in ipairs(lines or {}) do
|
||||||
|
local lnum = start_line + i - 1
|
||||||
|
if line:match("^%s*PASS%s+%d+") then
|
||||||
|
highlight_label_word(buf, summary_ns, lnum, line, "PASS", "TestSamuraiSummaryPass")
|
||||||
|
highlight_label_number(buf, summary_ns, lnum, line, "PASS", "TestSamuraiSummaryPass")
|
||||||
|
highlight_label_word(buf, summary_ns, lnum, line, "SKIPPED", "TestSamuraiSummarySkip")
|
||||||
|
highlight_label_number(buf, summary_ns, lnum, line, "SKIPPED", "TestSamuraiSummarySkip")
|
||||||
|
highlight_label_word(buf, summary_ns, lnum, line, "FAILED", "TestSamuraiSummaryFail")
|
||||||
|
highlight_label_number(buf, summary_ns, lnum, line, "FAILED", "TestSamuraiSummaryFail")
|
||||||
|
elseif line:match("^TOTAL%s+%d+") then
|
||||||
|
highlight_label_word(buf, summary_ns, lnum, line, "TOTAL", "TestSamuraiSummaryBold")
|
||||||
|
highlight_label_number(buf, summary_ns, lnum, line, "TOTAL", "TestSamuraiSummaryBold")
|
||||||
|
elseif line:match("^DURATION%s+") then
|
||||||
|
highlight_label_word(buf, summary_ns, lnum, line, "DURATION", "TestSamuraiSummaryBold")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_result_highlights(buf, start_line, lines)
|
||||||
|
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i, line in ipairs(lines or {}) do
|
||||||
|
local lnum = start_line + i - 1
|
||||||
|
if line:match("^%[ PASS %]") then
|
||||||
|
highlight_label_word(buf, result_ns, lnum, line, "PASS", "TestSamuraiResultPass")
|
||||||
|
elseif line:match("^%[ FAIL %]") then
|
||||||
|
highlight_label_word(buf, result_ns, lnum, line, "FAIL", "TestSamuraiResultFail")
|
||||||
|
elseif line:match("^%[ SKIP %]") then
|
||||||
|
highlight_label_word(buf, result_ns, lnum, line, "SKIP", "TestSamuraiResultSkip")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function run_command(command, opts)
|
local function run_command(command, opts)
|
||||||
local options = opts or {}
|
local options = opts or {}
|
||||||
if command and type(command.cmd) == "table" and #command.cmd > 0 then
|
if command and type(command.cmd) == "table" and #command.cmd > 0 then
|
||||||
@@ -378,6 +571,14 @@ local function run_command(command, opts)
|
|||||||
local parser_state = {}
|
local parser_state = {}
|
||||||
parser_state.scope_kind = options.scope_kind
|
parser_state.scope_kind = options.scope_kind
|
||||||
local had_parsed_output = false
|
local had_parsed_output = false
|
||||||
|
local summary_enabled = options.scope_kind == "file" or options.scope_kind == "all" or options.scope_kind == "nearest"
|
||||||
|
local summary = make_summary_tracker(summary_enabled)
|
||||||
|
local result_counts = make_summary_tracker(true)
|
||||||
|
state.last_border_kind = "default"
|
||||||
|
local start_time = nil
|
||||||
|
if vim.loop and vim.loop.hrtime then
|
||||||
|
start_time = vim.loop.hrtime()
|
||||||
|
end
|
||||||
|
|
||||||
local output_lines = {}
|
local output_lines = {}
|
||||||
|
|
||||||
@@ -410,6 +611,8 @@ local function run_command(command, opts)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
had_parsed_output = true
|
had_parsed_output = true
|
||||||
|
update_summary(summary, results)
|
||||||
|
update_summary(result_counts, results)
|
||||||
if options.track_scope then
|
if options.track_scope then
|
||||||
if results.failures_all ~= nil then
|
if results.failures_all ~= nil then
|
||||||
state.last_scope_failures = results.failures_all
|
state.last_scope_failures = results.failures_all
|
||||||
@@ -422,7 +625,9 @@ local function run_command(command, opts)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
ensure_output_started()
|
ensure_output_started()
|
||||||
|
local start_line = vim.api.nvim_buf_line_count(buf)
|
||||||
append_lines(buf, lines)
|
append_lines(buf, lines)
|
||||||
|
apply_result_highlights(buf, start_line, lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
run_cmd(cmd, cwd, {
|
run_cmd(cmd, cwd, {
|
||||||
@@ -472,6 +677,15 @@ local function run_command(command, opts)
|
|||||||
handle_parsed(results)
|
handle_parsed(results)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local pass_count, fail_count = count_summary(result_counts)
|
||||||
|
if fail_count > 0 then
|
||||||
|
state.last_border_kind = "fail"
|
||||||
|
elseif pass_count > 0 then
|
||||||
|
state.last_border_kind = "pass"
|
||||||
|
else
|
||||||
|
state.last_border_kind = "default"
|
||||||
|
end
|
||||||
|
apply_border_kind(state.last_win, state.last_border_kind)
|
||||||
if parser then
|
if parser then
|
||||||
if not had_parsed_output and #output_lines > 0 then
|
if not had_parsed_output and #output_lines > 0 then
|
||||||
ensure_output_started()
|
ensure_output_started()
|
||||||
@@ -479,6 +693,17 @@ local function run_command(command, opts)
|
|||||||
elseif not has_output then
|
elseif not has_output then
|
||||||
ensure_output_started()
|
ensure_output_started()
|
||||||
end
|
end
|
||||||
|
if summary_enabled then
|
||||||
|
local elapsed = nil
|
||||||
|
if start_time and vim.loop and vim.loop.hrtime then
|
||||||
|
elapsed = (vim.loop.hrtime() - start_time) / 1000000000
|
||||||
|
end
|
||||||
|
ensure_output_started()
|
||||||
|
local summary_lines = format_summary_lines(summary, elapsed)
|
||||||
|
local start_line = vim.api.nvim_buf_line_count(buf)
|
||||||
|
append_lines(buf, summary_lines)
|
||||||
|
apply_summary_highlights(buf, start_line, summary_lines)
|
||||||
|
end
|
||||||
append_lines(buf, { "", "[exit code] " .. tostring(code) })
|
append_lines(buf, { "", "[exit code] " .. tostring(code) })
|
||||||
else
|
else
|
||||||
if not has_output then
|
if not has_output then
|
||||||
@@ -487,6 +712,17 @@ local function run_command(command, opts)
|
|||||||
vim.api.nvim_buf_set_lines(buf, #cur - 1, #cur, false, {})
|
vim.api.nvim_buf_set_lines(buf, #cur - 1, #cur, false, {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if summary_enabled then
|
||||||
|
local elapsed = nil
|
||||||
|
if start_time and vim.loop and vim.loop.hrtime then
|
||||||
|
elapsed = (vim.loop.hrtime() - start_time) / 1000000000
|
||||||
|
end
|
||||||
|
ensure_output_started()
|
||||||
|
local summary_lines = format_summary_lines(summary, elapsed)
|
||||||
|
local start_line = vim.api.nvim_buf_line_count(buf)
|
||||||
|
append_lines(buf, summary_lines)
|
||||||
|
apply_summary_highlights(buf, start_line, summary_lines)
|
||||||
|
end
|
||||||
append_lines(buf, { "", "[exit code] " .. tostring(code) })
|
append_lines(buf, { "", "[exit code] " .. tostring(code) })
|
||||||
end
|
end
|
||||||
if options.track_scope then
|
if options.track_scope then
|
||||||
|
|||||||
@@ -164,6 +164,161 @@ describe("test-samurai output formatting", function()
|
|||||||
assert.is_true(has_fail)
|
assert.is_true(has_fail)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("applies PASS/FAIL/SKIP highlights to parsed output", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
{ status = "skipped", title = "inner skip", fullName = "outer inner skip" },
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 1, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_highlight_parsed.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"",
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 7, 0 })
|
||||||
|
|
||||||
|
core.run_nearest()
|
||||||
|
|
||||||
|
local out_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local ns = vim.api.nvim_create_namespace("TestSamuraiResult")
|
||||||
|
local extmarks = vim.api.nvim_buf_get_extmarks(out_buf, ns, 0, -1, { details = true })
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
|
||||||
|
local groups = {}
|
||||||
|
for _, mark in ipairs(extmarks) do
|
||||||
|
local details = mark[4] or {}
|
||||||
|
if details.hl_group then
|
||||||
|
groups[details.hl_group] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(groups.TestSamuraiResultPass)
|
||||||
|
assert.is_true(groups.TestSamuraiResultFail)
|
||||||
|
assert.is_true(groups.TestSamuraiResultSkip)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sets pass border when there are only passing tests", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 0, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_border_pass.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 3, 0 })
|
||||||
|
|
||||||
|
core.run_nearest()
|
||||||
|
|
||||||
|
local win = vim.api.nvim_get_current_win()
|
||||||
|
local hl = vim.api.nvim_win_get_option(win, "winhighlight")
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
|
||||||
|
assert.equals("FloatBorder:TestSamuraiBorderPass", hl)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sets fail border when there are failing tests", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 1, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_border_fail.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 3, 0 })
|
||||||
|
|
||||||
|
core.run_nearest()
|
||||||
|
|
||||||
|
local win = vim.api.nvim_get_current_win()
|
||||||
|
local hl = vim.api.nvim_win_get_option(win, "winhighlight")
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
|
||||||
|
assert.equals("FloatBorder:TestSamuraiBorderFail", hl)
|
||||||
|
end)
|
||||||
|
|
||||||
it("does not print raw JSON output for jest runs", function()
|
it("does not print raw JSON output for jest runs", function()
|
||||||
local json = vim.json.encode({
|
local json = vim.json.encode({
|
||||||
testResults = {
|
testResults = {
|
||||||
@@ -334,7 +489,7 @@ describe("test-samurai output formatting", function()
|
|||||||
has_skip = true
|
has_skip = true
|
||||||
elseif line == "[ FAIL ] - inner 2" then
|
elseif line == "[ FAIL ] - inner 2" then
|
||||||
has_fail = true
|
has_fail = true
|
||||||
elseif line:match("^%s*PASS%s+") then
|
elseif line:match("^%s*PASS%s+") and line ~= " PASS 1 - SKIPPED 1 - FAILED 1" then
|
||||||
has_raw_verbose = true
|
has_raw_verbose = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -468,7 +623,7 @@ describe("test-samurai output formatting", function()
|
|||||||
has_skip = true
|
has_skip = true
|
||||||
elseif line == "[ FAIL ] - inner 2" then
|
elseif line == "[ FAIL ] - inner 2" then
|
||||||
has_fail = true
|
has_fail = true
|
||||||
elseif line:match("^%s*PASS%s+") then
|
elseif line:match("^%s*PASS%s+") and line ~= " PASS 1 - SKIPPED 1 - FAILED 1" then
|
||||||
has_raw_verbose = true
|
has_raw_verbose = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -479,6 +634,365 @@ describe("test-samurai output formatting", function()
|
|||||||
assert.is_false(has_raw_verbose)
|
assert.is_false(has_raw_verbose)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("adds a summary line for TSamFile output", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
{ status = "skipped", title = "inner skip", fullName = "outer inner skip" },
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 0, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local orig_hrtime = vim.loop.hrtime
|
||||||
|
local hr_calls = 0
|
||||||
|
vim.loop.hrtime = function()
|
||||||
|
hr_calls = hr_calls + 1
|
||||||
|
if hr_calls == 1 then
|
||||||
|
return 1000000000
|
||||||
|
end
|
||||||
|
return 3500000000
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_summary_file.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"",
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 7, 0 })
|
||||||
|
|
||||||
|
core.run_file()
|
||||||
|
|
||||||
|
local out_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
vim.loop.hrtime = orig_hrtime
|
||||||
|
|
||||||
|
local has_total = false
|
||||||
|
local has_breakdown = false
|
||||||
|
local has_duration = false
|
||||||
|
local saw_blank = false
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line == "" then
|
||||||
|
saw_blank = true
|
||||||
|
elseif line == "TOTAL 3" then
|
||||||
|
has_total = true
|
||||||
|
elseif line == "DURATION 00m 02s" then
|
||||||
|
has_duration = true
|
||||||
|
elseif line == " PASS 1 - SKIPPED 1 - FAILED 1" then
|
||||||
|
has_breakdown = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(saw_blank)
|
||||||
|
assert.is_true(has_total)
|
||||||
|
assert.is_true(has_breakdown)
|
||||||
|
assert.is_true(has_duration)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("adds a summary line for TSamAll output", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
{ status = "skipped", title = "inner skip", fullName = "outer inner skip" },
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 0, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local orig_hrtime = vim.loop.hrtime
|
||||||
|
local hr_calls = 0
|
||||||
|
vim.loop.hrtime = function()
|
||||||
|
hr_calls = hr_calls + 1
|
||||||
|
if hr_calls == 1 then
|
||||||
|
return 500000000
|
||||||
|
end
|
||||||
|
return 2000000000
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_summary_all.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"",
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 7, 0 })
|
||||||
|
|
||||||
|
core.run_all()
|
||||||
|
|
||||||
|
local out_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
vim.loop.hrtime = orig_hrtime
|
||||||
|
|
||||||
|
local has_total = false
|
||||||
|
local has_breakdown = false
|
||||||
|
local has_duration = false
|
||||||
|
local saw_blank = false
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line == "" then
|
||||||
|
saw_blank = true
|
||||||
|
elseif line == "TOTAL 3" then
|
||||||
|
has_total = true
|
||||||
|
elseif line == "DURATION 00m 01s" then
|
||||||
|
has_duration = true
|
||||||
|
elseif line == " PASS 1 - SKIPPED 1 - FAILED 1" then
|
||||||
|
has_breakdown = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(saw_blank)
|
||||||
|
assert.is_true(has_total)
|
||||||
|
assert.is_true(has_breakdown)
|
||||||
|
assert.is_true(has_duration)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("adds a summary line for TSamNearest output", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
{ status = "skipped", title = "inner skip", fullName = "outer inner skip" },
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 0, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local orig_hrtime = vim.loop.hrtime
|
||||||
|
local hr_calls = 0
|
||||||
|
vim.loop.hrtime = function()
|
||||||
|
hr_calls = hr_calls + 1
|
||||||
|
if hr_calls == 1 then
|
||||||
|
return 2000000000
|
||||||
|
end
|
||||||
|
return 4500000000
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_summary_nearest.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"",
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 3, 0 })
|
||||||
|
|
||||||
|
core.run_nearest()
|
||||||
|
|
||||||
|
local out_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false)
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
vim.loop.hrtime = orig_hrtime
|
||||||
|
|
||||||
|
local has_total = false
|
||||||
|
local has_breakdown = false
|
||||||
|
local has_duration = false
|
||||||
|
local saw_blank = false
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line == "" then
|
||||||
|
saw_blank = true
|
||||||
|
elseif line == "TOTAL 3" then
|
||||||
|
has_total = true
|
||||||
|
elseif line == "DURATION 00m 02s" then
|
||||||
|
has_duration = true
|
||||||
|
elseif line == " PASS 1 - SKIPPED 1 - FAILED 1" then
|
||||||
|
has_breakdown = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(saw_blank)
|
||||||
|
assert.is_true(has_total)
|
||||||
|
assert.is_true(has_breakdown)
|
||||||
|
assert.is_true(has_duration)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("applies summary highlights for TSamAll output", function()
|
||||||
|
local json = vim.json.encode({
|
||||||
|
testResults = {
|
||||||
|
{
|
||||||
|
assertionResults = {
|
||||||
|
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
|
||||||
|
{ status = "skipped", title = "inner skip", fullName = "outer inner skip" },
|
||||||
|
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local orig_jobstart = vim.fn.jobstart
|
||||||
|
vim.fn.jobstart = function(_cmd, opts)
|
||||||
|
if opts and opts.on_stdout then
|
||||||
|
opts.on_stdout(1, { json }, nil)
|
||||||
|
end
|
||||||
|
if opts and opts.on_exit then
|
||||||
|
opts.on_exit(1, 0, nil)
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local orig_hrtime = vim.loop.hrtime
|
||||||
|
local hr_calls = 0
|
||||||
|
vim.loop.hrtime = function()
|
||||||
|
hr_calls = hr_calls + 1
|
||||||
|
if hr_calls == 1 then
|
||||||
|
return 1000000000
|
||||||
|
end
|
||||||
|
return 2500000000
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/output_summary_highlight_all.test.ts")
|
||||||
|
vim.bo[bufnr].filetype = "typescript"
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||||
|
'describe("outer", function() {',
|
||||||
|
' it("inner 1", function() {',
|
||||||
|
" -- inside 1",
|
||||||
|
" })",
|
||||||
|
"",
|
||||||
|
' it("inner 2", function() {',
|
||||||
|
" -- inside 2",
|
||||||
|
" })",
|
||||||
|
"})",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 7, 0 })
|
||||||
|
|
||||||
|
core.run_all()
|
||||||
|
|
||||||
|
local out_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local ns = vim.api.nvim_create_namespace("TestSamuraiSummary")
|
||||||
|
local extmarks = vim.api.nvim_buf_get_extmarks(out_buf, ns, 0, -1, { details = true })
|
||||||
|
|
||||||
|
vim.fn.jobstart = orig_jobstart
|
||||||
|
vim.loop.hrtime = orig_hrtime
|
||||||
|
|
||||||
|
local groups = {}
|
||||||
|
for _, mark in ipairs(extmarks) do
|
||||||
|
local details = mark[4] or {}
|
||||||
|
if details.hl_group then
|
||||||
|
groups[details.hl_group] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.is_true(groups.TestSamuraiSummaryBold)
|
||||||
|
assert.is_true(groups.TestSamuraiSummaryPass)
|
||||||
|
assert.is_true(groups.TestSamuraiSummaryFail)
|
||||||
|
assert.is_true(groups.TestSamuraiSummarySkip)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("derives summary highlight colors from diagnostic fallbacks", function()
|
||||||
|
local orig_get_hl = vim.api.nvim_get_hl
|
||||||
|
local orig_set_hl = vim.api.nvim_set_hl
|
||||||
|
local set_calls = {}
|
||||||
|
|
||||||
|
vim.api.nvim_get_hl = function(_ns, opts)
|
||||||
|
if opts.name == "DiffAdd" or opts.name == "DiffDelete" then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if opts.name == "DiagnosticOk" then
|
||||||
|
return { fg = 111 }
|
||||||
|
end
|
||||||
|
if opts.name == "DiagnosticError" then
|
||||||
|
return { fg = 222 }
|
||||||
|
end
|
||||||
|
if opts.name == "DiagnosticInfo" then
|
||||||
|
return { fg = 333 }
|
||||||
|
end
|
||||||
|
if opts.name == "Normal" then
|
||||||
|
return { fg = 99 }
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_set_hl = function(_ns, name, opts)
|
||||||
|
set_calls[name] = opts
|
||||||
|
end
|
||||||
|
|
||||||
|
core.setup()
|
||||||
|
|
||||||
|
vim.api.nvim_get_hl = orig_get_hl
|
||||||
|
vim.api.nvim_set_hl = orig_set_hl
|
||||||
|
|
||||||
|
assert.is_true(set_calls.TestSamuraiSummaryPass.fg == 111)
|
||||||
|
assert.is_true(set_calls.TestSamuraiSummaryFail.fg == 222)
|
||||||
|
assert.is_true(set_calls.TestSamuraiSummarySkip.fg == 333)
|
||||||
|
end)
|
||||||
|
|
||||||
it("formats go subtests as short names", function()
|
it("formats go subtests as short names", function()
|
||||||
local json_line = vim.json.encode({
|
local json_line = vim.json.encode({
|
||||||
Action = "pass",
|
Action = "pass",
|
||||||
|
|||||||
Reference in New Issue
Block a user