|
| 1 | +local CursorSpinner = require('opencode.quick_chat.spinner') |
| 2 | + |
| 3 | +describe('quick_chat CursorSpinner', function() |
| 4 | + local buf |
| 5 | + local spinners |
| 6 | + |
| 7 | + before_each(function() |
| 8 | + spinners = {} |
| 9 | + buf = vim.api.nvim_create_buf(false, true) |
| 10 | + vim.api.nvim_set_current_buf(buf) |
| 11 | + end) |
| 12 | + |
| 13 | + after_each(function() |
| 14 | + for _, spinner in ipairs(spinners) do |
| 15 | + if spinner then |
| 16 | + spinner:stop() |
| 17 | + end |
| 18 | + end |
| 19 | + if buf and vim.api.nvim_buf_is_valid(buf) then |
| 20 | + vim.api.nvim_buf_delete(buf, { force = true }) |
| 21 | + end |
| 22 | + end) |
| 23 | + |
| 24 | + describe('build_layout', function() |
| 25 | + local SPINNER_BYTES = #(' ' .. '⠋' .. ' ') |
| 26 | + |
| 27 | + it('renders spinner and cancel key when no model is provided', function() |
| 28 | + local spinner = CursorSpinner.new(buf, 0, 0) |
| 29 | + table.insert(spinners, spinner) |
| 30 | + |
| 31 | + local content, highlights, width = spinner:build_layout() |
| 32 | + |
| 33 | + assert.is_truthy(content:find('to cancel', 1, true)) |
| 34 | + assert.is_truthy(content:find('<C-c>', 1, true)) |
| 35 | + assert.equals(1, #highlights) |
| 36 | + assert.equals('WarningMsg', highlights[1].hl_group) |
| 37 | + assert.equals(SPINNER_BYTES + 1, highlights[1].start_col) |
| 38 | + assert.equals(SPINNER_BYTES + 1 + #('<C-c>'), highlights[1].end_col) |
| 39 | + assert.equals(#content + 1, width) |
| 40 | + end) |
| 41 | + |
| 42 | + it('includes the provider/model name when given', function() |
| 43 | + local spinner = CursorSpinner.new(buf, 0, 0, { model = 'openai/gpt-4' }) |
| 44 | + table.insert(spinners, spinner) |
| 45 | + |
| 46 | + local content, highlights, width = spinner:build_layout() |
| 47 | + |
| 48 | + assert.is_truthy(content:find('openai/gpt-4', 1, true)) |
| 49 | + |
| 50 | + local model_hl |
| 51 | + for _, hl in ipairs(highlights) do |
| 52 | + if hl.hl_group == 'OpencodeHint' then |
| 53 | + model_hl = hl |
| 54 | + break |
| 55 | + end |
| 56 | + end |
| 57 | + assert.is_not_nil(model_hl) |
| 58 | + assert.equals(SPINNER_BYTES + 1, model_hl.start_col) |
| 59 | + assert.equals(model_hl.start_col + #('openai/gpt-4'), model_hl.end_col) |
| 60 | + assert.equals(#content + 1, width) |
| 61 | + end) |
| 62 | + |
| 63 | + it('renders the variant with OpencodeVariant highlight after the model', function() |
| 64 | + local spinner = CursorSpinner.new(buf, 0, 0, { model = 'openai/gpt-4', variant = 'max' }) |
| 65 | + table.insert(spinners, spinner) |
| 66 | + |
| 67 | + local content, highlights, _ = spinner:build_layout() |
| 68 | + |
| 69 | + assert.is_truthy(content:find('openai/gpt-4', 1, true)) |
| 70 | + assert.is_truthy(content:find('·', 1, true)) |
| 71 | + assert.is_truthy(content:find('max', 1, true)) |
| 72 | + |
| 73 | + local model_hl, variant_hl |
| 74 | + for _, hl in ipairs(highlights) do |
| 75 | + if hl.hl_group == 'OpencodeHint' then |
| 76 | + model_hl = hl |
| 77 | + elseif hl.hl_group == 'OpencodeVariant' then |
| 78 | + variant_hl = hl |
| 79 | + end |
| 80 | + end |
| 81 | + |
| 82 | + assert.is_not_nil(model_hl) |
| 83 | + assert.is_not_nil(variant_hl) |
| 84 | + assert.equals(SPINNER_BYTES + 1, model_hl.start_col) |
| 85 | + assert.equals(model_hl.start_col + #('openai/gpt-4'), model_hl.end_col) |
| 86 | + local sep_bytes = #('·') |
| 87 | + assert.equals(model_hl.end_col + sep_bytes, variant_hl.start_col) |
| 88 | + assert.equals(variant_hl.start_col + #('max'), variant_hl.end_col) |
| 89 | + end) |
| 90 | + |
| 91 | + it('omits the model when config.ui.display_model is false', function() |
| 92 | + local config = require('opencode.config') |
| 93 | + local original = config.ui.display_model |
| 94 | + config.ui.display_model = false |
| 95 | + |
| 96 | + local spinner = CursorSpinner.new(buf, 0, 0, { model = 'openai/gpt-4', variant = 'max' }) |
| 97 | + table.insert(spinners, spinner) |
| 98 | + |
| 99 | + local content, highlights, _ = spinner:build_layout() |
| 100 | + |
| 101 | + assert.is_falsy(content:find('openai/gpt-4', 1, true)) |
| 102 | + assert.is_falsy(content:find('·', 1, true)) |
| 103 | + |
| 104 | + for _, hl in ipairs(highlights) do |
| 105 | + assert.are_not.equal('OpencodeHint', hl.hl_group) |
| 106 | + assert.are_not.equal('OpencodeVariant', hl.hl_group) |
| 107 | + end |
| 108 | + |
| 109 | + config.ui.display_model = original |
| 110 | + end) |
| 111 | + end) |
| 112 | + |
| 113 | + describe('render', function() |
| 114 | + it('writes content and highlight extmarks to the float buffer', function() |
| 115 | + local spinner = CursorSpinner.new(buf, 0, 0, { model = 'openai/gpt-4' }) |
| 116 | + table.insert(spinners, spinner) |
| 117 | + |
| 118 | + local lines = vim.api.nvim_buf_get_lines(spinner.float_buf, 0, -1, false) |
| 119 | + assert.equals(1, #lines) |
| 120 | + assert.is_truthy(lines[1]:find('openai/gpt-4', 1, true)) |
| 121 | + |
| 122 | + local extmarks = vim.api.nvim_buf_get_extmarks(spinner.float_buf, spinner.ns_id, 0, -1, { details = true }) |
| 123 | + local hl_groups = {} |
| 124 | + for _, mark in ipairs(extmarks) do |
| 125 | + if mark[4] and mark[4].hl_group then |
| 126 | + table.insert(hl_groups, mark[4].hl_group) |
| 127 | + end |
| 128 | + end |
| 129 | + |
| 130 | + assert.is_true(vim.tbl_contains(hl_groups, 'OpencodeHint')) |
| 131 | + assert.is_true(vim.tbl_contains(hl_groups, 'WarningMsg')) |
| 132 | + end) |
| 133 | + end) |
| 134 | +end) |
0 commit comments