261015.5 · 9405672f62767695d5f20db171336f7f4911ce86
ci: add tests for git_status
ci: yet another flakey Windows test
reading declarations
@@ -1,5 +1,21 @@ nvim ?= nvim +#+# Run under git-bash on Windows (requires: scoop install make git)+#+ifeq ($(OS),Windows_NT)+ ifneq ($(MSYSTEM),)+ else+ BASH_DIR := C:/Users/$(USERNAME)/scoop/apps/git/current/usr/bin+ ifneq ($(shell [ -f "$(BASH_DIR)/bash.exe" ] && echo 1),)+ $(info Using git-bash "$(BASH_DIR)/bash.exe")+ export PATH := $(BASH_DIR):$(PATH)+ SHELL := bash+ .SHELLFLAGS := -c+ endif+ endif+endif+ # # Run all tests or specic module tests #
@@ -176,6 +176,18 @@ M.new_child_neovim = function() -- using "FZF_DEFAULT_OPTS" hangs the command on the -- child process and the loading indicator never stops local defaults = { fzf_opts = { ["--gutter"] = " " } }+ -- Windows: $FZF_DEFAULT_COMMAND is only honored when fzf's stdin is a tty+ -- (`FZF_DEFAULT_COMMAND - Default command to use when input is tty`, see+ -- src/util/util_windows.go Executor with `/s/c`). Inside mini.test child+ -- processes (headless nvim started via jobstart without pty, nested+ -- termopen pty on Windows) fzf's IsTty() is false, so it ignores the env+ -- and blocks waiting for stdin EOF that never arrives: the headless+ -- `nvim -l spawn.lua` never even starts (no spawn log/marker), fzf shows+ -- `0/0` with loading spinner forever, `load` never fires and `wait_until`+ -- timeout+retries (5x on Windows) make the suite appear hung. Piping via+ -- `cmd.exe /c "(cmd) | fzf"` uses a stdin pipe (which closes) and works+ -- regardless of tty detection, so default to pipe for headless (tests, cli)+ -- on Windows. GUI sessions keep env (better exit kill + powershell fallback). if M.IS_WIN() then defaults.pipe_cmd = true end child.lua(function(...) require("fzf-lua").setup(vim.tbl_deep_extend("keep", ..., {
@@ -19,8 +19,15 @@ T["api"]["fzf_exec"]["table"] = function(multiprocess) table.insert(contents, tostring((i - 1) * 3 + j) .. ": " .. s) end end- helpers.FzfLua.fzf_exec(child, contents,- { __expect_lines = true, __postprocess_wait = true, multiprocess = multiprocess == true })+ helpers.FzfLua.fzf_exec(child, contents, {+ __expect_lines = true,+ __postprocess_wait = true,+ multiprocess = multiprocess == true,+ __after_open = function()+ child.wait_until_screenshot_text_match("300/300")+ sleep(100); if not helpers.IS_LINUX() then sleep(250) end+ end,+ }) end T["api"]["fzf_exec"]["function"] = new_set({ parametrize = { { "sync" }, { "async" } } }, {@@ -293,6 +300,7 @@ T["api"]["events"] = new_set( T["api"]["resume"] = new_set({ parametrize = { { "fzf_exec" }, { "fzf_live" } },+ n_retry = not helpers.IS_LINUX() and 5 or nil, }, { function(api) helpers.FzfLua[api](child, "echo resume string content", {@@ -301,7 +309,12 @@ T["api"]["resume"] = new_set({ exec_empty_query = true, debug = 1, })- helpers.FzfLua.resume(child, { __expect_lines = true })+ helpers.FzfLua.resume(child, {+ __expect_lines = true,+ __after_open = function()+ sleep(100); if not helpers.IS_LINUX() then sleep(250) end+ end,+ }) helpers.FzfLua[api](child, api == "fzf_exec" and function(fzf_cb)@@ -317,7 +330,12 @@ T["api"]["resume"] = new_set({ exec_empty_query = true, debug = 1, })- helpers.FzfLua.resume(child, { __expect_lines = true })+ helpers.FzfLua.resume(child, {+ __expect_lines = true,+ __after_open = function()+ sleep(100); if not helpers.IS_LINUX() then sleep(250) end+ end,+ }) end, })
@@ -0,0 +1,121 @@+---@diagnostic disable: unused-local, unused-function, unused+local MiniTest = require("mini.test")+local helpers = require("fzf-lua.test.helpers")+local child = helpers.new_child_neovim()+local eq = helpers.expect.equality+local new_set = MiniTest.new_set+local exec_lua = child.lua+local sleep = function(ms) helpers.sleep(ms, child) end++local repo ---@type string?+local orig_cwd ---@type string?++local T = helpers.new_set_with_child(child, {+ hooks = {+ pre_case = function()+ orig_cwd = child.fn.getcwd()+ repo = exec_lua([[+ local dir = vim.fn.tempname()+ vim.fn.mkdir(dir, "p")+ -- Initialize repo and disable signing so commits work even when the+ -- environment has SSH signing configured and the key does not exist.+ vim.fn.system({ "git", "init", dir })+ vim.fn.system({ "git", "-C", dir, "config", "user.email", "test@fzf-lua" })+ vim.fn.system({ "git", "-C", dir, "config", "user.name", "fzf-lua test" })+ vim.fn.system({ "git", "-C", dir, "config", "commit.gpgsign", "false" })+ vim.fn.system({ "git", "-C", dir, "config", "gpg.format", "openpgp" })+ local file = dir .. "/tracked.txt"+ local fd = io.open(file, "w")+ fd:write("hello\n")+ fd:close()+ vim.fn.system({ "git", "-C", dir, "add", "tracked.txt" })+ vim.fn.system({ "git", "-C", dir, "commit", "--no-gpg-sign", "-m", "initial" })+ return dir+ ]])+ child.cmd("cd " .. vim.fn.fnameescape(assert(repo)))+ end,+ post_case = function()+ if repo then+ -- Return child to the original working directory before deleting repo.+ child.cmd("cd " .. vim.fn.fnameescape(assert(orig_cwd)))+ -- Only clear the reference if deletion succeeded.+ eq(0, vim.fn.delete(repo, "rf"))+ repo = nil+ orig_cwd = nil+ end+ end,+ },+})++T["git_status"] = new_set({ n_retry = not helpers.IS_LINUX() and 5 or nil })++T["git_status"]["stage unstage reset"] = function()+ -- Modify a tracked file so git_status shows an unstaged change.+ exec_lua([[+ local file = vim.fn.getcwd() .. "/tracked.txt"+ local fd = io.open(file, "a")+ fd:write("-- useless comment\n")+ fd:close()+ ]])++ -- Auto-confirm the reset dialog so the headless test does not block.+ exec_lua([[FzfLua.utils.confirm = function() return 1 end]])++ helpers.FzfLua.git_status(child, {+ __no_abort = true,+ __expect_lines = true,+ __screen_opts = { ignore_text = { 28 } },+ previewer = false,+ headers = false,+ multiprocess = false,+ __after_open = function()+ -- Screenshot: unstaged modification (M on the right)+ sleep(100)+ if helpers.IS_WIN() then sleep(250) end+ child.expect_screen_lines({ ignore_text = { 28 } })++ -- Stage with left: M should move to the staged (left) column.+ exec_lua([[_G._fzf_load_called = nil]])+ child.type_keys("<Left>")+ sleep(100)+ if helpers.IS_WIN() then sleep(250) end+ child.wait_until(function() return exec_lua([[return _G._fzf_load_called]]) == true end)+ child.expect_screen_lines({ ignore_text = { 28 } })++ -- Unstage with right: M should return to the unstaged (right) column.+ exec_lua([[_G._fzf_load_called = nil]])+ child.type_keys("<Right>")+ sleep(100)+ if helpers.IS_WIN() then sleep(250) end+ child.wait_until(function() return exec_lua([[return _G._fzf_load_called]]) == true end)+ child.expect_screen_lines({ ignore_text = { 28 } })++ -- Reset with ctrl-x: file is restored and the picker reloads empty.+ exec_lua([[_G._fzf_load_called = nil]])+ child.type_keys("<c-x>")+ sleep(100)+ if helpers.IS_WIN() then sleep(250) end+ child.wait_until(function() return exec_lua([[return _G._fzf_load_called]]) == true end)+ end,+ })++ -- Close the picker.+ child.type_keys("<c-c>")+ child.wait_until(function() return exec_lua([[return _G._fzf_lua_on_create]]) == vim.NIL end)++ -- Verify the working tree is clean after reset.+ eq("", exec_lua([[+ return vim.fn.system({ "git", "-C", vim.fn.getcwd(), "status", "--porcelain" })+ :gsub("%s+$", "")+ ]]))++ -- Commit the clean state, ignoring any SSH/GPG signing configuration.+ exec_lua([[+ vim.fn.system({+ "git", "-C", vim.fn.getcwd(), "commit", "--allow-empty", "--no-gpg-sign",+ "-m", "git_status test end",+ })+ ]])+end++return T
tests/screenshots/tests-git_status_spec.lua---git_status---stage-unstage-reset
@@ -0,0 +1,29 @@+--|---------|---------|---------|---------|---------|---------|----+01| +02|~ +03|~ ╭────────────────── Git Status ───────────────────╮ +04|~ │> 1/1 (0) │ +05|~ │──────────────────────────────────────────────── │ +06|~ │▌ M tracked.txt │ +07|~ │ │ +08|~ │ │ +09|~ │ │ +10|~ │ │ +11|~ │ │ +12|~ │ │ +13|~ │ │ +14|~ │ │ +15|~ │ │ +16|~ │ │ +17|~ │ │ +18|~ │ │ +19|~ │ │ +20|~ │ │ +21|~ │ │ +22|~ ╰─────────────────────────────────────────────────╯ +23|~ +24|[No Name] 0,0-1 All+25| +26| +27| +28|-- TERMINAL -- 1,3 All
tests/screenshots/tests-git_status_spec.lua---git_status---stage-unstage-reset-002
@@ -0,0 +1,29 @@+--|---------|---------|---------|---------|---------|---------|----+01| +02|~ +03|~ ╭────────────────── Git Status ───────────────────╮ +04|~ │> 1/1 (0) │ +05|~ │──────────────────────────────────────────────── │ +06|~ │▌ M tracked.txt │ +07|~ │ │ +08|~ │ │ +09|~ │ │ +10|~ │ │ +11|~ │ │ +12|~ │ │ +13|~ │ │ +14|~ │ │ +15|~ │ │ +16|~ │ │ +17|~ │ │ +18|~ │ │ +19|~ │ │ +20|~ │ │ +21|~ │ │ +22|~ ╰─────────────────────────────────────────────────╯ +23|~ +24|[No Name] 0,0-1 All+25| +26| +27| +28|-- TERMINAL -- 1,3 All
tests/screenshots/tests-git_status_spec.lua---git_status---stage-unstage-reset-003
@@ -0,0 +1,29 @@+--|---------|---------|---------|---------|---------|---------|----+01| +02|~ +03|~ ╭────────────────── Git Status ───────────────────╮ +04|~ │> 1/1 (0) │ +05|~ │──────────────────────────────────────────────── │ +06|~ │▌ M tracked.txt │ +07|~ │ │ +08|~ │ │ +09|~ │ │ +10|~ │ │ +11|~ │ │ +12|~ │ │ +13|~ │ │ +14|~ │ │ +15|~ │ │ +16|~ │ │ +17|~ │ │ +18|~ │ │ +19|~ │ │ +20|~ │ │ +21|~ │ │ +22|~ ╰─────────────────────────────────────────────────╯ +23|~ +24|[No Name] 0,0-1 All+25| +26| +27| +28|-- TERMINAL -- 1,3 All
tests/screenshots/tests-git_status_spec.lua---git_status---stage-unstage-reset-004
@@ -0,0 +1,29 @@+--|---------|---------|---------|---------|---------|---------|----+01| +02|~ +03|~ ╭────────────────── Git Status ───────────────────╮ +04|~ │> 0/0 (0) │ +05|~ │──────────────────────────────────────────────── │ +06|~ │ │ +07|~ │ │ +08|~ │ │ +09|~ │ │ +10|~ │ │ +11|~ │ │ +12|~ │ │ +13|~ │ │ +14|~ │ │ +15|~ │ │ +16|~ │ │ +17|~ │ │ +18|~ │ │ +19|~ │ │ +20|~ │ │ +21|~ │ │ +22|~ ╰─────────────────────────────────────────────────╯ +23|~ +24|[No Name] 0,0-1 All+25| +26| +27| +28|-- TERMINAL -- 1,3 All