1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-04 00:50:04 +08:00
SpaceVim/bundle/nui.nvim/tests/nui/tree/init_spec.lua
2023-05-30 21:09:18 +08:00

896 lines
20 KiB
Lua
Vendored

pcall(require, "luacov")
local Text = require("nui.text")
local Tree = require("nui.tree")
local h = require("tests.helpers")
local eq = h.eq
describe("nui.tree", function()
local winid, bufnr
before_each(function()
winid = vim.api.nvim_get_current_win()
bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_win_set_buf(winid, bufnr)
end)
after_each(function()
vim.api.nvim_buf_delete(bufnr, { force = true })
end)
describe("(#deprecated) o.winid", function()
it("throws if missing", function()
local ok, err = pcall(Tree, {})
eq(ok, false)
eq(type(string.match(err, "missing bufnr")), "string")
end)
it("throws if invalid", function()
local ok, err = pcall(Tree, { winid = 999 })
eq(ok, false)
eq(type(string.match(err, "invalid winid ")), "string")
end)
it("sets t.winid and t.bufnr properly", function()
local tree = Tree({ winid = winid })
eq(tree.winid, winid)
eq(tree.bufnr, bufnr)
end)
end)
describe("o.bufnr", function()
it("throws if missing", function()
local ok, err = pcall(Tree, {})
eq(ok, false)
eq(type(string.match(err, "missing bufnr")), "string")
end)
it("throws if invalid", function()
local ok, err = pcall(Tree, { bufnr = 999 })
eq(ok, false)
eq(type(string.match(err, "invalid bufnr ")), "string")
end)
it("sets t.bufnr properly", function()
local tree = Tree({ bufnr = bufnr })
eq(tree.winid, nil)
eq(tree.bufnr, bufnr)
end)
end)
it("throws on duplicated node id", function()
local ok, err = pcall(Tree, {
bufnr = bufnr,
nodes = {
Tree.Node({ id = "id", text = "text" }),
Tree.Node({ id = "id", text = "text" }),
},
})
eq(ok, false)
eq(type(err), "string")
end)
it("sets default buf options emulating scratch-buffer", function()
local tree = Tree({ bufnr = bufnr })
h.assert_buf_options(tree.bufnr, {
bufhidden = "hide",
buflisted = false,
buftype = "nofile",
swapfile = false,
})
end)
describe("(#deprecated) o.win_options", function()
it("sets default values for handling folds", function()
local tree = Tree({ winid = winid })
h.assert_win_options(tree.winid, {
foldmethod = "manual",
foldcolumn = "0",
wrap = false,
})
end)
it("sets values", function()
local initial_statusline = vim.api.nvim_win_get_option(winid, "statusline")
local statusline = "test: win_options " .. math.random()
local tree = Tree({
winid = winid,
win_options = {
statusline = statusline,
},
})
h.assert_win_options(tree.winid, {
statusline = statusline,
})
vim.api.nvim_win_set_option(tree.winid, "statusline", initial_statusline)
end)
it("has no effect if o.bufnr is present", function()
local initial_statusline = vim.api.nvim_win_get_option(winid, "statusline")
Tree({
bufnr = bufnr,
win_options = {
statusline = "test: win_options" .. math.random(),
},
})
h.assert_win_options(winid, {
statusline = initial_statusline,
})
end)
end)
it("sets t.ns_id if o.ns_id is string", function()
local ns = "NuiTreeTest"
local tree = Tree({ bufnr = bufnr, ns_id = ns })
local namespaces = vim.api.nvim_get_namespaces()
eq(tree.ns_id, namespaces[ns])
end)
it("sets t.ns_id if o.ns_id is number", function()
local ns = "NuiTreeTest"
local ns_id = vim.api.nvim_create_namespace(ns)
local tree = Tree({ bufnr = bufnr, ns_id = ns_id })
eq(tree.ns_id, ns_id)
end)
it("uses o.get_node_id if provided", function()
local node_d2 = Tree.Node({ key = "depth two" })
local node_d1 = Tree.Node({ key = "depth one" }, { node_d2 })
Tree({
bufnr = bufnr,
nodes = { node_d1 },
get_node_id = function(node)
return node.key
end,
})
eq(node_d1:get_id(), node_d1.key)
eq(node_d2:get_id(), node_d2.key)
end)
describe("default get_node_id", function()
it("returns id using n.id", function()
local node = Tree.Node({ id = "id", text = "text" })
Tree({ bufnr = bufnr, nodes = { node } })
eq(node:get_id(), "-id")
end)
it("returns id using parent_id + depth + n.text", function()
local node_d2 = Tree.Node({ text = { "depth two a", Text("depth two b") } })
local node_d1 = Tree.Node({ text = "depth one" }, { node_d2 })
Tree({ bufnr = bufnr, nodes = { node_d1 } })
eq(node_d1:get_id(), string.format("-%s-%s", node_d1:get_depth(), node_d1.text))
eq(
node_d2:get_id(),
string.format(
"%s-%s-%s",
node_d2:get_parent_id(),
node_d2:get_depth(),
table.concat({ node_d2.text[1], node_d2.text[2]:content() }, "-")
)
)
end)
it("returns id using random number", function()
math.randomseed(0)
local expected_id = "-" .. math.random()
math.randomseed(0)
local node = Tree.Node({})
Tree({ bufnr = bufnr, nodes = { node } })
eq(node:get_id(), expected_id)
end)
end)
it("uses o.prepare_node if provided", function()
local function prepare_node(node, parent_node)
if not parent_node then
return node.text
end
return parent_node.text .. ":" .. node.text
end
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
Tree.Node({ text = "b-2" }),
}),
Tree.Node({ text = "c" }),
}
nodes[2]:expand()
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
prepare_node = prepare_node,
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
"a",
"b",
"b:b-1",
"b:b-2",
"c",
})
end)
describe("default prepare_node", function()
it("throws if missing n.text", function()
local nodes = {
Tree.Node({ txt = "a" }),
Tree.Node({ txt = "b" }),
Tree.Node({ txt = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
})
local ok, err = pcall(tree.render, tree)
eq(ok, false)
eq(type(err), "string")
end)
it("uses n.text", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = { "b-1", "b-2" } }),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b-1",
" b-2",
" c",
})
end)
it("renders arrow if children are present", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
Tree.Node({ text = { "b-2", "b-3" } }),
}),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
" c",
})
nodes[2]:expand()
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
" b-1",
" b-2",
" b-3",
" c",
})
end)
end)
describe("method :get_node", function()
it("can get node under cursor", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
})
tree:render()
local linenr = 3
vim.api.nvim_win_set_cursor(winid, { linenr, 0 })
eq({ tree:get_node() }, { nodes[3], linenr, linenr })
end)
it("can get node with id", function()
local b_node_children = {
Tree.Node({ text = "b-1" }),
Tree.Node({ text = { "b-2", "b-3" } }),
}
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, b_node_children),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return type(node.text) == "table" and table.concat(node.text, "-") or node.text
end,
})
tree:render()
eq({ tree:get_node("b") }, { nodes[2], 2, 2 })
tree:get_node("b"):expand()
tree:render()
eq({ tree:get_node("b-2-b-3") }, { b_node_children[2], 4, 5 })
end)
it("can get node on linenr", function()
local b_node_children = {
Tree.Node({ id = "b-1-b-2", text = { "b-1", "b-2" } }),
}
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, b_node_children),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
})
tree:render()
eq({ tree:get_node(1) }, { nodes[1], 1, 1 })
tree:get_node(2):expand()
tree:render()
eq({ tree:get_node(3) }, { b_node_children[1], 3, 4 })
eq({ tree:get_node(4) }, { b_node_children[1], 3, 4 })
end)
end)
describe("method :get_nodes", function()
it("can get nodes at root", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
}),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
eq(tree:get_nodes(), nodes)
end)
it("can get nodes under parent node", function()
local child_nodes = {
Tree.Node({ text = "b-1" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, child_nodes),
},
get_node_id = function(node)
return node.text
end,
})
eq(tree:get_nodes("b"), child_nodes)
end)
end)
describe("method :add_node", function()
it("throw if invalid parent_id", function()
local tree = Tree({
bufnr = bufnr,
nodes = {
Tree.Node({ text = "x" }),
},
})
local ok, err = pcall(tree.add_node, tree, Tree.Node({ text = "y" }), "invalid_parent_id")
eq(ok, false)
eq(type(err), "string")
end)
it("can add node at root", function()
local tree = Tree({
bufnr = bufnr,
nodes = {
Tree.Node({ text = "x" }),
},
})
tree:add_node(Tree.Node({ text = "y" }))
tree:render()
h.assert_buf_lines(tree.bufnr, {
" x",
" y",
})
tree:add_node(Tree.Node({ text = "z" }))
tree:render()
h.assert_buf_lines(tree.bufnr, {
" x",
" y",
" z",
})
end)
it("can add node under parent node", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
}),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
tree:add_node(Tree.Node({ text = "b-2" }), "b")
tree:get_node("b"):expand()
tree:add_node(Tree.Node({ text = "c-1" }), "c")
tree:get_node("c"):expand()
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
" b-1",
" b-2",
" c",
" c-1",
})
end)
end)
describe("method :set_nodes", function()
it("throw if invalid parent_id", function()
local tree = Tree({
bufnr = bufnr,
nodes = {
Tree.Node({ text = "x" }),
},
})
local ok, err = pcall(tree.set_nodes, tree, {}, "invalid_parent_id")
eq(ok, false)
eq(type(err), "string")
end)
it("can set nodes at root", function()
local tree = Tree({
bufnr = bufnr,
nodes = {
Tree.Node({ text = "x" }),
},
})
tree:set_nodes({
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }),
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
})
tree:set_nodes({
Tree.Node({ text = "c" }),
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
" c",
})
end)
it("can set nodes under parent node", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
}),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
tree:set_nodes({
Tree.Node({ text = "b-2" }),
}, "b")
tree:get_node("b"):expand()
tree:set_nodes({
Tree.Node({ text = "c-1" }),
Tree.Node({ text = "c-2" }),
}, "c")
tree:get_node("c"):expand()
tree:render()
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
" b-2",
" c",
" c-1",
" c-2",
})
end)
end)
describe("method :remove_node", function()
it("can remove node w/o parent", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
}),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
tree:remove_node("a")
tree:get_node("b"):expand()
tree:render()
eq(
vim.tbl_map(function(node)
return node:get_id()
end, tree:get_nodes()),
{ "b", "c" }
)
h.assert_buf_lines(tree.bufnr, {
" b",
" b-1",
" c",
})
end)
it("can remove node w/ parent", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, {
Tree.Node({ text = "b-1" }),
}),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
tree:remove_node("b-1")
tree:render()
eq(tree:get_node("b"):get_child_ids(), {})
h.assert_buf_lines(tree.bufnr, {
" a",
" b",
" c",
})
end)
it("removes children nodes recursively", function()
local nodes = {
Tree.Node({ text = "a" }, {
Tree.Node({ text = "a-1" }, {
Tree.Node({ text = "a-1-x" }),
}),
}),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
h.neq(tree:get_node("a"), nil)
h.neq(tree:get_node("a-1"), nil)
h.neq(tree:get_node("a-1-x"), nil)
tree:remove_node("a")
eq(tree:get_node("a"), nil)
eq(tree:get_node("a-1"), nil)
eq(tree:get_node("a-1-x"), nil)
end)
end)
describe("method :render", function()
it("handles unexpected case of missing node", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
-- this should not happen normally
tree.nodes.by_id["a"] = nil
tree:render()
h.assert_buf_lines(tree.bufnr, {
" b",
" c",
})
end)
it("skips node if o.prepare_node returns nil", function()
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }),
Tree.Node({ text = "c" }),
}
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
prepare_node = function(node)
if node:get_id() == "b" then
return nil
end
return node.text
end,
})
tree:render()
h.assert_buf_lines(tree.bufnr, {
"a",
"c",
})
end)
it("supports param linenr_start", function()
local b_node_children = {
Tree.Node({ text = "b-1" }),
Tree.Node({ text = "b-2" }),
}
local nodes = {
Tree.Node({ text = "a" }),
Tree.Node({ text = "b" }, b_node_children),
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
"NuiTreeTest",
"",
"NuiTreeTest",
})
local tree = Tree({
bufnr = bufnr,
nodes = nodes,
get_node_id = function(node)
return node.text
end,
})
tree:render(2)
h.assert_buf_lines(tree.bufnr, {
"NuiTreeTest",
" a",
" b",
"NuiTreeTest",
})
nodes[2]:expand()
tree:render()
h.assert_buf_lines(tree.bufnr, {
"NuiTreeTest",
" a",
" b",
" b-1",
" b-2",
"NuiTreeTest",
})
nodes[2]:collapse()
tree:render(3)
h.assert_buf_lines(tree.bufnr, {
"NuiTreeTest",
"",
" a",
" b",
"NuiTreeTest",
})
end)
end)
end)
describe("nui.tree.Node", function()
describe("method :has_children", function()
it("works before initialization", function()
local node_wo_children = Tree.Node({ text = "a" })
local node_w_children = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
eq(node_wo_children._initialized, false)
eq(node_wo_children:has_children(), false)
eq(node_w_children._initialized, false)
eq(type(node_w_children.__children), "table")
eq(node_w_children:has_children(), true)
end)
it("works after initialization", function()
local node_wo_children = Tree.Node({ text = "a" })
local node_w_children = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
Tree({
bufnr = vim.api.nvim_win_get_buf(vim.api.nvim_get_current_win()),
nodes = { node_wo_children, node_w_children },
})
eq(node_wo_children._initialized, true)
eq(node_wo_children:has_children(), false)
eq(node_w_children._initialized, true)
eq(type(node_w_children.__children), "nil")
eq(node_w_children:has_children(), true)
end)
end)
describe("method :expand", function()
it("returns true if not already expanded", function()
local node = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
eq(node:is_expanded(), false)
eq(node:expand(), true)
eq(node:is_expanded(), true)
end)
it("returns false if already expanded", function()
local node = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
node:expand()
eq(node:is_expanded(), true)
eq(node:expand(), false)
eq(node:is_expanded(), true)
end)
it("does not work w/o children", function()
local node = Tree.Node({ text = "a" })
eq(node:is_expanded(), false)
eq(node:expand(), false)
eq(node:is_expanded(), false)
end)
end)
describe("method :collapse", function()
it("returns true if not already collapsed", function()
local node = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
node:expand()
eq(node:is_expanded(), true)
eq(node:collapse(), true)
eq(node:is_expanded(), false)
end)
it("returns false if already collapsed", function()
local node = Tree.Node({ text = "b" }, { Tree.Node({ text = "b-1" }) })
eq(node:is_expanded(), false)
eq(node:collapse(), false)
eq(node:is_expanded(), false)
end)
it("does not work w/o children", function()
local node = Tree.Node({ text = "a" })
eq(node:is_expanded(), false)
eq(node:collapse(), false)
eq(node:is_expanded(), false)
end)
end)
end)