local util = require("test-samurai.util") local M = {} local default_filetypes = { javascript = true, javascriptreact = true, typescript = true, typescriptreact = true, } local default_patterns = { ".test.", ".spec." } local function is_js_test_file(bufnr, filetypes, patterns) local ft = vim.bo[bufnr].filetype if not filetypes[ft] then return false end local path = util.get_buf_path(bufnr) if not path or path == "" then return false end for _, pat in ipairs(patterns) do if path:find(pat, 1, true) then return true end end return false end local function find_nearest_test(bufnr, row) local lines = util.get_buf_lines(bufnr) 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 call, name = line:match("^%s*(it|test|describe)%s*%(%s*['"`]([^'"`]+)['"`]") if call and name then return { kind = call, name = name, line = i - 1, } end end return nil end function M.new(opts) local cfg = opts or {} local runner = {} runner.name = cfg.name or "js" runner.framework = cfg.framework or "jest" runner.command = cfg.command or { "npx", runner.framework } runner.filetypes = {} if cfg.filetypes then for _, ft in ipairs(cfg.filetypes) do runner.filetypes[ft] = true end else runner.filetypes = vim.deepcopy(default_filetypes) end runner.patterns = cfg.patterns or default_patterns function runner.is_test_file(bufnr) return is_js_test_file(bufnr, runner.filetypes, runner.patterns) end function runner.find_nearest(bufnr, row, _col) if not runner.is_test_file(bufnr) then return nil, "not a JS/TS test file" end local hit = find_nearest_test(bufnr, row) if not hit then 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", }) return { file = path, cwd = root, framework = runner.framework, test_name = hit.name, kind = hit.kind, } end local function build_jest(spec) local cmd = vim.deepcopy(runner.command) table.insert(cmd, spec.file) table.insert(cmd, "-t") table.insert(cmd, spec.test_name) return cmd end local function build_mocha(spec) local cmd = vim.deepcopy(runner.command) table.insert(cmd, spec.file) table.insert(cmd, "--grep") table.insert(cmd, spec.test_name) return cmd end local function build_vitest(spec) local cmd = vim.deepcopy(runner.command) table.insert(cmd, spec.file) table.insert(cmd, "-t") table.insert(cmd, spec.test_name) return cmd end function runner.build_command(spec) local fw = runner.framework local cmd if fw == "jest" then cmd = build_jest(spec) elseif fw == "mocha" then cmd = build_mocha(spec) elseif fw == "vitest" then cmd = build_vitest(spec) else cmd = vim.deepcopy(runner.command) table.insert(cmd, spec.file) end return { cmd = cmd, cwd = spec.cwd, } end return runner end return M