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", "/tmp/project/foo_failed_only.test.ts", "-t", "inner 2" }, calls[1].cmd ) assert.are.same( { "npx", "jest", "--json", "-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("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", "-t", "outer inner 2", "/tmp/project/foo_failed_only_last.test.ts" }, calls[2].cmd ) end) end)