Files
dmtrkovalenko--fff/tests/picker_ui_snap.lua
T
wehub-resource-sync d5f207424b
e2e Tests / e2e (macos-latest) (push) Waiting to run
Nix CI / check (push) Waiting to run
Python CI / Python bindings (macos-latest) (push) Waiting to run
e2e Tests / e2e (windows-latest) (push) Waiting to run
Python CI / Python bindings (windows-latest) (push) Waiting to run
Build & Publish / Build Neovim aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build Neovim aarch64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build Neovim x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build Neovim x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build C FFI aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build C FFI aarch64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build C FFI x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build C FFI x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build MCP aarch64-apple-darwin (push) Waiting to run
Build & Publish / Build MCP aarch64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build MCP x86_64-apple-darwin (push) Waiting to run
Build & Publish / Build MCP x86_64-pc-windows-msvc (push) Waiting to run
Build & Publish / Build Python wheels aarch64 (macos-latest) (push) Waiting to run
Build & Publish / Build Python wheels x86_64 (macos-latest) (push) Waiting to run
Build & Publish / Build Python wheels x86_64 (windows-latest) (push) Waiting to run
Build & Publish / Release (push) Blocked by required conditions
Build & Publish / Publish Python wheels to PyPI (push) Blocked by required conditions
Build & Publish / Publish Rust crates (push) Blocked by required conditions
Build & Publish / Publish npm packages (push) Blocked by required conditions
Rust CI / Fuzz Tests (windows-latest) (push) Waiting to run
Rust CI / Test (macos-latest) (push) Waiting to run
Rust CI / Test (windows-latest) (push) Waiting to run
Rust CI / Fuzz Tests (macos-latest) (push) Waiting to run
Python CI / Python bindings (ubuntu-latest) (push) Failing after 0s
Build & Publish / Build Neovim aarch64-linux-android (push) Failing after 0s
Build & Publish / Build Neovim aarch64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build MCP x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build Python wheels aarch64 (ubuntu-latest) (push) Failing after 0s
Build & Publish / Build Python wheels x86_64 (ubuntu-latest) (push) Failing after 1s
Build & Publish / Build Python sdist (push) Failing after 1s
Rust CI / Fuzz Tests (ubuntu-latest) (push) Failing after 1s
Rust CI / Build i686-unknown-linux-gnu (push) Failing after 0s
Rust CI / cargo clippy (push) Failing after 1s
e2e Tests / e2e (ubuntu-latest) (push) Failing after 1s
Lua CI / luacheck lint (push) Failing after 1s
Build & Publish / Build Neovim aarch64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build Neovim x86_64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build Neovim x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build C FFI aarch64-linux-android (push) Failing after 0s
Build & Publish / Build C FFI aarch64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build C FFI aarch64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build C FFI x86_64-unknown-linux-gnu (push) Failing after 0s
Build & Publish / Build C FFI x86_64-unknown-linux-musl (push) Failing after 1s
Build & Publish / Build MCP aarch64-unknown-linux-gnu (push) Failing after 1s
Build & Publish / Build MCP aarch64-unknown-linux-musl (push) Failing after 0s
Build & Publish / Build MCP x86_64-unknown-linux-gnu (push) Failing after 1s
Spelling / Spell Check with Typos (push) Failing after 0s
Rust CI / Test (ubuntu-latest) (push) Failing after 0s
Rust CI / cargo fmt (push) Failing after 0s
Stylua / Check lua files using Stylua (push) Failing after 1s
Lua CI / lua-language-server type check (push) Failing after 12m29s
e2e Tests / e2e (alpine-musl) (push) Successful in 57m31s
chore: import upstream snapshot with attribution
2026-07-13 12:27:16 +08:00

243 lines
7.2 KiB
Lua

--- End-to-end snapshot tests for fff.nvim's picker UI.
local MiniTest = require('mini.test')
local fixture_lib = require('tests.snapshot.fixture')
local PLUGIN_DIR = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':p:h:h')
local FORCE = vim.env.UPDATE_SNAPSHOTS == '1'
local child, fixture
local function setup(geometry, opts)
opts = opts or {}
fixture = fixture_lib.create()
child = MiniTest.new_child_neovim()
child.start({
'--clean',
'-n',
'-i',
'NONE',
'--cmd',
string.format('let &lines = %d', geometry.rows),
'--cmd',
string.format('let &columns = %d', geometry.cols),
}, {
connection_timeout = 15000,
})
child.o.lines = geometry.rows
child.o.columns = geometry.cols
local debug_enabled = opts.debug == true
-- Default show_file_info hides timings: Modified/Accessed timestamps drift
-- between runs and would otherwise force `ignore_text` on those rows. Tests
-- can override (or restore) by passing `opts.show_file_info`.
local show_file_info = opts.show_file_info
or { file_info = true, score_breakdown = true, timings = false, full_path = true }
child.lua(
string.format(
[[
local plugin = %q
vim.opt.runtimepath:prepend(plugin)
package.path = plugin .. '/lua/?.lua;' .. plugin .. '/lua/?/init.lua;' .. package.path
vim.cmd('cd ' .. vim.fn.fnameescape(%q))
vim.cmd('syntax off')
local winborder = %q
if winborder ~= '' then vim.o.winborder = winborder end
vim.g.fff = {
prompt = '> ',
frecency = { enabled = true, db_path = %q },
history = { enabled = true, db_path = %q },
logging = { enabled = false },
debug = {
enabled = %s,
show_scores = %s,
show_file_info = %s,
},
}
require('fff.core').ensure_initialized()
require('fff.rust').wait_for_initial_scan(8000)
]],
PLUGIN_DIR,
fixture.root,
geometry.winborder or '',
fixture.frecency_db,
fixture.history_db,
tostring(debug_enabled),
tostring(debug_enabled),
vim.inspect(show_file_info)
)
)
end
local function teardown()
if child and child.is_running() then pcall(child.stop) end
if fixture then fixture_lib.cleanup(fixture) end
child, fixture = nil, nil
end
--- @param opts table|nil { ignore_text?: number[] }
local function assert_snapshot_match(opts)
opts = opts or {}
MiniTest.expect.reference_screenshot(child.get_screenshot(), nil, {
force = FORCE,
ignore_text = opts.ignore_text or false,
})
end
local function open_picker(prompt_position, query)
child.lua(
string.format('require("fff.picker_ui.picker_ui").open({ layout = { prompt_position = %q } })', prompt_position)
)
vim.loop.sleep(400)
if query and query ~= '' then
child.type_keys(query)
vim.loop.sleep(400)
end
end
local LAYOUTS = {
{ name = 'wide', cols = 180, rows = 40, winborder = 'double' },
{ name = 'default', cols = 140, rows = 32 }, -- standard on most screens
{ name = 'narrow', cols = 70, rows = 24, winborder = 'rounded' },
-- Extra-wide: exists primarily so the file_info panel hits the H2 layout.
{ name = 'xwide', cols = 240, rows = 48 },
}
local T = MiniTest.new_set()
local PROMPT_POSITIONS = { 'bottom', 'top' }
for _, geometry in ipairs(LAYOUTS) do
local set = MiniTest.new_set({
hooks = {
pre_case = function() setup(geometry) end,
post_case = teardown,
},
})
-- Run every per-geometry case for both prompt positions: layout math and
-- list rendering diverge between top/bottom (see AGENTS.md), so a snapshot
-- on a single side would silently miss regressions in the other.
for _, prompt in ipairs(PROMPT_POSITIONS) do
set['empty_' .. prompt] = function()
open_picker(prompt)
assert_snapshot_match()
end
set['query_main_' .. prompt] = function()
open_picker(prompt, 'main')
assert_snapshot_match()
end
set['no_results_' .. prompt] = function()
open_picker(prompt, 'zzzzzzzzz')
assert_snapshot_match()
end
set['cursor_second_item_' .. prompt] = function()
open_picker(prompt)
-- Bottom prompt visually goes up with <Down>; top prompt goes down with <Down>.
-- Either way one keypress moves to the second item — what we want to capture.
child.type_keys('<Down>')
vim.loop.sleep(200)
assert_snapshot_match()
end
end
T[geometry.name] = set
end
-- File info panel snapshots. Timings are disabled at the config level (see
-- `setup`) so the snapshots stay deterministic — Modified/Accessed timestamps
-- drift between runs. We snapshot a narrow and a wide variant for both prompt
-- positions to keep the adaptive panel layout (label widths, section headers,
-- top vs bottom prompt geometry) covered.
local debug_narrow_set = MiniTest.new_set({
hooks = {
pre_case = function() setup(LAYOUTS[2], { debug = true }) end, -- default 140x32, panel ~57 cols
post_case = teardown,
},
})
for _, prompt in ipairs(PROMPT_POSITIONS) do
debug_narrow_set['file_info_panel_' .. prompt] = function()
open_picker(prompt, 'main')
assert_snapshot_match()
end
end
T['debug_narrow'] = debug_narrow_set
local debug_wide_set = MiniTest.new_set({
hooks = {
pre_case = function() setup(LAYOUTS[4], { debug = true }) end, -- xwide 240x48, panel ~96 cols
post_case = teardown,
},
})
for _, prompt in ipairs(PROMPT_POSITIONS) do
debug_wide_set['file_info_panel_' .. prompt] = function()
open_picker(prompt, 'main')
assert_snapshot_match()
end
end
T['debug_wide'] = debug_wide_set
T['combo'] = MiniTest.new_set({
hooks = {
pre_case = function() setup({ cols = 140, rows = 32 }) end,
post_case = teardown,
},
})
local function train_combo()
-- Train: "main" → src/main.rs four times so open_count crosses the default
-- min_combo_count (3). track_query_completion is async; pause between calls
-- + final settle for the lmdb writer.
for _ = 1, 4 do
child.lua(
string.format('require("fff.rust").track_query_completion(%q, %q)', 'main', fixture.root .. '/src/main.rs')
)
vim.loop.sleep(120)
end
vim.loop.sleep(400)
end
for _, prompt in ipairs(PROMPT_POSITIONS) do
T['combo']['boost_' .. prompt] = function()
train_combo()
open_picker(prompt, 'main')
-- Combo overlay float renders asynchronously after render_list.
vim.loop.sleep(400)
assert_snapshot_match()
end
end
T['scrollbar'] = MiniTest.new_set({
hooks = {
pre_case = function() setup({ cols = 140, rows = 32 }) end,
post_case = teardown,
},
})
-- Cursor advance key differs by prompt position: bottom prompt iterates the
-- list in reverse so `<Up>` walks toward higher indices; top prompt is
-- conventional. Either way we walk past the last in-page item to trigger
-- load_next_page and surface the scrollbar thumb at the new offset.
local SCROLL_KEY = { bottom = '<Up>', top = '<Down>' }
for _, prompt in ipairs(PROMPT_POSITIONS) do
T['scrollbar']['next_page_' .. prompt] = function()
open_picker(prompt)
for _ = 1, 30 do
child.type_keys(SCROLL_KEY[prompt])
vim.loop.sleep(20)
end
vim.loop.sleep(400)
assert_snapshot_match()
end
end
return T