add test runner for lua plenary tests
This commit is contained in:
@@ -6,6 +6,7 @@ local defaults = {
|
||||
"test-samurai.runners.js-jest",
|
||||
"test-samurai.runners.js-mocha",
|
||||
"test-samurai.runners.js-vitest",
|
||||
"test-samurai.runners.lua-plenary",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
240
lua/test-samurai/runners/lua-plenary.lua
Normal file
240
lua/test-samurai/runners/lua-plenary.lua
Normal file
@@ -0,0 +1,240 @@
|
||||
local util = require("test-samurai.util")
|
||||
|
||||
local runner = {
|
||||
name = "lua-plenary",
|
||||
framework = "plenary",
|
||||
}
|
||||
|
||||
local function is_lua_test_path(path)
|
||||
if not path or path == "" then
|
||||
return false
|
||||
end
|
||||
if not path:match("%.lua$") then
|
||||
return false
|
||||
end
|
||||
if path:match("_spec%.lua$") then
|
||||
return true
|
||||
end
|
||||
if path:match("/tests/") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function runner.is_test_file(bufnr)
|
||||
local path = util.get_buf_path(bufnr)
|
||||
return is_lua_test_path(path)
|
||||
end
|
||||
|
||||
local function find_repo_root(file)
|
||||
local root = util.find_root(file, { ".git", "tests", "lua", "plugin" })
|
||||
if not root or root == "" then
|
||||
root = vim.loop.cwd()
|
||||
end
|
||||
return root
|
||||
end
|
||||
|
||||
local function minimal_init_for(root)
|
||||
return vim.fs.joinpath(root, "tests", "minimal_init.lua")
|
||||
end
|
||||
|
||||
local function escape_for_ex_double_quotes(s)
|
||||
s = s or ""
|
||||
s = s:gsub("\\", "\\\\")
|
||||
s = s:gsub('"', '\\"')
|
||||
return s
|
||||
end
|
||||
|
||||
local function count_keyword(line, kw)
|
||||
local c = 0
|
||||
local pat = "%f[%w]" .. kw .. "%f[%W]"
|
||||
for _ in line:gmatch(pat) do
|
||||
c = c + 1
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local function find_end_lua_block(lines, start_idx)
|
||||
local depth = 0
|
||||
local started = false
|
||||
for i = start_idx, #lines do
|
||||
local line = lines[i]
|
||||
local fnc = count_keyword(line, "function")
|
||||
local endc = count_keyword(line, "end")
|
||||
if fnc > 0 then
|
||||
depth = depth + fnc
|
||||
started = true
|
||||
end
|
||||
if started and endc > 0 then
|
||||
depth = depth - endc
|
||||
if depth <= 0 then
|
||||
return i - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return #lines - 1
|
||||
end
|
||||
|
||||
local function collect_lua_structs(lines)
|
||||
local describes = {}
|
||||
local tests = {}
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
local dname = line:match("^%s*describe%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if dname then
|
||||
local start0 = i - 1
|
||||
local end0 = find_end_lua_block(lines, i)
|
||||
table.insert(describes, { kind = "describe", name = dname, start = start0, ["end"] = end0 })
|
||||
else
|
||||
local tname = line:match("^%s*it%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if tname then
|
||||
local start0 = i - 1
|
||||
local end0 = find_end_lua_block(lines, i)
|
||||
table.insert(tests, { kind = "it", name = tname, start = start0, ["end"] = end0 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return describes, tests
|
||||
end
|
||||
|
||||
local function build_full_name(lines, idx, leaf_name)
|
||||
local parts = { leaf_name }
|
||||
for i = idx - 1, 1, -1 do
|
||||
local line = lines[i]
|
||||
local dname = line:match("^%s*describe%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if dname then
|
||||
table.insert(parts, 1, dname)
|
||||
end
|
||||
end
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
function runner.find_nearest(bufnr, row, _col)
|
||||
if not runner.is_test_file(bufnr) then
|
||||
return nil, "not a lua test file"
|
||||
end
|
||||
|
||||
local lines = util.get_buf_lines(bufnr)
|
||||
local describes, tests = collect_lua_structs(lines)
|
||||
|
||||
for _, t in ipairs(tests) do
|
||||
if row >= t.start and row <= t["end"] then
|
||||
local full = build_full_name(lines, t.start + 1, t.name)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
local root = find_repo_root(file)
|
||||
return { file = file, cwd = root, test_name = t.name, full_name = full, kind = "it" }
|
||||
end
|
||||
end
|
||||
|
||||
local best_describe = nil
|
||||
for _, d in ipairs(describes) do
|
||||
if row >= d.start and row <= d["end"] then
|
||||
if not best_describe or d.start >= best_describe.start then
|
||||
best_describe = d
|
||||
end
|
||||
end
|
||||
end
|
||||
if best_describe then
|
||||
local full = build_full_name(lines, best_describe.start + 1, best_describe.name)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
local root = find_repo_root(file)
|
||||
return { file = file, cwd = root, test_name = best_describe.name, full_name = full, kind = "describe" }
|
||||
end
|
||||
|
||||
local start = row + 1
|
||||
if start > #lines then
|
||||
start = #lines
|
||||
elseif start < 1 then
|
||||
start = 1
|
||||
end
|
||||
for i = start, 1, -1 do
|
||||
local line = lines[i]
|
||||
local tname = line:match("^%s*it%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if tname then
|
||||
local full = build_full_name(lines, i, tname)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
local root = find_repo_root(file)
|
||||
return { file = file, cwd = root, test_name = tname, full_name = full, kind = "it" }
|
||||
end
|
||||
local dname = line:match("^%s*describe%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if dname then
|
||||
local full = build_full_name(lines, i, dname)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
local root = find_repo_root(file)
|
||||
return { file = file, cwd = root, test_name = dname, full_name = full, kind = "describe" }
|
||||
end
|
||||
end
|
||||
|
||||
return nil, "no test call found"
|
||||
end
|
||||
|
||||
local function ex_plenary_busted_file(file, filter_name)
|
||||
local ex = "PlenaryBustedFile " .. vim.fn.fnameescape(file)
|
||||
if filter_name and filter_name ~= "" then
|
||||
local f = escape_for_ex_double_quotes(filter_name)
|
||||
ex = ex .. ' { busted_args = { "--filter", "' .. f .. '" } }'
|
||||
end
|
||||
return ex
|
||||
end
|
||||
|
||||
function runner.build_command(spec)
|
||||
local root = spec and spec.cwd or vim.loop.cwd()
|
||||
local minit = minimal_init_for(root)
|
||||
|
||||
local cmd = {
|
||||
"nvim",
|
||||
"--headless",
|
||||
"-u",
|
||||
minit,
|
||||
"-c",
|
||||
ex_plenary_busted_file(spec.file, spec.test_name),
|
||||
"-c",
|
||||
"qa",
|
||||
}
|
||||
|
||||
return { cmd = cmd, cwd = root }
|
||||
end
|
||||
|
||||
function runner.build_file_command(bufnr)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
if not file or file == "" then
|
||||
return nil
|
||||
end
|
||||
local root = find_repo_root(file)
|
||||
local minit = minimal_init_for(root)
|
||||
|
||||
local cmd = {
|
||||
"nvim",
|
||||
"--headless",
|
||||
"-u",
|
||||
minit,
|
||||
"-c",
|
||||
ex_plenary_busted_file(file, nil),
|
||||
"-c",
|
||||
"qa",
|
||||
}
|
||||
|
||||
return { cmd = cmd, cwd = root }
|
||||
end
|
||||
|
||||
function runner.build_all_command(bufnr)
|
||||
local file = util.get_buf_path(bufnr)
|
||||
local root = find_repo_root(file)
|
||||
local minit = minimal_init_for(root)
|
||||
|
||||
local cmd = {
|
||||
"nvim",
|
||||
"--headless",
|
||||
"-u",
|
||||
minit,
|
||||
"-c",
|
||||
"PlenaryBustedDirectory tests",
|
||||
"-c",
|
||||
"qa",
|
||||
}
|
||||
|
||||
return { cmd = cmd, cwd = root }
|
||||
end
|
||||
|
||||
return runner
|
||||
Reference in New Issue
Block a user