local test_samurai = require("test-samurai") local core = require("test-samurai.core") local config = require("test-samurai.config") describe("test-samurai core (no bundled runners)", function() before_each(function() test_samurai.setup() end) 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("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") vim.bo[bufnr].filetype = "go" vim.api.nvim_set_current_buf(bufnr) core.run_nearest() vim.notify = orig_notify assert.equals(1, #notified) assert.equals("[test-samurai] no runner installed for this kind of test", notified[1].msg) end) it("fills quickfix after failed-only runs using last failures", function() local runner = { name = "test-runner", } 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) local passes = {} local failures = {} if type(output) == "string" then if output:find("FAIL TestA", 1, true) then failures = { "TestA" } end if output:find("PASS TestA", 1, true) then passes = { "TestA" } end end return { passes = passes, failures = failures, skips = {} } end function runner.output_parser() return { on_line = function(line, _state) if line == "FAIL TestA" then return { passes = {}, failures = { "TestA" }, skips = {} } end if line == "PASS TestA" then return { passes = { "TestA" }, failures = {}, skips = {} } end 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) local items = {} for _, name in ipairs(failures or {}) do table.insert(items, { filename = "file", lnum = 1, col = 1, text = name }) end return items end package.loaded["test-samurai-test-runner"] = runner test_samurai.setup({ runner_modules = { "test-samurai-test-runner" } }) local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/test_runner_quickfix.go") vim.bo[bufnr].filetype = "go" vim.api.nvim_set_current_buf(bufnr) local qf_calls = {} local orig_setqflist = vim.fn.setqflist vim.fn.setqflist = function(_, _, opts) if opts and type(opts.items) == "table" then table.insert(qf_calls, opts.items) end end local job_outputs = { { "FAIL TestA" }, { "PASS TestA" }, } local orig_jobstart = vim.fn.jobstart vim.fn.jobstart = function(_cmd, opts) local lines = table.remove(job_outputs, 1) or {} if opts.on_stdout then opts.on_stdout(nil, lines, nil) end if opts.on_exit then opts.on_exit(nil, 0, nil) end return 1 end core.run_nearest() core.run_failed_only() vim.fn.jobstart = orig_jobstart vim.fn.setqflist = orig_setqflist local last = qf_calls[#qf_calls] assert.is_true(type(last) == "table") assert.equals(1, #last) assert.equals("TestA", last[1].text) end) it("shows help with TSam commands and keymaps in the detail float", function() local runner = { name = "test-runner-help", } 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 = {}, 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-help-runner"] = runner test_samurai.setup({ runner_modules = { "test-samurai-help-runner" } }) local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_help.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() local listing_buf = vim.api.nvim_get_current_buf() local maps = vim.api.nvim_buf_get_keymap(listing_buf, "n") local has_help = false local has_cb = false local has_cj = false for _, map in ipairs(maps) do if map.lhs == "?" then has_help = true elseif map.lhs and map.lhs:sub(-2) == "cb" then has_cb = true elseif map.lhs and map.lhs:sub(-2) == "cj" then has_cj = true end end assert.is_true(has_help) assert.is_true(has_cb) assert.is_true(has_cj) core.show_help() local detail_buf = vim.api.nvim_get_current_buf() local lines = vim.api.nvim_buf_get_lines(detail_buf, 0, -1, false) local joined = table.concat(lines, "\n") assert.is_true(joined:find("TSamNearest", 1, true) ~= nil) assert.is_true(joined:find("TSamShowOutput", 1, true) ~= nil) assert.is_true(joined:find("tn", 1, true) ~= nil) assert.is_true(joined:find("to", 1, true) ~= nil) assert.is_true(joined:find("cb", 1, true) ~= nil) assert.is_true(joined:find("cj", 1, true) ~= nil) assert.is_true(joined:find("breaks test-command onto multiple lines", 1, true) ~= nil) assert.is_true(joined:find("joins test-command onto single line", 1, true) ~= nil) vim.fn.jobstart = orig_jobstart end) it("applies listing break/join substitutions", function() local buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(buf) vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "alpha -- beta", "gamma -- delta", }) core.listing_break_on_dashes() local broken = vim.api.nvim_buf_get_lines(buf, 0, -1, false) assert.same({ "alpha \\", "\t-- beta", "gamma \\", "\t-- delta", }, broken) core.listing_join_backslashes() local joined = vim.api.nvim_buf_get_lines(buf, 0, -1, false) assert.same({ "alpha -- beta", "gamma -- delta", }, joined) end) it("filters listing entries and restores them", function() local runner = { name = "test-runner-filter", } 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 = { "TestC" }, skips = { "TestB" } } 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-filter-runner"] = runner test_samurai.setup({ runner_modules = { "test-samurai-filter-runner" } }) local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_filter.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() local listing_buf = vim.api.nvim_get_current_buf() local original = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) core.filter_listing_failures() local failures_only = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) local failures_joined = table.concat(failures_only, "\n") assert.is_true(failures_joined:find("[ FAIL ] - TestC", 1, true) ~= nil) assert.is_true(failures_joined:find("[ SKIP ] - TestB", 1, true) == nil) assert.equals(original[1], failures_only[1]) assert.equals("", failures_only[2]) assert.is_true(failures_joined:find("TOTAL", 1, true) ~= nil) core.filter_listing_skips() local skips_only = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) local skips_joined = table.concat(skips_only, "\n") assert.is_true(skips_joined:find("[ SKIP ] - TestB", 1, true) ~= nil) assert.is_true(skips_joined:find("[ FAIL ] - TestC", 1, true) == nil) core.filter_listing_all() local restored = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) assert.equals(table.concat(original, "\n"), table.concat(restored, "\n")) vim.fn.jobstart = orig_jobstart end) it("keeps the listing unchanged when no filter matches exist", function() local runner = { name = "test-runner-no-matches", } 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 = {}, 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-no-match-runner"] = runner test_samurai.setup({ runner_modules = { "test-samurai-no-match-runner" } }) local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_no_match.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() local listing_buf = vim.api.nvim_get_current_buf() local original = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) core.filter_listing_failures() core.filter_listing_skips() local after = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) assert.equals(table.concat(original, "\n"), table.concat(after, "\n")) vim.fn.jobstart = orig_jobstart end) it("runs the test under the cursor from the listing", function() local runner = { name = "test-runner-run-cursor", } 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 local build_specs = {} function runner.build_command(spec) table.insert(build_specs, vim.deepcopy(spec)) return { cmd = { "echo", "single" }, 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 = { "TestC" }, skips = { "TestB" } } 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-run-cursor-runner"] = runner test_samurai.setup({ runner_modules = { "test-samurai-run-cursor-runner" } }) local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/test_samurai_run_cursor.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 runner._last_mocha_titles = { TestC = "Suite TestC" } core.run_nearest() local listing_buf = vim.api.nvim_get_current_buf() local lines = vim.api.nvim_buf_get_lines(listing_buf, 0, -1, false) local target = nil for i, line in ipairs(lines) do if line:match("^%[ FAIL %] %-") then target = i break end end assert.is_true(target ~= nil) vim.api.nvim_win_set_cursor(0, { target, 0 }) core.run_test_at_cursor() vim.api.nvim_win_set_cursor(0, { 1, 0 }) core.run_test_at_cursor() vim.fn.jobstart = orig_jobstart assert.equals(2, #build_specs) assert.equals("TestC", build_specs[2].test_name) assert.equals("TestC", build_specs[2].full_name) assert.equals("Suite TestC", build_specs[2].mocha_full_title) end) end)