Skip to content

Commit 6727eae

Browse files
committed
add tests
1 parent a8bb8ec commit 6727eae

8 files changed

Lines changed: 828 additions & 16 deletions

docs/configuration.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,6 @@ require("eca").setup({
5959
auto_repo_map = true,
6060
},
6161

62-
-- === TODOS PANEL ===
63-
todos = {
64-
enabled = true, -- Enable or disable todos integration
65-
max_height = 5, -- Maximum height for the todos container
66-
},
67-
68-
-- === SELECTED CODE PANEL ===
69-
selected_code = {
70-
enabled = true, -- Show currently selected code in the UI
71-
max_height = 8, -- Maximum height for the selected code container
72-
},
73-
7462
-- === KEY MAPPINGS ===
7563
mappings = {
7664
chat = "<leader>ec", -- Open chat

lua/eca/commands.lua

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,27 @@ function M.setup()
505505
for _, name in ipairs(names) do
506506
local tool = tools[name] or {}
507507

508+
-- Build a human-readable preview string that always includes the
509+
-- tool name and its primary "kind" field (when available),
510+
-- followed by a full vim.inspect dump for debugging.
511+
local preview_text
512+
if next(tool) ~= nil then
513+
local kind_value = tool.kind ~= nil and tostring(tool.kind) or "(unknown)"
514+
preview_text = string.format("name: %s\nkind: %s", name, kind_value)
515+
516+
local inspected = vim.inspect(tool)
517+
if inspected and inspected ~= "" then
518+
preview_text = preview_text .. "\n" .. inspected
519+
end
520+
else
521+
preview_text = string.format("name: %s\n<empty tool>", name)
522+
end
523+
508524
table.insert(items, {
509525
text = name,
510526
idx = name,
511527
preview = {
512-
text = vim.inspect(tool),
528+
text = preview_text,
513529
ft = "lua",
514530
},
515531
})

lua/eca/sidebar.lua

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,18 @@ end
6868

6969
-- Tool call icons (can be overridden via Config.windows.chat.tool_call.icons)
7070
local function _get_chat_config()
71-
-- Prefer `windows.chat`, but fall back to top-level `chat` for backwards compatibility
72-
return (Config.windows and Config.windows.chat) or Config.chat or {}
71+
-- Merge top-level `chat` (backwards compatible) with `windows.chat`.
72+
-- `windows.chat` provides modern defaults, while a user-provided
73+
-- `chat.tool_call` block (legacy style) can still override fields
74+
-- like `diff_label` and `diff_start_expanded`.
75+
local win_chat = (Config.windows and Config.windows.chat) or {}
76+
local top_chat = Config.chat or {}
77+
78+
if next(top_chat) == nil then
79+
return win_chat
80+
end
81+
82+
return vim.tbl_deep_extend("force", win_chat, top_chat)
7383
end
7484

7585
local function get_tool_call_icons()
@@ -1996,7 +2006,10 @@ function M:_handle_reason_started(content)
19962006
end
19972007

19982008
-- Whether "Thinking" blocks should start expanded by default
1999-
local reasoning_cfg = (Config.chat and Config.chat.reasoning) or {}
2009+
-- Use the merged chat config so both legacy `chat.reasoning` and
2010+
-- modern `windows.chat.reasoning` can control this behavior.
2011+
local chat_cfg = _get_chat_config()
2012+
local reasoning_cfg = chat_cfg.reasoning or {}
20002013
local expand = reasoning_cfg.expanded == true
20012014

20022015
local call = {

tests/test_eca.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ T["config"]["has default values"] = function()
1616
MiniTest.expect.equality(type(config), "table")
1717
end
1818

19+
T["config"]["has usage window defaults"] = function()
20+
local config = require("eca.config")
21+
MiniTest.expect.equality(
22+
config.options.windows.usage.format,
23+
"{session_tokens_short} / {limit_tokens_short} (${session_cost})"
24+
)
25+
end
26+
1927
-- Test utilities
2028
T["utils"] = MiniTest.new_set()
2129

tests/test_highlights.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
local MiniTest = require("mini.test")
2+
local eq = MiniTest.expect.equality
3+
local child = MiniTest.new_child_neovim()
4+
5+
local T = MiniTest.new_set({
6+
hooks = {
7+
pre_case = function()
8+
child.restart({ "-u", "scripts/minimal_init.lua" })
9+
child.lua([[require('eca.highlights').setup()]])
10+
end,
11+
post_once = child.stop,
12+
},
13+
})
14+
15+
T["highlights"] = MiniTest.new_set()
16+
17+
T["highlights"]["defines ECA highlight groups used in sidebar"] = function()
18+
local ok_label = child.lua_get("pcall(vim.api.nvim_get_hl, 0, { name = 'EcaLabel' })")
19+
local ok_tool = child.lua_get("pcall(vim.api.nvim_get_hl, 0, { name = 'EcaToolCall' })")
20+
local ok_link = child.lua_get("pcall(vim.api.nvim_get_hl, 0, { name = 'EcaHyperlink' })")
21+
22+
eq(ok_label, true)
23+
eq(ok_tool, true)
24+
eq(ok_link, true)
25+
end
26+
27+
return T

tests/test_picker.lua

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
local MiniTest = require("mini.test")
2+
local eq = MiniTest.expect.equality
3+
local child = MiniTest.new_child_neovim()
4+
5+
local T = MiniTest.new_set({
6+
hooks = {
7+
pre_case = function()
8+
child.restart({ "-u", "scripts/minimal_init.lua" })
9+
child.lua([[
10+
_G.captured_notifications = {}
11+
local Logger = require('eca.logger')
12+
_G._original_notify = Logger.notify
13+
Logger.notify = function(msg, level, opts)
14+
table.insert(_G.captured_notifications, {
15+
message = msg,
16+
level = level,
17+
opts = opts or {},
18+
})
19+
end
20+
]])
21+
end,
22+
post_case = function()
23+
child.lua([[
24+
local Logger = require('eca.logger')
25+
if _G._original_notify then
26+
Logger.notify = _G._original_notify
27+
end
28+
_G.captured_notifications = nil
29+
]])
30+
end,
31+
post_once = child.stop,
32+
},
33+
})
34+
35+
T["picker wrapper"] = MiniTest.new_set()
36+
37+
T["picker wrapper"]["logs error when snacks is missing"] = function()
38+
child.lua([[
39+
package.loaded['snacks'] = nil
40+
local Picker = require('eca.ui.picker')
41+
_G.result = Picker.pick({ source = 'test-source' })
42+
]])
43+
44+
local result = child.lua_get("_G.result")
45+
eq(result, vim.NIL)
46+
47+
local notifications = child.lua_get("_G.captured_notifications")
48+
eq(#notifications, 1)
49+
eq(notifications[1].message, "snacks.nvim is not available")
50+
eq(notifications[1].level, child.lua_get("vim.log.levels.ERROR"))
51+
end
52+
53+
T["picker wrapper"]["delegates to snacks.picker when available"] = function()
54+
child.lua([[
55+
local calls = {}
56+
package.loaded['snacks'] = {
57+
picker = function(config)
58+
table.insert(calls, config)
59+
return 'OK'
60+
end,
61+
}
62+
_G.snacks_calls = calls
63+
64+
local Picker = require('eca.ui.picker')
65+
_G.result = Picker.pick({ source = 'test-source', extra = true })
66+
]])
67+
68+
local result = child.lua_get("_G.result")
69+
eq(result, "OK")
70+
71+
local calls = child.lua_get("_G.snacks_calls")
72+
eq(#calls, 1)
73+
eq(calls[1].source, "test-source")
74+
eq(calls[1].extra, true)
75+
end
76+
77+
return T
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
local MiniTest = require("mini.test")
2+
local eq = MiniTest.expect.equality
3+
local child = MiniTest.new_child_neovim()
4+
5+
local function flush(ms)
6+
vim.uv.sleep(ms or 50)
7+
child.api.nvim_eval("1")
8+
end
9+
10+
local function setup_env()
11+
require('eca.commands').setup()
12+
13+
-- Stub Picker.pick so commands can run without snacks.nvim
14+
local Picker = require('eca.ui.picker')
15+
_G.picker_calls = {}
16+
Picker.pick = function(config)
17+
table.insert(_G.picker_calls, config)
18+
end
19+
end
20+
21+
local T = MiniTest.new_set({
22+
hooks = {
23+
pre_case = function()
24+
child.restart({ "-u", "scripts/minimal_init.lua" })
25+
child.lua_func(setup_env)
26+
end,
27+
post_case = function()
28+
child.lua("_G.picker_calls = nil")
29+
end,
30+
post_once = child.stop,
31+
},
32+
})
33+
34+
T["EcaServerMessages"] = MiniTest.new_set()
35+
36+
T["EcaServerMessages"]["uses picker and filters invalid JSON messages"] = function()
37+
child.lua([[
38+
local eca = require('eca')
39+
eca.server = eca.server or {}
40+
eca.server.messages = {
41+
{ id = 1, direction = 'send', content = vim.json.encode({ jsonrpc = '2.0', method = 'test/method', id = 1 }) },
42+
{ id = 2, direction = 'recv', content = 'not-json' },
43+
}
44+
45+
vim.cmd('EcaServerMessages')
46+
]])
47+
48+
flush()
49+
50+
child.lua([[
51+
local calls = _G.picker_calls or {}
52+
local cfg = calls[1]
53+
_G.picker_info = {
54+
count = #calls,
55+
source = cfg and cfg.source or nil,
56+
}
57+
]])
58+
59+
local picker_info = child.lua_get("_G.picker_info")
60+
61+
eq(picker_info.count, 1)
62+
eq(picker_info.source, "eca messages")
63+
64+
-- Run finder and inspect produced items
65+
child.lua([[
66+
local cfg = _G.picker_calls[1]
67+
local items = cfg.finder({}, {})
68+
_G.result_messages = {
69+
count = #items,
70+
first = items[1],
71+
}
72+
]])
73+
74+
local result = child.lua_get("_G.result_messages")
75+
76+
-- Only the valid JSON message should be included
77+
eq(result.count, 1)
78+
eq(type(result.first), "table")
79+
eq(result.first.idx, 1)
80+
eq(result.first.preview.ft, "lua")
81+
82+
-- Preview text should contain the method name
83+
local has_method = child.lua_get("string.find(..., 'test/method', 1, true) ~= nil", { result.first.preview.text })
84+
eq(has_method, true)
85+
86+
-- Confirm callback yanks preview text and closes picker
87+
child.lua([[
88+
local cfg = _G.picker_calls[1]
89+
local item = cfg.finder({}, {})[1]
90+
local picker = { closed = false }
91+
function picker:close() self.closed = true end
92+
93+
cfg.confirm(picker, item, nil)
94+
95+
_G.confirm_messages = {
96+
reg = vim.fn.getreg(''),
97+
closed = picker.closed,
98+
}
99+
]])
100+
101+
local confirm_ok = child.lua_get("_G.confirm_messages")
102+
103+
eq(confirm_ok.closed, true)
104+
eq(type(confirm_ok.reg), "string")
105+
local has_method_in_reg = child.lua_get("string.find(..., 'test/method', 1, true) ~= nil", { confirm_ok.reg })
106+
eq(has_method_in_reg, true)
107+
end
108+
109+
T["EcaServerTools"] = MiniTest.new_set()
110+
111+
T["EcaServerTools"]["lists tools from state in sorted order"] = function()
112+
child.lua([[
113+
local eca = require('eca')
114+
eca.state = eca.state or {}
115+
eca.state.tools = {
116+
zebra = { kind = 'z' },
117+
alpha = { kind = 'a' },
118+
middle = { kind = 'm' },
119+
}
120+
121+
vim.cmd('EcaServerTools')
122+
]])
123+
124+
flush()
125+
126+
child.lua([[
127+
local calls = _G.picker_calls or {}
128+
_G.picker_info = {
129+
count = #calls,
130+
}
131+
]])
132+
133+
local picker_info = child.lua_get("_G.picker_info")
134+
135+
eq(picker_info.count, 1)
136+
137+
child.lua([[
138+
local cfg = _G.picker_calls[1]
139+
local items = cfg.finder({}, {})
140+
_G.result_tools = {
141+
count = #items,
142+
names = { items[1].text, items[2].text, items[3].text },
143+
first = items[1],
144+
}
145+
]])
146+
147+
local result = child.lua_get("_G.result_tools")
148+
149+
eq(result.count, 3)
150+
-- Names must be sorted alphabetically
151+
eq(result.names[1], "alpha")
152+
eq(result.names[2], "middle")
153+
eq(result.names[3], "zebra")
154+
155+
-- Preview contains vim.inspect output of the tool
156+
local has_kind = child.lua_get("string.find(..., 'kind%p a', 1) ~= nil", { result.first.preview.text })
157+
eq(has_kind, true)
158+
159+
-- Confirm yanks preview text and closes picker
160+
child.lua([[
161+
local cfg = _G.picker_calls[1]
162+
local items = cfg.finder({}, {})
163+
local picker = { closed = false }
164+
function picker:close() self.closed = true end
165+
166+
cfg.confirm(picker, items[2], nil)
167+
168+
_G.confirm_tools = {
169+
reg = vim.fn.getreg(''),
170+
closed = picker.closed,
171+
}
172+
]])
173+
174+
local confirm_ok = child.lua_get("_G.confirm_tools")
175+
176+
eq(confirm_ok.closed, true)
177+
eq(type(confirm_ok.reg), "string")
178+
local has_middle = child.lua_get("string.find(..., 'middle', 1, true) ~= nil", { confirm_ok.reg })
179+
eq(has_middle, true)
180+
end
181+
182+
return T

0 commit comments

Comments
 (0)