finish first MVP with test-runner-detection for Go, Mocha.js, Jest.js and ViTest.js
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
local config = require("test-samurai.config")
|
||||
local util = require("test-samurai.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -30,19 +31,105 @@ function M.reload_runners()
|
||||
load_runners()
|
||||
end
|
||||
|
||||
local function detect_js_framework(file)
|
||||
local root = util.find_root(file, { "package.json", "node_modules" })
|
||||
if not root or root == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local pkg_path = vim.fs.joinpath(root, "package.json")
|
||||
local stat = vim.loop.fs_stat(pkg_path)
|
||||
if not stat or stat.type ~= "file" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ok_read, lines = pcall(vim.fn.readfile, pkg_path)
|
||||
if not ok_read or type(lines) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local json = table.concat(lines, "\n")
|
||||
local ok_json, pkg = pcall(vim.json.decode, json)
|
||||
if not ok_json or type(pkg) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local present = {}
|
||||
|
||||
local function scan_section(section)
|
||||
if type(section) ~= "table" then
|
||||
return
|
||||
end
|
||||
for name, _ in pairs(section) do
|
||||
if name == "mocha" or name == "jest" or name == "vitest" then
|
||||
present[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scan_section(pkg.dependencies)
|
||||
scan_section(pkg.devDependencies)
|
||||
|
||||
if next(present) == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return present
|
||||
end
|
||||
|
||||
function M.get_runner_for_buf(bufnr)
|
||||
local path = util.get_buf_path(bufnr)
|
||||
|
||||
local candidates = {}
|
||||
|
||||
for _, runner in ipairs(state.runners) do
|
||||
if type(runner.is_test_file) == "function" then
|
||||
local ok, is_test = pcall(runner.is_test_file, bufnr)
|
||||
if ok and is_test then
|
||||
return runner
|
||||
table.insert(candidates, runner)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #candidates == 1 then
|
||||
return candidates[1]
|
||||
elseif #candidates > 1 then
|
||||
local frameworks = nil
|
||||
if path and path ~= "" then
|
||||
frameworks = detect_js_framework(path)
|
||||
end
|
||||
if frameworks then
|
||||
for _, runner in ipairs(candidates) do
|
||||
if runner.framework and frameworks[runner.framework] then
|
||||
return runner
|
||||
end
|
||||
end
|
||||
end
|
||||
return candidates[1]
|
||||
end
|
||||
|
||||
if not path or path == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if path:sub(-8) == "_test.go" then
|
||||
local ok, go = pcall(require, "test-samurai.runners.go")
|
||||
if ok and type(go) == "table" then
|
||||
return go
|
||||
end
|
||||
end
|
||||
|
||||
if path:find(".test.", 1, true) or path:find(".spec.", 1, true) then
|
||||
local ok, jsjest = pcall(require, "test-samurai.runners.js-jest")
|
||||
if ok and type(jsjest) == "table" then
|
||||
return jsjest
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function open_float(lines)
|
||||
local function create_output_win(initial_lines)
|
||||
if state.last_win and vim.api.nvim_win_is_valid(state.last_win) then
|
||||
pcall(vim.api.nvim_win_close, state.last_win, true)
|
||||
end
|
||||
@@ -58,7 +145,7 @@ local function open_float(lines)
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
|
||||
vim.api.nvim_buf_set_option(buf, "filetype", "test-samurai-output")
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, initial_lines or {})
|
||||
|
||||
local win = vim.api.nvim_open_win(buf, true, {
|
||||
relative = "editor",
|
||||
@@ -78,42 +165,59 @@ local function open_float(lines)
|
||||
|
||||
state.last_win = win
|
||||
state.last_buf = buf
|
||||
|
||||
return buf, win
|
||||
end
|
||||
|
||||
local function run_cmd(cmd, cwd, on_exit)
|
||||
if vim.system then
|
||||
vim.system(cmd, { cwd = cwd, text = true }, function(obj)
|
||||
local code = obj.code or -1
|
||||
local stdout = obj.stdout or ""
|
||||
local stderr = obj.stderr or ""
|
||||
vim.schedule(function()
|
||||
on_exit(code, stdout, stderr)
|
||||
end)
|
||||
end)
|
||||
else
|
||||
local stdout_chunks = {}
|
||||
local stderr_chunks = {}
|
||||
vim.fn.jobstart(cmd, {
|
||||
cwd = cwd,
|
||||
stdout_buffered = true,
|
||||
stderr_buffered = true,
|
||||
on_stdout = function(_, data)
|
||||
if data then
|
||||
table.insert(stdout_chunks, table.concat(data, "\n"))
|
||||
end
|
||||
end,
|
||||
on_stderr = function(_, data)
|
||||
if data then
|
||||
table.insert(stderr_chunks, table.concat(data, "\n"))
|
||||
end
|
||||
end,
|
||||
on_exit = function(_, code)
|
||||
local stdout = table.concat(stdout_chunks, "\n")
|
||||
local stderr = table.concat(stderr_chunks, "\n")
|
||||
on_exit(code, stdout, stderr)
|
||||
end,
|
||||
})
|
||||
local function append_lines(buf, new_lines)
|
||||
if not (buf and vim.api.nvim_buf_is_valid(buf)) then
|
||||
return
|
||||
end
|
||||
if not new_lines or #new_lines == 0 then
|
||||
return
|
||||
end
|
||||
local existing = vim.api.nvim_buf_line_count(buf)
|
||||
vim.api.nvim_buf_set_lines(buf, existing, existing, false, new_lines)
|
||||
end
|
||||
|
||||
local function run_cmd(cmd, cwd, handlers)
|
||||
local h = handlers or {}
|
||||
|
||||
if h.on_start then
|
||||
pcall(h.on_start)
|
||||
end
|
||||
|
||||
local function handle_chunk(fn, data)
|
||||
if not fn or not data then
|
||||
return
|
||||
end
|
||||
local lines = {}
|
||||
for _, line in ipairs(data) do
|
||||
if line ~= nil and line ~= "" then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
end
|
||||
if #lines > 0 then
|
||||
fn(lines)
|
||||
end
|
||||
end
|
||||
|
||||
vim.fn.jobstart(cmd, {
|
||||
cwd = cwd,
|
||||
stdout_buffered = false,
|
||||
stderr_buffered = false,
|
||||
on_stdout = function(_, data, _)
|
||||
handle_chunk(h.on_stdout, data)
|
||||
end,
|
||||
on_stderr = function(_, data, _)
|
||||
handle_chunk(h.on_stderr, data)
|
||||
end,
|
||||
on_exit = function(_, code, _)
|
||||
if h.on_exit then
|
||||
pcall(h.on_exit, code or 0)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function M.run_nearest()
|
||||
@@ -153,23 +257,53 @@ function M.run_nearest()
|
||||
local cmd = command.cmd
|
||||
local cwd = command.cwd or vim.loop.cwd()
|
||||
|
||||
run_cmd(cmd, cwd, function(code, stdout, stderr)
|
||||
local header = "$ " .. table.concat(cmd, " ")
|
||||
local lines = { header, "" }
|
||||
if stdout ~= "" then
|
||||
local out_lines = vim.split(stdout, "\n", { plain = true })
|
||||
vim.list_extend(lines, out_lines)
|
||||
end
|
||||
if stderr ~= "" then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "[stderr]")
|
||||
local err_lines = vim.split(stderr, "\n", { plain = true })
|
||||
vim.list_extend(lines, err_lines)
|
||||
end
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "[exit code] " .. tostring(code))
|
||||
open_float(lines)
|
||||
end)
|
||||
local header = "$ " .. table.concat(cmd, " ")
|
||||
local buf = nil
|
||||
local has_output = false
|
||||
|
||||
run_cmd(cmd, cwd, {
|
||||
on_start = function()
|
||||
buf = select(1, create_output_win({ header, "", "[running...]" }))
|
||||
end,
|
||||
on_stdout = function(lines)
|
||||
if not buf then
|
||||
buf = select(1, create_output_win({ header, "" }))
|
||||
end
|
||||
if not has_output then
|
||||
local cur = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
||||
if #cur > 0 and cur[#cur] == "[running...]" then
|
||||
vim.api.nvim_buf_set_lines(buf, #cur - 1, #cur, false, {})
|
||||
end
|
||||
has_output = true
|
||||
end
|
||||
append_lines(buf, lines)
|
||||
end,
|
||||
on_stderr = function(lines)
|
||||
if not buf then
|
||||
buf = select(1, create_output_win({ header, "" }))
|
||||
end
|
||||
if not has_output then
|
||||
local cur = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
||||
if #cur > 0 and cur[#cur] == "[running...]" then
|
||||
vim.api.nvim_buf_set_lines(buf, #cur - 1, #cur, false, {})
|
||||
end
|
||||
has_output = true
|
||||
end
|
||||
append_lines(buf, lines)
|
||||
end,
|
||||
on_exit = function(code)
|
||||
if not buf then
|
||||
buf = select(1, create_output_win({ header }))
|
||||
end
|
||||
if not has_output then
|
||||
local cur = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
||||
if #cur > 0 and cur[#cur] == "[running...]" then
|
||||
vim.api.nvim_buf_set_lines(buf, #cur - 1, #cur, false, {})
|
||||
end
|
||||
end
|
||||
append_lines(buf, { "", "[exit code] " .. tostring(code) })
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user