local test_samurai = require("test-samurai") local core = require("test-samurai.core") describe("test-samurai public API", function() it("delegates show_output to core", function() local called = false local orig = core.show_output core.show_output = function() called = true end test_samurai.show_output() core.show_output = orig assert.is_true(called) end) it("delegates test_nearest to core.run_nearest", function() local called = false local orig = core.run_nearest core.run_nearest = function() called = true end test_samurai.test_nearest() core.run_nearest = orig assert.is_true(called) end) it("delegates test_file to core.run_file", function() local called = false local orig = core.run_file core.run_file = function() called = true end test_samurai.test_file() core.run_file = orig assert.is_true(called) end) it("delegates test_all to core.run_all", function() local called = false local orig = core.run_all core.run_all = function() called = true end test_samurai.test_all() core.run_all = orig assert.is_true(called) end) it("delegates test_last to core.run_last", function() local called = false local orig = core.run_last core.run_last = function() called = true end test_samurai.test_last() core.run_last = orig assert.is_true(called) end) it("delegates test_failed_only to core.run_failed_only", function() local called = false local orig = core.run_failed_only core.run_failed_only = function() called = true end test_samurai.test_failed_only() core.run_failed_only = orig assert.is_true(called) end) end) describe("test-samurai output formatting", function() before_each(function() test_samurai.setup() end) it("formats JSON output as PASS/FAIL lines", 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_format.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 lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false) vim.fn.jobstart = orig_jobstart local has_pass = false local has_skip = false local has_fail = false for _, line in ipairs(lines) do if line == "[ PASS ] - outer inner 1" then has_pass = true elseif line == "[ SKIP ] - outer inner skip" then has_skip = true elseif line == "[ FAIL ] - outer inner 2" then has_fail = true end end assert.is_true(has_pass) assert.is_true(has_skip) assert.is_true(has_fail) end) it("does not print raw JSON output for jest runs", 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_raw_json.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 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 local has_raw_json = false for _, line in ipairs(lines) do if line == json then has_raw_json = true break end end assert.is_false(has_raw_json) end) it("does not print raw JSON output for mocha json-stream", function() test_samurai.setup({ runner_modules = { "test-samurai.runners.js-mocha", }, }) local json_line = vim.json.encode({ event = "pass", fullTitle = "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_line }, 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_raw_json.test.js") vim.bo[bufnr].filetype = "javascript" 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 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 local has_raw_json = false for _, line in ipairs(lines) do if line == json_line then has_raw_json = true break end end assert.is_false(has_raw_json) end) it("formats mocha json-stream array output as PASS/FAIL lines", function() test_samurai.setup({ runner_modules = { "test-samurai.runners.js-mocha", }, }) local pass_line = vim.json.encode({ "pass", { title = "GET: /", fullTitle = "API :: /brands... GET: /", }, }) local orig_jobstart = vim.fn.jobstart vim.fn.jobstart = function(_cmd, opts) if opts and opts.on_stdout then opts.on_stdout(1, { pass_line }, 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_mocha_json_stream.test.js") vim.bo[bufnr].filetype = "javascript" 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 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 local has_pass = false local has_raw_json = false for _, line in ipairs(lines) do if line == "[ PASS ] - API :: /brands... GET: /" then has_pass = true elseif line == pass_line then has_raw_json = true end end assert.is_true(has_pass) assert.is_false(has_raw_json) end) it("does not print raw JSON when JSON arrives on stdout and stderr", function() test_samurai.setup({ runner_modules = { "test-samurai.runners.js-jest", }, }) local json1 = vim.json.encode({ testResults = { { assertionResults = { { status = "passed", title = "inner 1", fullName = "outer inner 1" }, }, }, }, }) local json2 = 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, { json1 }, nil) end if opts and opts.on_stderr then opts.on_stderr(1, { json2 }, 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_raw_json_both.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 lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false) vim.fn.jobstart = orig_jobstart local has_raw_json = false for _, line in ipairs(lines) do if line == json1 or line == json2 then has_raw_json = true break end end assert.is_false(has_raw_json) end) it("handles mixed mocha json-stream events and tracks failures for failed-only", function() test_samurai.setup({ runner_modules = { "test-samurai.runners.js-mocha", }, }) local pass_line = vim.json.encode({ event = "pass", fullTitle = "outer inner 1", }) local fail_line = vim.json.encode({ event = "fail", fullTitle = "outer inner 2", }) local job_calls = {} local orig_jobstart = vim.fn.jobstart vim.fn.jobstart = function(cmd, opts) table.insert(job_calls, { cmd = cmd, opts = opts }) if #job_calls == 1 then if opts and opts.on_stdout then opts.on_stdout(1, { pass_line, fail_line }, nil) end if opts and opts.on_exit then opts.on_exit(1, 1, nil) end else if opts and opts.on_exit then opts.on_exit(1, 1, nil) end end return 1 end local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_name(bufnr, "/tmp/output_mixed_json.test.js") vim.bo[bufnr].filetype = "javascript" 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 lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false) core.run_failed_only() vim.fn.jobstart = orig_jobstart local has_pass = false local has_fail = false for _, line in ipairs(lines) do if line == "[ PASS ] - outer inner 1" then has_pass = true elseif line == "[ FAIL ] - outer inner 2" then has_fail = true end end assert.is_true(has_pass) assert.is_true(has_fail) assert.is_true(#job_calls >= 2) local failed_cmd = job_calls[2].cmd or {} local saw_grep = false local saw_fgrep = false local saw_title = false for _, arg in ipairs(failed_cmd) do if arg == "--grep" then saw_grep = true elseif arg == "--fgrep" then saw_fgrep = true elseif arg == "outer inner 2" then saw_title = true end end assert.is_false(saw_grep) assert.is_true(saw_fgrep) assert.is_true(saw_title) end) it("formats TAP output as PASS/FAIL lines", function() test_samurai.setup({ runner_modules = { "test-samurai.runners.js-vitest", }, }) local orig_jobstart = vim.fn.jobstart vim.fn.jobstart = function(_cmd, opts) if opts and opts.on_stdout then opts.on_stdout(1, { "TAP version 13", "1..2", "ok 1 - outer inner 1 # time=1.00ms", "ok 2 - outer inner skip # SKIP not now", "not ok 2 - outer inner 2 # time=2.00ms", }, 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_format.tap.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 lines = vim.api.nvim_buf_get_lines(out_buf, 0, -1, false) vim.fn.jobstart = orig_jobstart local has_pass = false local has_skip = false local has_fail = false for _, line in ipairs(lines) do if line == "[ PASS ] - outer inner 1" then has_pass = true elseif line == "[ SKIP ] - outer inner skip" then has_skip = true elseif line == "[ FAIL ] - outer inner 2" then has_fail = true end end assert.is_true(has_pass) assert.is_true(has_skip) assert.is_true(has_fail) end) end)