Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ jobs:
uses: lunarmodules/luacheck@v1
with:
args: lua/

- name: Install Neovim
run: |
sudo add-apt-repository ppa:neovim-ppa/unstable -y
sudo apt-get update
sudo apt-get install -y neovim

- name: Run headless tests
run: make test
10 changes: 9 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ Plugins register binaries via `require("glaze").register(name, url, opts)`. Regi

## Testing

**No automated tests exist.** Manual testing workflow:
Automated checks:

```bash
make format # apply StyLua
make lint # run luacheck
make test # headless Neovim regression tests
```

Manual testing workflow:

```lua
-- In Neovim:
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
.PHONY: demo clean
.PHONY: demo clean format lint test

PLUGIN_PATH := $(shell pwd)

format:
stylua .

lint:
luacheck lua/

test:
nvim --headless -u NONE -c "lua dofile('tests/run.lua')" -c qa

demo:
@echo "Recording demo.gif..."
PLUGIN_PATH=$(PLUGIN_PATH) vhs docs/demo.tape
Expand Down
17 changes: 13 additions & 4 deletions lua/glaze/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ M._auto_install_queue = {}
---@type number? Timer for batched auto-install
M._auto_install_timer = nil

---@param name string
---@param fn function
---@param opts table
local function ensure_user_command(name, fn, opts)
if vim.fn.exists(":" .. name) ~= 2 then
vim.api.nvim_create_user_command(name, fn, opts)
end
end

---@param opts? GlazeConfig
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", M.config, opts or {})
Expand All @@ -102,11 +111,11 @@ function M.setup(opts)
end

-- Create commands
vim.api.nvim_create_user_command("Glaze", function()
ensure_user_command("Glaze", function()
require("glaze.view").open()
end, { desc = "Open Glaze UI" })

vim.api.nvim_create_user_command("GlazeUpdate", function(cmd)
ensure_user_command("GlazeUpdate", function(cmd)
if cmd.args and cmd.args ~= "" then
require("glaze.runner").update({ cmd.args })
else
Expand All @@ -120,7 +129,7 @@ function M.setup(opts)
end,
})

vim.api.nvim_create_user_command("GlazeInstall", function(cmd)
ensure_user_command("GlazeInstall", function(cmd)
if cmd.args and cmd.args ~= "" then
require("glaze.runner").install({ cmd.args })
else
Expand All @@ -134,7 +143,7 @@ function M.setup(opts)
end,
})

vim.api.nvim_create_user_command("GlazeCheck", function()
ensure_user_command("GlazeCheck", function()
require("glaze.checker").check()
end, { desc = "Check for binary updates" })

Expand Down
117 changes: 117 additions & 0 deletions tests/run.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package.path = table.concat({
"./lua/?.lua",
"./lua/?/init.lua",
package.path,
}, ";")

local function assert_eq(actual, expected, message)
if actual ~= expected then
error((message or "assertion failed") .. ": expected " .. vim.inspect(expected) .. ", got " .. vim.inspect(actual))
end
end

local function assert_truthy(value, message)
if not value then
error(message or "expected truthy value")
end
end

local results = {}
local function test(name, fn)
table.insert(results, { name = name, fn = fn })
end

local function reset_glaze()
for _, module in ipairs({
"glaze",
"glaze.init",
"glaze.runner",
"glaze.checker",
"glaze.view",
"glaze.float",
"glaze.text",
"glaze.colors",
"glaze.health",
}) do
package.loaded[module] = nil
end

for _, command in ipairs({ "Glaze", "GlazeUpdate", "GlazeInstall", "GlazeCheck" }) do
pcall(vim.api.nvim_del_user_command, command)
end

return require("glaze")
end

test("setup is idempotent and preserves updated config", function()
local glaze = reset_glaze()

glaze.setup({ auto_check = { enabled = false }, concurrency = 2 })
glaze.setup({ auto_check = { enabled = false }, concurrency = 7 })

assert_eq(glaze.config.concurrency, 7, "second setup should update config")
assert_truthy(vim.fn.exists(":Glaze") == 2, "Glaze command should exist")
assert_truthy(vim.fn.exists(":GlazeUpdate") == 2, "GlazeUpdate command should exist")
assert_truthy(vim.fn.exists(":GlazeInstall") == 2, "GlazeInstall command should exist")
assert_truthy(vim.fn.exists(":GlazeCheck") == 2, "GlazeCheck command should exist")
end)

test("register batches auto-install requests into one install call", function()
local glaze = reset_glaze()
local runner = require("glaze.runner")
local original_install = runner.install
local install_calls = {}

runner.install = function(names, opts)
table.insert(install_calls, {
names = vim.deepcopy(names),
opts = vim.deepcopy(opts),
})
end

glaze.setup({
auto_check = { enabled = false },
auto_install = { enabled = true, silent = true },
})

glaze.is_installed = function()
return false
end

glaze.register("freeze", "github.com/charmbracelet/freeze", { plugin = "freeze.nvim" })
glaze.register("glow", "github.com/charmbracelet/glow", { plugin = "glow.nvim" })
vim.wait(350)

assert_eq(#install_calls, 1, "auto-install should batch into one runner call")
table.sort(install_calls[1].names)
assert_eq(install_calls[1].names[1], "freeze")
assert_eq(install_calls[1].names[2], "glow")
assert_eq(install_calls[1].opts.silent, true, "batched install should preserve silent option")

runner.install = original_install
end)

test("register merges duplicate plugin registrations without duplicates", function()
local glaze = reset_glaze()

glaze.setup({ auto_check = { enabled = false }, auto_install = { enabled = false } })
glaze.register("freeze", "github.com/charmbracelet/freeze", { plugin = "freeze.nvim" })
glaze.register("freeze", "github.com/charmbracelet/freeze", { plugin = "freeze.nvim" })
glaze.register("freeze", "github.com/charmbracelet/freeze", { plugin = "blast.nvim" })

local binary = glaze.binaries().freeze
assert_truthy(binary ~= nil, "binary should be registered")
table.sort(binary.plugins)
assert_eq(#binary.plugins, 2, "plugins list should stay deduplicated")
assert_eq(binary.plugins[1], "blast.nvim")
assert_eq(binary.plugins[2], "freeze.nvim")
end)

for _, case in ipairs(results) do
local ok, err = pcall(case.fn)
if not ok then
io.stderr:write("FAIL: " .. case.name .. "\n" .. tostring(err) .. "\n")
os.exit(1)
end
print("PASS: " .. case.name)
end
Loading