Skip to content

Commit

Permalink
Use busted directly (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
MisanthropicBit authored Aug 30, 2024
1 parent d1ac5d5 commit 1dbdd3c
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 40 deletions.
14 changes: 14 additions & 0 deletions .busted.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
return {
-- Use neotest-busted in all tasks
_all = {
lua = "nvim -l path/to/neotest-busted/scripts/test-runner.lua",
},
-- Default task to run if no task was specified
default = {
verbose = true,
},
integration = {
tags = "integration",
shuffle_files = true,
},
}
2 changes: 1 addition & 1 deletion .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: 0.16.1
args: --check lua/ tests/
args: --check lua/ tests/ scripts/
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ the command will automatically try to find your tests in a `spec/`, `test/`, or
$ nvim -l <path-to-neotest-busted>/scripts/test-runner.lua tests/my_spec.lua
```

### Using busted directly

You can also provide a `.busted` config file and run your tests using busted.
Learn more about busted configuration files from the [official
docs](https://lunarmodules.github.io/busted/#usage) or take a look at the example [here](/.busted.example).

Pass extra arguments to `neotest` to run a specific task. For example, to run
the `"integration"` task in a test file:

```lua
require("neotest").run.run({ vim.fn.expand("%"), extra_args = { "--run", "integration" } })
```

## Debugging tests

`neotest-busted` has support for debugging tests via [`local-lua-debugger-vscode`](https://github.com/tomblind/local-lua-debugger-vscode)
Expand Down
10 changes: 9 additions & 1 deletion lua/neotest-busted/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,18 @@ function BustedNeotestAdapter.create_test_command(results_path, paths, filters,

vim.list_extend(arguments, busted_command)

if vim.tbl_islist(config.busted_args) and #config.busted_args > 0 then
if vim.tbl_islist(config.busted_args) then
vim.list_extend(arguments, config.busted_args)
end

if vim.tbl_islist(_options.busted_arguments) then
for _, busted_arg in ipairs(_options.busted_arguments) do
local arg = _options.quote_strings and quote_string(busted_arg) or busted_arg

table.insert(arguments, arg)
end
end

-- Add test filters
for _, filter in ipairs(filters) do
local escaped_filter = escape_test_pattern_filter(filter)
Expand Down
3 changes: 2 additions & 1 deletion lua/neotest-busted/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
---@field lua_cpaths string[]

---@class neotest-busted.TestCommandOptions
---@field busted_arguments string[]?
---@field busted_output_handler string?
---@field busted_output_handler_options string[]?
---@field quote_strings boolean
---@field quote_strings boolean?

---@class neotest-busted.TestCommandConfig
---@field nvim_command string
Expand Down
131 changes: 94 additions & 37 deletions scripts/test-runner.lua
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
local help_message = [[test-runner [...options] [...test_files] [-- [...busted_options]
Run tests using neotest-busted from the commandline. Options given after '--'
are forwarded to busted.
Usage:
-h, --help Show this help message.
]]

---@class ParsedArgs
---@field help boolean
---@field paths string[]
---@field busted_args string[]

---@enum Color
local Color = {
Red = 31,
Expand Down Expand Up @@ -32,93 +47,127 @@ local level_options = {
color = Color.White,
hl_group = "MoreMsg",
},
[vim.log.levels.OFF] = {
name = "",
color = Color.Reset,
hl_group = "",
},
}

local function is_windows()
if jit then
return not vim.tbl_contains({ "linux", "osx", "bsd", "posix", "other" }, jit.os:lower())
else
return package.config:sub(1, 1) == "\\"
end
end

local _is_windows = is_windows()

local function is_headless()
return #vim.api.nvim_list_uis() == 0
end

---@param color integer
---@return string
local function color_code(color)
if _is_windows then
return ""
end

return ("\x1b[%dm"):format(color)
end

---@param message string
---@param level vim.log.levels?
local function print_level(message, level)
local options = level_options[level] or level_options[vim.log.levels.ERROR]
local _level = level or vim.log.levels.OFF
local options = level_options[_level]
local prefix = ""

if is_headless() then
io.stderr:write(
("%s%s%s: %s\n"):format(
if _level ~= vim.log.levels.OFF then
prefix = ("%s%s%s: "):format(
color_code(options.color),
options.name,
color_code(Color.Reset),
message
color_code(Color.Reset)
)
)
end

io.stderr:write(("%s%s\n"):format(prefix, message))
else
if _level ~= vim.log.levels.OFF then
prefix = ("[neotest-busted:%s]: "):format(options.name)
end

vim.api.nvim_echo({
{ ("[neotest-busted:%s]: "):format(options.name), options.hl_group },
{ prefix, options.hl_group },
{ message },
}, true, {})
end
end

---@return string?
local function find_minimal_init()
-- NOTE: Do not use util.glob as we haven't loaded neotest-busted at this point
local glob_matches = vim.fn.glob("**/minimal_init.lua", false, true)

if #glob_matches == 0 then
print_level("Could not find minimal_init.lua")
print_level("Could not find minimal_init.lua", vim.log.levels.ERROR)
return
end

return glob_matches[1]
end

---@param module_name string
---@return any
local function require_checked(module_name)
local ok, module_or_error = pcall(require, module_name)

if not ok then
return nil
---@return ParsedArgs
local function parse_args()
local parsed_args = {
help = false,
paths = {},
busted_args = {},
}

-- Start from the third argument to skip busted executable and "--ignore-lua" flag
-- TODO: Should we just use them instead of skipping them?
for idx = 3, #_G.arg do
local arg = _G.arg[idx]

if arg == "-h" or arg == "--help" then
parsed_args.help = true
elseif arg == "--" then
vim.list_extend(parsed_args.busted_args, _G.arg, idx + 1)
break
else
table.insert(parsed_args.paths, arg)
end
end

return module_or_error
end

---@return string[]
local function parse_args()
return vim.list_slice(_G.arg, 1)
return parsed_args
end

---@return string[]
local function collect_tests()
local tests = {}
local util = require("neotest-busted.util")

vim.list_extend(tests, util.glob("./test/**/*_spec.lua"))
vim.list_extend(tests, util.glob("./tests/**/*_spec.lua"))
vim.list_extend(tests, util.glob("./spec/**/*_spec.lua"))
-- TODO: Support other test file patterns (via .busted)
vim.list_extend(tests, util.glob("test/**/*_spec.lua"))
vim.list_extend(tests, util.glob("tests/**/*_spec.lua"))
vim.list_extend(tests, util.glob("spec/**/*_spec.lua"))

return tests
end

local function run()
if not is_headless() then
print_level("Script must be run from the command line")
print_level("Script must be run from the command line", vim.log.levels.ERROR)
return
end

local paths = parse_args()
local minimal_init = find_minimal_init()

if not minimal_init then
print_level("Could not find a minimal_init.lua file")
print_level("Could not find a minimal_init.lua file", vim.log.levels.ERROR)
return
end

Expand All @@ -135,25 +184,33 @@ local function run()
return
end

if #paths == 0 then
paths = collect_tests()
local parsed_args = parse_args()

if parsed_args.help then
print_level(help_message)
return
end

local busted = adapter_or_error.create_test_command(nil, paths, {}, {
local paths = #parsed_args.paths > 0 and parsed_args.paths or collect_tests()

local test_command = adapter_or_error.create_test_command(nil, paths, {}, {
busted_output_handler = "utfTerminal",
busted_output_handler_options = { "--color" },
-- If we don't add --ignore-lua the subsequent busted command (run via
-- neovim) will use the .busted config file and use the 'lua' option
-- again for running the tests (this script) which will cause an
-- infinite process spawning loop
busted_arguments = vim.list_extend({ "--ignore-lua" }, parsed_args.busted_args),
})

if not busted then
print_level("Could not find a busted executable")
if not test_command then
print_level("Could not find a busted executable", vim.log.levels.ERROR)
return
end

local command = vim.list_extend({ busted.nvim_command }, busted.arguments)
local command = vim.list_extend({ test_command.nvim_command }, test_command.arguments)

io.stdout:write(
vim.fn.system(table.concat(vim.tbl_map(vim.fn.shellescape, command), " "))
)
io.stdout:write(vim.fn.system(table.concat(vim.tbl_map(vim.fn.shellescape, command), " ")))
end

run()

0 comments on commit 1dbdd3c

Please sign in to comment.