Skip to content

Commit b74cb08

Browse files
committed
test(context-bar): add unit tests for context-bar rendering
1 parent 9073824 commit b74cb08

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

tests/unit/context_bar_spec.lua

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
local context_bar = require('opencode.ui.context_bar')
2+
local context = require('opencode.context')
3+
local state = require('opencode.state')
4+
local icons = require('opencode.ui.icons')
5+
local assert = require('luassert')
6+
7+
describe('opencode.ui.context_bar', function()
8+
local original_delta_context
9+
local original_is_context_enabled
10+
local original_get_icon
11+
local original_subscribe
12+
local original_schedule
13+
local original_api_win_is_valid
14+
local original_api_get_option_value
15+
local original_api_set_option_value
16+
local original_vim_tbl_contains
17+
local original_wo
18+
local mock_context
19+
20+
local function create_mock_window(win_id)
21+
local captured_winbar = {}
22+
vim.wo[win_id] = setmetatable({}, {
23+
__newindex = function(_, key, value)
24+
if key == 'winbar' then
25+
captured_winbar.value = value
26+
captured_winbar.set = true
27+
end
28+
end,
29+
})
30+
return captured_winbar
31+
end
32+
33+
before_each(function()
34+
original_delta_context = context.delta_context
35+
original_is_context_enabled = context.is_context_enabled
36+
original_get_icon = icons.get
37+
original_subscribe = state.subscribe
38+
original_schedule = vim.schedule
39+
original_api_win_is_valid = vim.api.nvim_win_is_valid
40+
original_api_get_option_value = vim.api.nvim_get_option_value
41+
original_api_set_option_value = vim.api.nvim_set_option_value
42+
original_vim_tbl_contains = vim.tbl_contains
43+
original_wo = vim.wo
44+
45+
mock_context = {
46+
current_file = nil,
47+
mentioned_files = nil,
48+
mentioned_subagents = nil,
49+
selections = nil,
50+
linter_errors = nil,
51+
cursor_data = nil,
52+
}
53+
54+
context.delta_context = function()
55+
return mock_context
56+
end
57+
58+
context.is_context_enabled = function(_)
59+
return true -- Enable all context types by default
60+
end
61+
62+
state.subscribe = function(_, _)
63+
-- Mock implementation
64+
end
65+
66+
vim.schedule = function(callback)
67+
callback() -- Execute immediately for tests
68+
end
69+
70+
vim.api.nvim_win_is_valid = function(_)
71+
return true
72+
end
73+
74+
vim.api.nvim_get_option_value = function(_, _)
75+
return 'StatusLine:MyStatusLine'
76+
end
77+
78+
vim.api.nvim_set_option_value = function(_, _, _)
79+
-- Mock implementation
80+
end
81+
82+
vim.tbl_contains = function(table, value)
83+
for _, v in ipairs(table) do
84+
if v == value then
85+
return true
86+
end
87+
end
88+
return false
89+
end
90+
91+
vim.wo = {}
92+
93+
-- Reset state
94+
state.windows = nil
95+
end)
96+
97+
after_each(function()
98+
-- Restore original functions
99+
context.delta_context = original_delta_context
100+
context.is_context_enabled = original_is_context_enabled
101+
icons.get = original_get_icon
102+
state.subscribe = original_subscribe
103+
vim.schedule = original_schedule
104+
vim.api.nvim_win_is_valid = original_api_win_is_valid
105+
vim.api.nvim_get_option_value = original_api_get_option_value
106+
vim.api.nvim_set_option_value = original_api_set_option_value
107+
vim.tbl_contains = original_vim_tbl_contains
108+
vim.wo = original_wo
109+
end)
110+
111+
describe('opencode.ui.context_bar', function()
112+
it('renders minimal winbar with context icon only', function()
113+
local mock_input_win = 2001
114+
local winbar_capture = create_mock_window(mock_input_win)
115+
116+
state.windows = { input_win = mock_input_win }
117+
context_bar.render()
118+
119+
assert.is_string(winbar_capture.value)
120+
assert.is_not_nil(winbar_capture.value:find(icons.get('context')))
121+
end)
122+
123+
it('renders winbar with current file when present', function()
124+
mock_context.current_file = {
125+
name = 'test.lua',
126+
path = '/tmp/test.lua',
127+
}
128+
129+
local mock_input_win = 2002
130+
local winbar_capture = create_mock_window(mock_input_win)
131+
132+
state.windows = { input_win = mock_input_win }
133+
context_bar.render()
134+
135+
assert.is_string(winbar_capture.value)
136+
assert.is_not_nil(winbar_capture.value:find(icons.get('attached_file') .. 'test%.lua'))
137+
end)
138+
139+
it('renders winbar with multiple context elements', function()
140+
mock_context.current_file = { name = 'main.lua', path = '/src/main.lua' }
141+
mock_context.mentioned_files = { '/file1.lua', '/file2.lua' }
142+
mock_context.mentioned_subagents = { 'agent1' }
143+
mock_context.selections = { { text = 'code' } }
144+
mock_context.cursor_data = { line = 10, col = 5 }
145+
146+
local mock_input_win = 2003
147+
local winbar_capture = create_mock_window(mock_input_win)
148+
149+
state.windows = { input_win = mock_input_win }
150+
context_bar.render()
151+
152+
assert.is_string(winbar_capture.value)
153+
assert.is_not_nil(winbar_capture.value:find(icons.get('attached_file') .. 'main%.lua'))
154+
assert.is_not_nil(winbar_capture.value:find(icons.get('file') .. '%(2%)'))
155+
assert.is_not_nil(winbar_capture.value:find(icons.get('agent') .. '%(1%)'))
156+
assert.is_not_nil(winbar_capture.value:find('L:10')) -- Cursor data
157+
end)
158+
159+
it('renders winbar with diagnostics', function()
160+
mock_context.linter_errors = {
161+
{ severity = 1 }, -- ERROR
162+
{ severity = 1 }, -- ERROR
163+
{ severity = 2 }, -- WARN
164+
{ severity = 3 }, -- INFO
165+
}
166+
167+
local mock_input_win = 2004
168+
local winbar_capture = create_mock_window(mock_input_win)
169+
170+
state.windows = { input_win = mock_input_win }
171+
context_bar.render()
172+
173+
assert.is_string(winbar_capture.value)
174+
assert.is_not_nil(winbar_capture.value:find(icons.get('error') .. '%(2%)')) -- 2 errors
175+
assert.is_not_nil(winbar_capture.value:find(icons.get('warning') .. '%(1%)')) -- Warning icon
176+
assert.is_not_nil(winbar_capture.value:find(icons.get('info') .. '%(1%)')) -- Info icon
177+
end)
178+
179+
it('respects context enabled settings', function()
180+
context.is_context_enabled = function(context_type)
181+
return context_type == 'current_file' -- Only enable current_file
182+
end
183+
184+
mock_context.current_file = { name = 'test.lua', path = '/test.lua' }
185+
mock_context.mentioned_files = { '/file1.lua' }
186+
mock_context.selections = { { text = 'code' } }
187+
188+
local mock_input_win = 2005
189+
local winbar_capture = create_mock_window(mock_input_win)
190+
191+
state.windows = { input_win = mock_input_win }
192+
context_bar.render()
193+
194+
assert.is_string(winbar_capture.value)
195+
assert.is_not_nil(winbar_capture.value:find(icons.get('attached_file') .. 'test%.lua')) -- attahced file icon
196+
assert.is_nil(winbar_capture.value:find(icons.get('file') .. '%(1%)'))
197+
assert.is_nil(winbar_capture.value:find(icons.get('selection') .. '%(1%)'))
198+
end)
199+
200+
it('handles empty winbar gracefully', function()
201+
mock_context = {} -- Empty context
202+
203+
local mock_input_win = 2006
204+
local winbar_capture = create_mock_window(mock_input_win)
205+
206+
state.windows = { input_win = mock_input_win }
207+
context_bar.render()
208+
209+
assert.is_string(winbar_capture.value)
210+
assert.is_not_nil(winbar_capture.value:find(icons.get('context'))) -- Should still have context icon
211+
end)
212+
213+
it('does nothing when window is invalid', function()
214+
vim.api.nvim_win_is_valid = function(_)
215+
return false
216+
end
217+
218+
local mock_input_win = 2007
219+
local winbar_capture = create_mock_window(mock_input_win)
220+
221+
context_bar.render({ input_win = mock_input_win })
222+
assert.is_nil(winbar_capture.set)
223+
end)
224+
225+
it('uses provided windows parameter', function()
226+
local custom_windows = { input_win = 2008 }
227+
local winbar_capture = create_mock_window(2008)
228+
229+
context_bar.render(custom_windows)
230+
assert.is_string(winbar_capture.value)
231+
end)
232+
end)
233+
234+
describe('setup', function()
235+
it('subscribes to state changes', function()
236+
local subscription_called = false
237+
local captured_keys = nil
238+
239+
state.subscribe = function(keys, callback)
240+
subscription_called = true
241+
captured_keys = keys
242+
assert.is_table(keys)
243+
assert.is_function(callback)
244+
end
245+
246+
context_bar.setup()
247+
assert.is_true(subscription_called)
248+
assert.is_table(captured_keys)
249+
250+
local expected_keys = { 'current_context_config', 'current_code_buf', 'opencode_focused', 'context_updated_at' }
251+
for _, expected_key in ipairs(expected_keys) do
252+
local found = false
253+
for _, key in ipairs(captured_keys) do
254+
if key == expected_key then
255+
found = true
256+
break
257+
end
258+
end
259+
assert.is_true(found, 'Expected key not found: ' .. expected_key)
260+
end
261+
end)
262+
end)
263+
end)

0 commit comments

Comments
 (0)