Files
nvim/test/git/status_view_test.lua
T

254 lines
7.4 KiB
Lua

local t = require("test")
require("git").init()
---@param dir string
---@param ... string
local function git(dir, ...)
local r = vim.system({ "git", ... }, { cwd = dir, text = true }):wait()
if r.code ~= 0 then
error(
string.format(
"git %s failed: %s",
table.concat({ ... }, " "),
vim.trim(r.stderr or "")
),
2
)
end
return r
end
---Build a temporary git repo with the given committed contents and
---queue cleanup (stop fs watchers, drop test buffers, delete the dir).
---@param files table<string, string>?
---@return string dir
local function make_repo(files)
local dir = vim.fn.tempname()
vim.fn.mkdir(dir, "p")
git(dir, "init", "-q", "-b", "main")
git(dir, "config", "user.email", "t@t.com")
git(dir, "config", "user.name", "t")
if files and next(files) then
for path, content in pairs(files) do
t.write(dir, path, content)
end
git(dir, "add", ".")
git(dir, "commit", "-q", "-m", "init")
end
t.defer(function()
pcall(vim.cmd.cd, "/tmp")
pcall(function()
require("git.repo").stop_all()
end)
vim.wait(60)
for _, b in ipairs(vim.api.nvim_list_bufs()) do
local name = vim.api.nvim_buf_get_name(b)
if name:find(dir, 1, true) or name:match("^git[a-z]*://") then
pcall(vim.api.nvim_buf_delete, b, { force = true })
end
end
vim.fn.delete(dir, "rf")
end)
return dir
end
---Replicate the user's global cursor-restore autocmd. Scoped to a
---named augroup + cleanup so it doesn't leak between tests.
local function install_cursor_restore_autocmd()
local group =
vim.api.nvim_create_augroup("test.cursor_restore", { clear = true })
vim.api.nvim_create_autocmd("BufReadPost", {
group = group,
pattern = "*",
command = 'silent! normal! g`"zv',
})
t.defer(function()
pcall(vim.api.nvim_del_augroup_by_name, "test.cursor_restore")
end)
end
---@param sidebar_buf integer
---@param needle string
---@return integer?
local function find_line(sidebar_buf, needle)
for i, l in ipairs(vim.api.nvim_buf_get_lines(sidebar_buf, 0, -1, false)) do
if l:match(needle) then
return i
end
end
end
---@return integer? sidebar_buf, integer? sidebar_win
local function find_sidebar()
local buf, win
for _, b in ipairs(vim.api.nvim_list_bufs()) do
if vim.bo[b].filetype == "gitstatus" then
buf = b
end
end
for _, w in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_buf(w) == buf then
win = w
end
end
return buf, win
end
---@param role string
---@return integer?
local function find_diff_win(role)
for _, w in ipairs(vim.api.nvim_list_wins()) do
if vim.w[w].git_diff_role == role then
return w
end
end
end
---@param file_path string
---@param committed_content string
---@param worktree_content string
---@return integer sidebar_win
---@return integer entry_line
local function setup_sidebar_with_unstaged_file(
file_path,
committed_content,
worktree_content
)
local repo = make_repo({ [file_path] = committed_content })
t.write(repo, file_path, worktree_content)
vim.cmd("cd " .. repo)
require("git.status_view").open({ placement = "sidebar" })
local sidebar_buf, sidebar_win = find_sidebar()
assert(sidebar_buf, "sidebar buffer should exist")
assert(sidebar_win, "sidebar window should exist")
local r = assert(
require("git.repo").find(vim.fn.getcwd()),
"repo should resolve for the test worktree"
)
r:refresh()
vim.wait(1000, function()
return r.status and #r.status:by_kind("unstaged") > 0
end)
local entry_line = assert(
find_line(sidebar_buf, vim.pesc(file_path) .. "$"),
file_path .. " should appear in sidebar"
)
return sidebar_win, entry_line
end
local function press(keys)
local rhs = vim.api.nvim_replace_termcodes(keys, true, false, true)
vim.api.nvim_feedkeys(rhs, "x", false)
end
---@param cond fun(): boolean
---@param msg string
local function wait_for(cond, msg)
t.truthy(vim.wait(1000, cond), "timed out waiting for: " .. msg)
end
t.test("stage with diff open: sidebar cursor stays put", function()
install_cursor_restore_autocmd()
local sidebar_win, line = setup_sidebar_with_unstaged_file(
"zsh/rc",
"ZSH=true\n",
"ZSH=true\nmodified\n"
)
vim.api.nvim_set_current_win(sidebar_win)
vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 })
press("<Tab>")
wait_for(function()
return find_diff_win("left") ~= nil
end, "diff windows to appear")
local r = assert(require("git.repo").find(vim.fn.getcwd()))
vim.api.nvim_set_current_win(sidebar_win)
press("s")
wait_for(function()
return #r.status:by_kind("staged") > 0
end, "stage to propagate to repo state")
t.eq(
vim.api.nvim_win_get_cursor(sidebar_win),
{ line, 0 },
"sidebar cursor should remain at the entry's original line"
)
end)
t.test(
"stage with diff open: diff foldmethod is preserved on refresh",
function()
local sidebar_win, line = setup_sidebar_with_unstaged_file(
"zsh/rc",
"# vim: set ft=zsh nowrap:\nZSH=true\n",
"# vim: set ft=zsh nowrap:\nZSH=true\nmodified\n"
)
vim.api.nvim_set_current_win(sidebar_win)
vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 })
press("<Tab>")
wait_for(function()
return find_diff_win("left") ~= nil
end, "diff windows to appear")
local left_win = assert(find_diff_win("left"))
t.eq(
vim.wo[left_win].foldmethod,
"diff",
"left diff foldmethod should be 'diff' after Tab"
)
local r = assert(require("git.repo").find(vim.fn.getcwd()))
vim.api.nvim_set_current_win(sidebar_win)
press("s")
wait_for(function()
return #r.status:by_kind("staged") > 0
end, "stage to propagate to repo state")
t.eq(
vim.wo[left_win].foldmethod,
"diff",
"left diff foldmethod should still be 'diff' after stage refresh"
)
end
)
t.test("refresh on stage updates the index URI buffer's content", function()
local sidebar_win, line =
setup_sidebar_with_unstaged_file("foo.txt", "v1\n", "v2\n")
vim.api.nvim_set_current_win(sidebar_win)
vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 })
press("<Tab>")
wait_for(function()
return find_diff_win("left") ~= nil
end, "diff windows to appear")
local left_win = assert(find_diff_win("left"))
local index_buf = vim.api.nvim_win_get_buf(left_win)
t.eq(
vim.api.nvim_buf_get_lines(index_buf, 0, -1, false),
{ "v1" },
"index pane should initially show committed content"
)
vim.api.nvim_set_current_win(sidebar_win)
press("s")
wait_for(function()
local first = vim.api.nvim_buf_get_lines(index_buf, 0, -1, false)[1]
return first == "v2"
end, "index pane to refresh to staged content")
t.eq(
vim.api.nvim_buf_get_lines(index_buf, 0, -1, false),
{ "v2" },
"index pane should reflect staged content after refresh"
)
end)