tests: add statusline test module & update lualine_spec accordingly(#588)
* enhance: add statusline test module * update old tests to use statusline test setup * show diff in statusline:expect * Revert "show diff in statusline:expect" This reverts commit cea25185fde1b99110759f198ef1fc724626306c. * autoformat * fix testcov reports * update statusline test module * revert lualine_spec * update lualine_spec using statusline module * apply formater * add tabline support to statusline test module * re enable tabline tests * fix inconsistency in buffers mode test due to bufnr changing Should we be showing bufnr/buffer_position in buffers component * autoformat * minor tweeks to stl test module * use nvim_eval_statusline in nvim-0.6+ * fix tabs component on nvim-0.5 * enable disabled branch test
This commit is contained in:
parent
21fdf7b9fa
commit
747ef49b98
2
.luacov
2
.luacov
@ -15,6 +15,6 @@ return {
|
||||
},
|
||||
statsfile = 'luacov.stats',
|
||||
reportfile = 'luacov.report',
|
||||
includeuntestedfiles = true,
|
||||
-- includeuntestedfiles = true,
|
||||
}
|
||||
-- vim:ft=lua
|
||||
|
@ -19,7 +19,8 @@ end
|
||||
--- of the tab.
|
||||
---@return string
|
||||
function Tab:label()
|
||||
local custom_tabname = vim.t[self.tabId].tabname
|
||||
local ok,custom_tabname = pcall(vim.api.nvim_tabpage_get_var, self.tabId, 'tabname')
|
||||
if not ok then custom_tabname = nil end
|
||||
if custom_tabname and custom_tabname ~= '' then
|
||||
return modules.utils.stl_escape(custom_tabname)
|
||||
end
|
||||
|
@ -73,4 +73,27 @@ M.P = function(t)
|
||||
print(vim.inspect(t))
|
||||
end
|
||||
|
||||
function M.dedent(str, leave_indent)
|
||||
-- find minimum common indent across lines
|
||||
local indent = nil
|
||||
for line in str:gmatch('[^\n]+') do
|
||||
local line_indent = line:match('^%s+') or ''
|
||||
if indent == nil or #line_indent < #indent then
|
||||
indent = line_indent
|
||||
end
|
||||
end
|
||||
if indent == nil or #indent == 0 then
|
||||
-- no minimum common indent
|
||||
return str
|
||||
end
|
||||
local left_indent = (' '):rep(leave_indent or 0)
|
||||
-- create a pattern for the indent
|
||||
indent = indent:gsub('%s', '[ \t]')
|
||||
-- strip it from the first line
|
||||
str = str:gsub('^' .. indent, left_indent)
|
||||
-- strip it from the remaining lines
|
||||
str = str:gsub('[\n]' .. indent, '\n' .. left_indent)
|
||||
return str
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -568,7 +568,7 @@ describe('Branch component', function()
|
||||
local tmpdir
|
||||
local file
|
||||
local git = function(...)
|
||||
return vim.fn.system('git -C ' .. tmpdir .. ' ' .. string.format(...))
|
||||
return vim.fn.system("git -c user.name='asdf' -c user.email='asdf@jlk.org' -C " .. tmpdir .. ' ' .. string.format(...))
|
||||
end
|
||||
local assert_comp_ins = helpers.assert_component_instence
|
||||
|
||||
@ -604,20 +604,19 @@ describe('Branch component', function()
|
||||
assert_comp_ins(branch_comp, 'test_branch2')
|
||||
end)
|
||||
|
||||
-- TODO: Figure out why this test fails in CI
|
||||
-- it('works in detached head mode', function()
|
||||
-- local opts = build_component_opts {
|
||||
-- component_separators = { left = '', right = '' },
|
||||
-- icons_enabled = false,
|
||||
-- padding = 0,
|
||||
-- }
|
||||
-- git 'checkout -b test_branch2'
|
||||
-- git 'commit --allow-empty -m "test commit1"'
|
||||
-- git 'commit --allow-empty -m "test commit2"'
|
||||
-- git 'commit --allow-empty -m "test commit3"'
|
||||
-- git('checkout HEAD~1')
|
||||
-- vim.cmd('e ' .. file)
|
||||
-- local rev = git('rev-parse --short=6 HEAD'):sub(1, 6)
|
||||
-- assert_component('branch', opts, rev)
|
||||
-- end)
|
||||
it('works in detached head mode', function()
|
||||
local opts = build_component_opts {
|
||||
component_separators = { left = '', right = '' },
|
||||
icons_enabled = false,
|
||||
padding = 0,
|
||||
}
|
||||
git 'checkout -b test_branch2'
|
||||
git 'commit --allow-empty -m "test commit1"'
|
||||
git 'commit --allow-empty -m "test commit2"'
|
||||
git 'commit --allow-empty -m "test commit3"'
|
||||
git('checkout HEAD~1')
|
||||
vim.cmd('e ' .. file)
|
||||
local rev = git('rev-parse --short=6 HEAD'):sub(1, 6)
|
||||
assert_component('branch', opts, rev)
|
||||
end)
|
||||
end)
|
||||
|
File diff suppressed because it is too large
Load Diff
316
lua/tests/statusline.lua
Normal file
316
lua/tests/statusline.lua
Normal file
@ -0,0 +1,316 @@
|
||||
-- Copyright (c) 2020-2021 shadmansaleh
|
||||
-- MIT license, see LICENSE for more details.
|
||||
|
||||
--- ## Testing module for lualines statusline
|
||||
---
|
||||
--- ###Uses:
|
||||
---
|
||||
--- Create a new instence with status width 120 & for active statusline
|
||||
--- like following.
|
||||
---
|
||||
--- ``lua
|
||||
--- local statusline = require('tests.statusline').new(120, 'active')
|
||||
--- ```
|
||||
---
|
||||
--- To create a new instence with status width 80 & for inactive statusline use following.
|
||||
---
|
||||
--- ``lua
|
||||
--- local statusline = require('tests.statusline').new(120, 'inactive')
|
||||
--- ```
|
||||
---
|
||||
--- Now setup the state you want to test.
|
||||
--- To test you'll call `expect` pmethod on statusline for example.
|
||||
---
|
||||
--- To create a new instence with status width 80 & tabline
|
||||
---
|
||||
--- ``lua
|
||||
--- local statusline = require('tests.statusline').new(120, 'tabline')
|
||||
--- ```
|
||||
---
|
||||
--- Now setup the state you want to test.
|
||||
--- To test you'll call `expect` method on statusline for example.
|
||||
---
|
||||
--- ``lua
|
||||
--- statusline:expect([===[
|
||||
--- highlights = {
|
||||
--- 1: lualine_c_inactive = { bg = "#3c3836", fg = "#a89984" }
|
||||
--- }
|
||||
--- |{1: [No Name] }
|
||||
--- {1: }
|
||||
--- {1: 0:1 }|
|
||||
---
|
||||
---]===])
|
||||
--- ```
|
||||
---
|
||||
--- For more flexibility you can match a patten in expect block.
|
||||
--- ``lua
|
||||
--- statusline:expect([===[
|
||||
--- highlights = {
|
||||
--- 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" }
|
||||
--- 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" }
|
||||
--- 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" }
|
||||
--- 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
|
||||
--- 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" }
|
||||
--- }
|
||||
--- {MATCH:|{1: %d+ }}
|
||||
--- {MATCH:{1: %d+ }}
|
||||
--- {2:}
|
||||
--- {MATCH:{3: %d+ }}
|
||||
--- {4:}
|
||||
--- {MATCH:{5:%s+}|}
|
||||
---
|
||||
---]===])
|
||||
--- ```
|
||||
---
|
||||
--- An easy way to create an expect block is to call `snapshot` method
|
||||
--- on statusline where you'll call expect and run the test. It will print
|
||||
--- an expect block based on the state of statusline. You can copy it and
|
||||
--- replace the snapshot call with the expect call.
|
||||
---
|
||||
--- ``lua
|
||||
--- statusline:snapshot()
|
||||
--- ```
|
||||
|
||||
local ffi = require('ffi')
|
||||
local helpers = require('tests.helpers')
|
||||
local stub = require('luassert.stub')
|
||||
|
||||
local M = {}
|
||||
|
||||
ffi.cdef([[
|
||||
typedef unsigned char char_u;
|
||||
typedef struct window_S win_T;
|
||||
extern win_T *curwin;
|
||||
typedef struct {
|
||||
char_u *start;
|
||||
int userhl;
|
||||
} stl_hlrec_t;
|
||||
typedef struct {
|
||||
} StlClickDefinition;
|
||||
typedef struct {
|
||||
StlClickDefinition def;
|
||||
const char *start;
|
||||
} StlClickRecord;
|
||||
int build_stl_str_hl(
|
||||
win_T *wp,
|
||||
char_u *out,
|
||||
size_t outlen,
|
||||
char_u *fmt,
|
||||
int use_sandbox,
|
||||
char_u fillchar,
|
||||
int maxwidth,
|
||||
stl_hlrec_t **hltab,
|
||||
StlClickRecord **tabtab
|
||||
);
|
||||
]])
|
||||
|
||||
local function process_hlrec(hltab, stlbuf, eval_type)
|
||||
local function default_hl()
|
||||
if eval_type == 'tabline' then
|
||||
return "TabLineFill"
|
||||
elseif eval_type == 'inactive' then
|
||||
return "StatusLineNC"
|
||||
else
|
||||
return "StatusLine"
|
||||
end
|
||||
end
|
||||
local len = #ffi.string(stlbuf)
|
||||
local hltab_data = hltab[0]
|
||||
local result = {}
|
||||
if hltab_data[0].start ~= stlbuf then
|
||||
table.insert(result, {
|
||||
group = default_hl(),
|
||||
start = 0,
|
||||
})
|
||||
end
|
||||
|
||||
local n = 0
|
||||
while hltab_data[n].start ~= nil do
|
||||
local group_name
|
||||
if hltab_data[n].userhl == 0 then
|
||||
group_name = default_hl()
|
||||
elseif hltab_data[n].userhl < 0 then
|
||||
group_name = vim.fn.synIDattr(-1 * hltab_data[n].userhl, 'name')
|
||||
else
|
||||
group_name = string.format('User%d', hltab_data[n].userhl)
|
||||
end
|
||||
|
||||
local hl_pos = { group = group_name }
|
||||
|
||||
if n == 0 then
|
||||
hl_pos.start = hltab_data[n].start - stlbuf
|
||||
else
|
||||
hl_pos.start = result[#result].start + result[#result].len
|
||||
end
|
||||
if hltab_data[n + 1].start ~= nil then
|
||||
hl_pos.len = hltab_data[n + 1].start - hltab_data[n].start
|
||||
else
|
||||
hl_pos.len = (stlbuf + len) - hltab_data[n].start
|
||||
end
|
||||
table.insert(result, hl_pos)
|
||||
n = n + 1
|
||||
end
|
||||
return vim.tbl_filter(function(x)
|
||||
return x.len ~= 0
|
||||
end, result)
|
||||
end
|
||||
|
||||
local function gen_stl(stl_fmt, width, eval_type)
|
||||
local stlbuf = ffi.new('char_u [?]', width + 100)
|
||||
local fmt = ffi.cast('char_u *', stl_fmt)
|
||||
local fillchar = ffi.cast('char_u', 0x20)
|
||||
local hltab = ffi.new('stl_hlrec_t *[1]', ffi.new('stl_hlrec_t *'))
|
||||
ffi.C.build_stl_str_hl(ffi.C.curwin, stlbuf, width + 100, fmt, 0, fillchar, width, hltab, nil)
|
||||
return { str = ffi.string(stlbuf), highlights = process_hlrec(hltab, stlbuf, eval_type) }
|
||||
end
|
||||
|
||||
local function eval_stl(stl_expr, width, eval_type)
|
||||
local stl_buf, hl_list, stl_eval_res
|
||||
if vim.fn.has('nvim-0.6') == 1 then
|
||||
stl_eval_res = vim.api.nvim_eval_statusline(
|
||||
stl_expr,
|
||||
{ maxwidth = width, highlights = true, fillchar = ' ', use_tabline = (eval_type == 'tabline')}
|
||||
)
|
||||
else
|
||||
stl_eval_res = gen_stl(stl_expr, width, eval_type)
|
||||
end
|
||||
stl_buf, hl_list = stl_eval_res.str, stl_eval_res.highlights
|
||||
|
||||
local hl_map = {}
|
||||
|
||||
local buf = { 'highlights = {' }
|
||||
local hl_id = 1
|
||||
for _, hl in ipairs(hl_list) do
|
||||
local hl_name = hl.group
|
||||
if not hl_map[hl_name] then
|
||||
hl_map[hl_name] = require('lualine.utils.utils').extract_highlight_colors(hl_name) or {}
|
||||
table.insert(
|
||||
buf,
|
||||
string.format(' %4d: %s = %s', hl_id, hl_name, vim.inspect(hl_map[hl_name], { newline = ' ', indent = '' }))
|
||||
)
|
||||
hl_map[hl_name].id = hl_id
|
||||
hl_id = hl_id + 1
|
||||
end
|
||||
end
|
||||
table.insert(buf, '}')
|
||||
|
||||
local stl = {}
|
||||
for i = 1, #hl_list do
|
||||
local start, finish = hl_list[i].start, hl_list[i + 1] and hl_list[i + 1].start or #stl_buf
|
||||
if start ~= finish then
|
||||
table.insert(
|
||||
stl,
|
||||
string.format('{%d:%s}', hl_map[hl_list[i].group].id, vim.fn.strpart(stl_buf, start, finish - start))
|
||||
)
|
||||
end
|
||||
end
|
||||
table.insert(buf, '|' .. table.concat(stl, '\n') .. '|')
|
||||
table.insert(buf, '')
|
||||
return table.concat(buf, '\n')
|
||||
end
|
||||
|
||||
function M:expect_expr(expect, expr)
|
||||
expect = helpers.dedent(expect)
|
||||
local actual = eval_stl(expr, self.width, self.type)
|
||||
local matched = true
|
||||
local errmsg = {}
|
||||
if expect ~= actual then
|
||||
expect = vim.split(expect, '\n')
|
||||
actual = vim.split(actual, '\n')
|
||||
if expect[#expect] == '' then
|
||||
expect[#expect] = nil
|
||||
end
|
||||
if actual[#actual] == '' then
|
||||
actual[#actual] = nil
|
||||
end
|
||||
for i = 1, math.max(#expect, #actual) do
|
||||
if expect[i] and actual[i] then
|
||||
local match_pat = expect[i]:match('{MATCH:(.*)}')
|
||||
if expect[i] == actual[i] or (match_pat and actual[i]:match(match_pat)) then
|
||||
expect[i] = string.rep(' ', 2) .. expect[i]
|
||||
actual[i] = string.rep(' ', 2) .. actual[i]
|
||||
goto loop_end
|
||||
end
|
||||
end
|
||||
matched = false
|
||||
if expect[i] then
|
||||
expect[i] = '*' .. string.rep(' ', 1) .. expect[i]
|
||||
end
|
||||
if actual[i] then
|
||||
actual[i] = '*' .. string.rep(' ', 1) .. actual[i]
|
||||
end
|
||||
::loop_end::
|
||||
end
|
||||
end
|
||||
if not matched then
|
||||
table.insert(errmsg, 'Unexpected statusline')
|
||||
table.insert(errmsg, 'Expected:')
|
||||
table.insert(errmsg, table.concat(expect, '\n') .. '\n')
|
||||
table.insert(errmsg, 'Actual:')
|
||||
table.insert(errmsg, table.concat(actual, '\n'))
|
||||
end
|
||||
assert(matched, table.concat(errmsg, '\n'))
|
||||
end
|
||||
|
||||
function M:snapshot_expr(expr)
|
||||
local type_map = {
|
||||
active = 'statusline',
|
||||
inactive = 'inactive_statusline',
|
||||
tabline = 'tabline',
|
||||
}
|
||||
print((type_map[self.type] or 'statusline') .. ':expect([===[')
|
||||
print(eval_stl(expr, self.width, self.type) .. ']===])')
|
||||
end
|
||||
|
||||
function M:snapshot()
|
||||
local utils = require('lualine.utils.utils')
|
||||
stub(utils, 'is_focused')
|
||||
utils.is_focused.returns(self.type ~= 'inactive')
|
||||
local expr
|
||||
if self.type == 'inactive' then
|
||||
expr = require('lualine').statusline(false)
|
||||
elseif self.type == 'tabline' then
|
||||
expr = require('lualine').tabline()
|
||||
else
|
||||
expr = require('lualine').statusline(true)
|
||||
end
|
||||
self:snapshot_expr(expr)
|
||||
utils.is_focused:revert()
|
||||
end
|
||||
|
||||
function M:expect(result)
|
||||
local utils = require('lualine.utils.utils')
|
||||
stub(utils, 'is_focused')
|
||||
utils.is_focused.returns(self.type ~= 'inactive')
|
||||
local expr
|
||||
if self.type == 'inactive' then
|
||||
expr = require('lualine').statusline(false)
|
||||
elseif self.type == 'tabline' then
|
||||
expr = require('lualine').tabline()
|
||||
else
|
||||
expr = require('lualine').statusline(true)
|
||||
end
|
||||
self:expect_expr(result, expr)
|
||||
utils.is_focused:revert()
|
||||
end
|
||||
|
||||
function M.new(_, width, eval_type)
|
||||
if type(_) ~= 'table' then
|
||||
eval_type = width
|
||||
width = _
|
||||
end
|
||||
local self = {}
|
||||
self.width = width or 120
|
||||
self.type = eval_type
|
||||
if self.type == nil then
|
||||
self.type = 'active'
|
||||
end
|
||||
return setmetatable(self, {
|
||||
__index = M,
|
||||
__call = function(_, ...)
|
||||
M.new(...)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return M.new()
|
Loading…
x
Reference in New Issue
Block a user