lazyvim/tests/util/copy_path_spec.lua

336 lines
12 KiB
Lua

---@module "luassert"
-- Ensure LazyVim global is available for copy_path module
_G.LazyVim = _G.LazyVim or require("lazyvim.util")
local copy_path = require("lazyvim.util.copy_path")
describe("copy_path", function()
-- Create a temporary file and switch to it for tests that need a real buffer.
local tmpdir, tmppath ---@type string, string
local old_buf ---@type number|nil
before_each(function()
tmpdir = os.tmpname()
vim.fn.delete(tmpdir) -- remove the file so we can mkdir
vim.fn.mkdir(tmpdir, "p")
tmppath = tmpdir .. "/test_file.lua"
-- Write enough lines so cursor positioning works reliably
local f = io.open(tmppath, "w")
if f then
f:write("-- line 1\n-- line 2\nlocal x = 42\n-- line 4\n-- line 5\n")
f:close()
end
-- Open the file in a new buffer and make it current
old_buf = vim.api.nvim_get_current_buf()
local buf = vim.fn.bufadd(tmppath)
vim.fn.bufload(buf)
vim.api.nvim_set_current_buf(buf)
-- Set cursor to line 3, column 5 (0-based: row=2, col=4) for predictable tests
-- "local x = 42" — column 5 is the 'x' character
vim.api.nvim_win_set_cursor(0, { 3, 4 })
end)
after_each(function()
if old_buf and vim.fn.bufexists(old_buf) == 1 then
pcall(vim.api.nvim_set_current_buf, old_buf)
end
-- Cleanup temp file and dir
pcall(vim.fn.delete, tmppath)
pcall(vim.fn.delete, tmpdir)
end)
describe("bufpath", function()
it("returns the buffer name for a file buffer", function()
local p = copy_path.bufpath()
assert.is_not_nil(p)
assert.truthy(p:find("test_file.lua"))
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.bufpath())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("absolute", function()
it("returns the normalized absolute path of the current buffer", function()
local p = copy_path.absolute()
assert.is_not_nil(p)
if LazyVim.is_win() then
assert.truthy(p:match("^%a:") ~= nil)
else
assert.are.equal(string.sub(p, 1, 1), "/")
end
assert.truthy(p:find("test_file.lua"))
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.absolute())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("directory", function()
it("returns the parent directory with trailing slash", function()
local d = copy_path.directory()
assert.is_not_nil(d)
assert.are.equal(string.sub(d, -1), "/")
assert.truthy(d:find(tmpdir))
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.directory())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("relative", function()
it("returns a path containing the filename (relative if possible, absolute as fallback)", function()
local r = copy_path.relative()
assert.is_not_nil(r)
-- In headless test mode LazyVim.root() may be unavailable, so relative falls back to absolute.
-- Either way the result should contain the filename.
assert.truthy(r:find("test_file.lua"))
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.relative())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("filename", function()
it("returns the filename with extension", function()
assert.are.equal(copy_path.filename(), "test_file.lua")
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.filename())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("filename_no_ext", function()
it("returns the filename without extension", function()
assert.are.equal(copy_path.filename_no_ext(), "test_file")
end)
it("returns the full name when there is no extension", function()
local noext_path = tmpdir .. "/README"
local f = io.open(noext_path, "w")
if f then f:write("no ext"); f:close() end
local buf2 = vim.fn.bufadd(noext_path)
vim.fn.bufload(buf2)
vim.api.nvim_set_current_buf(buf2)
assert.are.equal(copy_path.filename_no_ext(), "README")
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
pcall(vim.fn.delete, noext_path)
end)
it("returns nil for non-file buffers", function()
local scratch = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(scratch)
assert.is_nil(copy_path.filename_no_ext())
vim.api.nvim_set_current_buf(vim.fn.bufnr(tmppath))
end)
end)
describe("line_number and column_number", function()
it("returns the current line number (1-based)", function()
-- Cursor was set to row 2 (0-based) = line 3 in before_each
assert.are.equal(copy_path.line_number(), 3)
end)
it("returns the current column number (1-based)", function()
-- Cursor was set to col 4 (0-based) = column 5 in before_each
assert.are.equal(copy_path.column_number(), 5)
end)
end)
describe("with_line", function()
it("appends ':line' to the path", function()
local p = copy_path.absolute() .. ":3" -- line was set to 3 in before_each
local result = copy_path.with_line(copy_path.absolute())
assert.are.equal(result, p)
end)
end)
describe("with_column", function()
it("appends ':line:column' to the path", function()
local base = copy_path.absolute() .. ":3" -- line 3 from before_each
local result = copy_path.with_column(base)
assert.are.equal(result, base .. ":5") -- column 5 from before_each
end)
end)
describe("project_root", function()
it("returns the git root with trailing slash or cwd fallback", function()
local ok, p = pcall(copy_path.project_root)
-- In headless test mode LazyVim.root.git() may fail, so we accept either outcome.
-- If it succeeds, the result should be a non-empty string ending with /
if not ok then
return -- skip assertion in headless mode where root detection fails
end
assert.is_not_nil(p)
assert.are.equal(string.sub(p, -1), "/")
end)
end)
describe("copy (clipboard + notify)", function()
it("copies text to the system clipboard and notifies", function()
-- Mock vim.notify so we can capture the message
local notified = false
local notify_msg = ""
_G.original_notify = vim.notify
vim.notify = function(msg, level, opts)
notified = true
notify_msg = msg or ""
end
-- Mock vim.fn.setreg to capture clipboard writes (works in headless CI)
local setreg_called = false
local captured_reg, captured_text, captured_mode ---@type boolean, string|nil, string|nil, string|nil
_G.original_setreg = vim.fn.setreg
vim.fn.setreg = function(reg, text, mode)
setreg_called = true
captured_reg = reg
captured_text = text
captured_mode = mode
end
copy_path.copy(function() return "test/path.lua" end, "Test Copy")
assert.is_true(notified, "vim.notify should have been called")
assert.truthy(notify_msg:find("Copied:"), "Notification should contain 'Copied:'")
assert.truthy(notify_msg:find("test/path.lua"), "Notification should contain the path")
-- Check clipboard was written via setreg mock (reliable in headless CI)
assert.is_true(setreg_called, "vim.fn.setreg should have been called")
assert.are.equal(captured_reg, "+", "Should write to system register (+)")
assert.are.equal(captured_text, "test/path.lua", "System clipboard should contain the path")
-- Restore mocks
vim.notify = _G.original_notify
_G.original_notify = nil
vim.fn.setreg = _G.original_setreg
_G.original_setreg = nil
end)
it("warns when buffer has no file (get_path returns nil)", function()
local warned = false
_G.original_warn = LazyVim.warn
LazyVim.warn = function(msg, opts)
warned = true
end
copy_path.copy(function() return nil end, "Test Copy")
assert.is_true(warned, "Should warn when path is nil")
LazyVim.warn = _G.original_warn
_G.original_warn = nil
end)
end)
describe("convenience functions", function()
it("copy_absolute writes the absolute path to clipboard and notifies", function()
-- Mock vim.notify so we can capture the message
local notified = false
_G.original_notify = vim.notify
vim.notify = function(msg, level, opts)
notified = true
end
-- Mock vim.fn.setreg to capture clipboard writes (works in headless CI)
local captured_text ---@type string|nil
_G.original_setreg = vim.fn.setreg
vim.fn.setreg = function(reg, text, mode)
captured_text = text
end
copy_path.copy_absolute()
assert.is_true(notified)
-- Check clipboard via setreg mock (reliable in headless CI)
if LazyVim.is_win() then
assert.truthy(captured_text:match("^%a:") ~= nil or captured_text:find("test_file.lua"))
else
assert.truthy(string.sub(captured_text, 1, 1) == "/" and captured_text:find("test_file.lua"))
end
-- Restore mocks
vim.notify = _G.original_notify
_G.original_notify = nil
vim.fn.setreg = _G.original_setreg
_G.original_setreg = nil
end)
it("copy_relative_with_line_column appends line:column", function()
-- In headless mode LazyVim.root() may fail. If relative path falls back to
-- absolute (which is valid per the module logic), we still get :line:col appended.
local captured_text = ""
_G.original_notify = vim.notify
vim.notify = function(msg, level, opts)
captured_text = msg or ""
end
copy_path.copy_relative_with_line_column()
-- Should contain :line:col at the end regardless of whether path was relative or absolute
assert.truthy(captured_text:find(":%d+:%d+$"), "Should end with :line:col")
vim.notify = _G.original_notify
_G.original_notify = nil
end)
it("copy_filename writes just the filename", function()
local captured_text = ""
_G.original_notify = vim.notify
vim.notify = function(msg, level, opts)
captured_text = msg or ""
end
copy_path.copy_filename()
assert.truthy(captured_text:find("test_file.lua"))
vim.notify = _G.original_notify
_G.original_notify = nil
end)
it("copy_filename_no_ext writes the filename without extension", function()
local notify_text = ""
_G.original_notify = vim.notify
vim.notify = function(msg, level, opts)
notify_text = msg or ""
end
-- Mock vim.fn.setreg to capture clipboard writes (works in headless CI)
local captured_text ---@type string|nil
_G.original_setreg = vim.fn.setreg
vim.fn.setreg = function(reg, text, mode)
captured_text = text
end
copy_path.copy_filename_no_ext()
assert.truthy(notify_text:find("test_file"))
-- Should NOT contain .lua in the copied register
assert.falsy(captured_text:find("%.lua"), "Clipboard should not contain extension")
-- Restore mocks
vim.notify = _G.original_notify
_G.original_notify = nil
vim.fn.setreg = _G.original_setreg
_G.original_setreg = nil
end)
end)
end)