#!/usr/bin/env lua --- Simple Test Runner for JQuote Plugin --- @author Jan --- @license MIT local test_runner = {} -- Test result tracking local stats = { total = 0, passed = 0, failed = 0, errors = {}, start_time = os.clock() } -- ANSI color codes for terminal output local colors = { reset = "\27[0m", red = "\27[31m", green = "\27[32m", yellow = "\27[33m", blue = "\27[34m", magenta = "\27[35m", cyan = "\27[36m", white = "\27[37m", bold = "\27[1m" } -- Simple assertion library local assert = { is_true = function(value, message) if value ~= true then error(message or string.format("Expected true, got %s", tostring(value))) end end, is_false = function(value, message) if value ~= false then error(message or string.format("Expected false, got %s", tostring(value))) end end, are = { equal = function(expected, actual, message) if expected ~= actual then error(message or string.format("Expected %s, got %s", tostring(expected), tostring(actual))) end end, same = function(expected, actual, message) if not test_runner.deep_equal(expected, actual) then error(message or string.format("Tables are not the same.\nExpected: %s\nActual: %s", test_runner.table_tostring(expected), test_runner.table_tostring(actual))) end end }, is_not_nil = function(value, message) if value == nil then error(message or "Expected non-nil value") end end, is_string = function(value, message) if type(value) ~= "string" then error(message or string.format("Expected string, got %s", type(value))) end end, is_table = function(value, message) if type(value) ~= "table" then error(message or string.format("Expected table, got %s", type(value))) end end, matches = function(pattern, str, message) if not string.match(str, pattern) then error(message or string.format("String '%s' does not match pattern '%s'", str, pattern)) end end } -- Make assert global for tests _G.assert = assert -- Test context management local current_context = "" local current_test = "" -- Utility functions function test_runner.deep_equal(a, b) if type(a) ~= type(b) then return false end if type(a) ~= "table" then return a == b end for k, v in pairs(a) do if not test_runner.deep_equal(v, b[k]) then return false end end for k, v in pairs(b) do if not test_runner.deep_equal(v, a[k]) then return false end end return true end function test_runner.table_tostring(t, indent) if type(t) ~= "table" then return tostring(t) end indent = indent or 0 local spaces = string.rep(" ", indent) local result = "{\n" for k, v in pairs(t) do result = result .. spaces .. " " .. tostring(k) .. " = " if type(v) == "table" then result = result .. test_runner.table_tostring(v, indent + 1) else result = result .. tostring(v) end result = result .. ",\n" end result = result .. spaces .. "}" return result end -- Test framework functions function describe(description, func) current_context = description print(colors.blue .. colors.bold .. "\n" .. description .. colors.reset) func() current_context = "" end function it(description, func) current_test = description stats.total = stats.total + 1 local success, error_msg = pcall(func) if success then stats.passed = stats.passed + 1 print(colors.green .. " āœ“ " .. description .. colors.reset) else stats.failed = stats.failed + 1 local full_description = current_context .. " - " .. description table.insert(stats.errors, { test = full_description, error = error_msg }) print(colors.red .. " āœ— " .. description .. colors.reset) print(colors.red .. " " .. error_msg .. colors.reset) end current_test = "" end function before_each(func) -- Simple implementation - just call the function before each test -- In a more sophisticated runner, this would be stored and called appropriately _G._before_each = func end -- Override 'it' to call before_each if it exists local original_it = it function it(description, func) if _G._before_each then _G._before_each() end original_it(description, func) end -- Test execution function test_runner.run_tests() print(colors.cyan .. colors.bold .. "JQuote Plugin Test Suite" .. colors.reset) print(colors.cyan .. "========================" .. colors.reset) -- Load and run tests local success, error_msg = pcall(dofile, "tests/init_spec.lua") if not success then print(colors.red .. colors.bold .. "Failed to load test file:" .. colors.reset) print(colors.red .. error_msg .. colors.reset) return false end -- Print summary local duration = os.clock() - stats.start_time print(colors.cyan .. "\n========================" .. colors.reset) print(colors.white .. colors.bold .. "Test Summary:" .. colors.reset) print(string.format(" Total tests: %d", stats.total)) print(colors.green .. string.format(" Passed: %d", stats.passed) .. colors.reset) if stats.failed > 0 then print(colors.red .. string.format(" Failed: %d", stats.failed) .. colors.reset) print(colors.red .. "\nFailure Details:" .. colors.reset) for _, error_info in ipairs(stats.errors) do print(colors.red .. " • " .. error_info.test .. colors.reset) print(colors.red .. " " .. error_info.error .. colors.reset) end end print(string.format(" Duration: %.3fs", duration)) local success_rate = stats.total > 0 and (stats.passed / stats.total * 100) or 0 if success_rate == 100 then print(colors.green .. colors.bold .. "\nšŸŽ‰ All tests passed!" .. colors.reset) else print(colors.red .. colors.bold .. string.format("\nāŒ %.1f%% tests passed", success_rate) .. colors.reset) end return stats.failed == 0 end -- Command line interface if arg and arg[0] and arg[0]:match("test_runner%.lua$") then local success = test_runner.run_tests() os.exit(success and 0 or 1) end return test_runner