mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 06:14:59 +00:00
- Implemented core testing functionalities including describe/it blocks, before/after hooks, and assertion methods. - Added support for mocks and spies to facilitate testing of functions and methods. - Introduced helper utilities for generating test data, parameterized tests, and snapshot testing. - Developed a test runner that executes suites and generates detailed reports in both text and JSON formats. - Created a manifest for the lua_test package to define scripts and their purposes. feat(screenshot_analyzer): introduce screenshot analysis package - Added metadata for the screenshot_analyzer package, detailing its components and scripts. - Defined dependencies and bindings for browser interactions.
249 lines
5.6 KiB
Lua
249 lines
5.6 KiB
Lua
-- Mock and spy utilities for testing
|
|
-- Allows tracking function calls and replacing implementations
|
|
|
|
local M = {}
|
|
|
|
-- Create a mock function
|
|
function M.fn(implementation)
|
|
local mock = {
|
|
calls = {},
|
|
results = {},
|
|
implementation = implementation
|
|
}
|
|
|
|
-- The callable mock function
|
|
local callable = function(...)
|
|
local args = {...}
|
|
mock.calls[#mock.calls + 1] = args
|
|
|
|
local result
|
|
if mock.implementation then
|
|
result = {mock.implementation(...)}
|
|
else
|
|
result = {}
|
|
end
|
|
|
|
mock.results[#mock.results + 1] = result
|
|
return table.unpack(result)
|
|
end
|
|
|
|
-- Attach mock metadata to the function via a metatable
|
|
return setmetatable({}, {
|
|
__call = function(_, ...) return callable(...) end,
|
|
__index = {
|
|
-- Get call count
|
|
getCallCount = function()
|
|
return #mock.calls
|
|
end,
|
|
|
|
-- Check if called
|
|
wasCalled = function()
|
|
return #mock.calls > 0
|
|
end,
|
|
|
|
-- Check if called with specific args
|
|
wasCalledWith = function(...)
|
|
local expectedArgs = {...}
|
|
for _, callArgs in ipairs(mock.calls) do
|
|
local match = true
|
|
for i, expected in ipairs(expectedArgs) do
|
|
if callArgs[i] ~= expected then
|
|
match = false
|
|
break
|
|
end
|
|
end
|
|
if match then return true end
|
|
end
|
|
return false
|
|
end,
|
|
|
|
-- Get specific call args
|
|
getCall = function(index)
|
|
return mock.calls[index]
|
|
end,
|
|
|
|
-- Get last call args
|
|
getLastCall = function()
|
|
return mock.calls[#mock.calls]
|
|
end,
|
|
|
|
-- Get all calls
|
|
getCalls = function()
|
|
return mock.calls
|
|
end,
|
|
|
|
-- Get all results
|
|
getResults = function()
|
|
return mock.results
|
|
end,
|
|
|
|
-- Clear call history
|
|
reset = function()
|
|
mock.calls = {}
|
|
mock.results = {}
|
|
end,
|
|
|
|
-- Set return value
|
|
mockReturnValue = function(value)
|
|
mock.implementation = function() return value end
|
|
end,
|
|
|
|
-- Set return values in sequence
|
|
mockReturnValueOnce = function(value)
|
|
local originalImpl = mock.implementation
|
|
local called = false
|
|
mock.implementation = function(...)
|
|
if not called then
|
|
called = true
|
|
return value
|
|
elseif originalImpl then
|
|
return originalImpl(...)
|
|
end
|
|
end
|
|
end,
|
|
|
|
-- Set implementation
|
|
mockImplementation = function(fn)
|
|
mock.implementation = fn
|
|
end,
|
|
|
|
-- Restore original (for spies)
|
|
mockRestore = function()
|
|
mock.implementation = nil
|
|
mock.calls = {}
|
|
mock.results = {}
|
|
end
|
|
}
|
|
})
|
|
end
|
|
|
|
-- Create a spy on an existing object method
|
|
function M.spyOn(obj, methodName)
|
|
local original = obj[methodName]
|
|
if type(original) ~= "function" then
|
|
error("Cannot spy on non-function: " .. methodName)
|
|
end
|
|
|
|
local spy = M.fn(original)
|
|
|
|
-- Add restore functionality
|
|
local meta = getmetatable(spy).__index
|
|
local originalRestore = meta.mockRestore
|
|
meta.mockRestore = function()
|
|
obj[methodName] = original
|
|
originalRestore()
|
|
end
|
|
|
|
-- Replace the method
|
|
obj[methodName] = function(...)
|
|
return spy(...)
|
|
end
|
|
|
|
return spy
|
|
end
|
|
|
|
-- Create a mock object with multiple mock functions
|
|
function M.mockObject(methods)
|
|
local obj = {}
|
|
local mocks = {}
|
|
|
|
for name, impl in pairs(methods or {}) do
|
|
mocks[name] = M.fn(impl)
|
|
obj[name] = function(...) return mocks[name](...) end
|
|
end
|
|
|
|
obj._mocks = mocks
|
|
|
|
obj._resetAll = function()
|
|
for _, mock in pairs(mocks) do
|
|
mock.reset()
|
|
end
|
|
end
|
|
|
|
return obj
|
|
end
|
|
|
|
-- Timer mocks for testing time-dependent code
|
|
function M.useFakeTimers()
|
|
local timers = {
|
|
now = 0,
|
|
scheduled = {}
|
|
}
|
|
|
|
return {
|
|
-- Get current fake time
|
|
now = function()
|
|
return timers.now
|
|
end,
|
|
|
|
-- Schedule a callback (like setTimeout)
|
|
schedule = function(callback, delay)
|
|
local id = #timers.scheduled + 1
|
|
timers.scheduled[id] = {
|
|
callback = callback,
|
|
time = timers.now + delay,
|
|
id = id
|
|
}
|
|
return id
|
|
end,
|
|
|
|
-- Cancel a scheduled callback
|
|
cancel = function(id)
|
|
timers.scheduled[id] = nil
|
|
end,
|
|
|
|
-- Advance time and run scheduled callbacks
|
|
advance = function(ms)
|
|
local targetTime = timers.now + ms
|
|
|
|
-- Sort by scheduled time
|
|
local pending = {}
|
|
for _, timer in pairs(timers.scheduled) do
|
|
if timer.time <= targetTime then
|
|
pending[#pending + 1] = timer
|
|
end
|
|
end
|
|
|
|
table.sort(pending, function(a, b) return a.time < b.time end)
|
|
|
|
-- Run each callback at its scheduled time
|
|
for _, timer in ipairs(pending) do
|
|
timers.now = timer.time
|
|
timer.callback()
|
|
timers.scheduled[timer.id] = nil
|
|
end
|
|
|
|
timers.now = targetTime
|
|
end,
|
|
|
|
-- Run all pending timers
|
|
runAll = function()
|
|
while next(timers.scheduled) do
|
|
local nextTimer
|
|
local nextTime = math.huge
|
|
|
|
for id, timer in pairs(timers.scheduled) do
|
|
if timer.time < nextTime then
|
|
nextTime = timer.time
|
|
nextTimer = timer
|
|
end
|
|
end
|
|
|
|
if nextTimer then
|
|
timers.now = nextTimer.time
|
|
nextTimer.callback()
|
|
timers.scheduled[nextTimer.id] = nil
|
|
end
|
|
end
|
|
end,
|
|
|
|
-- Reset timers
|
|
reset = function()
|
|
timers.now = 0
|
|
timers.scheduled = {}
|
|
end
|
|
}
|
|
end
|
|
|
|
return M
|