217 lines
No EOL
6.4 KiB
Lua
217 lines
No EOL
6.4 KiB
Lua
#!/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 |