finish first MVP with test-runner-detection for Go, Mocha.js, Jest.js and ViTest.js
This commit is contained in:
@@ -28,8 +28,168 @@ local function is_js_test_file(bufnr, filetypes, patterns)
|
||||
return false
|
||||
end
|
||||
|
||||
local function find_block_end(lines, start_idx)
|
||||
local depth = 0
|
||||
local started = false
|
||||
for i = start_idx, #lines do
|
||||
local line = lines[i]
|
||||
for j = 1, #line do
|
||||
local ch = line:sub(j, j)
|
||||
if ch == "{" then
|
||||
depth = depth + 1
|
||||
started = true
|
||||
elseif ch == "}" then
|
||||
if started then
|
||||
depth = depth - 1
|
||||
if depth == 0 then
|
||||
return i - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return #lines - 1
|
||||
end
|
||||
|
||||
local jest_config_files = { "jest.config.js", "jest.config.ts" }
|
||||
|
||||
local function find_jest_root(file)
|
||||
if not file or file == "" then
|
||||
return util.find_root(file, {
|
||||
"jest.config.js",
|
||||
"jest.config.ts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.js",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
})
|
||||
end
|
||||
|
||||
local dir = vim.fs.dirname(file)
|
||||
local found = vim.fs.find(jest_config_files, { path = dir, upward = true })
|
||||
if found and #found > 0 then
|
||||
local cfg = found[1]
|
||||
local sep = package.config:sub(1, 1)
|
||||
local marker = sep .. "test" .. sep .. ".bin" .. sep
|
||||
local idx = cfg:find(marker, 1, true)
|
||||
if idx then
|
||||
local root = cfg:sub(1, idx - 1)
|
||||
if root == "" then
|
||||
root = sep
|
||||
end
|
||||
return root
|
||||
end
|
||||
return vim.fs.dirname(cfg)
|
||||
end
|
||||
|
||||
return util.find_root(file, {
|
||||
"jest.config.js",
|
||||
"jest.config.ts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.js",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
})
|
||||
end
|
||||
|
||||
local function match_test_call(line)
|
||||
local call, name = line:match("^%s*(it)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if call then
|
||||
return call, name
|
||||
end
|
||||
call, name = line:match("^%s*(test)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if call then
|
||||
return call, name
|
||||
end
|
||||
call, name = line:match("^%s*(describe)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if call and name then
|
||||
return call, name
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function collect_js_structs(lines)
|
||||
local describes = {}
|
||||
local tests = {}
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
local dcall, dname = line:match("^%s*(describe)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if dcall and dname then
|
||||
local start0 = i - 1
|
||||
local end0 = find_block_end(lines, i)
|
||||
table.insert(describes, {
|
||||
kind = dcall,
|
||||
name = dname,
|
||||
start = start0,
|
||||
["end"] = end0,
|
||||
})
|
||||
else
|
||||
local tcall, tname = line:match("^%s*(it)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if not tcall then
|
||||
tcall, tname = line:match("^%s*(test)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
end
|
||||
if tcall and tname then
|
||||
local start0 = i - 1
|
||||
local end0 = find_block_end(lines, i)
|
||||
table.insert(tests, {
|
||||
kind = tcall,
|
||||
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 call, name = line:match("^%s*(describe)%s*%(%s*['\"`]([^'\"`]+)['\"`]")
|
||||
if call and name then
|
||||
table.insert(parts, 1, name)
|
||||
end
|
||||
end
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
local function find_nearest_test(bufnr, row)
|
||||
local lines = util.get_buf_lines(bufnr)
|
||||
local describes, tests = collect_js_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)
|
||||
return {
|
||||
kind = t.kind,
|
||||
name = t.name,
|
||||
full_name = full,
|
||||
line = t.start,
|
||||
}
|
||||
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)
|
||||
return {
|
||||
kind = "describe",
|
||||
name = best_describe.name,
|
||||
full_name = full,
|
||||
line = best_describe.start,
|
||||
}
|
||||
end
|
||||
|
||||
local start = row + 1
|
||||
if start > #lines then
|
||||
start = #lines
|
||||
@@ -38,15 +198,18 @@ local function find_nearest_test(bufnr, row)
|
||||
end
|
||||
for i = start, 1, -1 do
|
||||
local line = lines[i]
|
||||
local call, name = line:match("^%s*(it|test|describe)%s*%(%s*['"`]([^'"`]+)['"`]")
|
||||
local call, name = match_test_call(line)
|
||||
if call and name then
|
||||
local full = build_full_name(lines, i, name)
|
||||
return {
|
||||
kind = call,
|
||||
name = name,
|
||||
full_name = full,
|
||||
line = i - 1,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -82,19 +245,25 @@ function M.new(opts)
|
||||
return nil, "no test call found"
|
||||
end
|
||||
local path = util.get_buf_path(bufnr)
|
||||
local root = util.find_root(path, {
|
||||
"jest.config.js",
|
||||
"jest.config.ts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.js",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
})
|
||||
local root
|
||||
if runner.framework == "jest" then
|
||||
root = find_jest_root(path)
|
||||
else
|
||||
root = util.find_root(path, {
|
||||
"jest.config.js",
|
||||
"jest.config.ts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.js",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
})
|
||||
end
|
||||
return {
|
||||
file = path,
|
||||
cwd = root,
|
||||
framework = runner.framework,
|
||||
test_name = hit.name,
|
||||
full_name = hit.full_name,
|
||||
kind = hit.kind,
|
||||
}
|
||||
end
|
||||
@@ -109,9 +278,10 @@ function M.new(opts)
|
||||
|
||||
local function build_mocha(spec)
|
||||
local cmd = vim.deepcopy(runner.command)
|
||||
local target = spec.full_name or spec.test_name
|
||||
table.insert(cmd, "--fgrep")
|
||||
table.insert(cmd, target)
|
||||
table.insert(cmd, spec.file)
|
||||
table.insert(cmd, "--grep")
|
||||
table.insert(cmd, spec.test_name)
|
||||
return cmd
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user