From 37cad7129135412dbfcf3c57ba0a17e4d3e6c8da Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sun, 16 Jun 2024 16:42:38 +0100 Subject: [PATCH] test: move the suite over to busted Summary: Right now we are using a custom test runner. This move the suite over to busted, this will make things much more maintainable going forward. The two main reasons for moving are. 1) The custom runner as some bugs, when running assertions we are not getting the correct results. 2) All of the busted mocking features. We can use spy and mock, this will allow us to remove the nvim_mock. This file is not amazing and confuses the lsp often. Test Plan: CI --- .luacheckrc | 5 +- lua/ivy/controller_spec.lua | 57 +++++++++++++++++ lua/ivy/libivy_spec.lua | 36 +++++++++++ lua/ivy/matcher_spec.lua | 29 +++++++++ lua/ivy/prompt_spec.lua | 91 +++++++++++++++++++++++++++ lua/ivy/utils.lua | 4 +- lua/ivy/utils_escape_spec.lua | 11 ++++ lua/ivy/utils_line_action_spec.lua | 28 +++++++++ lua/ivy/utils_vimgrep_action_spec.lua | 56 +++++++++++++++++ lua/ivy/window_spec.lua | 32 ++++++++++ scripts/busted.lua | 8 +++ 11 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 lua/ivy/controller_spec.lua create mode 100644 lua/ivy/libivy_spec.lua create mode 100644 lua/ivy/matcher_spec.lua create mode 100644 lua/ivy/prompt_spec.lua create mode 100644 lua/ivy/utils_escape_spec.lua create mode 100644 lua/ivy/utils_line_action_spec.lua create mode 100644 lua/ivy/utils_vimgrep_action_spec.lua create mode 100644 lua/ivy/window_spec.lua create mode 100755 scripts/busted.lua diff --git a/.luacheckrc b/.luacheckrc index 337ac8e..2d46e75 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -10,9 +10,12 @@ self = false read_globals = { "vim", - "it", "after", "after_each", + "assert", "before", "before_each", + "describe", + "it", + "spy", } diff --git a/lua/ivy/controller_spec.lua b/lua/ivy/controller_spec.lua new file mode 100644 index 0000000..6556cee --- /dev/null +++ b/lua/ivy/controller_spec.lua @@ -0,0 +1,57 @@ +local window = require "ivy.window" +local controller = require "ivy.controller" + +describe("controller", function() + before_each(function() + vim.cmd "highlight IvyMatch cterm=bold gui=bold" + window.initialize() + end) + + after_each(function() + controller.destroy() + end) + + it("will run the completion", function() + controller.run("Testing", function() + return { { content = "Some content" } } + end, function() + return {} + end) + + -- Run all the scheduled tasks + vim.wait(0) + + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true) + assert.is_equal(#lines, 1) + assert.is_equal(lines[1], "Some content") + end) + + it("will not try and highlight the buffer if there is nothing to highlight", function() + spy.on(vim, "cmd") + + controller.items = function() + return { { content = "Hello" } } + end + + controller.update "" + + vim.wait(0) + + assert.spy(vim.cmd).was_called_with "syntax clear IvyMatch" + assert.spy(vim.cmd).was_not_called_with "syntax match IvyMatch '[H]'" + end) + + it("will escape a - when passing it to be highlighted", function() + spy.on(vim, "cmd") + + controller.items = function() + return { { content = "Hello" } } + end + + controller.update "some-file" + + vim.wait(0) + + assert.spy(vim.cmd).was_called_with "syntax match IvyMatch '[some\\-file]'" + end) +end) diff --git a/lua/ivy/libivy_spec.lua b/lua/ivy/libivy_spec.lua new file mode 100644 index 0000000..8faf503 --- /dev/null +++ b/lua/ivy/libivy_spec.lua @@ -0,0 +1,36 @@ +require "busted.runner"() + +local libivy = require "ivy.libivy" + +describe("libivy", function() + it("should run a simple match", function() + local score = libivy.ivy_match("term", "I am a serch term") + + assert.is_true(score > 0) + end) + + it("should find a dot file", function() + local current_dir = libivy.ivy_cwd() + local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir) + + assert.is_equal(2, results.length, "Incorrect number of results found") + assert.is_equal(".github/workflows/ci.yml", results[2].content, "Invalid matches") + end) + + it("will allow you to access the length via the metatable", function() + local current_dir = libivy.ivy_cwd() + local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir) + + local mt = getmetatable(results) + + assert.is_equal(results.length, mt.__len(results), "The `length` property does not match the __len metamethod") + end) + + it("will create an iterator", function() + local iter = libivy.ivy_files(".github/workflows/ci.yml", libivy.ivy_cwd()) + local mt = getmetatable(iter) + + assert.is_equal(type(mt["__index"]), "function") + assert.is_equal(type(mt["__len"]), "function") + end) +end) diff --git a/lua/ivy/matcher_spec.lua b/lua/ivy/matcher_spec.lua new file mode 100644 index 0000000..7128a2c --- /dev/null +++ b/lua/ivy/matcher_spec.lua @@ -0,0 +1,29 @@ +local libivy = require "ivy.libivy" + +-- Helper function to test a that string `one` has a higher match score than +-- string `two`. If string `one` has a lower score than string `two` a string +-- will be returned that can be used in body of an error. If not then `nil` is +-- returned and all is good. +local match_test = function(term, one, two) + local score_one = libivy.ivy_match(term, one) + local score_two = libivy.ivy_match(term, two) + + assert.is_true( + score_one > score_two, + ("The score of %s (%d) ranked higher than %s (%d)"):format(one, score_one, two, score_two) + ) +end + +describe("ivy matcher", function() + it("should match path separator", function() + match_test("file", "some/file.lua", "somefile.lua") + end) + + -- it("should match pattern with spaces", function() + -- match_test("so fi", "some/file.lua", "somefile.lua") + -- end) + + it("should match the start of a string", function() + match_test("file", "file.lua", "somefile.lua") + end) +end) diff --git a/lua/ivy/prompt_spec.lua b/lua/ivy/prompt_spec.lua new file mode 100644 index 0000000..452a091 --- /dev/null +++ b/lua/ivy/prompt_spec.lua @@ -0,0 +1,91 @@ +local prompt = require "ivy.prompt" + +-- Input a list of strings into the prompt +local input = function(input_table) + for index = 1, #input_table do + prompt.input(input_table[index]) + end +end + +describe("prompt", function() + before_each(function() + prompt.destroy() + end) + + it("starts with empty text", function() + assert.is_same(prompt.text(), "") + end) + + it("can input some text", function() + input { "A", "d", "e" } + assert.is_same(prompt.text(), "Ade") + end) + + it("can delete a char", function() + input { "A", "d", "e", "BACKSPACE" } + assert.is_same(prompt.text(), "Ad") + end) + + it("will reset the text", function() + input { "A", "d", "e" } + prompt.set "New" + assert.is_same(prompt.text(), "New") + end) + + it("can move around the a word", function() + input { "P", "r", "o", "p", "t", "LEFT", "LEFT", "LEFT", "RIGHT", "m" } + assert.is_same(prompt.text(), "Prompt") + end) + + it("can delete a word", function() + prompt.set "Ade Attwood" + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), "Ade ") + end) + + it("can delete a word in the middle and leave the cursor at that word", function() + prompt.set "Ade middle A" + input { "LEFT", "LEFT", "DELETE_WORD", "a" } + + assert.is_same(prompt.text(), "Ade a A") + end) + + it("will delete the space and the word if the last word is single space", function() + prompt.set "some.thing " + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), "some.") + end) + + it("will only delete one word from path", function() + prompt.set "some/nested/path" + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), "some/nested/") + end) + + it("will delete tailing space", function() + prompt.set "word " + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), "") + end) + + it("will leave a random space", function() + prompt.set "some word " + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), "some ") + end) + + local special_characters = { ".", "/", "^" } + for _, char in ipairs(special_characters) do + it(string.format("will stop at a %s", char), function() + prompt.set(string.format("key%sValue", char)) + input { "DELETE_WORD" } + + assert.is_same(prompt.text(), string.format("key%s", char)) + end) + end +end) diff --git a/lua/ivy/utils.lua b/lua/ivy/utils.lua index 910e57f..40489a8 100644 --- a/lua/ivy/utils.lua +++ b/lua/ivy/utils.lua @@ -99,7 +99,9 @@ end utils.line_action = function() return function(item) local line = item:match "^%s+(%d+):" - vim.cmd(line) + if line ~= nil then + vim.cmd(line) + end end end diff --git a/lua/ivy/utils_escape_spec.lua b/lua/ivy/utils_escape_spec.lua new file mode 100644 index 0000000..7a47331 --- /dev/null +++ b/lua/ivy/utils_escape_spec.lua @@ -0,0 +1,11 @@ +local utils = require "ivy.utils" + +it("will escape a dollar in the file name", function() + local result = utils.escape_file_name "/path/to/$file/$name.lua" + assert.is_same(result, "/path/to/\\$file/\\$name.lua") +end) + +it("will escape a brackets in the file name", function() + local result = utils.escape_file_name "/path/to/[file]/[name].lua" + assert.is_same(result, "/path/to/\\[file\\]/\\[name\\].lua") +end) diff --git a/lua/ivy/utils_line_action_spec.lua b/lua/ivy/utils_line_action_spec.lua new file mode 100644 index 0000000..8e4484e --- /dev/null +++ b/lua/ivy/utils_line_action_spec.lua @@ -0,0 +1,28 @@ +local utils = require "ivy.utils" +local line_action = utils.line_action() + +describe("utils line_action", function() + before_each(function() + spy.on(vim, "cmd") + end) + + it("will run the line command", function() + line_action " 4: Some text" + + assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once") + assert.spy(vim.cmd).was_called_with "4" + end) + + it("will run with more numbers", function() + line_action " 44: Some text" + + assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once") + assert.spy(vim.cmd).was_called_with "44" + end) + + it("dose not run any action if no line is found", function() + line_action "Some text" + + assert.spy(vim.cmd).was_not_called() + end) +end) diff --git a/lua/ivy/utils_vimgrep_action_spec.lua b/lua/ivy/utils_vimgrep_action_spec.lua new file mode 100644 index 0000000..51cb55d --- /dev/null +++ b/lua/ivy/utils_vimgrep_action_spec.lua @@ -0,0 +1,56 @@ +local utils = require "ivy.utils" +local vimgrep_action = utils.vimgrep_action() + +local test_data = { + { + it = "will edit some file and goto the line", + completion = "some/file.lua:2: This is some text", + action = utils.actions.EDIT, + commands = { + "edit some/file.lua", + "2", + }, + }, + { + it = "will skip the line if its not matched", + completion = "some/file.lua: This is some text", + action = utils.actions.EDIT, + commands = { "buffer some/file.lua" }, + }, + { + it = "will run the vsplit command", + completion = "some/file.lua: This is some text", + action = utils.actions.VSPLIT, + commands = { "vsplit | buffer some/file.lua" }, + }, + { + it = "will run the split command", + completion = "some/file.lua: This is some text", + action = utils.actions.SPLIT, + commands = { "split | buffer some/file.lua" }, + }, +} + +describe("utils vimgrep_action", function() + before_each(function() + spy.on(vim, "cmd") + end) + + after_each(function() + vim.cmd:revert() + end) + + for i = 1, #test_data do + local data = test_data[i] + it(data.it, function() + assert.is_true(#data.commands > 0, "You must assert that at least one command is run") + + vimgrep_action(data.completion, data.action) + assert.is_equal(#vim.cmd.calls, #data.commands, "The `vim.cmd` function should be called once") + + for j = 1, #data.commands do + assert.spy(vim.cmd).was_called_with(data.commands[j]) + end + end) + end +end) diff --git a/lua/ivy/window_spec.lua b/lua/ivy/window_spec.lua new file mode 100644 index 0000000..873504a --- /dev/null +++ b/lua/ivy/window_spec.lua @@ -0,0 +1,32 @@ +local window = require "ivy.window" +local controller = require "ivy.controller" + +describe("window", function() + before_each(function() + vim.cmd "highlight IvyMatch cterm=bold gui=bold" + window.initialize() + end) + + after_each(function() + controller.destroy() + end) + + it("can initialize and destroy the window", function() + assert.is_equal(vim.api.nvim_get_current_buf(), window.buffer) + + window.destroy() + assert.is_equal(nil, window.buffer) + end) + + it("can set items", function() + window.set_items { { content = "Line one" } } + assert.is_equal("Line one", window.get_current_selection()) + end) + + it("will set the items when a string is passed in", function() + local items = table.concat({ "One", "Two", "Three" }, "\n") + window.set_items(items) + + assert.is_equal(items, table.concat(vim.api.nvim_buf_get_lines(window.buffer, 0, -1, true), "\n")) + end) +end) diff --git a/scripts/busted.lua b/scripts/busted.lua new file mode 100755 index 0000000..753c0e6 --- /dev/null +++ b/scripts/busted.lua @@ -0,0 +1,8 @@ +-- Script to run the busted cli tool. You can use this under nvim using be +-- below command. Any arguments can be passed in the same as the busted cli. +-- +-- ```bash +-- nvim -l scripts/busted.lua +-- ``` +vim.opt.rtp:append(vim.fn.getcwd()) +require "busted.runner" { standalone = false }