fix missing summary after TSamLast call
All checks were successful
tests / test (push) Successful in 9s

This commit is contained in:
2026-01-09 16:24:55 +01:00
parent 238d5f9634
commit fd3952bedf
4 changed files with 194 additions and 4 deletions

View File

@@ -70,10 +70,12 @@ Additional keymaps:
- `<leader>ss` -> filter the listing to `[ SKIP ] - ...` entries - `<leader>ss` -> filter the listing to `[ SKIP ] - ...` entries
- `<leader>sa` -> clear the listing filter and show all entries - `<leader>sa` -> clear the listing filter and show all entries
- `<leader>tt` -> run the test under the cursor in the listing - `<leader>tt` -> run the test under the cursor in the listing
- `<leader>cb` -> breaks test-command onto multiple lines - `<leader>cb` -> breaks test-command onto multiple lines (clears search highlight)
- `<leader>cj` -> joins test-command onto single line - `<leader>cj` -> joins test-command onto single line
- `?` -> show help with TSam commands and standard keymaps in the Detail-Float - `?` -> show help with TSam commands and standard keymaps in the Detail-Float
Before running any test command, test-samurai runs `:wall` to save all buffers.
## Output UI ## Output UI
- Output is shown in a floating container called **Testing-Float**. - Output is shown in a floating container called **Testing-Float**.
@@ -84,6 +86,7 @@ Additional keymaps:
- ANSI color translation is only applied in the **Detail-Float**; the **Test-Listing-Float** shows raw text without ANSI translation. - ANSI color translation is only applied in the **Detail-Float**; the **Test-Listing-Float** shows raw text without ANSI translation.
- `<esc><esc>` hides the floating window; `TSamShowOutput` reopens it. - `<esc><esc>` hides the floating window; `TSamShowOutput` reopens it.
- If no output is captured for a test, the **Detail-Float** shows `No output captured`. - If no output is captured for a test, the **Detail-Float** shows `No output captured`.
- Summary lines (`TOTAL`/`DURATION`) are appended in the listing output, including `TSamLast`.
## Runner architecture ## Runner architecture

View File

@@ -58,7 +58,7 @@ Additional keymaps:
<leader>ss Filter listing to [ SKIP ] only <leader>ss Filter listing to [ SKIP ] only
<leader>sa Show all listing entries (clear filter) <leader>sa Show all listing entries (clear filter)
<leader>tt Run the test under the cursor in the listing <leader>tt Run the test under the cursor in the listing
<leader>cb breaks test-command onto multiple lines <leader>cb breaks test-command onto multiple lines (clears search highlight)
<leader>cj joins test-command onto single line <leader>cj joins test-command onto single line
<leader>o Jump to test location <leader>o Jump to test location
<leader>z Toggle Detail-Float full width <leader>z Toggle Detail-Float full width
@@ -67,6 +67,9 @@ Additional keymaps:
<esc><esc> Close Testing-Float <esc><esc> Close Testing-Float
<C-c> Close Detail-Float (when focused) <C-c> Close Detail-Float (when focused)
Notes:
Buffers are saved via :wall before every test run.
OUTPUT UI *test-samurai-ui* OUTPUT UI *test-samurai-ui*
@@ -80,6 +83,9 @@ the Detail-Float with a 20/80 split. ANSI colors are translated only
inside the Detail-Float. If no output is captured for a test, the inside the Detail-Float. If no output is captured for a test, the
Detail-Float shows "No output captured". Detail-Float shows "No output captured".
Summary lines (TOTAL/DURATION) are rendered in the listing output,
including after :TSamLast.
RUNNER ARCHITECTURE *test-samurai-runners* RUNNER ARCHITECTURE *test-samurai-runners*

View File

@@ -76,7 +76,7 @@ local function help_lines()
" <C-h> Focus Test-Listing-Float", " <C-h> Focus Test-Listing-Float",
" <leader>z Toggle Detail-Float full width", " <leader>z Toggle Detail-Float full width",
" <leader>o Jump to test location", " <leader>o Jump to test location",
" <leader>cb breaks test-command onto multiple lines", " <leader>cb breaks test-command onto multiple lines (clears search highlight)",
" <leader>cj joins test-command onto single line", " <leader>cj joins test-command onto single line",
" ? Show this help", " ? Show this help",
"", "",
@@ -91,6 +91,7 @@ local function help_lines()
"", "",
"Notes:", "Notes:",
" No output captured -> shows placeholder text", " No output captured -> shows placeholder text",
" Buffers are saved via :wall before every test run",
} }
end end
@@ -1361,6 +1362,7 @@ end
function M.listing_break_on_dashes() function M.listing_break_on_dashes()
apply_listing_substitution([[%s/--/\\\r\t--/g]]) apply_listing_substitution([[%s/--/\\\r\t--/g]])
vim.cmd("noh")
end end
function M.listing_join_backslashes() function M.listing_join_backslashes()
@@ -1885,6 +1887,7 @@ end
run_command = function(command, opts) run_command = function(command, opts)
local options = opts or {} local options = opts or {}
vim.cmd("wall")
state.last_test_outputs = {} state.last_test_outputs = {}
state.last_result_line_map = {} state.last_result_line_map = {}
state.last_raw_output = nil state.last_raw_output = nil
@@ -1935,7 +1938,10 @@ run_command = function(command, opts)
skips = {}, skips = {},
} }
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_enabled = options.scope_kind == "file"
or options.scope_kind == "all"
or options.scope_kind == "nearest"
or options.scope_kind == "last"
local summary = make_summary_tracker(summary_enabled) local summary = make_summary_tracker(summary_enabled)
local result_counts = make_summary_tracker(true) local result_counts = make_summary_tracker(true)
state.last_border_kind = "default" state.last_border_kind = "default"
@@ -2209,6 +2215,7 @@ function M.run_last()
end end
run_command(command, { run_command(command, {
runner = runner, runner = runner,
scope_kind = state.last_scope_kind or "last",
output_parser = parser or (runner and runner.parse_results), output_parser = parser or (runner and runner.parse_results),
}) })
end end

View File

@@ -262,8 +262,24 @@ describe("test-samurai core (no bundled runners)", function()
"gamma -- delta", "gamma -- delta",
}) })
local cmd_calls = {}
local orig_cmd = vim.cmd
vim.cmd = function(cmd)
table.insert(cmd_calls, cmd)
return orig_cmd(cmd)
end
core.listing_break_on_dashes() core.listing_break_on_dashes()
local has_noh = false
for _, cmd in ipairs(cmd_calls) do
if cmd == "noh" then
has_noh = true
break
end
end
assert.is_true(has_noh)
local broken = vim.api.nvim_buf_get_lines(buf, 0, -1, false) local broken = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
assert.same({ assert.same({
"alpha \\", "alpha \\",
@@ -279,6 +295,164 @@ describe("test-samurai core (no bundled runners)", function()
"alpha -- beta", "alpha -- beta",
"gamma -- delta", "gamma -- delta",
}, joined) }, joined)
vim.cmd = orig_cmd
end)
it("saves all buffers before running tests", function()
local runner = {
name = "test-runner-save-buffers",
}
function runner.is_test_file(_bufnr)
return true
end
function runner.find_nearest(bufnr, _row, _col)
return { file = vim.api.nvim_buf_get_name(bufnr), cwd = vim.loop.cwd(), test_name = "TestA" }
end
function runner.build_command(spec)
return { cmd = { "echo", "nearest" }, cwd = spec.cwd }
end
function runner.parse_results(_output)
return { passes = {}, failures = {}, skips = {} }
end
function runner.output_parser()
return {
on_line = function(_line, _state)
return nil
end,
on_complete = function(output, _state)
return runner.parse_results(output)
end,
}
end
function runner.parse_test_output(_output)
return {}
end
function runner.collect_failed_locations(_failures, _command, _scope_kind)
return {}
end
package.loaded["test-samurai-save-buffers-runner"] = runner
test_samurai.setup({ runner_modules = { "test-samurai-save-buffers-runner" } })
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_save_buffers.go")
vim.bo[bufnr].filetype = "go"
vim.api.nvim_set_current_buf(bufnr)
local cmd_calls = {}
local orig_cmd = vim.cmd
vim.cmd = function(cmd)
table.insert(cmd_calls, cmd)
end
local orig_jobstart = vim.fn.jobstart
vim.fn.jobstart = function(_cmd, opts)
if opts.on_exit then
opts.on_exit(nil, 0, nil)
end
return 1
end
core.run_nearest()
vim.fn.jobstart = orig_jobstart
vim.cmd = orig_cmd
local has_wall = false
for _, cmd in ipairs(cmd_calls) do
if cmd == "wall" then
has_wall = true
break
end
end
assert.is_true(has_wall)
end)
it("renders the summary in the listing after TSamLast", function()
local runner = {
name = "test-runner-last-summary",
}
function runner.is_test_file(_bufnr)
return true
end
function runner.find_nearest(bufnr, _row, _col)
return { file = vim.api.nvim_buf_get_name(bufnr), cwd = vim.loop.cwd(), test_name = "TestA" }
end
function runner.build_command(spec)
return { cmd = { "echo", "nearest" }, cwd = spec.cwd }
end
function runner.build_file_command(_bufnr)
return { cmd = { "echo", "file" } }
end
function runner.build_all_command(_bufnr)
return { cmd = { "echo", "all" } }
end
function runner.build_failed_command(last_command, _failures, _scope_kind)
return { cmd = { "echo", "failed" }, cwd = last_command and last_command.cwd or nil }
end
function runner.parse_results(_output)
return { passes = { "TestA" }, failures = { "TestB" }, skips = {} }
end
function runner.output_parser()
return {
on_line = function(_line, _state)
return nil
end,
on_complete = function(output, _state)
return runner.parse_results(output)
end,
}
end
function runner.parse_test_output(_output)
return {}
end
function runner.collect_failed_locations(_failures, _command, _scope_kind)
return {}
end
package.loaded["test-samurai-last-summary-runner"] = runner
test_samurai.setup({ runner_modules = { "test-samurai-last-summary-runner" } })
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_last_summary.go")
vim.bo[bufnr].filetype = "go"
vim.api.nvim_set_current_buf(bufnr)
local orig_jobstart = vim.fn.jobstart
vim.fn.jobstart = function(_cmd, opts)
if opts.on_exit then
opts.on_exit(nil, 0, nil)
end
return 1
end
core.run_nearest()
core.run_last()
local listing_buf = vim.api.nvim_get_current_buf()
local lines = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false)
local joined = table.concat(lines, "\n")
assert.is_true(joined:find("TOTAL", 1, true) ~= nil)
vim.fn.jobstart = orig_jobstart
end) end)
it("filters listing entries and restores them", function() it("filters listing entries and restores them", function()