From 31e410341bdba06a5d0a7fa72a615fd8e9dc5168 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Fri, 16 Feb 2024 16:34:16 -0500 Subject: [PATCH 01/14] initial stab at a dynamic config --- lua/neotest-pest/config.lua | 33 +++++++++++++++++++++++++++++++++ lua/neotest-pest/init.lua | 32 ++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 lua/neotest-pest/config.lua diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua new file mode 100644 index 0000000..e8675e5 --- /dev/null +++ b/lua/neotest-pest/config.lua @@ -0,0 +1,33 @@ +local M = {} + +M.enable_sail = function() + return vim.fn.filereadable("vendor/bin/sail") == 1 +end + +M.get_pest_cmd = function() + local binary = "pest" + + if vim.fn.filereadable("vendor/bin/pest") == 1 then + binary = "vendor/bin/pest" + end + + return binary +end + +M.get_env = function() + return {} +end + +M.get_root_ignore_files = function() + return {} +end + +M.get_root_files = function() + return { "tests/Pest.php" } +end + +M.get_filter_dirs = function() + return { ".git", "node_modules", "vendor" } +end + +return M diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 672fe1f..033e5ad 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -1,7 +1,7 @@ local lib = require('neotest.lib') -local async = require('neotest.async') local logger = require('neotest.logging') local utils = require('neotest-pest.utils') +local config = require('neotest-pest.config') ---@class neotest.Adapter ---@field name string @@ -13,7 +13,21 @@ local NeotestAdapter = { name = "neotest-pest" } ---@async ---@param dir string @Directory to treat as cwd ---@return string | nil @Absolute root dir of test suite -NeotestAdapter.root = lib.files.match_root_pattern("tests/Pest.php") +function NeotestAdapter.root() + local result = nil + + for _, root_ignore_file in ipairs(config.get_root_ignore_files()) do + result = lib.files.match_root_pattern(root_ignore_file)(dir) + if result then return nil end + end + + for _, root_file in ipairs(config.get_root_files()) do + result = lib.files.match_root_pattern(root_file)(dir) + if result then break end + end + + return result +end ---Filter directories when searching for test files ---@async @@ -52,16 +66,6 @@ function NeotestAdapter.discover_positions(path) }) end -local function get_pest_cmd() - local binary = "pest" - - if vim.fn.filereadable("vendor/bin/pest") == 1 then - binary = "vendor/bin/pest" - end - - return binary -end - local is_callable = function(obj) return type(obj) == "function" or (type(obj) == "table" and obj.__call) end @@ -72,11 +76,11 @@ function NeotestAdapter.build_spec(args) local position = args.tree:data() local results_path = "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") - local binary = get_pest_cmd() + local binary = config.get_pest_cmd() local command = {} - if vim.fn.filereadable("vendor/bin/sail") == 1 then + if config.enable_sail() then command = vim.tbl_flatten({ "vendor/bin/sail", "bin", "pest", position.name ~= "tests" and ("/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd()) + 1)), From 6bef79c7660c60b62585b98022b58de60232f551 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Fri, 16 Feb 2024 16:52:29 -0500 Subject: [PATCH 02/14] Initial setup of some dynamic config bits --- lua/neotest-pest/init.lua | 63 ++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 033e5ad..eb4ddf7 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -13,19 +13,35 @@ local NeotestAdapter = { name = "neotest-pest" } ---@async ---@param dir string @Directory to treat as cwd ---@return string | nil @Absolute root dir of test suite -function NeotestAdapter.root() +function NeotestAdapter.root(dir) local result = nil + logger.info("Finding root...") + for _, root_ignore_file in ipairs(config.get_root_ignore_files()) do - result = lib.files.match_root_pattern(root_ignore_file)(dir) - if result then return nil end + logger.info("Checking root ignore file", root_ignore_file) + + result = lib.files.match_root_pattern(root_ignore_file)(dir) + + if result then + logger.info("Ignoring root because file", root_ignore_file) + return nil + end end for _, root_file in ipairs(config.get_root_files()) do - result = lib.files.match_root_pattern(root_file)(dir) - if result then break end + logger.info("Checking root file", root_file) + + result = lib.files.match_root_pattern(root_file)(dir) + + if result then + logger.info("Found root", result) + break + end end + logger.info("Root not found") + return result end @@ -43,6 +59,7 @@ end ---@param file_path string ---@return boolean function NeotestAdapter.is_test_file(file_path) + logger.info("Checking file" .. file_path) return vim.endswith(file_path, "Test.php") end @@ -146,13 +163,45 @@ end setmetatable(NeotestAdapter, { __call = function(_, opts) + logger.info("Initializing opts") if is_callable(opts.pest_cmd) then - get_pest_cmd = opts.pest_cmd + config.get_pest_cmd = opts.pest_cmd elseif opts.pest_cmd then - get_pest_cmd = function() + config.get_pest_cmd = function() return opts.pest_cmd end end + if is_callable(opts.root_ignore_files) then + config.get_root_ignore_files = opts.root_ignore_files + elseif opts.root_ignore_files then + config.get_root_ignore_files = function() + return opts.root_ignore_files + end + end + if is_callable(opts.root_files) then + config.get_root_files = opts.root_files + elseif opts.root_files then + config.get_root_files = function() + return opts.root_files + end + end + if is_callable(opts.filter_dirs) then + config.get_filter_dirs = opts.filter_dirs + elseif opts.filter_dirs then + config.get_filter_dirs = function() + return opts.filter_dirs + end + end + if is_callable(opts.env) then + config.get_env = opts.env + elseif type(opts.env) == "table" then + config.get_env = function() + return opts.env + end + end + if type(opts.dap) == "table" then + dap_configuration = opts.dap + end return NeotestAdapter end, }) From d12585b5b8404b4da91fc05eecb0f8c148aeafa1 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Fri, 16 Feb 2024 17:04:10 -0500 Subject: [PATCH 03/14] Make this even slicker --- lua/neotest-pest/config.lua | 49 ++++++++++++++++++++++++++++++------- lua/neotest-pest/init.lua | 49 ++++--------------------------------- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index e8675e5..9647496 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -1,10 +1,41 @@ +local is_callable = function(obj) + return type(obj) == "function" or (type(obj) == "table" and obj.__call) +end + local M = {} +M.opts = {} + +M.available_opts = { + "enable_sail", + "pest_cmd", + "root_ignore_files", + "root_files", + "filter_dirs", + "env", +} + +M.get = function(key) + if M.opts[key] then + if is_callable(M.opts[key]) then + return M.opts[key]() + end + + return M.opts[key] + end + + if M[key] then + return M[key]() + end + + return {} +end + M.enable_sail = function() return vim.fn.filereadable("vendor/bin/sail") == 1 end -M.get_pest_cmd = function() +M.pest_cmd = function() local binary = "pest" if vim.fn.filereadable("vendor/bin/pest") == 1 then @@ -14,20 +45,20 @@ M.get_pest_cmd = function() return binary end -M.get_env = function() - return {} +M.env = function() + return {} end -M.get_root_ignore_files = function() - return {} +M.root_ignore_files = function() + return {} end -M.get_root_files = function() - return { "tests/Pest.php" } +M.root_files = function() + return { "tests/Pest.php" } end -M.get_filter_dirs = function() - return { ".git", "node_modules", "vendor" } +M.filter_dirs = function() + return { ".git", "node_modules", "vendor" } end return M diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index eb4ddf7..9f53923 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -18,7 +18,7 @@ function NeotestAdapter.root(dir) logger.info("Finding root...") - for _, root_ignore_file in ipairs(config.get_root_ignore_files()) do + for _, root_ignore_file in ipairs(config.get("root_ignore_files")) do logger.info("Checking root ignore file", root_ignore_file) result = lib.files.match_root_pattern(root_ignore_file)(dir) @@ -29,7 +29,7 @@ function NeotestAdapter.root(dir) end end - for _, root_file in ipairs(config.get_root_files()) do + for _, root_file in ipairs(config.get("root_files")) do logger.info("Checking root file", root_file) result = lib.files.match_root_pattern(root_file)(dir) @@ -83,10 +83,6 @@ function NeotestAdapter.discover_positions(path) }) end -local is_callable = function(obj) - return type(obj) == "function" or (type(obj) == "table" and obj.__call) -end - ---@param args neotest.RunArgs ---@return neotest.RunSpec | nil function NeotestAdapter.build_spec(args) @@ -164,44 +160,9 @@ end setmetatable(NeotestAdapter, { __call = function(_, opts) logger.info("Initializing opts") - if is_callable(opts.pest_cmd) then - config.get_pest_cmd = opts.pest_cmd - elseif opts.pest_cmd then - config.get_pest_cmd = function() - return opts.pest_cmd - end - end - if is_callable(opts.root_ignore_files) then - config.get_root_ignore_files = opts.root_ignore_files - elseif opts.root_ignore_files then - config.get_root_ignore_files = function() - return opts.root_ignore_files - end - end - if is_callable(opts.root_files) then - config.get_root_files = opts.root_files - elseif opts.root_files then - config.get_root_files = function() - return opts.root_files - end - end - if is_callable(opts.filter_dirs) then - config.get_filter_dirs = opts.filter_dirs - elseif opts.filter_dirs then - config.get_filter_dirs = function() - return opts.filter_dirs - end - end - if is_callable(opts.env) then - config.get_env = opts.env - elseif type(opts.env) == "table" then - config.get_env = function() - return opts.env - end - end - if type(opts.dap) == "table" then - dap_configuration = opts.dap - end + + config.opts = opts or {} + return NeotestAdapter end, }) From 4a1cde91213c6d6cc811e891d0951cf3d1d1a139 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Fri, 16 Feb 2024 17:48:42 -0500 Subject: [PATCH 04/14] wip --- lua/neotest-pest/config.lua | 56 ++++++++++++++++++++----------------- lua/neotest-pest/init.lua | 18 +++++++----- lua/neotest-pest/utils.lua | 10 +++---- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index 9647496..f9f4d76 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -1,21 +1,14 @@ +local logger = require('neotest.logging') + local is_callable = function(obj) return type(obj) == "function" or (type(obj) == "table" and obj.__call) end -local M = {} - -M.opts = {} - -M.available_opts = { - "enable_sail", - "pest_cmd", - "root_ignore_files", - "root_files", - "filter_dirs", - "env", +local M = { + opts = {}, } -M.get = function(key) +function M.get(key) if M.opts[key] then if is_callable(M.opts[key]) then return M.opts[key]() @@ -24,18 +17,31 @@ M.get = function(key) return M.opts[key] end - if M[key] then - return M[key]() + return M[key]() +end + +function M.enable_sail() + if vim.fn.filereadable("vendor/bin/sail") ~= 1 then + logger.error("Sail executable not found") + return false end - return {} + logger.debug("Attempting to check if sail is running") + local sail_ps_output = vim.fn.system("vendor/bin/sail ps | wc -l") + + logger.debug("Sail ps output:", sail_ps_output) + + if sail_ps_output > 1 then + logger.debug("Sail is running") + return true + end + + logger.debug("Sail is not running") + + return false end -M.enable_sail = function() - return vim.fn.filereadable("vendor/bin/sail") == 1 -end - -M.pest_cmd = function() +function M.pest_cmd() local binary = "pest" if vim.fn.filereadable("vendor/bin/pest") == 1 then @@ -45,20 +51,20 @@ M.pest_cmd = function() return binary end -M.env = function() +function M.env() return {} end -M.root_ignore_files = function() +function M.root_ignore_files() return {} end -M.root_files = function() +function M.root_files() return { "tests/Pest.php" } end -M.filter_dirs = function() - return { ".git", "node_modules", "vendor" } +function M.filter_dirs() + return { "tests" } end return M diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 9f53923..bb5ecc2 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -16,31 +16,31 @@ local NeotestAdapter = { name = "neotest-pest" } function NeotestAdapter.root(dir) local result = nil - logger.info("Finding root...") + logger.debug("Finding root...") for _, root_ignore_file in ipairs(config.get("root_ignore_files")) do - logger.info("Checking root ignore file", root_ignore_file) + logger.debug("Checking root ignore file", root_ignore_file) result = lib.files.match_root_pattern(root_ignore_file)(dir) if result then - logger.info("Ignoring root because file", root_ignore_file) + logger.debug("Ignoring root because file", root_ignore_file) return nil end end for _, root_file in ipairs(config.get("root_files")) do - logger.info("Checking root file", root_file) + logger.debug("Checking root file", root_file) result = lib.files.match_root_pattern(root_file)(dir) if result then - logger.info("Found root", result) + logger.debug("Found root", result) break end end - logger.info("Root not found") + logger.debug("Root not found") return result end @@ -52,7 +52,11 @@ end ---@param root string Root directory of project ---@return boolean True when matching function NeotestAdapter.filter_dir(name, rel_path, root) - return vim.startswith(rel_path, "tests") + for _, filter_dir in ipairs(config.get("filter_dirs")) do + if vim.startswith(rel_path, filter_dir) then return true end + end + + return false end ---@async diff --git a/lua/neotest-pest/utils.lua b/lua/neotest-pest/utils.lua index e238680..f649021 100644 --- a/lua/neotest-pest/utils.lua +++ b/lua/neotest-pest/utils.lua @@ -15,8 +15,8 @@ M.make_test_id = function(position) local path = string.sub(position.path, string.len(vim.loop.cwd()) + 2) local id = path .. separator .. position.name - logger.info("Path to test file:", { position.path }) - logger.info("Treesitter id:", { id }) + logger.debug("Path to test file:", { position.path }) + logger.debug("Treesitter id:", { id }) return id end @@ -65,14 +65,14 @@ end ---@param output_file string ---@return table local function make_outputs(test, output_file) - logger.info("Pre-output test:", test) + logger.debug("Pre-output test:", test) local test_attr = test["_attr"] or test[1]["_attr"] local name = string.gsub(test_attr.name, "it (.*)", "%1") -- Difference to neotest-phpunit as of PHPUnit 10: -- Pest's test IDs are in the format "path/to/test/file::test name" local test_id = string.gsub(test_attr.file, "(.*)::(.*)", "%1") .. separator .. name - logger.info("Pest id:", { test_id }) + logger.debug("Pest id:", { test_id }) local test_output = { status = "passed", @@ -83,7 +83,7 @@ local function make_outputs(test, output_file) local test_failed, errors, fails = errors_or_fails(test) if test_failed then - logger.info("test_failed:", { test_failed, errors, fails }) + logger.debug("test_failed:", { test_failed, errors, fails }) test_output.status = "failed" if #errors > 0 then From a8263451f500c87502f5e94e6a2d462564fc3fc4 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Fri, 16 Feb 2024 22:35:36 -0500 Subject: [PATCH 05/14] Bunch of config reworks, better sail configurability --- lua/neotest-pest/config.lua | 127 ++++++++++++++++++++++++++---------- lua/neotest-pest/init.lua | 33 ++++++---- 2 files changed, 116 insertions(+), 44 deletions(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index f9f4d76..bd9bb6c 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -1,47 +1,118 @@ local logger = require('neotest.logging') +local ok, async = pcall(require, "nio") +if not ok then + async = require("neotest.async") +end local is_callable = function(obj) return type(obj) == "function" or (type(obj) == "table" and obj.__call) end local M = { - opts = {}, + env = { + root_ignore_files = {}, + root_files = { "tests/Pest.php" }, + filter_dirs = { "vendor" }, + test_file_suffix = { "Test.php" }, + autostart_sail = false, + sail_executable = "vendor/bin/sail", + }, + + _sail_error = false, + _sail_running = false, } function M.get(key) - if M.opts[key] then - if is_callable(M.opts[key]) then - return M.opts[key]() - end - - return M.opts[key] + if is_callable(M.env[key] or nil) then + return M.env[key]() end - return M[key]() + return M.env[key] or {} end -function M.enable_sail() - if vim.fn.filereadable("vendor/bin/sail") ~= 1 then +function M.env.sail_enabled() + if M.sail_available() == false then logger.error("Sail executable not found") return false end - logger.debug("Attempting to check if sail is running") - local sail_ps_output = vim.fn.system("vendor/bin/sail ps | wc -l") - - logger.debug("Sail ps output:", sail_ps_output) - - if sail_ps_output > 1 then - logger.debug("Sail is running") + if M.sail_running() then + logger.info("Sail is already running") return true end - logger.debug("Sail is not running") + if M.get('autostart_sail') then + return M.start_sail(); + end + + vim.api.nvim_echo({ { + "Sail not running, and not configured to autostart! add 'autostart_sail = true' to your config or start it manually", + "None", + } }, false, {}) + return false +end + +function M.env.results_path() + if M.get('sail_enabled') then + return "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") + end + + return async.fn.tempname() +end + +function M.sail_error() + return M._sail_error +end + +function M.sail_available() + return vim.fn.filereadable(M.get('sail_executable')) == 1 +end + +function M.sail_running() + if (M._sail_running) then + return true + end + + logger.info("Attempting to check if sail is running") + local sail_ps_output = vim.fn.system("vendor/bin/sail ps | wc -l") + + logger.info("Sail ps output:", sail_ps_output) + + if tonumber(sail_ps_output) > 1 then + logger.info("Sail is running") + M._sail_running = true + return true + end + + logger.info("Sail is not running") return false end -function M.pest_cmd() +function M.start_sail() + logger.info("Attempting to start sail") + vim.api.nvim_echo({ { "Sail not running! Attempting to start it...", "None" } }, false, {}) + + local sail_up_output = vim.fn.system("vendor/bin/sail up -d") or "" + logger.info("Sail up output:", sail_up_output) + + if vim.v.shell_error == 0 then + logger.info("Sail started successfully") + vim.api.nvim_echo({ { "Sail started!", "None" } }, false, {}) + M._sail_running = true + return true + end + + logger.error("Failed to start sail") + logger.error("Sail up output:", sail_up_output) + logger.error("Sail up error:", vim.v.shell_error) + vim.api.nvim_echo({ { "Failed to start sail!", "ErrorMsg" } }, false, {}) + M._sail_error = true + + return false +end + +function M.env.pest_cmd() local binary = "pest" if vim.fn.filereadable("vendor/bin/pest") == 1 then @@ -51,20 +122,10 @@ function M.pest_cmd() return binary end -function M.env() - return {} -end - -function M.root_ignore_files() - return {} -end - -function M.root_files() - return { "tests/Pest.php" } -end - -function M.filter_dirs() - return { "tests" } +function M.merge(env) + for key, value in pairs(env) do + M.env[key] = value + end end return M diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index bb5ecc2..4ca5e9a 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -53,18 +53,21 @@ end ---@return boolean True when matching function NeotestAdapter.filter_dir(name, rel_path, root) for _, filter_dir in ipairs(config.get("filter_dirs")) do - if vim.startswith(rel_path, filter_dir) then return true end + if name == filter_dir then return false end end - return false + return true end ---@async ---@param file_path string ---@return boolean function NeotestAdapter.is_test_file(file_path) - logger.info("Checking file" .. file_path) - return vim.endswith(file_path, "Test.php") + for _, suffix in ipairs(config.get("test_file_suffix")) do + if vim.endswith(file_path, suffix) then return true end + end + + return false end function NeotestAdapter.discover_positions(path) @@ -91,26 +94,32 @@ end ---@return neotest.RunSpec | nil function NeotestAdapter.build_spec(args) local position = args.tree:data() - local results_path = "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") + local results_path = config.get('results_path') + local binary = config.get('pest_cmd') - local binary = config.get_pest_cmd() + logger.info("Building spec for:", position) + logger.info("Results path:", results_path) local command = {} - if config.enable_sail() then + if config.get('sail_enabled') then + logger.info("Sail enabled") command = vim.tbl_flatten({ "vendor/bin/sail", "bin", "pest", - position.name ~= "tests" and ("/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd()) + 1)), - "--log-junit=" .. results_path, + position.name ~= "tests" and ("/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd() or "") + 1)), }) else + logger.info("Sail not enabled") command = vim.tbl_flatten({ binary, position.name ~= "tests" and position.path, - "--log-junit=" .. results_path, }) end + command = vim.tbl_flatten({ + command, + "--log-junit=" .. results_path, + }) if position.type == "test" then local script_args = vim.tbl_flatten({ @@ -124,6 +133,8 @@ function NeotestAdapter.build_spec(args) }) end + logger.info("Command:", command) + return { command = command, context = { @@ -165,7 +176,7 @@ setmetatable(NeotestAdapter, { __call = function(_, opts) logger.info("Initializing opts") - config.opts = opts or {} + config.merge(opts or {}) return NeotestAdapter end, From 9f7192cee5fc8b45efa064434a6ab6f338e798d2 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 13:06:57 -0500 Subject: [PATCH 06/14] Simplify even further --- lua/neotest-pest/config.lua | 97 ++++++++----------------------------- lua/neotest-pest/init.lua | 14 +++--- 2 files changed, 27 insertions(+), 84 deletions(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index bd9bb6c..d78ca29 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -16,44 +16,26 @@ local M = { test_file_suffix = { "Test.php" }, autostart_sail = false, sail_executable = "vendor/bin/sail", + pest_cmd = "vendor/bin/pest", }, _sail_error = false, - _sail_running = false, + _sail_enabled = false, } -function M.get(key) - if is_callable(M.env[key] or nil) then - return M.env[key]() - end - - return M.env[key] or {} -end - function M.env.sail_enabled() - if M.sail_available() == false then - logger.error("Sail executable not found") - return false - end - - if M.sail_running() then - logger.info("Sail is already running") + -- Cache and short-circuit so we don't have to check the disk for + -- vnedor/bin/sail every time. If the user removes the sail executable + -- out from under us, that's their problem. + if M._sail_enabled then return true end - if M.get('autostart_sail') then - return M.start_sail(); - end - - vim.api.nvim_echo({ { - "Sail not running, and not configured to autostart! add 'autostart_sail = true' to your config or start it manually", - "None", - } }, false, {}) - return false + return M.sail_available() end function M.env.results_path() - if M.get('sail_enabled') then + if M('sail_enabled') then return "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") end @@ -65,61 +47,12 @@ function M.sail_error() end function M.sail_available() - return vim.fn.filereadable(M.get('sail_executable')) == 1 -end - -function M.sail_running() - if (M._sail_running) then + if vim.fn.filereadable(M('sail_executable')) == 1 then return true end - logger.info("Attempting to check if sail is running") - local sail_ps_output = vim.fn.system("vendor/bin/sail ps | wc -l") - - logger.info("Sail ps output:", sail_ps_output) - - if tonumber(sail_ps_output) > 1 then - logger.info("Sail is running") - M._sail_running = true - return true - end - - logger.info("Sail is not running") - - return false -end - -function M.start_sail() - logger.info("Attempting to start sail") - vim.api.nvim_echo({ { "Sail not running! Attempting to start it...", "None" } }, false, {}) - - local sail_up_output = vim.fn.system("vendor/bin/sail up -d") or "" - logger.info("Sail up output:", sail_up_output) - - if vim.v.shell_error == 0 then - logger.info("Sail started successfully") - vim.api.nvim_echo({ { "Sail started!", "None" } }, false, {}) - M._sail_running = true - return true - end - - logger.error("Failed to start sail") - logger.error("Sail up output:", sail_up_output) - logger.error("Sail up error:", vim.v.shell_error) - vim.api.nvim_echo({ { "Failed to start sail!", "ErrorMsg" } }, false, {}) M._sail_error = true - - return false -end - -function M.env.pest_cmd() - local binary = "pest" - - if vim.fn.filereadable("vendor/bin/pest") == 1 then - binary = "vendor/bin/pest" - end - - return binary + logger.debug("Sail executable not found") end function M.merge(env) @@ -128,4 +61,14 @@ function M.merge(env) end end +setmetatable(M, { + __call = function(_, key) + if is_callable(M.env[key] or nil) then + return M.env[key]() + end + + return M.env[key] or {} + end +}) + return M diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 4ca5e9a..b6b457e 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -18,7 +18,7 @@ function NeotestAdapter.root(dir) logger.debug("Finding root...") - for _, root_ignore_file in ipairs(config.get("root_ignore_files")) do + for _, root_ignore_file in ipairs(config("root_ignore_files")) do logger.debug("Checking root ignore file", root_ignore_file) result = lib.files.match_root_pattern(root_ignore_file)(dir) @@ -29,7 +29,7 @@ function NeotestAdapter.root(dir) end end - for _, root_file in ipairs(config.get("root_files")) do + for _, root_file in ipairs(config("root_files")) do logger.debug("Checking root file", root_file) result = lib.files.match_root_pattern(root_file)(dir) @@ -52,7 +52,7 @@ end ---@param root string Root directory of project ---@return boolean True when matching function NeotestAdapter.filter_dir(name, rel_path, root) - for _, filter_dir in ipairs(config.get("filter_dirs")) do + for _, filter_dir in ipairs(config("filter_dirs")) do if name == filter_dir then return false end end @@ -63,7 +63,7 @@ end ---@param file_path string ---@return boolean function NeotestAdapter.is_test_file(file_path) - for _, suffix in ipairs(config.get("test_file_suffix")) do + for _, suffix in ipairs(config("test_file_suffix")) do if vim.endswith(file_path, suffix) then return true end end @@ -94,15 +94,15 @@ end ---@return neotest.RunSpec | nil function NeotestAdapter.build_spec(args) local position = args.tree:data() - local results_path = config.get('results_path') - local binary = config.get('pest_cmd') + local results_path = config('results_path') + local binary = config('pest_cmd') logger.info("Building spec for:", position) logger.info("Results path:", results_path) local command = {} - if config.get('sail_enabled') then + if config('sail_enabled') then logger.info("Sail enabled") command = vim.tbl_flatten({ "vendor/bin/sail", "bin", "pest", From 05a3a8fe97ccdcf6f22e4fe9f617ceb07b4c3c68 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 13:28:03 -0500 Subject: [PATCH 07/14] Further simplification --- lua/neotest-pest/config.lua | 9 +++++++ lua/neotest-pest/init.lua | 52 ++++++++++++++++--------------------- phpunit.xml | 28 +++++++++----------- phpunit.xml.bak | 18 +++++++++++++ 4 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 phpunit.xml.bak diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index d78ca29..2b930ce 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -34,6 +34,14 @@ function M.env.sail_enabled() return M.sail_available() end +function M.env.pest_cmd() + if M('sail_enabled') then + return { "vendor/bin/sail", "bin", "pest" } + end + + return { "vendor/bin/pest" } +end + function M.env.results_path() if M('sail_enabled') then return "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") @@ -48,6 +56,7 @@ end function M.sail_available() if vim.fn.filereadable(M('sail_executable')) == 1 then + M._sail_enabled = true return true end diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index b6b457e..ead045f 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -3,6 +3,9 @@ local logger = require('neotest.logging') local utils = require('neotest-pest.utils') local config = require('neotest-pest.config') +local info = logger.info +local debug = logger.debug + ---@class neotest.Adapter ---@field name string local NeotestAdapter = { name = "neotest-pest" } @@ -16,31 +19,31 @@ local NeotestAdapter = { name = "neotest-pest" } function NeotestAdapter.root(dir) local result = nil - logger.debug("Finding root...") + debug("Finding root...") for _, root_ignore_file in ipairs(config("root_ignore_files")) do - logger.debug("Checking root ignore file", root_ignore_file) + debug("Checking root ignore file", root_ignore_file) result = lib.files.match_root_pattern(root_ignore_file)(dir) if result then - logger.debug("Ignoring root because file", root_ignore_file) + debug("Ignoring root because file", root_ignore_file) return nil end end for _, root_file in ipairs(config("root_files")) do - logger.debug("Checking root file", root_file) + debug("Checking root file", root_file) result = lib.files.match_root_pattern(root_file)(dir) if result then - logger.debug("Found root", result) + debug("Found root", result) break end end - logger.debug("Root not found") + debug("Root not found") return result end @@ -95,45 +98,34 @@ end function NeotestAdapter.build_spec(args) local position = args.tree:data() local results_path = config('results_path') - local binary = config('pest_cmd') - logger.info("Building spec for:", position) - logger.info("Results path:", results_path) + info("Building spec for:", position) + info("Results path:", results_path) - local command = {} + local path = position.path; if config('sail_enabled') then - logger.info("Sail enabled") - command = vim.tbl_flatten({ - "vendor/bin/sail", "bin", "pest", - position.name ~= "tests" and ("/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd() or "") + 1)), - }) - else - logger.info("Sail not enabled") - command = vim.tbl_flatten({ - binary, - position.name ~= "tests" and position.path, - }) + info("Sail enabled, adjusting path") + path = "/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd() or "") + 1) end - command = vim.tbl_flatten({ - command, + local command = vim.tbl_flatten({ + config('pest_cmd'), + path, "--log-junit=" .. results_path, }) if position.type == "test" then - local script_args = vim.tbl_flatten({ + command = vim.tbl_flatten({ + command, "--filter", position.name, }) - - command = vim.tbl_flatten({ - command, - script_args, - }) + else + debug("Position type:", position.type) end - logger.info("Command:", command) + info("Command:", command) return { command = command, diff --git a/phpunit.xml b/phpunit.xml index 39c5df8..2023371 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,18 +1,14 @@ - - - - ./tests - - - - - ./app - ./src - - + + + + ./tests + + + + + ./app + ./src + + diff --git a/phpunit.xml.bak b/phpunit.xml.bak new file mode 100644 index 0000000..39c5df8 --- /dev/null +++ b/phpunit.xml.bak @@ -0,0 +1,18 @@ + + + + + ./tests + + + + + ./app + ./src + + + From e429ba711da7d4319a3d07be6242455a5bd71133 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 13:31:05 -0500 Subject: [PATCH 08/14] Change info to debug --- lua/neotest-pest/init.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index ead045f..4bb51f7 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -2,8 +2,6 @@ local lib = require('neotest.lib') local logger = require('neotest.logging') local utils = require('neotest-pest.utils') local config = require('neotest-pest.config') - -local info = logger.info local debug = logger.debug ---@class neotest.Adapter @@ -99,13 +97,13 @@ function NeotestAdapter.build_spec(args) local position = args.tree:data() local results_path = config('results_path') - info("Building spec for:", position) - info("Results path:", results_path) + debug("Building spec for:", position) + debug("Results path:", results_path) local path = position.path; if config('sail_enabled') then - info("Sail enabled, adjusting path") + debug("Sail enabled, adjusting path") path = "/var/www/html" .. string.sub(position.path, string.len(vim.loop.cwd() or "") + 1) end @@ -125,7 +123,7 @@ function NeotestAdapter.build_spec(args) debug("Position type:", position.type) end - info("Command:", command) + debug("Command:", command) return { command = command, @@ -166,8 +164,6 @@ end setmetatable(NeotestAdapter, { __call = function(_, opts) - logger.info("Initializing opts") - config.merge(opts or {}) return NeotestAdapter From e2a4c6b83b5ae4eef28055dfbbc032f42ef76350 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 17:02:32 -0500 Subject: [PATCH 09/14] Use vim-notify when errors happen --- lua/neotest-pest/init.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 4bb51f7..726d4a2 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -4,6 +4,20 @@ local utils = require('neotest-pest.utils') local config = require('neotest-pest.config') local debug = logger.debug +local notify = function(msg, level) + vim.notify(msg, level, { + title = "neotest-pest", + }) +end + +local error = function(msg) + notify(msg, "error") +end + +local info = function(msg) + notify(msg, "info") +end + ---@class neotest.Adapter ---@field name string local NeotestAdapter = { name = "neotest-pest" } @@ -143,18 +157,21 @@ function NeotestAdapter.results(test, result, tree) local ok, data = pcall(lib.files.read, output_file) if not ok then + error("No test output file found! Should have been at: " .. output_file) logger.error("No test output file found:", output_file) return {} end local ok, parsed_data = pcall(lib.xml.parse, data) if not ok then + error("Failed to parse test output!") logger.error("Failed to parse test output:", output_file) return {} end local ok, results = pcall(utils.get_test_results, parsed_data, output_file) if not ok then + error("Could not get test results!") logger.error("Could not get test results", output_file) return {} end From ec3545a77375e4be9ff90b09d0b830a35a35c5dc Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 17:05:31 -0500 Subject: [PATCH 10/14] Config for parallel testing --- lua/neotest-pest/config.lua | 6 +++++- lua/neotest-pest/init.lua | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index 2b930ce..1e7cc04 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -14,9 +14,9 @@ local M = { root_files = { "tests/Pest.php" }, filter_dirs = { "vendor" }, test_file_suffix = { "Test.php" }, - autostart_sail = false, sail_executable = "vendor/bin/sail", pest_cmd = "vendor/bin/pest", + parallel = 0, }, _sail_error = false, @@ -34,6 +34,10 @@ function M.env.sail_enabled() return M.sail_available() end +function M.env.is_parallel() + return M('parallel') > 0 +end + function M.env.pest_cmd() if M('sail_enabled') then return { "vendor/bin/sail", "bin", "pest" } diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 726d4a2..92006e3 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -137,6 +137,14 @@ function NeotestAdapter.build_spec(args) debug("Position type:", position.type) end + if config('is_parallel') then + command = vim.tbl_flatten({ + command, + "--parallel", + config('parallel'), + }) + end + debug("Command:", command) return { From bccafece1a774f3903f7b91a1f23ec3cf91ced98 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Sat, 17 Feb 2024 21:09:01 -0500 Subject: [PATCH 11/14] wip --- lua/neotest-pest/config.lua | 1 + lua/neotest-pest/init.lua | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index 1e7cc04..caa9d78 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -17,6 +17,7 @@ local M = { sail_executable = "vendor/bin/sail", pest_cmd = "vendor/bin/pest", parallel = 0, + compact = false, }, _sail_error = false, diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index 92006e3..dd8c33c 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -141,10 +141,18 @@ function NeotestAdapter.build_spec(args) command = vim.tbl_flatten({ command, "--parallel", - config('parallel'), + "--processes=" .. config('parallel'), }) end + -- if config('compact') == true then + -- info("Using compact output") + -- command = vim.tbl_flatten({ + -- command, + -- "--compact", + -- }) + -- end + debug("Command:", command) return { From 20ebc86066ee289543f4512ca886f5b833385c87 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Mon, 19 Feb 2024 08:58:32 -0500 Subject: [PATCH 12/14] Support for compact output mode --- lua/neotest-pest/init.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index dd8c33c..ad323be 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -145,13 +145,13 @@ function NeotestAdapter.build_spec(args) }) end - -- if config('compact') == true then - -- info("Using compact output") - -- command = vim.tbl_flatten({ - -- command, - -- "--compact", - -- }) - -- end + if config('compact') == true then + info("Using compact output") + command = vim.tbl_flatten({ + command, + "--compact", + }) + end debug("Command:", command) From 3e65ff5a13afdae8783456a0407d6fe33eca670d Mon Sep 17 00:00:00 2001 From: V13Axel Date: Mon, 19 Feb 2024 09:38:50 -0500 Subject: [PATCH 13/14] Update readme to include new config options --- README.md | 93 ++++++++++++++++++++++++++++--------- lua/neotest-pest/config.lua | 5 +- lua/neotest-pest/init.lua | 4 +- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index cd653d4..e0df067 100644 --- a/README.md +++ b/README.md @@ -3,45 +3,91 @@ This plugin provides a [Pest](https://pestphp.com) adapter for the [Neotest](https://github.com/nvim-neotest/neotest) framework. This is a fork of `neotest-pest` originally by [@theutz](https://github.com/theutz/neotest-pest), with some fixes and updates: + - Updated to work with [Pest](https://pestphp.com) 2.0 - Support for (and automatic detection of) Laravel Sail - Note: This also moves junit output files into `storage/app/` +- Parallel testing support :warning: _Ive only focused on making this work for me. Please test against your Pest tests_ :warning: ## :package: Installation -Install the plugin using packer: +Install the plugin using your favorite package manager. + +Here's an example using lazy.nvim: ```lua -use({ - 'nvim-neotest/neotest', - requires = { - ..., - 'V13Axel/neotest-pest', - }, - config = function() - require('neotest').setup({ - ..., - adapters = { - require('neotest-pest'), - } - }) - end -}) +{ + 'nvim-neotest/neotest', + dependencies = { + ..., + 'V13Axel/neotest-pest', + }, + config = function() + require('neotest').setup({ + ..., + adapters = { + require('neotest-pest'), + } + }) + end +} ``` ## :wrench: Configuration -The plugin may be configured as below: +> [!TIP] +> Any of these options can be set to a lua function that returns the desired result. For example, wanna run tests in parallel, one for each CPU core? +> `parallel = function() #vim.loop.cpu_info() end` ```lua adapters = { - require('neotest-pest')({ - pest_cmd = function() - return "vendor/bin/pest" - end - }), + require('neotest-pest')({ + -- Ignore these directories when looking for tests + -- -- Default: { "vendor", "node_modules" } + ignore_dirs = { "vendor", "node_modules" } + + -- Ignore any projects containing "phpunit-only.tests" + -- -- Default: {} + root_ignore_files = { "phpunit-only.tests" }, + + -- Specify suffixes for files that should be considered tests + -- -- Default: { "Test.php" } + test_file_suffixes = { "Test.php", "_test.php", "PestTest.php" }, + + -- Sail not properly detected? Explicitly enable it. + -- -- Default: function() that checks for sail presence + sail_enabled = false, + + -- Custom sail executable. Not running in Sail, but running bare Docker? + -- Set `sail_enabled` = true and `sail_executable` to { "docker", "exec", "[somecontainer]" } + -- -- Default: "vendor/bin/sail" + sail_executable = "vendor/bin/sail", + + -- Custom pest binary. + -- -- Default: function that checks for sail presence + pest_cmd = "vendor/bin/pest", + + -- Run N tests in parallel, <=1 doesn't pass --parallel to pest at all + -- -- Default: 0 + parallel = 16 + + -- Enable ["compact" output printer](https://pestphp.com/docs/optimizing-tests#content-compact-printer) + -- -- Default: false + compact = false, + + -- Set a custom path for the results XML file, parsed by this adapter + -- + ------------------------------------------------------------------------------------ + -- NOTE: This must be a path accessible by both your test runner AND your editor! -- + ------------------------------------------------------------------------------------ + -- + -- -- Default: function that checks for sail presence. + -- -- - If no sail: Numbered file in randomized /tmp/ directory (using async.fn.tempname()) + -- -- - If sail: "storage/app/" .. os.date("junit-%Y%m%d-%H%M%S") + results_path = function() "/some/accessible/path" end, + }), } ``` @@ -62,6 +108,7 @@ vim.keymap.set('n', 'tn', function() require('neotest').run.run() end) To test a file run `lua require('neotest').run.run(vim.fn.expand('%'))` Example - t(est)f(ile): + ```lua vim.keymap.set('n', 'tf', function() require('neotest').run.run(vim.fn.expand('%')) end) ``` @@ -76,7 +123,7 @@ To test the full test suite run `lua require('neotest').run.run({ suite = true } ## :gift: Contributing -This fork is maintained by one guy, in my spare time and when I can get to it. Please raise a PR if you are interested in adding new functionality or fixing any bugs. When submitting a bug, please include an example test that I can test against. +I'm just one guy, maintaining this in my spare time and when I can get to it. Please raise a PR if you are interested in adding new functionality or fixing any bugs. When submitting a bug, please include an example test that I can test against. To trigger the tests for the adapter, run: diff --git a/lua/neotest-pest/config.lua b/lua/neotest-pest/config.lua index caa9d78..9e33d2e 100644 --- a/lua/neotest-pest/config.lua +++ b/lua/neotest-pest/config.lua @@ -12,10 +12,9 @@ local M = { env = { root_ignore_files = {}, root_files = { "tests/Pest.php" }, - filter_dirs = { "vendor" }, - test_file_suffix = { "Test.php" }, + ignore_dirs = { "vendor", "node_modules" }, + test_file_suffixes = { "Test.php" }, sail_executable = "vendor/bin/sail", - pest_cmd = "vendor/bin/pest", parallel = 0, compact = false, }, diff --git a/lua/neotest-pest/init.lua b/lua/neotest-pest/init.lua index ad323be..1aac6d7 100644 --- a/lua/neotest-pest/init.lua +++ b/lua/neotest-pest/init.lua @@ -67,7 +67,7 @@ end ---@param root string Root directory of project ---@return boolean True when matching function NeotestAdapter.filter_dir(name, rel_path, root) - for _, filter_dir in ipairs(config("filter_dirs")) do + for _, filter_dir in ipairs(config("ignore_dirs")) do if name == filter_dir then return false end end @@ -78,7 +78,7 @@ end ---@param file_path string ---@return boolean function NeotestAdapter.is_test_file(file_path) - for _, suffix in ipairs(config("test_file_suffix")) do + for _, suffix in ipairs(config("test_file_suffixes")) do if vim.endswith(file_path, suffix) then return true end end From 601a8c56b941f774ba24cbcdc1e171ef72c9cc86 Mon Sep 17 00:00:00 2001 From: V13Axel Date: Mon, 19 Feb 2024 09:47:24 -0500 Subject: [PATCH 14/14] Make the example work --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0df067..1f6b0d6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Here's an example using lazy.nvim: > [!TIP] > Any of these options can be set to a lua function that returns the desired result. For example, wanna run tests in parallel, one for each CPU core? -> `parallel = function() #vim.loop.cpu_info() end` +> `parallel = function() return #vim.loop.cpu_info() end,` ```lua adapters = {