remove built-in runner

This commit is contained in:
2026-01-02 15:21:42 +01:00
parent 58f0edc14b
commit f5fc9822ce
31 changed files with 23 additions and 6334 deletions

View File

@@ -1,81 +1,35 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local util = require("test-samurai.util")
local config = require("test-samurai.config")
local orig_find_root = util.find_root
local orig_fs_stat = vim.loop.fs_stat
local orig_readfile = vim.fn.readfile
describe("test-samurai core", function()
describe("test-samurai core (no bundled runners)", function()
before_each(function()
test_samurai.setup()
util.find_root = orig_find_root
vim.loop.fs_stat = orig_fs_stat
vim.fn.readfile = orig_readfile
end)
it("selects Go runner for _test.go files", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/foo_test.go")
local runner = core.get_runner_for_buf(bufnr)
assert.is_not_nil(runner)
assert.equals("go", runner.name)
it("defaults to an empty runner_modules list", function()
local cfg = config.get()
assert.is_true(type(cfg.runner_modules) == "table")
assert.equals(0, #cfg.runner_modules)
end)
it("does not fallback to Go runner when no runners are configured", function()
test_samurai.setup({ runner_modules = {} })
it("warns when no runner is installed for a test file", function()
local notified = {}
local orig_notify = vim.notify
vim.notify = function(msg, level)
table.insert(notified, { msg = msg, level = level })
end
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/no_runner_test.go")
local runner = core.get_runner_for_buf(bufnr)
assert.is_nil(runner)
end)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_set_current_buf(bufnr)
it("selects JS jest runner for *.test.ts files", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/foo.test.ts")
vim.bo[bufnr].filetype = "typescript"
core.run_nearest()
local runner = core.get_runner_for_buf(bufnr)
assert.is_not_nil(runner)
assert.equals("js-jest", runner.name)
end)
vim.notify = orig_notify
it("prefers mocha runner when mocha is in package.json", function()
util.find_root = function(_, _)
return "/tmp/mocha_proj"
end
vim.loop.fs_stat = function(path)
if path == "/tmp/mocha_proj/package.json" then
return { type = "file" }
end
return nil
end
vim.fn.readfile = function(path)
if path == "/tmp/mocha_proj/package.json" then
return {
"{",
' "devDependencies": { "mocha": "^10.0.0" }',
"}",
}
end
return {}
end
test_samurai.setup()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/mocha_proj/foo.test.ts")
vim.bo[bufnr].filetype = "typescript"
local runner = core.get_runner_for_buf(bufnr)
util.find_root = orig_find_root
vim.loop.fs_stat = orig_fs_stat
vim.fn.readfile = orig_readfile
assert.is_not_nil(runner)
assert.equals("js-mocha", runner.name)
assert.equals(1, #notified)
assert.equals("[test-samurai] no runner installed for this kind of test", notified[1].msg)
end)
end)

View File

@@ -1,339 +0,0 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local function mkbuf(path, ft, lines)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = ft
if lines then
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
end
return bufnr
end
local function stub_jobstart(opts_config)
local calls = {}
local orig = vim.fn.jobstart
local idx = 0
local config = opts_config or {}
vim.fn.jobstart = function(cmd, opts)
idx = idx + 1
table.insert(calls, { cmd = cmd, opts = opts })
local code = 0
if type(config.exit_codes) == "table" then
code = config.exit_codes[idx] or 0
elseif type(config.exit_codes) == "number" then
code = config.exit_codes
end
local out = config.stdout and config.stdout[idx] or nil
if out and opts and opts.on_stdout then
if type(out) == "string" then
out = { out }
end
opts.on_stdout(1, out, nil)
end
local err = config.stderr and config.stderr[idx] or nil
if err and opts and opts.on_stderr then
if type(err) == "string" then
err = { err }
end
opts.on_stderr(1, err, nil)
end
if opts and opts.on_exit then
opts.on_exit(1, code, nil)
end
return 1
end
return calls, orig
end
describe("TSamFailedOnly", function()
before_each(function()
test_samurai.setup()
end)
it("reruns failed jest tests with --onlyFailures", function()
local json = vim.json.encode({
testResults = {
{
assertionResults = {
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
},
},
},
})
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 1, 0 },
stdout = { { json } },
})
local bufnr = mkbuf("/tmp/project/foo_failed_only.test.ts", "typescript", {
'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()
core.run_failed_only()
vim.fn.jobstart = orig_jobstart
assert.equals(2, #calls)
assert.are.same(
{ "npx", "jest", "--json", "--verbose", "/tmp/project/foo_failed_only.test.ts", "-t", "inner 2" },
calls[1].cmd
)
assert.are.same(
{ "npx", "jest", "--json", "--verbose", "-t", "outer inner 2", "/tmp/project/foo_failed_only.test.ts" },
calls[2].cmd
)
end)
it("falls back to TSamLast when last run had no failures", function()
local json = vim.json.encode({
testResults = {
{
assertionResults = {
{ status = "passed", title = "inner 1", fullName = "outer inner 1" },
},
},
},
})
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 0, 0 },
stdout = { { json } },
})
local bufnr = mkbuf("/tmp/project/foo_failed_only_pass.test.ts", "typescript", {
'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()
core.run_failed_only()
vim.fn.jobstart = orig_jobstart
assert.equals(2, #calls)
assert.are.same(calls[1].cmd, calls[2].cmd)
end)
it("reruns failed go tests with -run regex", function()
local json_lines = {
vim.json.encode({ Action = "fail", Test = "TestFoo/first" }),
vim.json.encode({ Action = "fail", Test = "TestBar" }),
}
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 1, 0 },
stdout = { json_lines },
})
local bufnr = mkbuf("/tmp/project/foo_failed_only_test.go", "go", {
"package main",
"import \"testing\"",
"",
"func TestFoo(t *testing.T) {",
" t.Run(\"first\", func(t *testing.T) {",
" -- inside first",
" })",
"}",
"",
"func TestBar(t *testing.T) {",
" -- inside bar",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
core.run_all()
core.run_failed_only()
vim.fn.jobstart = orig_jobstart
assert.equals(2, #calls)
assert.are.same({ "go", "test", "-json", "./..." }, calls[1].cmd)
assert.are.same({ "go", "test", "-json", "./...", "-run", "^(TestFoo/first|TestBar)$" }, calls[2].cmd)
end)
it("uses go parser for failed-only output (no raw JSON)", function()
local json_line = vim.json.encode({
Action = "fail",
Test = "TestHandleGet/returns_200",
})
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 1, 1 },
stdout = { { json_line }, { json_line } },
})
local bufnr = mkbuf("/tmp/project/foo_failed_only_output_test.go", "go", {
"package main",
"import \"testing\"",
"",
"func TestHandleGet(t *testing.T) {",
" t.Run(\"returns_200\", func(t *testing.T) {",
" -- inside test",
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
core.run_all()
core.run_failed_only()
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
assert.equals(2, #calls)
local has_raw = false
for _, line in ipairs(lines) do
if line == json_line then
has_raw = true
break
end
end
assert.is_false(has_raw)
end)
it("reruns failed mocha tests from json-stream array output without raw JSON", function()
test_samurai.setup({
runner_modules = {
"test-samurai.runners.js-mocha",
},
})
local fail_line = vim.json.encode({
event = "fail",
fullTitle = "API :: /brands... GET: /",
})
local start_line = vim.json.encode({ "start", { total = 1 } })
local end_line = vim.json.encode({ "end", { tests = 0 } })
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 1, 1 },
stdout = { { fail_line }, { start_line, end_line } },
})
local bufnr = mkbuf("/tmp/project/brands.test.js", "javascript", {
'describe("API :: /brands...", function() {',
' it("GET: /", function() {',
" -- inside test",
" })",
"})",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 3, 0 })
core.run_file()
core.run_failed_only()
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
assert.equals(2, #calls)
assert.are.same(
{ "npx", "mocha", "--reporter", "json-stream", "/tmp/project/brands.test.js" },
calls[1].cmd
)
local failed_cmd = calls[2].cmd or {}
local saw_grep = false
local saw_fgrep = false
local saw_title = false
local plain_title = "API :: /brands... GET: /"
for _, arg in ipairs(failed_cmd) do
if arg == "--grep" then
saw_grep = true
elseif arg == "--fgrep" then
saw_fgrep = true
elseif arg == plain_title then
saw_title = true
end
end
assert.is_false(saw_grep)
assert.is_true(saw_fgrep)
assert.is_true(saw_title)
local has_raw = false
for _, line in ipairs(lines) do
if line == start_line or line == end_line then
has_raw = true
break
end
end
assert.is_false(has_raw)
end)
it("does not affect TSamLast history", function()
local json = vim.json.encode({
testResults = {
{
assertionResults = {
{ status = "failed", title = "inner 2", fullName = "outer inner 2" },
},
},
},
})
local calls, orig_jobstart = stub_jobstart({
exit_codes = { 1, 1, 1 },
stdout = { { json } },
})
local bufnr = mkbuf("/tmp/project/foo_failed_only_last.test.ts", "typescript", {
'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()
core.run_failed_only()
core.run_last()
vim.fn.jobstart = orig_jobstart
assert.equals(3, #calls)
assert.are.same(calls[1].cmd, calls[3].cmd)
assert.are.same(
{ "npx", "jest", "--json", "--verbose", "-t", "outer inner 2", "/tmp/project/foo_failed_only_last.test.ts" },
calls[2].cmd
)
end)
end)

View File

@@ -1,147 +0,0 @@
local go_runner = require("test-samurai.runners.go")
local util = require("test-samurai.util")
describe("test-samurai go runner", function()
it("detects Go test files by suffix", function()
local bufnr1 = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr1, "/tmp/go_suffix_test.go")
assert.is_true(go_runner.is_test_file(bufnr1))
local bufnr2 = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr2, "/tmp/go_main.go")
assert.is_false(go_runner.is_test_file(bufnr2))
end)
it("finds subtest when cursor is inside t.Run block", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/go_subtest_test.go")
local lines = {
"package main",
"import \"testing\"",
"",
"func TestFoo(t *testing.T) {",
" t.Run(\"first\", func(t *testing.T) {",
" -- inside first",
" })",
"",
" t.Run(\"second\", func(t *testing.T) {",
" -- inside second",
" })",
"}",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_fs_find = vim.fs.find
vim.fs.find = function(markers, opts)
return { "/tmp/go.mod" }
end
local row_inside_first = 5
local spec, err = go_runner.find_nearest(bufnr, row_inside_first, 0)
vim.fs.find = orig_fs_find
assert.is_nil(err)
assert.is_not_nil(spec)
assert.equals("TestFoo/first", spec.test_path)
assert.equals("subtest", spec.scope)
assert.is_true(spec.file:match("go_subtest_test%.go$") ~= nil)
assert.is_true(spec.cwd:match("tmp$") ~= nil)
end)
it("falls back to whole test function when between subtests", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/go_between_test.go")
local lines = {
"package main",
"import \"testing\"",
"",
"func TestFoo(t *testing.T) {",
" t.Run(\"first\", func(t *testing.T) {",
" -- inside first",
" })",
"",
" t.Run(\"second\", func(t *testing.T) {",
" -- inside second",
" })",
"}",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_fs_find = vim.fs.find
vim.fs.find = function(markers, opts)
return { "/tmp/go.mod" }
end
local row_between = 7
local spec, err = go_runner.find_nearest(bufnr, row_between, 0)
vim.fs.find = orig_fs_find
assert.is_nil(err)
assert.is_not_nil(spec)
assert.equals("TestFoo", spec.test_path)
assert.equals("function", spec.scope)
assert.is_true(spec.file:match("go_between_test%.go$") ~= nil)
assert.is_true(spec.cwd:match("tmp$") ~= nil)
end)
it("build_command uses current package and correct run pattern", function()
local spec_sub = {
file = "/tmp/project/pkg/foo_test.go",
cwd = "/tmp/project",
test_path = "TestFoo/first",
scope = "subtest",
}
local cmd_spec_sub = go_runner.build_command(spec_sub)
assert.are.same(
{ "go", "test", "-json", "./pkg", "-run", "^TestFoo/first$" },
cmd_spec_sub.cmd
)
local spec_func = {
file = "/tmp/project/foo_test.go",
cwd = "/tmp/project",
test_path = "TestFoo",
scope = "function",
}
local cmd_spec_func = go_runner.build_command(spec_func)
assert.are.same(
{ "go", "test", "-json", "./", "-run", "^TestFoo($|/)" },
cmd_spec_func.cmd
)
end)
it("build_file_command uses exact test names from current file", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/project/get_test.go")
local lines = {
"package main",
"import \"testing\"",
"",
"func TestHandleGet(t *testing.T) {",
" t.Run(\"returns_200\", func(t *testing.T) {",
" -- inside test",
" })",
"}",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_find_root = util.find_root
util.find_root = function(path, markers)
return "/tmp/project"
end
local cmd_spec = go_runner.build_file_command(bufnr)
util.find_root = orig_find_root
assert.are.same(
{ "go", "test", "-json", "./", "-run", "^(TestHandleGet)$" },
cmd_spec.cmd
)
assert.equals("/tmp/project", cmd_spec.cwd)
end)
end)

View File

@@ -1,203 +0,0 @@
local jest = require("test-samurai.runners.js-jest")
local mocha = require("test-samurai.runners.js-mocha")
local vitest = require("test-samurai.runners.js-vitest")
local util = require("test-samurai.util")
describe("test-samurai js runner (jest)", function()
it("detects JS/TS test files by name and filetype", function()
local bufnr1 = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr1, "/tmp/foo_detect.test.ts")
vim.bo[bufnr1].filetype = "typescript"
assert.is_true(jest.is_test_file(bufnr1))
local bufnr2 = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr2, "/tmp/foo_detect.ts")
vim.bo[bufnr2].filetype = "typescript"
assert.is_false(jest.is_test_file(bufnr2))
end)
it("finds nearest it() call as test name and builds full_name when cursor is inside the test", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/foo_nearest.test.ts")
vim.bo[bufnr].filetype = "typescript"
local lines = {
'describe("outer", function() {',
' it("inner 1", function() {',
" -- inside 1",
" })",
"",
' it("inner 2", function() {',
" -- inside 2",
" })",
"})",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_fs_find = vim.fs.find
vim.fs.find = function(names, opts)
return { "/tmp/package.json" }
end
local row_inside_second = 6
local spec, err = jest.find_nearest(bufnr, row_inside_second, 0)
vim.fs.find = orig_fs_find
assert.is_nil(err)
assert.is_not_nil(spec)
assert.equals("inner 2", spec.test_name)
assert.equals("outer inner 2", spec.full_name)
assert.equals("jest", spec.framework)
assert.is_true(spec.file:match("foo_nearest%.test%.ts$") ~= nil)
assert.is_true(spec.cwd:match("tmp$") ~= nil)
local cmd_spec = jest.build_command(spec)
assert.are.same({ "npx", "jest", "--json", "--verbose", spec.file, "-t", "inner 2" }, cmd_spec.cmd)
end)
it("returns describe block when cursor is between it() calls", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/foo_between.test.ts")
vim.bo[bufnr].filetype = "typescript"
local lines = {
'describe("outer", function() {',
' it("inner 1", function() {',
" -- inside 1",
" })",
"",
' it("inner 2", function() {',
" -- inside 2",
" })",
"})",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_fs_find = vim.fs.find
vim.fs.find = function(names, opts)
return { "/tmp/package.json" }
end
local row_between = 4
local spec, err = jest.find_nearest(bufnr, row_between, 0)
vim.fs.find = orig_fs_find
assert.is_nil(err)
assert.is_not_nil(spec)
assert.equals("outer", spec.test_name)
assert.equals("outer", spec.full_name)
assert.equals("jest", spec.framework)
end)
it("treats jest.config in test/.bin as project root parent", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/foo_binroot.test.ts")
vim.bo[bufnr].filetype = "typescript"
local lines = {
'describe("outer", function() {',
' it("inner 1", function() {',
" -- inside 1",
" })",
"",
' it("inner 2", function() {',
" -- inside 2",
" })",
"})",
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local orig_fs_find = vim.fs.find
vim.fs.find = function(names, opts)
return { "/tmp/test/.bin/jest.config.js" }
end
local row_inside_second = 6
local spec, err = jest.find_nearest(bufnr, row_inside_second, 0)
vim.fs.find = orig_fs_find
assert.is_nil(err)
assert.is_not_nil(spec)
assert.equals("/tmp", spec.cwd)
end)
end)
describe("test-samurai js runner (mocha)", function()
it("builds mocha command with fgrep and full test title", function()
local spec = {
file = "/tmp/project/test/foo_nearest.test.ts",
cwd = "/tmp/project",
test_name = "inner 2",
full_name = "outer inner 2",
}
local cmd_spec = mocha.build_command(spec)
assert.are.same(
{ "npx", "mocha", "--reporter", "json-stream", "--fgrep", "outer inner 2", spec.file },
cmd_spec.cmd
)
assert.equals("/tmp/project", cmd_spec.cwd)
end)
it("builds mocha all command with default glob", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/project/test/foo_all.test.js")
vim.bo[bufnr].filetype = "javascript"
local orig_find_root = util.find_root
util.find_root = function(path, markers)
return "/tmp/project"
end
local cmd_spec = mocha.build_all_command(bufnr)
util.find_root = orig_find_root
assert.are.same(
{ "npx", "mocha", "--reporter", "json-stream", "test/**/*.test.js" },
cmd_spec.cmd
)
assert.equals("/tmp/project", cmd_spec.cwd)
end)
end)
describe("test-samurai js runner (vitest)", function()
it("builds vitest command with tap-flat reporter", function()
local spec = {
file = "/tmp/project/test/foo_nearest.test.ts",
cwd = "/tmp/project",
test_name = "inner 2",
full_name = "outer inner 2",
}
local cmd_spec = vitest.build_command(spec)
assert.are.same(
{ "npx", "vitest", "--reporter", "tap-flat", spec.file, "-t", "inner 2" },
cmd_spec.cmd
)
assert.equals("/tmp/project", cmd_spec.cwd)
end)
it("builds vitest all command with tap-flat reporter", function()
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, "/tmp/project/test/foo_all.test.ts")
vim.bo[bufnr].filetype = "typescript"
local orig_find_root = util.find_root
util.find_root = function(path, markers)
return "/tmp/project"
end
local cmd_spec = vitest.build_all_command(bufnr)
util.find_root = orig_find_root
assert.are.same(
{ "npx", "vitest", "--reporter", "tap-flat" },
cmd_spec.cmd
)
assert.equals("/tmp/project", cmd_spec.cwd)
end)
end)

View File

@@ -1,139 +0,0 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local function mkbuf(path, ft, lines)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = ft
if lines then
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
end
return bufnr
end
local function capture_jobstart()
local calls = {}
local orig = vim.fn.jobstart
vim.fn.jobstart = function(cmd, opts)
table.insert(calls, { cmd = cmd, opts = opts })
return 1
end
return calls, orig
end
describe("TSamLast", function()
before_each(function()
test_samurai.setup()
end)
it("reruns last Go command", function()
local calls, orig_jobstart = capture_jobstart()
local bufnr = mkbuf("/tmp/project/foo_test.go", "go", {
"package main",
"import \"testing\"",
"",
"func TestFoo(t *testing.T) {",
" t.Run(\"first\", func(t *testing.T) {",
" -- inside first",
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 6, 0 })
core.run_nearest()
core.run_last()
vim.fn.jobstart = orig_jobstart
assert.equals(2, #calls)
assert.are.same({ "go", "test", "-json", "./", "-run", "^TestFoo/first$" }, calls[1].cmd)
assert.are.same(calls[1].cmd, calls[2].cmd)
assert.equals(calls[1].opts.cwd, calls[2].opts.cwd)
end)
it("uses go parser for TSamLast output (no raw JSON)", function()
local json_line = vim.json.encode({
Action = "fail",
Test = "TestHandleGet/returns_200",
})
local calls = {}
local orig_jobstart = vim.fn.jobstart
vim.fn.jobstart = function(cmd, opts)
table.insert(calls, { cmd = cmd, opts = opts })
if opts and opts.on_stdout then
opts.on_stdout(1, { json_line }, nil)
end
if opts and opts.on_exit then
opts.on_exit(1, 1, nil)
end
return 1
end
local bufnr = mkbuf("/tmp/project/foo_last_output_test.go", "go", {
"package main",
"import \"testing\"",
"",
"func TestHandleGet(t *testing.T) {",
" t.Run(\"returns_200\", func(t *testing.T) {",
" -- inside test",
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
core.run_all()
core.run_last()
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
assert.equals(2, #calls)
local has_raw = false
for _, line in ipairs(lines) do
if line == json_line then
has_raw = true
break
end
end
assert.is_false(has_raw)
end)
it("reruns last JS command", function()
local calls, orig_jobstart = capture_jobstart()
local bufnr = mkbuf("/tmp/project/foo_last.test.ts", "typescript", {
'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()
core.run_last()
vim.fn.jobstart = orig_jobstart
assert.equals(2, #calls)
assert.are.same(
{ "npx", "jest", "--json", "--verbose", "/tmp/project/foo_last.test.ts", "-t", "inner 2" },
calls[1].cmd
)
assert.are.same(calls[1].cmd, calls[2].cmd)
assert.equals(calls[1].opts.cwd, calls[2].opts.cwd)
end)
end)

File diff suppressed because it is too large Load Diff

View File

@@ -1,97 +0,0 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local function close_output_container()
local keys = vim.api.nvim_replace_termcodes("<esc><esc>", true, false, true)
local attempts = 5
while attempts > 0 do
local float_win = nil
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
float_win = win
break
end
end
if not float_win then
break
end
vim.api.nvim_set_current_win(float_win)
vim.api.nvim_feedkeys(keys, "x", false)
vim.wait(20, function()
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
return false
end
end
return true
end)
attempts = attempts - 1
end
end
local function stub_jobstart(opts_config)
local orig = vim.fn.jobstart
local config = opts_config or {}
vim.fn.jobstart = function(_cmd, opts)
local out = config.stdout or nil
if out and opts and opts.on_stdout then
if type(out) == "string" then
out = { out }
end
opts.on_stdout(1, out, nil)
end
if opts and opts.on_exit then
opts.on_exit(1, config.exit_code or 0, nil)
end
return 1
end
return orig
end
describe("test-samurai quickfix (js)", function()
before_each(function()
test_samurai.setup()
end)
after_each(function()
close_output_container()
vim.fn.setqflist({}, "r")
end)
it("mappt jest-verbose Failures auf die Zeile des Tests", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_js")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_qf.test.ts"
vim.fn.writefile({
'describe("outer", function() {',
' it("inner 1", function() {',
" })",
"",
' it("inner 2", function() {',
" })",
"})",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "typescript"
vim.api.nvim_set_current_buf(bufnr)
local fail_symbol = string.char(0xE2, 0x9C, 0x95)
local orig_jobstart = stub_jobstart({
exit_code = 1,
stdout = { " " .. fail_symbol .. " inner 2" },
})
core.run_file()
local qf = vim.fn.getqflist()
assert.equals(1, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(5, qf[1].lnum)
vim.fn.jobstart = orig_jobstart
end)
end)

View File

@@ -1,409 +0,0 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local function close_output_container()
local keys = vim.api.nvim_replace_termcodes("<esc><esc>", true, false, true)
local attempts = 5
while attempts > 0 do
local float_win = nil
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
float_win = win
break
end
end
if not float_win then
break
end
vim.api.nvim_set_current_win(float_win)
vim.api.nvim_feedkeys(keys, "x", false)
vim.wait(20, function()
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
return false
end
end
return true
end)
attempts = attempts - 1
end
end
local function stub_jobstart(opts_config)
local orig = vim.fn.jobstart
local idx = 0
local config = opts_config or {}
vim.fn.jobstart = function(cmd, opts)
idx = idx + 1
local code = 0
if type(config.exit_codes) == "table" then
code = config.exit_codes[idx] or 0
elseif type(config.exit_codes) == "number" then
code = config.exit_codes
end
local out = config.stdout and config.stdout[idx] or nil
if out and opts and opts.on_stdout then
if type(out) == "string" then
out = { out }
end
opts.on_stdout(1, out, nil)
end
if opts and opts.on_exit then
opts.on_exit(1, code, nil)
end
return 1
end
return orig
end
describe("test-samurai quickfix", function()
before_each(function()
test_samurai.setup()
end)
after_each(function()
close_output_container()
vim.fn.setqflist({}, "r")
end)
it("fuellt die Quickfix-Liste mit Fehltests und leert sie bei Erfolg", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_test.go"
vim.fn.writefile({
"package foo",
"",
"func TestFoo(t *testing.T) {",
' t.Run("bar", func(t *testing.T) {',
" })",
"}",
"",
"func TestBaz(t *testing.T) {",
"}",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_set_current_buf(bufnr)
local orig_jobstart = stub_jobstart({
exit_codes = { 1, 0 },
stdout = {
{
vim.json.encode({ Action = "fail", Test = "TestFoo/bar" }),
vim.json.encode({ Action = "fail", Test = "TestBaz" }),
},
{ vim.json.encode({ Action = "pass", Test = "TestFoo" }) },
},
})
core.run_file()
local first = vim.fn.getqflist()
assert.equals(2, #first)
assert.equals(path, vim.fn.bufname(first[1].bufnr))
assert.equals(4, first[1].lnum)
assert.equals(path, vim.fn.bufname(first[2].bufnr))
assert.equals(8, first[2].lnum)
close_output_container()
vim.api.nvim_set_current_buf(bufnr)
core.run_file()
local second = vim.fn.getqflist()
assert.equals(0, #second)
vim.fn.jobstart = orig_jobstart
end)
it("enthaelt bei Go auch Eltern- und Subtest-Failures im Quickfix", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_go_sub")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_sub_test.go"
vim.fn.writefile({
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run("evergreen", func(t *testing.T) {',
" })",
"",
' t.Run("everred", func(t *testing.T) {',
" })",
"}",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run("evergreen", func(t *testing.T) {',
" })",
"",
' t.Run("everred", func(t *testing.T) {',
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 6, 0 })
local orig_jobstart = stub_jobstart({
exit_codes = { 1 },
stdout = {
{
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing/everred" }),
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing" }),
},
},
})
core.run_nearest()
local qf = vim.fn.getqflist()
assert.equals(2, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(path, vim.fn.bufname(qf[2].bufnr))
local lines = { qf[1].lnum, qf[2].lnum }
table.sort(lines)
assert.are.same({ 3, 7 }, lines)
vim.fn.jobstart = orig_jobstart
end)
it("vereinigt Failures aus Parser und Scope fuer Go", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_go_union")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_union_test.go"
vim.fn.writefile({
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run(\"evergreen\", func(t *testing.T) {',
" })",
"",
' t.Run(\"everred\", func(t *testing.T) {',
" })",
"}",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run(\"evergreen\", func(t *testing.T) {',
" })",
"",
' t.Run(\"everred\", func(t *testing.T) {',
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 6, 0 })
local go = require("test-samurai.runners.go")
local orig_parser = go.output_parser
go.output_parser = function()
local seen = 0
return {
on_line = function()
seen = seen + 1
if seen == 1 then
return {
passes = {},
failures = { "TestAwesomeThing/everred" },
skips = {},
display = { passes = {}, failures = { "everred" }, skips = {} },
failures_all = { "TestAwesomeThing" },
}
end
return {
passes = {},
failures = { "TestAwesomeThing" },
skips = {},
display = { passes = {}, failures = { "TestAwesomeThing" }, skips = {} },
failures_all = { "TestAwesomeThing" },
}
end,
on_complete = function()
return nil
end,
}
end
local orig_jobstart = stub_jobstart({
exit_codes = { 1 },
stdout = {
{
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing/everred" }),
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing" }),
},
},
})
core.run_nearest()
local qf = vim.fn.getqflist()
assert.equals(2, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(path, vim.fn.bufname(qf[2].bufnr))
local lines = { qf[1].lnum, qf[2].lnum }
table.sort(lines)
assert.are.same({ 3, 7 }, lines)
vim.fn.jobstart = orig_jobstart
go.output_parser = orig_parser
end)
it("nutzt Listing-Namen wenn Parser keine Failure-Liste liefert", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_go_listing")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_listing_test.go"
vim.fn.writefile({
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run(\"evergreen\", func(t *testing.T) {',
" })",
"",
' t.Run(\"everred\", func(t *testing.T) {',
" })",
"}",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
"package foo",
"",
"func TestAwesomeThing(t *testing.T) {",
' t.Run(\"evergreen\", func(t *testing.T) {',
" })",
"",
' t.Run(\"everred\", func(t *testing.T) {',
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 6, 0 })
local go = require("test-samurai.runners.go")
local orig_parser = go.output_parser
go.output_parser = function()
local step = 0
return {
on_line = function()
step = step + 1
if step == 1 then
return {
passes = {},
failures = {},
skips = {},
display = { passes = {}, failures = { "TestAwesomeThing" }, skips = {} },
}
end
return {
passes = {},
failures = {},
skips = {},
display = { passes = {}, failures = { "everred" }, skips = {} },
}
end,
on_complete = function()
return nil
end,
}
end
local orig_jobstart = stub_jobstart({
exit_codes = { 1 },
stdout = {
{
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing/everred" }),
vim.json.encode({ Action = "fail", Test = "TestAwesomeThing" }),
},
},
})
core.run_nearest()
local qf = vim.fn.getqflist()
assert.equals(2, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(path, vim.fn.bufname(qf[2].bufnr))
local lines = { qf[1].lnum, qf[2].lnum }
table.sort(lines)
assert.are.same({ 3, 7 }, lines)
vim.fn.jobstart = orig_jobstart
go.output_parser = orig_parser
end)
it("mappt Go-Subtests mit durch Unterstriche normalisierten Namen", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_go_norm")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_norm_test.go"
vim.fn.writefile({
"package foo",
"",
"func TestHandleGet(t *testing.T) {",
' t.Run(\"returns 200 with an list of all badges\", func(t *testing.T) {',
" })",
"",
' t.Run(\"returns 500 on any db error\", func(t *testing.T) {',
" })",
"}",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "go"
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
"package foo",
"",
"func TestHandleGet(t *testing.T) {",
' t.Run(\"returns 200 with an list of all badges\", func(t *testing.T) {',
" })",
"",
' t.Run(\"returns 500 on any db error\", func(t *testing.T) {',
" })",
"}",
})
vim.api.nvim_set_current_buf(bufnr)
vim.api.nvim_win_set_cursor(0, { 6, 0 })
local orig_jobstart = stub_jobstart({
exit_codes = { 1 },
stdout = {
{
vim.json.encode({
Action = "fail",
Test = "TestHandleGet/returns_500_on_any_db_error",
}),
vim.json.encode({ Action = "fail", Test = "TestHandleGet" }),
},
},
})
core.run_nearest()
local qf = vim.fn.getqflist()
assert.equals(2, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(path, vim.fn.bufname(qf[2].bufnr))
local lines = { qf[1].lnum, qf[2].lnum }
table.sort(lines)
assert.are.same({ 3, 7 }, lines)
vim.fn.jobstart = orig_jobstart
end)
end)

View File

@@ -1,102 +0,0 @@
local test_samurai = require("test-samurai")
local core = require("test-samurai.core")
local function close_output_container()
local keys = vim.api.nvim_replace_termcodes("<esc><esc>", true, false, true)
local attempts = 5
while attempts > 0 do
local float_win = nil
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
float_win = win
break
end
end
if not float_win then
break
end
vim.api.nvim_set_current_win(float_win)
vim.api.nvim_feedkeys(keys, "x", false)
vim.wait(20, function()
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative ~= "" then
return false
end
end
return true
end)
attempts = attempts - 1
end
end
local function stub_jobstart(opts_config)
local orig = vim.fn.jobstart
local config = opts_config or {}
vim.fn.jobstart = function(_cmd, opts)
local out = config.stdout or nil
if out and opts and opts.on_stdout then
if type(out) == "string" then
out = { out }
end
opts.on_stdout(1, out, nil)
end
if opts and opts.on_exit then
opts.on_exit(1, config.exit_code or 0, nil)
end
return 1
end
return orig
end
describe("test-samurai quickfix (vitest)", function()
before_each(function()
test_samurai.setup()
end)
after_each(function()
close_output_container()
vim.fn.setqflist({}, "r")
end)
it("mappt tap-flat Failures mit >-Trenner auf die Testzeile", function()
local root = vim.fs.joinpath(vim.loop.cwd(), "tests", "tmp_qf_vitest")
vim.fn.mkdir(root, "p")
local path = root .. "/foo_qf.test.ts"
local pkg = root .. "/package.json"
vim.fn.writefile({
"{",
' "devDependencies": { "vitest": "^1.0.0" }',
"}",
}, pkg)
vim.fn.writefile({
'describe("outer", function() {',
' it("inner 1", function() {',
" })",
"",
' it("inner 2", function() {',
" })",
"})",
}, path)
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(bufnr, path)
vim.bo[bufnr].filetype = "typescript"
vim.api.nvim_set_current_buf(bufnr)
local orig_jobstart = stub_jobstart({
exit_code = 1,
stdout = { "not ok 1 - outer > inner 2 # time=12.3ms" },
})
core.run_file()
local qf = vim.fn.getqflist()
assert.equals(1, #qf)
assert.equals(path, vim.fn.bufname(qf[1].bufnr))
assert.equals(5, qf[1].lnum)
vim.fn.jobstart = orig_jobstart
end)
end)

View File

@@ -1 +0,0 @@
{ "name": "mocha-jump" }

View File

@@ -1,4 +0,0 @@
describe("suite one", function() {
it("test one", function() {
})
})

View File

@@ -1,4 +0,0 @@
describe("suite two", function() {
it("test two", function() {
})
})

View File

@@ -1,9 +0,0 @@
package foo
func TestFoo(t *testing.T) {
t.Run("bar", func(t *testing.T) {
})
}
func TestBaz(t *testing.T) {
}

View File

@@ -1,9 +0,0 @@
package foo
func TestAwesomeThing(t *testing.T) {
t.Run("evergreen", func(t *testing.T) {
})
t.Run("everred", func(t *testing.T) {
})
}

View File

@@ -1,9 +0,0 @@
package foo
func TestHandleGet(t *testing.T) {
t.Run("returns 200 with an list of all badges", func(t *testing.T) {
})
t.Run("returns 500 on any db error", func(t *testing.T) {
})
}

View File

@@ -1,9 +0,0 @@
package foo
func TestAwesomeThing(t *testing.T) {
t.Run("evergreen", func(t *testing.T) {
})
t.Run("everred", func(t *testing.T) {
})
}

View File

@@ -1,9 +0,0 @@
package foo
func TestAwesomeThing(t *testing.T) {
t.Run("evergreen", func(t *testing.T) {
})
t.Run("everred", func(t *testing.T) {
})
}

View File

@@ -1,7 +0,0 @@
describe("outer", function() {
it("inner 1", function() {
})
it("inner 2", function() {
})
})

View File

@@ -1,3 +0,0 @@
package foo
func TestFoo(t *testing.T) {}

View File

@@ -1,4 +0,0 @@
package foo
func TestFoo(t *testing.T) {
}

View File

@@ -1,4 +0,0 @@
package foo
func TestFoo(t *testing.T) {
}

View File

@@ -1,4 +0,0 @@
package foo
func TestFoo(t *testing.T) {
}

View File

@@ -1,7 +0,0 @@
describe("outer", function() {
it("inner 1", function() {
})
it("inner 2", function() {
})
})

View File

@@ -1,3 +0,0 @@
{
"devDependencies": { "vitest": "^1.0.0" }
}