Skip to content

Commit

Permalink
fix(output_panel): recreate window and term channel if panel closed (#…
Browse files Browse the repository at this point in the history
…477)

manually
  • Loading branch information
YaroSpace authored Dec 27, 2024
1 parent 5cd797d commit 55aedb9
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 3 deletions.
13 changes: 11 additions & 2 deletions lua/neotest/consumers/output_panel/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ local init = function(client)
if partial then
return
end
if not chan then

local channel_is_valid = function(chan_id)
return chan_id and pcall(vim.api.nvim_chan_send, chan_id, "\n")
end

if not channel_is_valid(chan) then
chan = lib.ui.open_term(panel.win:buffer())
-- neovim sometimes adds random blank lines when creating a terminal buffer
nio.api.nvim_buf_set_option(panel.win:buffer(), "modifiable", true)
Expand All @@ -50,7 +55,11 @@ local init = function(client)
for file, _ in pairs(files_to_read) do
local output = lib.files.read(file)
local dos_newlines = string.find(output, "\r\n") ~= nil
nio.api.nvim_chan_send(chan, dos_newlines and output or output:gsub("\n", "\r\n"))
if not pcall(nio.api.nvim_chan_send, chan, dos_newlines and output or output:gsub("\n", "\r\n")) then
lib.notify(("Error sending output to term channel: %s"):format(chan), vim.log.levels.ERROR)
chan = nil
break
end
end
end
end
Expand Down
12 changes: 11 additions & 1 deletion lua/neotest/lib/window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,20 @@ function PersistentWindow:open()
end

function PersistentWindow:buffer()
if self._bufnr and vim.fn.bufexists(self._bufnr) == 1 then
if
self._bufnr
and nio.api.nvim_buf_is_valid(self._bufnr)
and nio.api.nvim_buf_is_loaded(self._bufnr)
then
return self._bufnr
end

for _, bufnr in ipairs(nio.api.nvim_list_bufs()) do
if nio.api.nvim_buf_get_name(bufnr):find(self.name, 1, true) then
nio.api.nvim_buf_delete(bufnr, { force = true })
end
end

self._bufnr = nio.api.nvim_create_buf(false, true)
nio.api.nvim_buf_set_name(self._bufnr, self.name)
for k, v in pairs(self._bufopts) do
Expand Down
192 changes: 192 additions & 0 deletions tests/unit/consumers/output_panel_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
local neotest = require("neotest")

local nio = require("nio")
local a = nio.tests

local stub = require("luassert.stub")
local Tree = require("neotest.types").Tree
local lib = require("neotest.lib")

local NeotestClient = require("neotest.client")
local AdapterGroup = require("neotest.adapters")

describe("neotest consumer - output_panel", function()
---@type neotest.Client
local client

---@type neotest.Adapter
local mock_adapter
local mock_strategy
local exit_future_1, exit_future_2

local dir = vim.loop.cwd()
local files
local dirs = { dir }
local notify
local notify_msg = ''

---@return neotest.Tree
local get_pos = function(...)
---@diagnostic disable-next-line
return client:get_position(...)
end

before_each(function()
dirs = { dir }
files = { dir .. "/test_file_1", dir .. "/test_file_2" }

notify = function(message, level)
notify_msg = message
end

stub(lib, "notify", notify)
stub(lib.files, "find", files)
stub(lib.files, "read", "Test results - passed and failed\r\n")
stub(lib.files, "is_dir", function(path)
return vim.tbl_contains(dirs, path)
end)
stub(lib.files, "exists", function(path)
return path ~= ""
end)

exit_future_1, exit_future_2 = nio.control.future(), nio.control.future()

---@diagnostic disable-next-line: missing-fields
mock_adapter = {
name = "adapter",

is_test_file = function(file_path)
return file_path ~= "" and not vim.endswith(file_path, lib.files.sep)
end,

root = function()
return dir
end,

discover_positions = function(file_path)
return Tree.from_list({
{ id = file_path, type = "file", path = file_path, name = file_path },
{
{
id = file_path .. "::namespace",
type = "namespace",
path = file_path,
name = "namespace",
range = { 5, 0, 50, 0 },
},
{
id = file_path .. "::test_a",
type = "test",
path = file_path,
name = "test_a",
range = { 10, 0, 20, 50 },
},
},
}, function(pos)
return pos.id
end)
end,

build_spec = function()
return { strategy = { output = "not_a_file" } }
end,

results = function(_, _, tree)
return {}
end,
}

mock_strategy = function(spec)
return {
is_complete = function()
return true
end,

output = function()
return type(spec.strategy) == "table" and spec.strategy.output or "not_a_file"
end,

stop = function()
exit_future_1.set()
exit_future_2.set()
end,

result = function()
if not exit_future_1.is_set() then
exit_future_1.wait()
else
exit_future_2.wait()
end
return type(spec.strategy) == "table" and spec.strategy.exit_code or 0
end,
}
end

client = NeotestClient(AdapterGroup())
---@diagnostic disable-next-line
neotest.setup({ adapters = { mock_adapter }, output_panel = { enabled = true } })

require("neotest.consumers.output_panel")(client)
---@diagnostic disable-next-line
client.listeners.results = { output_panel = client.listeners.results }
end)

after_each(function()
lib.files.find:revert()
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
vim.api.nvim_buf_delete(buf, { force = true })
end
notify_msg = ''
end)

describe("user forcefully closes the panel", function()
local panel_bufnr = function()
return vim.tbl_filter(function(bufnr)
return nio.api.nvim_buf_get_name(bufnr):match("Neotest Output Panel")
end, nio.api.nvim_list_bufs())[1]
end

before_each(function()
neotest.output_panel.open()
end)

a.it("recreates terminal session if term channel is invalid", function()
local tree = get_pos(dir .. "/test_file_1")

nio.run(function()
client:run_tree(tree, { strategy = mock_strategy })
end)
exit_future_1.set()

nio.api.nvim_buf_delete(panel_bufnr(), { force = true })
neotest.output_panel.open()

nio.run(function()
assert.has_no_error(function()
client:run_tree(tree, { strategy = mock_strategy })
end)
assert.is_not.matches('Error sending output to term channel:', notify_msg)
end)
exit_future_2.set()
end)

it("recreates panel buffer if it was closed", function()
vim.api.nvim_buf_delete(panel_bufnr(), { force = true })

assert.has_no_error(function()
neotest.output_panel.open()
end)
end)

it("deletes panel buffer if it already exists with the same name", function()
vim.api.nvim_buf_delete(panel_bufnr(), { force = true })

local buf = vim.api.nvim_create_buf(true, false)
vim.api.nvim_buf_set_name(buf, "Neotest Output Panel")

assert.has_no_error(function()
neotest.output_panel.open()
end)
end)
end)
end)

0 comments on commit 55aedb9

Please sign in to comment.