toggleterm.nvim/tests/terminal_spec.lua

474 lines
16 KiB
Lua

_G.IS_TEST = true
local api = vim.api
local fn = vim.fn
local fmt = string.format
local spy = require("luassert.spy")
local match = require("luassert.match")
local toggleterm = require("toggleterm")
local constants = require("toggleterm.constants")
local ui = require("toggleterm.ui")
local t = require("toggleterm.terminal")
---@type Terminal
local Terminal = t.Terminal
---@type fun(): Terminal[]
local get_all = t.get_all
---Return if a terminal has windows
---@param term table
---@return boolean, TerminalWindow[]
local function term_has_windows(term)
return ui.find_open_windows(function(buf) return buf == term.bufnr end)
end
describe("ToggleTerm tests:", function()
-- We must set hidden to use the plugin
vim.o.hidden = true
after_each(function() require("toggleterm.terminal").__reset() end)
describe("toggling terminals - ", function()
it("new terminals are assigned incremental ids", function()
local test1 = Terminal:new():toggle()
local test2 = Terminal:new():toggle()
local test3 = Terminal:new():toggle()
assert.are.same(test1.id, 1)
assert.are.same(test2.id, 2)
assert.are.same(test3.id, 3)
end)
it("should assign the next id filling in any missing gaps", function()
Terminal:new({ id = 2 }):toggle() --2
Terminal:new():toggle() --1
Terminal:new():toggle() --3
Terminal:new():toggle() --4
Terminal:new({ id = 6 }):toggle() --6
local terms = get_all()
terms[3]:shutdown()
terms[1]:shutdown()
local new1 = Terminal:new():toggle()
assert.equal(1, new1.id)
local new3 = Terminal:new():toggle()
assert.equal(3, new3.id)
local new5 = Terminal:new():toggle()
assert.equal(5, new5.id)
local new7 = Terminal:new():toggle()
assert.equal(7, new7.id)
end)
it("should get terminals as a list", function()
Terminal:new({ id = 20 }):toggle()
Terminal:new():toggle()
local terms = get_all()
assert.equal(#terms, 2)
assert.equal(terms[#terms].id, 20)
end)
it("should open a terminal window on toggle", function()
local test1 = Terminal:new()
test1:toggle()
assert.is_true(api.nvim_buf_is_valid(test1.bufnr))
assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window))
end)
it("should close a terminal window if open", function()
local test1 = Terminal:new()
test1:toggle()
assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window))
test1:toggle()
assert.is_not_true(vim.tbl_contains(api.nvim_list_wins(), test1.window))
end)
it("should toggle a specific buffer if a count is passed", function()
toggleterm.toggle(2, 15)
local terminals = get_all()
assert.equals(#terminals, 1)
local term = terminals[1]
assert.is_true(term_has_windows(term))
end)
it("should not list hidden terminals", function()
Terminal:new({ hidden = true }):toggle()
local terminals = get_all()
assert.equal(#terminals, 0)
Terminal:new():toggle()
terminals = get_all()
assert.equal(#terminals, 1)
end)
-- FIXME: this test does not work despite the functionality seeming to work
-- the idea here is that if a custom terminal with hidden = true is created
-- then it shouldn't be toggled open or closed if the general toggleterm command
-- is run so I expect to still see that it's window is open
pending("should not toggle a terminal if hidden", function()
local term = Terminal:new({ cmd = "bash", hidden = true }):toggle()
assert.is_true(term_has_windows(term))
toggleterm.toggle(1)
assert.is_true(term_has_windows(term))
end)
it("should not toggle a terminal if not hidden", function()
local term = Terminal:new():toggle()
assert.is_true(term_has_windows(term))
toggleterm.toggle(1)
assert.is_false(term_has_windows(term))
end)
it("should create a terminal with a custom command", function()
Terminal:new({ cmd = "ls" }):toggle()
assert.truthy(vim.b.term_title:match("ls"))
end)
-- FIXME: Fix flaky test
pending("should spawn in the background", function()
local stdout = {}
local has_spawned = function() return table.concat(stdout, ""):match("SPAWNED") ~= nil end
Terminal:new({
cmd = [[echo SPAWNED]],
on_stdout = function(_, _, lines) vim.list_extend(stdout, lines) end,
}):spawn()
-- Wait some time if job is not ready
vim.wait(1500, has_spawned)
assert.is_true(has_spawned())
end)
-- FIXME: Fix flaky test
pending("should pass environmental variables", function()
local stdout = {}
local expected = "TESTVAR = 0123456789"
local find_end = function() return table.concat(stdout, ""):match(expected) end
Terminal:new({
cmd = [[echo "TESTVAR = $TESTVAR END"]],
env = { TESTVAR = "0123456789" },
on_stdout = function(_, _, lines) vim.list_extend(stdout, lines) end,
}):toggle()
-- Wait some time if job is not ready
vim.wait(500, find_end)
assert.are.equal(expected, table.concat(stdout, " "):match("TESTVAR = %S+"))
end)
it("should open the correct terminal if a user specifies a count", function()
local term = Terminal:new({ count = 5 }):toggle()
term:toggle()
assert.is_false(ui.term_has_open_win(term))
toggleterm.toggle(5)
assert.is_true(ui.term_has_open_win(term))
end)
it("should open the last toggled terminal", function()
-- GIVEN two opened terminals
toggleterm.toggle(1)
toggleterm.toggle(2)
-- AND then closed first terminal
toggleterm.toggle(1)
-- AND then close second terminal via smart toggle
toggleterm.toggle(0)
-- WHEN a smart toggle is hit
toggleterm.toggle(0)
local terms = get_all()
--- THEN only the second terminal should be opened
assert.is_true(ui.term_has_open_win(terms[2]))
assert.is_false(ui.term_has_open_win(terms[1]))
end)
it("should open the last toggled terminal view", function()
-- GIVEN two opened terminals
toggleterm.toggle(1)
toggleterm.toggle(2)
get_all()[1]:focus()
-- AND then closed terminal view via smart toggle
toggleterm.toggle(0)
-- WHEN a smart toggle is hit
toggleterm.toggle(0)
--- THEN both terminals should be opened
local terms = get_all()
local term1 = terms[1]
local term2 = terms[2]
assert.is_true(term1:is_focused())
assert.is_false(term2:is_focused())
assert.is_true(ui.term_has_open_win(term2))
assert.is_true(ui.term_has_open_win(term1))
end)
it("should open the last toggled nth terminal", function()
-- GIVEN two opened terminals
toggleterm.toggle(1)
toggleterm.toggle(2)
-- AND then closed terminal view via smart toggle
toggleterm.toggle(0)
-- AND then open and close 3rd terminal
toggleterm.toggle(3)
toggleterm.toggle(3)
-- WHEN a smart toggle is hit
toggleterm.toggle(0)
local terms = get_all()
--- THEN only 3rd terminal should be opened
assert.is_true(ui.term_has_open_win(terms[3]))
assert.is_false(ui.term_has_open_win(terms[2]))
assert.is_false(ui.term_has_open_win(terms[1]))
end)
it("should open a hidden terminal and a visible one", function()
local hidden = Terminal:new({ hidden = true }):toggle()
local visible = Terminal:new():toggle()
hidden:toggle()
visible:toggle()
end)
it("should close all open terminals using toggle all", function()
local test1 = Terminal:new():toggle()
local test2 = Terminal:new():toggle()
toggleterm.toggle_all()
assert.is_false(ui.term_has_open_win(test1))
assert.is_false(ui.term_has_open_win(test2))
end)
it("should open all open terminals using toggle all", function()
local test1 = Terminal:new():toggle()
local test2 = Terminal:new():toggle()
toggleterm.toggle_all()
assert.is_false(ui.term_has_open_win(test1))
assert.is_false(ui.term_has_open_win(test2))
toggleterm.toggle_all()
assert.is_true(ui.term_has_open_win(test1))
assert.is_true(ui.term_has_open_win(test2))
end)
-- FIXME: broken in CI
-- it("should close on exit", function()
-- local term = Terminal:new():toggle()
-- assert.is_true(ui.term_has_open_win(term))
-- term:send("exit")
-- vim.wait(1000, function() end)
-- assert.is_false(ui.term_has_open_win(term))
-- end)
end)
describe("terminal buffers options - ", function()
before_each(
function()
toggleterm.setup({
open_mapping = [[<c-\>]],
shade_filetypes = { "none" },
direction = "horizontal",
float_opts = {
height = 10,
width = 20,
},
})
end
)
it("should give each terminal a winhighlight", function()
local test1 = Terminal:new({ direction = "horizontal" }):toggle()
assert.is_true(test1:is_split())
local winhighlight = vim.wo[test1.window].winhighlight
assert.is.truthy(winhighlight:match("Normal:ToggleTerm" .. test1.id .. "Normal"))
end)
it("should set the correct filetype", function()
local test1 = Terminal:new():toggle()
local ft = vim.bo[test1.bufnr].filetype
assert.equals(constants.FILETYPE, ft)
end)
end)
describe("executing commands - ", function()
it("should open a terminal to execute commands", function()
toggleterm.exec("ls", 1)
local terminals = get_all()
assert.is_true(#terminals == 1)
assert.is_true(term_has_windows(terminals[1]))
end)
it("should change terminal's directory if specified", function()
toggleterm.exec("ls", 1, 15, fn.expand("~/"))
local terminals = get_all()
assert.is_true(#terminals == 1)
assert.is_true(term_has_windows(terminals[1]))
end)
it("should send commands to a terminal on exec", function()
local test1 = Terminal:new():toggle()
local _ = match._
spy.on(test1, "send")
toggleterm.exec('echo "hello world"', 1)
assert.spy(test1.send).was_called()
assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true)
assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window))
end)
it("should send commands to a terminal without opening its window", function()
local test1 = Terminal:new():toggle()
test1:close()
spy.on(test1, "send")
toggleterm.exec_command("cmd='echo \"hello world\"' open=0", 1)
assert.spy(test1.send).was_called_with(test1, 'echo "hello world"', false)
assert.is_false(vim.tbl_contains(api.nvim_list_wins(), test1.window))
end)
it("should execute the same regardless whether shell is a string or a function", function()
toggleterm.setup({ shell = function() return vim.o.shell end })
local test1 = Terminal:new():toggle()
local _ = match._
spy.on(test1, "send")
toggleterm.exec('echo "hello world"', 1)
assert.spy(test1.send).was_called()
assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true)
assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window))
end)
it("should expand vim wildcards", function()
local file = vim.fn.tempname() .. ".txt"
vim.cmd(fmt("e %s", file))
local test1 = Terminal:new():toggle()
vim.cmd("wincmd w")
spy.on(test1, "send")
toggleterm.exec_command("cmd='echo %'", 1)
assert.spy(test1.send).was_called_with(test1, fmt("echo %s", file), true)
end)
it("should handle nested quotes in cmd args", function()
local file = vim.fn.tempname() .. ".txt"
vim.cmd(fmt("e %s", file))
local test1 = Terminal:new():toggle()
vim.cmd("wincmd w")
spy.on(test1, "send")
toggleterm.exec_command("cmd='g++ -std=c++17 % -o run'", 1)
assert.spy(test1.send).was_called_with(test1, fmt("g++ -std=c++17 %s -o run", file), true)
end)
end)
describe("terminal mappings behaviour", function()
it("should respect terminal_mappings in terminal mode", function()
toggleterm.setup({ open_mapping = [[<space>t]], terminal_mappings = false })
t.Terminal:new():toggle()
local result = vim.fn.mapcheck("<space>t", "t")
assert.equal("", result)
end)
it("should map in terminal mode if terminal_mappings is true", function()
toggleterm.setup({ open_mapping = [[<space>t]], terminal_mappings = true })
t.Terminal:new():toggle()
local result = vim.fn.mapcheck("<space>t", "t")
assert.is_true(#result > 0)
end)
end)
describe("layout options - ", function()
before_each(
function()
toggleterm.setup({
open_mapping = [[<c-\>]],
shade_filetypes = { "none" },
direction = "horizontal",
float_opts = {
height = 10,
width = 20,
},
})
end
)
it("should open with the correct layout", function()
local term = Terminal:new({ direction = "float" }):toggle()
local _, wins = term_has_windows(term)
assert.equal(#wins, 1)
assert.equal("popup", fn.win_gettype(fn.win_id2win(wins[1].window)))
end)
it("should not change numbers when resolving size", function()
local term = Terminal:new()
local size = 20
assert.equal(size, ui._resolve_size(size))
assert.equal(size, ui._resolve_size(size, term))
end)
it("should evaluate custom functions when resolving size", function()
local term = Terminal:new({ direction = "vertical" })
local size1 = 20
local size2 = function(_t)
if _t.direction == "vertical" then return size1 end
return 0
end
assert.equal(ui._resolve_size(size2, term), size1)
end)
it("should correctly merge a users highlights", function()
toggleterm.setup({
shade_terminals = false,
highlights = {
Normal = {
guibg = "Red",
},
},
})
local config = require("toggleterm.config").get("highlights")
assert.equal(config.Normal.guibg, "Red")
assert.truthy(config.StatusLine.guibg)
end)
it("should prefer shading over a users highlights if they opt to shade terminals", function()
toggleterm.setup({
shade_terminals = true,
highlights = {
Normal = {
guibg = "Red",
},
},
})
local config = require("toggleterm.config").get("highlights")
assert.is_not_equal(config.Normal.guibg, "Red")
assert.truthy(config.StatusLine.guibg)
end)
-- FIXME the height is passed in correctly but is returned as 15
-- which seems to be an nvim quirk not the code
it("should open with user configuration if set", function()
local term = Terminal:new({ direction = "float" }):toggle()
local _, wins = term_has_windows(term)
---@type table
local config = api.nvim_win_get_config(wins[1].window)
assert.equal(config.width, 20)
end)
it("should use a user's selected highlights", function()
local normal = "#000000"
local border = "#ffffff"
local term = Terminal:new({
direction = "float",
highlights = {
NormalFloat = {
guibg = normal,
},
FloatBorder = {
guifg = border,
},
},
float_opts = {
winblend = 12,
},
}):toggle()
local winhighlight = vim.wo[term.window].winhighlight
local winblend = vim.wo[term.window].winblend
assert.equal(12, winblend)
assert.is.truthy(winhighlight:match("NormalFloat:ToggleTerm" .. term.id .. "NormalFloat"))
assert.is.truthy(winhighlight:match("FloatBorder:ToggleTerm" .. term.id .. "FloatBorder"))
end)
end)
end)