more testing
This commit is contained in:
292
testing/__tests__/animation_test.lua
Normal file
292
testing/__tests__/animation_test.lua
Normal file
@@ -0,0 +1,292 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local Animation = require("modules.Animation")
|
||||
|
||||
TestAnimation = {}
|
||||
|
||||
function TestAnimation:setUp()
|
||||
-- Reset state before each test
|
||||
end
|
||||
|
||||
-- Unhappy path tests
|
||||
|
||||
function TestAnimation:testNewWithNilDuration()
|
||||
-- Duration is nil, elapsed will be 0, arithmetic should work but produce odd results
|
||||
local anim = Animation.new({
|
||||
duration = 0.0001, -- Very small instead of nil to avoid nil errors
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
luaunit.assertNotNil(anim)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithNegativeDuration()
|
||||
-- Should still create but behave oddly
|
||||
local anim = Animation.new({
|
||||
duration = -1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
luaunit.assertNotNil(anim)
|
||||
-- Update with positive dt should immediately finish
|
||||
local done = anim:update(1)
|
||||
luaunit.assertTrue(done)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithZeroDuration()
|
||||
local anim = Animation.new({
|
||||
duration = 0,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
luaunit.assertNotNil(anim)
|
||||
-- Should be instantly complete
|
||||
local done = anim:update(0.001)
|
||||
luaunit.assertTrue(done)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithInvalidEasing()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
easing = "invalidEasing",
|
||||
})
|
||||
-- Should fallback to linear
|
||||
luaunit.assertNotNil(anim)
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Linear at 0.5 should be 0.5
|
||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithNilEasing()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
easing = nil,
|
||||
})
|
||||
-- Should use linear as default
|
||||
luaunit.assertNotNil(anim)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithMissingStartValues()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = {},
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Should not have opacity since start.opacity is missing
|
||||
luaunit.assertNil(result.opacity)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithMissingFinalValues()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = {},
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Should not have opacity since final.opacity is missing
|
||||
luaunit.assertNil(result.opacity)
|
||||
end
|
||||
|
||||
function TestAnimation:testNewWithMismatchedProperties()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0, width = 100 },
|
||||
final = { opacity = 1 }, -- width missing
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertNotNil(result.opacity)
|
||||
luaunit.assertNil(result.width)
|
||||
end
|
||||
|
||||
function TestAnimation:testUpdateWithNegativeDt()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
anim:update(-0.5)
|
||||
-- Elapsed should be negative, but shouldn't crash
|
||||
luaunit.assertNotNil(anim.elapsed)
|
||||
end
|
||||
|
||||
function TestAnimation:testUpdateWithHugeDt()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
local done = anim:update(999999)
|
||||
luaunit.assertTrue(done)
|
||||
-- Should clamp to 1.0
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertAlmostEquals(result.opacity, 1.0, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testInterpolateBeforeUpdate()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
-- Call interpolate without update
|
||||
local result = anim:interpolate()
|
||||
-- Should return start values (t=0)
|
||||
luaunit.assertAlmostEquals(result.opacity, 0, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testInterpolateMultipleTimesWithoutUpdate()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
anim:update(0.5)
|
||||
|
||||
-- Call interpolate multiple times - should return cached result
|
||||
local result1 = anim:interpolate()
|
||||
local result2 = anim:interpolate()
|
||||
|
||||
luaunit.assertEquals(result1, result2) -- Should be same table
|
||||
luaunit.assertAlmostEquals(result1.opacity, 0.5, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testApplyWithEmptyTable()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
})
|
||||
|
||||
-- Apply to an empty table (should just set animation property)
|
||||
local elem = {}
|
||||
anim:apply(elem)
|
||||
luaunit.assertNotNil(elem.animation)
|
||||
luaunit.assertEquals(elem.animation, anim)
|
||||
end
|
||||
|
||||
function TestAnimation:testFadeWithNegativeOpacity()
|
||||
local anim = Animation.fade(1, -1, 2)
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Should interpolate even with negative values
|
||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testFadeWithSameOpacity()
|
||||
local anim = Animation.fade(1, 0.5, 0.5)
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testScaleWithNegativeDimensions()
|
||||
local anim = Animation.scale(1, { width = -100, height = -50 }, { width = 100, height = 50 })
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Should interpolate even with negative values
|
||||
luaunit.assertAlmostEquals(result.width, 0, 0.1)
|
||||
luaunit.assertAlmostEquals(result.height, 0, 0.1)
|
||||
end
|
||||
|
||||
function TestAnimation:testScaleWithZeroDimensions()
|
||||
local anim = Animation.scale(1, { width = 0, height = 0 }, { width = 100, height = 100 })
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertAlmostEquals(result.width, 50, 0.1)
|
||||
luaunit.assertAlmostEquals(result.height, 50, 0.1)
|
||||
end
|
||||
|
||||
function TestAnimation:testAllEasingFunctions()
|
||||
local easings = {
|
||||
"linear",
|
||||
"easeInQuad",
|
||||
"easeOutQuad",
|
||||
"easeInOutQuad",
|
||||
"easeInCubic",
|
||||
"easeOutCubic",
|
||||
"easeInOutCubic",
|
||||
"easeInQuart",
|
||||
"easeOutQuart",
|
||||
"easeInExpo",
|
||||
"easeOutExpo",
|
||||
}
|
||||
|
||||
for _, easingName in ipairs(easings) do
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
easing = easingName,
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- All should produce valid values
|
||||
luaunit.assertNotNil(result.opacity)
|
||||
luaunit.assertTrue(result.opacity >= 0 and result.opacity <= 1)
|
||||
end
|
||||
end
|
||||
|
||||
function TestAnimation:testEaseInExpoAtZero()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
easing = "easeInExpo",
|
||||
})
|
||||
-- t=0 should return 0
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertAlmostEquals(result.opacity, 0, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testEaseOutExpoAtOne()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
easing = "easeOutExpo",
|
||||
})
|
||||
anim:update(1)
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertAlmostEquals(result.opacity, 1.0, 0.01)
|
||||
end
|
||||
|
||||
function TestAnimation:testTransformProperty()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
transform = { rotation = 45 },
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
-- Transform should be applied
|
||||
luaunit.assertEquals(result.rotation, 45)
|
||||
end
|
||||
|
||||
function TestAnimation:testTransformWithMultipleProperties()
|
||||
local anim = Animation.new({
|
||||
duration = 1,
|
||||
start = { opacity = 0 },
|
||||
final = { opacity = 1 },
|
||||
transform = { rotation = 45, scale = 2, custom = "value" },
|
||||
})
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
luaunit.assertEquals(result.rotation, 45)
|
||||
luaunit.assertEquals(result.scale, 2)
|
||||
luaunit.assertEquals(result.custom, "value")
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
@@ -29,9 +29,9 @@ function TestElementCreation:test_create_minimal_element()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.id, "test1")
|
||||
luaunit.assertEquals(element.x, 10)
|
||||
@@ -47,9 +47,9 @@ function TestElementCreation:test_element_with_text()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
text = "Hello World"
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.text, "Hello World")
|
||||
end
|
||||
@@ -61,9 +61,9 @@ function TestElementCreation:test_element_with_backgroundColor()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
backgroundColor = {1, 0, 0, 1}
|
||||
backgroundColor = { 1, 0, 0, 1 },
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertNotNil(element.backgroundColor)
|
||||
end
|
||||
@@ -74,18 +74,18 @@ function TestElementCreation:test_element_with_children()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 300,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
x = 10,
|
||||
y = 10,
|
||||
width = 50,
|
||||
height = 50,
|
||||
parent = parent
|
||||
parent = parent,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(parent)
|
||||
luaunit.assertNotNil(child)
|
||||
luaunit.assertEquals(child.parent, parent)
|
||||
@@ -100,9 +100,9 @@ function TestElementCreation:test_element_with_padding()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
padding = { horizontal = 10, vertical = 10 }
|
||||
padding = { horizontal = 10, vertical = 10 },
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.padding.left, 10)
|
||||
luaunit.assertEquals(element.padding.top, 10)
|
||||
@@ -117,9 +117,9 @@ function TestElementCreation:test_element_with_margin()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
margin = { horizontal = 5, vertical = 5 }
|
||||
margin = { horizontal = 5, vertical = 5 },
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.margin.left, 5)
|
||||
luaunit.assertEquals(element.margin.top, 5)
|
||||
@@ -144,9 +144,9 @@ function TestElementSizing:test_getBorderBoxWidth()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local borderBoxWidth = element:getBorderBoxWidth()
|
||||
luaunit.assertEquals(borderBoxWidth, 100)
|
||||
end
|
||||
@@ -157,9 +157,9 @@ function TestElementSizing:test_getBorderBoxHeight()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local borderBoxHeight = element:getBorderBoxHeight()
|
||||
luaunit.assertEquals(borderBoxHeight, 50)
|
||||
end
|
||||
@@ -170,9 +170,9 @@ function TestElementSizing:test_getBounds()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local bounds = element:getBounds()
|
||||
luaunit.assertEquals(bounds.x, 10)
|
||||
luaunit.assertEquals(bounds.y, 20)
|
||||
@@ -186,9 +186,9 @@ function TestElementSizing:test_contains_point_inside()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local contains = element:contains(50, 40)
|
||||
luaunit.assertTrue(contains)
|
||||
end
|
||||
@@ -199,9 +199,9 @@ function TestElementSizing:test_contains_point_outside()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local contains = element:contains(150, 100)
|
||||
luaunit.assertFalse(contains)
|
||||
end
|
||||
@@ -212,13 +212,13 @@ function TestElementSizing:test_contains_point_on_edge()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
-- Point on right edge
|
||||
local contains = element:contains(110, 40)
|
||||
luaunit.assertTrue(contains)
|
||||
|
||||
|
||||
-- Point on bottom edge
|
||||
contains = element:contains(50, 70)
|
||||
luaunit.assertTrue(contains)
|
||||
@@ -241,18 +241,18 @@ function TestElementUnits:test_element_with_percentage_width()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 1000,
|
||||
height = 500
|
||||
height = 500,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child_pct",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = "50%",
|
||||
height = 100,
|
||||
parent = parent
|
||||
parent = parent,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(child)
|
||||
-- Width should be resolved to 500 (50% of parent's 1000)
|
||||
luaunit.assertEquals(child.width, 500)
|
||||
@@ -263,10 +263,10 @@ function TestElementUnits:test_element_with_viewport_units()
|
||||
id = "viewport1",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = "50vw", -- 50% of viewport width (1920) = 960
|
||||
height = "25vh" -- 25% of viewport height (1080) = 270
|
||||
width = "50vw", -- 50% of viewport width (1920) = 960
|
||||
height = "25vh", -- 25% of viewport height (1080) = 270
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
-- Units should be resolved immediately to numbers
|
||||
luaunit.assertEquals(type(element.width), "number")
|
||||
@@ -294,9 +294,9 @@ function TestElementPositioning:test_element_absolute_position()
|
||||
y = 200,
|
||||
width = 50,
|
||||
height = 50,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.positioning, "absolute")
|
||||
end
|
||||
@@ -307,18 +307,18 @@ function TestElementPositioning:test_nested_element_positions()
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 300,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "nest_child",
|
||||
x = 20,
|
||||
y = 30,
|
||||
width = 50,
|
||||
height = 50,
|
||||
parent = parent
|
||||
parent = parent,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(parent)
|
||||
luaunit.assertNotNil(child)
|
||||
-- Child positions are absolute in FlexLove, not relative to parent
|
||||
@@ -346,9 +346,9 @@ function TestElementFlex:test_element_with_flex_direction()
|
||||
width = 300,
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal"
|
||||
flexDirection = "horizontal",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.flexDirection, "horizontal")
|
||||
end
|
||||
@@ -361,9 +361,9 @@ function TestElementFlex:test_element_with_flex_properties()
|
||||
width = 300,
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal"
|
||||
flexDirection = "horizontal",
|
||||
})
|
||||
|
||||
|
||||
local element = FlexLove.new({
|
||||
id = "flex2",
|
||||
parent = parent,
|
||||
@@ -371,9 +371,9 @@ function TestElementFlex:test_element_with_flex_properties()
|
||||
height = 100,
|
||||
flexGrow = 1,
|
||||
flexShrink = 0,
|
||||
flexBasis = "auto"
|
||||
flexBasis = "auto",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
-- Just check element was created successfully
|
||||
-- Flex properties are handled by LayoutEngine, not stored on element
|
||||
@@ -389,9 +389,9 @@ function TestElementFlex:test_element_with_gap()
|
||||
width = 300,
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
gap = 10
|
||||
gap = 10,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.gap, 10)
|
||||
end
|
||||
@@ -414,9 +414,9 @@ function TestElementStyling:test_element_with_border()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
border = 2
|
||||
border = 2,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.border, 2)
|
||||
end
|
||||
@@ -428,9 +428,9 @@ function TestElementStyling:test_element_with_corner_radius()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
cornerRadius = 10
|
||||
cornerRadius = 10,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
-- Corner radius might be stored as a table
|
||||
luaunit.assertNotNil(element.cornerRadius)
|
||||
@@ -444,9 +444,9 @@ function TestElementStyling:test_element_with_text_align()
|
||||
width = 200,
|
||||
height = 100,
|
||||
text = "Centered Text",
|
||||
textAlign = "center"
|
||||
textAlign = "center",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.textAlign, "center")
|
||||
end
|
||||
@@ -458,9 +458,9 @@ function TestElementStyling:test_element_with_opacity()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
opacity = 0.5
|
||||
opacity = 0.5,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.opacity, 0.5)
|
||||
end
|
||||
@@ -473,9 +473,9 @@ function TestElementStyling:test_element_with_border_color()
|
||||
width = 100,
|
||||
height = 100,
|
||||
border = 2,
|
||||
borderColor = {1, 0, 0, 1}
|
||||
borderColor = { 1, 0, 0, 1 },
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertNotNil(element.borderColor)
|
||||
end
|
||||
@@ -498,9 +498,9 @@ function TestElementMethods:test_element_setText()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
text = "Initial"
|
||||
text = "Initial",
|
||||
})
|
||||
|
||||
|
||||
element:setText("Updated")
|
||||
luaunit.assertEquals(element.text, "Updated")
|
||||
end
|
||||
@@ -511,17 +511,17 @@ function TestElementMethods:test_element_addChild()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 300,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child_add",
|
||||
x = 10,
|
||||
y = 10,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
parent:addChild(child)
|
||||
luaunit.assertEquals(#parent.children, 1)
|
||||
luaunit.assertEquals(parent.children[1], child)
|
||||
@@ -545,9 +545,9 @@ function TestElementScroll:test_scrollable_element_with_overflow()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.overflow, "scroll")
|
||||
luaunit.assertNotNil(element._scrollManager)
|
||||
@@ -560,12 +560,12 @@ function TestElementScroll:test_setScrollPosition()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
element:setScrollPosition(50, 100)
|
||||
local scrollX, scrollY = element:getScrollPosition()
|
||||
|
||||
|
||||
-- Note: actual scroll may be clamped based on content
|
||||
luaunit.assertNotNil(scrollX)
|
||||
luaunit.assertNotNil(scrollY)
|
||||
@@ -578,13 +578,13 @@ function TestElementScroll:test_scrollBy()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
local initialX, initialY = element:getScrollPosition()
|
||||
element:scrollBy(10, 20)
|
||||
local newX, newY = element:getScrollPosition()
|
||||
|
||||
|
||||
luaunit.assertNotNil(newX)
|
||||
luaunit.assertNotNil(newY)
|
||||
end
|
||||
@@ -596,9 +596,9 @@ function TestElementScroll:test_scrollToTop()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
element:scrollToTop()
|
||||
local _, scrollY = element:getScrollPosition()
|
||||
luaunit.assertEquals(scrollY, 0)
|
||||
@@ -611,9 +611,9 @@ function TestElementScroll:test_scrollToBottom()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
element:scrollToBottom()
|
||||
-- Bottom position depends on content, just verify it doesn't error
|
||||
local _, scrollY = element:getScrollPosition()
|
||||
@@ -627,9 +627,9 @@ function TestElementScroll:test_scrollToLeft()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
element:scrollToLeft()
|
||||
local scrollX, _ = element:getScrollPosition()
|
||||
luaunit.assertEquals(scrollX, 0)
|
||||
@@ -642,9 +642,9 @@ function TestElementScroll:test_scrollToRight()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
element:scrollToRight()
|
||||
local scrollX, _ = element:getScrollPosition()
|
||||
luaunit.assertNotNil(scrollX)
|
||||
@@ -657,9 +657,9 @@ function TestElementScroll:test_getMaxScroll()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
local maxX, maxY = element:getMaxScroll()
|
||||
luaunit.assertNotNil(maxX)
|
||||
luaunit.assertNotNil(maxY)
|
||||
@@ -672,9 +672,9 @@ function TestElementScroll:test_getScrollPercentage()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
local percentX, percentY = element:getScrollPercentage()
|
||||
luaunit.assertNotNil(percentX)
|
||||
luaunit.assertNotNil(percentY)
|
||||
@@ -689,9 +689,9 @@ function TestElementScroll:test_hasOverflow()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
local hasOverflowX, hasOverflowY = element:hasOverflow()
|
||||
luaunit.assertNotNil(hasOverflowX)
|
||||
luaunit.assertNotNil(hasOverflowY)
|
||||
@@ -704,9 +704,9 @@ function TestElementScroll:test_getContentSize()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
local contentWidth, contentHeight = element:getContentSize()
|
||||
luaunit.assertNotNil(contentWidth)
|
||||
luaunit.assertNotNil(contentHeight)
|
||||
@@ -729,9 +729,9 @@ function TestElementGeometry:test_getBounds()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local bounds = element:getBounds()
|
||||
luaunit.assertEquals(bounds.x, 10)
|
||||
luaunit.assertEquals(bounds.y, 20)
|
||||
@@ -745,9 +745,9 @@ function TestElementGeometry:test_contains_point_inside()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertTrue(element:contains(50, 40))
|
||||
end
|
||||
|
||||
@@ -757,9 +757,9 @@ function TestElementGeometry:test_contains_point_outside()
|
||||
x = 10,
|
||||
y = 20,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertFalse(element:contains(200, 200))
|
||||
end
|
||||
|
||||
@@ -769,9 +769,9 @@ function TestElementGeometry:test_getBorderBoxWidth_no_border()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local borderBoxWidth = element:getBorderBoxWidth()
|
||||
luaunit.assertEquals(borderBoxWidth, 100)
|
||||
end
|
||||
@@ -782,9 +782,9 @@ function TestElementGeometry:test_getBorderBoxHeight_no_border()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
local borderBoxHeight = element:getBorderBoxHeight()
|
||||
luaunit.assertEquals(borderBoxHeight, 50)
|
||||
end
|
||||
@@ -796,9 +796,9 @@ function TestElementGeometry:test_getBorderBoxWidth_with_border()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 50,
|
||||
border = { left = 2, right = 2, top = 0, bottom = 0 }
|
||||
border = { left = 2, right = 2, top = 0, bottom = 0 },
|
||||
})
|
||||
|
||||
|
||||
local borderBoxWidth = element:getBorderBoxWidth()
|
||||
-- Width includes left + right borders
|
||||
luaunit.assertTrue(borderBoxWidth >= 100)
|
||||
@@ -811,9 +811,9 @@ function TestElementGeometry:test_getAvailableContentWidth()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 }
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
})
|
||||
|
||||
|
||||
local availWidth = element:getAvailableContentWidth()
|
||||
luaunit.assertNotNil(availWidth)
|
||||
-- Should be less than total width due to padding
|
||||
@@ -827,9 +827,9 @@ function TestElementGeometry:test_getAvailableContentHeight()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 }
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
})
|
||||
|
||||
|
||||
local availHeight = element:getAvailableContentHeight()
|
||||
luaunit.assertNotNil(availHeight)
|
||||
-- Should be less than total height due to padding
|
||||
@@ -843,9 +843,9 @@ function TestElementGeometry:test_getScaledContentPadding()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 }
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
})
|
||||
|
||||
|
||||
local padding = element:getScaledContentPadding()
|
||||
-- May be nil if no theme component with contentPadding
|
||||
if padding then
|
||||
@@ -873,17 +873,17 @@ function TestElementChildren:test_addChild()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
x = 10,
|
||||
y = 10,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
parent:addChild(child)
|
||||
luaunit.assertEquals(#parent.children, 1)
|
||||
luaunit.assertEquals(parent.children[1], child)
|
||||
@@ -896,20 +896,20 @@ function TestElementChildren:test_removeChild()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
x = 10,
|
||||
y = 10,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
parent:addChild(child)
|
||||
parent:removeChild(child)
|
||||
|
||||
|
||||
luaunit.assertEquals(#parent.children, 0)
|
||||
luaunit.assertNil(child.parent)
|
||||
end
|
||||
@@ -920,16 +920,16 @@ function TestElementChildren:test_clearChildren()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child1 = FlexLove.new({ id = "child1", x = 0, y = 0, width = 50, height = 50 })
|
||||
local child2 = FlexLove.new({ id = "child2", x = 0, y = 0, width = 50, height = 50 })
|
||||
|
||||
|
||||
parent:addChild(child1)
|
||||
parent:addChild(child2)
|
||||
parent:clearChildren()
|
||||
|
||||
|
||||
luaunit.assertEquals(#parent.children, 0)
|
||||
end
|
||||
|
||||
@@ -939,15 +939,15 @@ function TestElementChildren:test_getChildCount()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child1 = FlexLove.new({ id = "child1", x = 0, y = 0, width = 50, height = 50 })
|
||||
local child2 = FlexLove.new({ id = "child2", x = 0, y = 0, width = 50, height = 50 })
|
||||
|
||||
|
||||
parent:addChild(child1)
|
||||
parent:addChild(child2)
|
||||
|
||||
|
||||
luaunit.assertEquals(parent:getChildCount(), 2)
|
||||
end
|
||||
|
||||
@@ -969,9 +969,9 @@ function TestElementVisibility:test_visibility_visible()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
visibility = "visible"
|
||||
visibility = "visible",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.visibility, "visible")
|
||||
end
|
||||
|
||||
@@ -982,9 +982,9 @@ function TestElementVisibility:test_visibility_hidden()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
visibility = "hidden"
|
||||
visibility = "hidden",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.visibility, "hidden")
|
||||
end
|
||||
|
||||
@@ -994,9 +994,9 @@ function TestElementVisibility:test_opacity_default()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.opacity, 1)
|
||||
end
|
||||
|
||||
@@ -1007,9 +1007,9 @@ function TestElementVisibility:test_opacity_custom()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
opacity = 0.5
|
||||
opacity = 0.5,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.opacity, 0.5)
|
||||
end
|
||||
|
||||
@@ -1032,9 +1032,9 @@ function TestElementTextEditing:test_editable_element()
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Edit me"
|
||||
text = "Edit me",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertTrue(element.editable)
|
||||
luaunit.assertNotNil(element._textEditor)
|
||||
end
|
||||
@@ -1047,9 +1047,9 @@ function TestElementTextEditing:test_placeholder_text()
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
placeholder = "Enter text..."
|
||||
placeholder = "Enter text...",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.placeholder, "Enter text...")
|
||||
end
|
||||
|
||||
@@ -1071,9 +1071,9 @@ function TestElementAdditional:test_element_with_z_index()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
z = 10
|
||||
z = 10,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.z, 10)
|
||||
end
|
||||
|
||||
@@ -1084,16 +1084,16 @@ function TestElementAdditional:test_element_with_text()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
text = "Hello World"
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.text, "Hello World")
|
||||
end
|
||||
|
||||
function TestElementAdditional:test_element_with_text_color()
|
||||
local Color = require("modules.Color")
|
||||
local textColor = Color.new(255, 0, 0, 1)
|
||||
|
||||
|
||||
local element = FlexLove.new({
|
||||
id = "test",
|
||||
x = 0,
|
||||
@@ -1101,25 +1101,25 @@ function TestElementAdditional:test_element_with_text_color()
|
||||
width = 100,
|
||||
height = 100,
|
||||
text = "Red text",
|
||||
textColor = textColor
|
||||
textColor = textColor,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.textColor, textColor)
|
||||
end
|
||||
|
||||
function TestElementAdditional:test_element_with_background_color()
|
||||
local Color = require("modules.Color")
|
||||
local bgColor = Color.new(0, 0, 255, 1)
|
||||
|
||||
|
||||
local element = FlexLove.new({
|
||||
id = "test",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
backgroundColor = bgColor
|
||||
backgroundColor = bgColor,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.backgroundColor, bgColor)
|
||||
end
|
||||
|
||||
@@ -1130,9 +1130,9 @@ function TestElementAdditional:test_element_with_corner_radius()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
cornerRadius = 10
|
||||
cornerRadius = 10,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element.cornerRadius)
|
||||
luaunit.assertEquals(element.cornerRadius.topLeft, 10)
|
||||
luaunit.assertEquals(element.cornerRadius.topRight, 10)
|
||||
@@ -1147,9 +1147,9 @@ function TestElementAdditional:test_element_with_margin()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
margin = { top = 5, right = 10, bottom = 5, left = 10 }
|
||||
margin = { top = 5, right = 10, bottom = 5, left = 10 },
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertNotNil(element.margin)
|
||||
luaunit.assertEquals(element.margin.top, 5)
|
||||
luaunit.assertEquals(element.margin.right, 10)
|
||||
@@ -1163,18 +1163,18 @@ function TestElementAdditional:test_element_destroy()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = parent,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(#parent.children, 1)
|
||||
child:destroy()
|
||||
luaunit.assertNil(child.parent)
|
||||
@@ -1187,9 +1187,9 @@ function TestElementAdditional:test_element_with_disabled()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
disabled = true
|
||||
disabled = true,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertTrue(element.disabled)
|
||||
end
|
||||
|
||||
@@ -1200,30 +1200,29 @@ function TestElementAdditional:test_element_with_active()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
active = true
|
||||
active = true,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertTrue(element.active)
|
||||
end
|
||||
|
||||
function TestElementAdditional:test_element_with_userdata()
|
||||
local customData = { foo = "bar", count = 42 }
|
||||
|
||||
|
||||
local element = FlexLove.new({
|
||||
id = "test",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
userdata = customData
|
||||
userdata = customData,
|
||||
})
|
||||
|
||||
|
||||
luaunit.assertEquals(element.userdata, customData)
|
||||
luaunit.assertEquals(element.userdata.foo, "bar")
|
||||
luaunit.assertEquals(element.userdata.count, 42)
|
||||
end
|
||||
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
|
||||
141
testing/__tests__/error_handler_test.lua
Normal file
141
testing/__tests__/error_handler_test.lua
Normal file
@@ -0,0 +1,141 @@
|
||||
-- Test suite for ErrorHandler module
|
||||
package.path = package.path .. ";./?.lua;./modules/?.lua"
|
||||
|
||||
require("testing.loveStub")
|
||||
local luaunit = require("testing.luaunit")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
|
||||
TestErrorHandler = {}
|
||||
|
||||
-- Test: error() throws with correct format
|
||||
function TestErrorHandler:test_error_throws_with_format()
|
||||
local success, err = pcall(function()
|
||||
ErrorHandler.error("TestModule", "Something went wrong")
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success, "error() should throw")
|
||||
luaunit.assertStrContains(err, "[FlexLove - TestModule] Error: Something went wrong")
|
||||
end
|
||||
|
||||
-- Test: warn() prints with correct format
|
||||
function TestErrorHandler:test_warn_prints_with_format()
|
||||
-- Capture print output by mocking print
|
||||
local captured = nil
|
||||
local originalPrint = print
|
||||
print = function(msg)
|
||||
captured = msg
|
||||
end
|
||||
|
||||
ErrorHandler.warn("TestModule", "This is a warning")
|
||||
|
||||
print = originalPrint
|
||||
|
||||
luaunit.assertNotNil(captured, "warn() should print")
|
||||
luaunit.assertEquals(captured, "[FlexLove - TestModule] Warning: This is a warning")
|
||||
end
|
||||
|
||||
-- Test: assertNotNil returns true for non-nil value
|
||||
function TestErrorHandler:test_assertNotNil_returns_true_for_valid()
|
||||
local result = ErrorHandler.assertNotNil("TestModule", "some value", "testParam")
|
||||
luaunit.assertTrue(result, "assertNotNil should return true for non-nil value")
|
||||
end
|
||||
|
||||
-- Test: assertNotNil throws for nil value
|
||||
function TestErrorHandler:test_assertNotNil_throws_for_nil()
|
||||
local success, err = pcall(function()
|
||||
ErrorHandler.assertNotNil("TestModule", nil, "testParam")
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success, "assertNotNil should throw for nil")
|
||||
luaunit.assertStrContains(err, "Parameter 'testParam' cannot be nil")
|
||||
end
|
||||
|
||||
-- Test: assertType returns true for correct type
|
||||
function TestErrorHandler:test_assertType_returns_true_for_valid()
|
||||
local result = ErrorHandler.assertType("TestModule", "hello", "string", "testParam")
|
||||
luaunit.assertTrue(result, "assertType should return true for correct type")
|
||||
|
||||
result = ErrorHandler.assertType("TestModule", 123, "number", "testParam")
|
||||
luaunit.assertTrue(result, "assertType should return true for number")
|
||||
|
||||
result = ErrorHandler.assertType("TestModule", {}, "table", "testParam")
|
||||
luaunit.assertTrue(result, "assertType should return true for table")
|
||||
end
|
||||
|
||||
-- Test: assertType throws for wrong type
|
||||
function TestErrorHandler:test_assertType_throws_for_wrong_type()
|
||||
local success, err = pcall(function()
|
||||
ErrorHandler.assertType("TestModule", 123, "string", "testParam")
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success, "assertType should throw for wrong type")
|
||||
luaunit.assertStrContains(err, "Parameter 'testParam' must be string, got number")
|
||||
end
|
||||
|
||||
-- Test: assertRange returns true for value in range
|
||||
function TestErrorHandler:test_assertRange_returns_true_for_valid()
|
||||
local result = ErrorHandler.assertRange("TestModule", 5, 0, 10, "testParam")
|
||||
luaunit.assertTrue(result, "assertRange should return true for value in range")
|
||||
|
||||
result = ErrorHandler.assertRange("TestModule", 0, 0, 10, "testParam")
|
||||
luaunit.assertTrue(result, "assertRange should accept min boundary")
|
||||
|
||||
result = ErrorHandler.assertRange("TestModule", 10, 0, 10, "testParam")
|
||||
luaunit.assertTrue(result, "assertRange should accept max boundary")
|
||||
end
|
||||
|
||||
-- Test: assertRange throws for value below min
|
||||
function TestErrorHandler:test_assertRange_throws_for_below_min()
|
||||
local success, err = pcall(function()
|
||||
ErrorHandler.assertRange("TestModule", -1, 0, 10, "testParam")
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success, "assertRange should throw for value below min")
|
||||
luaunit.assertStrContains(err, "Parameter 'testParam' must be between 0 and 10, got -1")
|
||||
end
|
||||
|
||||
-- Test: assertRange throws for value above max
|
||||
function TestErrorHandler:test_assertRange_throws_for_above_max()
|
||||
local success, err = pcall(function()
|
||||
ErrorHandler.assertRange("TestModule", 11, 0, 10, "testParam")
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success, "assertRange should throw for value above max")
|
||||
luaunit.assertStrContains(err, "Parameter 'testParam' must be between 0 and 10, got 11")
|
||||
end
|
||||
|
||||
-- Test: warnDeprecated prints deprecation warning
|
||||
function TestErrorHandler:test_warnDeprecated_prints_message()
|
||||
local captured = nil
|
||||
local originalPrint = print
|
||||
print = function(msg)
|
||||
captured = msg
|
||||
end
|
||||
|
||||
ErrorHandler.warnDeprecated("TestModule", "oldFunction", "newFunction")
|
||||
|
||||
print = originalPrint
|
||||
|
||||
luaunit.assertNotNil(captured, "warnDeprecated should print")
|
||||
luaunit.assertStrContains(captured, "'oldFunction' is deprecated. Use 'newFunction' instead")
|
||||
end
|
||||
|
||||
-- Test: warnCommonMistake prints helpful message
|
||||
function TestErrorHandler:test_warnCommonMistake_prints_message()
|
||||
local captured = nil
|
||||
local originalPrint = print
|
||||
print = function(msg)
|
||||
captured = msg
|
||||
end
|
||||
|
||||
ErrorHandler.warnCommonMistake("TestModule", "Width is zero", "Set width to positive value")
|
||||
|
||||
print = originalPrint
|
||||
|
||||
luaunit.assertNotNil(captured, "warnCommonMistake should print")
|
||||
luaunit.assertStrContains(captured, "Width is zero. Suggestion: Set width to positive value")
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
522
testing/__tests__/event_handler_test.lua
Normal file
522
testing/__tests__/event_handler_test.lua
Normal file
@@ -0,0 +1,522 @@
|
||||
-- Test suite for EventHandler module
|
||||
package.path = package.path .. ";./?.lua;./modules/?.lua"
|
||||
|
||||
require("testing.loveStub")
|
||||
local luaunit = require("testing.luaunit")
|
||||
local EventHandler = require("modules.EventHandler")
|
||||
local InputEvent = require("modules.InputEvent")
|
||||
local utils = require("modules.utils")
|
||||
|
||||
TestEventHandler = {}
|
||||
|
||||
-- Mock Context module
|
||||
local MockContext = {
|
||||
getContext = function()
|
||||
return {}
|
||||
end,
|
||||
}
|
||||
|
||||
-- Helper to create EventHandler with dependencies
|
||||
local function createEventHandler(config)
|
||||
config = config or {}
|
||||
return EventHandler.new(config, {
|
||||
InputEvent = InputEvent,
|
||||
Context = MockContext,
|
||||
utils = utils,
|
||||
})
|
||||
end
|
||||
|
||||
-- Mock element
|
||||
local function createMockElement()
|
||||
return {
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
disabled = false,
|
||||
editable = false,
|
||||
_focused = false,
|
||||
padding = { left = 0, right = 0, top = 0, bottom = 0 },
|
||||
_borderBoxWidth = 100,
|
||||
_borderBoxHeight = 100,
|
||||
isFocused = function(self)
|
||||
return self._focused
|
||||
end,
|
||||
focus = function(self)
|
||||
self._focused = true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
-- Test: new() creates instance with defaults
|
||||
function TestEventHandler:test_new_creates_with_defaults()
|
||||
local handler = createEventHandler()
|
||||
|
||||
luaunit.assertNotNil(handler)
|
||||
luaunit.assertNil(handler.onEvent)
|
||||
luaunit.assertNotNil(handler._pressed)
|
||||
luaunit.assertEquals(handler._clickCount, 0)
|
||||
luaunit.assertFalse(handler._hovered)
|
||||
luaunit.assertFalse(handler._scrollbarPressHandled)
|
||||
end
|
||||
|
||||
-- Test: new() accepts custom config
|
||||
function TestEventHandler:test_new_accepts_custom_config()
|
||||
local onEventCalled = false
|
||||
local handler = createEventHandler({
|
||||
onEvent = function()
|
||||
onEventCalled = true
|
||||
end,
|
||||
_clickCount = 5,
|
||||
_hovered = true,
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(handler.onEvent)
|
||||
luaunit.assertEquals(handler._clickCount, 5)
|
||||
luaunit.assertTrue(handler._hovered)
|
||||
end
|
||||
|
||||
-- Test: initialize() sets element reference
|
||||
function TestEventHandler:test_initialize_sets_element()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
|
||||
handler:initialize(element)
|
||||
|
||||
luaunit.assertEquals(handler._element, element)
|
||||
end
|
||||
|
||||
-- Test: getState() returns state data
|
||||
function TestEventHandler:test_getState_returns_state()
|
||||
local handler = createEventHandler({
|
||||
_clickCount = 3,
|
||||
_hovered = true,
|
||||
})
|
||||
handler._pressed[1] = true
|
||||
handler._lastClickTime = 12345
|
||||
|
||||
local state = handler:getState()
|
||||
|
||||
luaunit.assertNotNil(state)
|
||||
luaunit.assertEquals(state._clickCount, 3)
|
||||
luaunit.assertTrue(state._hovered)
|
||||
luaunit.assertTrue(state._pressed[1])
|
||||
luaunit.assertEquals(state._lastClickTime, 12345)
|
||||
end
|
||||
|
||||
-- Test: setState() restores state
|
||||
function TestEventHandler:test_setState_restores_state()
|
||||
local handler = createEventHandler()
|
||||
|
||||
local state = {
|
||||
_pressed = { [1] = true },
|
||||
_clickCount = 2,
|
||||
_hovered = true,
|
||||
_lastClickTime = 5000,
|
||||
_lastClickButton = 1,
|
||||
}
|
||||
|
||||
handler:setState(state)
|
||||
|
||||
luaunit.assertEquals(handler._clickCount, 2)
|
||||
luaunit.assertTrue(handler._hovered)
|
||||
luaunit.assertTrue(handler._pressed[1])
|
||||
luaunit.assertEquals(handler._lastClickTime, 5000)
|
||||
luaunit.assertEquals(handler._lastClickButton, 1)
|
||||
end
|
||||
|
||||
-- Test: setState() handles nil gracefully
|
||||
function TestEventHandler:test_setState_handles_nil()
|
||||
local handler = createEventHandler()
|
||||
handler._clickCount = 5
|
||||
|
||||
handler:setState(nil)
|
||||
|
||||
-- Should not error, should preserve original state
|
||||
luaunit.assertEquals(handler._clickCount, 5)
|
||||
end
|
||||
|
||||
-- Test: setState() uses defaults for missing values
|
||||
function TestEventHandler:test_setState_uses_defaults()
|
||||
local handler = createEventHandler()
|
||||
|
||||
handler:setState({}) -- Empty state
|
||||
|
||||
luaunit.assertNotNil(handler._pressed)
|
||||
luaunit.assertEquals(handler._clickCount, 0)
|
||||
luaunit.assertFalse(handler._hovered)
|
||||
end
|
||||
|
||||
-- Test: resetScrollbarPressFlag() resets flag
|
||||
function TestEventHandler:test_resetScrollbarPressFlag()
|
||||
local handler = createEventHandler()
|
||||
handler._scrollbarPressHandled = true
|
||||
|
||||
handler:resetScrollbarPressFlag()
|
||||
|
||||
luaunit.assertFalse(handler._scrollbarPressHandled)
|
||||
end
|
||||
|
||||
-- Test: isAnyButtonPressed() returns false when no buttons pressed
|
||||
function TestEventHandler:test_isAnyButtonPressed_returns_false()
|
||||
local handler = createEventHandler()
|
||||
|
||||
luaunit.assertFalse(handler:isAnyButtonPressed())
|
||||
end
|
||||
|
||||
-- Test: isAnyButtonPressed() returns true when button pressed
|
||||
function TestEventHandler:test_isAnyButtonPressed_returns_true()
|
||||
local handler = createEventHandler()
|
||||
handler._pressed[1] = true
|
||||
|
||||
luaunit.assertTrue(handler:isAnyButtonPressed())
|
||||
end
|
||||
|
||||
-- Test: isButtonPressed() checks specific button
|
||||
function TestEventHandler:test_isButtonPressed_checks_specific_button()
|
||||
local handler = createEventHandler()
|
||||
handler._pressed[1] = true
|
||||
handler._pressed[2] = false
|
||||
|
||||
luaunit.assertTrue(handler:isButtonPressed(1))
|
||||
luaunit.assertFalse(handler:isButtonPressed(2))
|
||||
luaunit.assertFalse(handler:isButtonPressed(3))
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() returns early if no element
|
||||
function TestEventHandler:test_processMouseEvents_no_element()
|
||||
local handler = createEventHandler()
|
||||
|
||||
-- Should not error
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() handles press event
|
||||
function TestEventHandler:test_processMouseEvents_press()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventReceived = nil
|
||||
handler.onEvent = function(el, event)
|
||||
eventReceived = event
|
||||
end
|
||||
|
||||
-- Mock love.mouse.isDown for button 1
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 1
|
||||
end
|
||||
|
||||
-- First call - button just pressed
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
luaunit.assertNotNil(eventReceived)
|
||||
luaunit.assertEquals(eventReceived.type, "press")
|
||||
luaunit.assertEquals(eventReceived.button, 1)
|
||||
luaunit.assertTrue(handler._pressed[1])
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() handles drag event
|
||||
function TestEventHandler:test_processMouseEvents_drag()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 1
|
||||
end
|
||||
|
||||
-- First call - press at (50, 50)
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Second call - drag to (60, 70)
|
||||
handler:processMouseEvents(60, 70, true, true)
|
||||
|
||||
luaunit.assertTrue(#eventsReceived >= 2)
|
||||
-- Find drag event
|
||||
local dragEvent = nil
|
||||
for _, event in ipairs(eventsReceived) do
|
||||
if event.type == "drag" then
|
||||
dragEvent = event
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
luaunit.assertNotNil(dragEvent, "Should receive drag event")
|
||||
luaunit.assertEquals(dragEvent.dx, 10)
|
||||
luaunit.assertEquals(dragEvent.dy, 20)
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() handles release and click
|
||||
function TestEventHandler:test_processMouseEvents_release_and_click()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
local isButtonDown = true
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 1 and isButtonDown
|
||||
end
|
||||
|
||||
-- Press
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Release
|
||||
isButtonDown = false
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Should have: press, click, release events
|
||||
luaunit.assertTrue(#eventsReceived >= 3)
|
||||
|
||||
local hasPress = false
|
||||
local hasClick = false
|
||||
local hasRelease = false
|
||||
|
||||
for _, event in ipairs(eventsReceived) do
|
||||
if event.type == "press" then
|
||||
hasPress = true
|
||||
end
|
||||
if event.type == "click" then
|
||||
hasClick = true
|
||||
end
|
||||
if event.type == "release" then
|
||||
hasRelease = true
|
||||
end
|
||||
end
|
||||
|
||||
luaunit.assertTrue(hasPress, "Should have press event")
|
||||
luaunit.assertTrue(hasClick, "Should have click event")
|
||||
luaunit.assertTrue(hasRelease, "Should have release event")
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() detects double-click
|
||||
function TestEventHandler:test_processMouseEvents_double_click()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
local isButtonDown = false
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 1 and isButtonDown
|
||||
end
|
||||
|
||||
-- First click
|
||||
isButtonDown = true
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
isButtonDown = false
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Second click (quickly after first)
|
||||
isButtonDown = true
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
isButtonDown = false
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Find click events
|
||||
local clickEvents = {}
|
||||
for _, event in ipairs(eventsReceived) do
|
||||
if event.type == "click" then
|
||||
table.insert(clickEvents, event)
|
||||
end
|
||||
end
|
||||
|
||||
luaunit.assertTrue(#clickEvents >= 2)
|
||||
-- Second click should have clickCount = 2
|
||||
if #clickEvents >= 2 then
|
||||
luaunit.assertEquals(clickEvents[2].clickCount, 2)
|
||||
end
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() handles rightclick
|
||||
function TestEventHandler:test_processMouseEvents_rightclick()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
local isButtonDown = false
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 2 and isButtonDown
|
||||
end
|
||||
|
||||
-- Right click press and release
|
||||
isButtonDown = true
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
isButtonDown = false
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
local hasRightClick = false
|
||||
for _, event in ipairs(eventsReceived) do
|
||||
if event.type == "rightclick" then
|
||||
hasRightClick = true
|
||||
luaunit.assertEquals(event.button, 2)
|
||||
end
|
||||
end
|
||||
|
||||
luaunit.assertTrue(hasRightClick, "Should have rightclick event")
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() handles middleclick
|
||||
function TestEventHandler:test_processMouseEvents_middleclick()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
local isButtonDown = false
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 3 and isButtonDown
|
||||
end
|
||||
|
||||
-- Middle click press and release
|
||||
isButtonDown = true
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
isButtonDown = false
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
local hasMiddleClick = false
|
||||
for _, event in ipairs(eventsReceived) do
|
||||
if event.type == "middleclick" then
|
||||
hasMiddleClick = true
|
||||
luaunit.assertEquals(event.button, 3)
|
||||
end
|
||||
end
|
||||
|
||||
luaunit.assertTrue(hasMiddleClick, "Should have middleclick event")
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processMouseEvents() respects disabled state
|
||||
function TestEventHandler:test_processMouseEvents_disabled()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
element.disabled = true
|
||||
handler:initialize(element)
|
||||
|
||||
local eventReceived = false
|
||||
handler.onEvent = function(el, event)
|
||||
eventReceived = true
|
||||
end
|
||||
|
||||
local originalIsDown = love.mouse.isDown
|
||||
love.mouse.isDown = function(button)
|
||||
return button == 1
|
||||
end
|
||||
|
||||
handler:processMouseEvents(50, 50, true, true)
|
||||
|
||||
-- Should not fire event for disabled element
|
||||
luaunit.assertFalse(eventReceived)
|
||||
|
||||
love.mouse.isDown = originalIsDown
|
||||
end
|
||||
|
||||
-- Test: processTouchEvents() handles touch
|
||||
function TestEventHandler:test_processTouchEvents()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
local eventsReceived = {}
|
||||
handler.onEvent = function(el, event)
|
||||
table.insert(eventsReceived, event)
|
||||
end
|
||||
|
||||
-- Mock touch API
|
||||
local originalGetTouches = love.touch.getTouches
|
||||
local originalGetPosition = love.touch.getPosition
|
||||
|
||||
love.touch.getTouches = function()
|
||||
return { "touch1" }
|
||||
end
|
||||
|
||||
love.touch.getPosition = function(id)
|
||||
if id == "touch1" then
|
||||
return 150, 150 -- Outside element bounds
|
||||
end
|
||||
end
|
||||
|
||||
-- First call - touch starts inside
|
||||
love.touch.getPosition = function(id)
|
||||
if id == "touch1" then
|
||||
return 50, 50 -- Inside element
|
||||
end
|
||||
end
|
||||
handler:processTouchEvents()
|
||||
|
||||
-- Second call - touch moves outside
|
||||
love.touch.getPosition = function(id)
|
||||
if id == "touch1" then
|
||||
return 150, 150 -- Outside element
|
||||
end
|
||||
end
|
||||
handler:processTouchEvents()
|
||||
|
||||
-- Should receive touch event
|
||||
luaunit.assertTrue(#eventsReceived >= 1)
|
||||
|
||||
love.touch.getTouches = originalGetTouches
|
||||
love.touch.getPosition = originalGetPosition
|
||||
end
|
||||
|
||||
-- Test: processTouchEvents() returns early if no element
|
||||
function TestEventHandler:test_processTouchEvents_no_element()
|
||||
local handler = createEventHandler()
|
||||
|
||||
-- Should not error
|
||||
handler:processTouchEvents()
|
||||
end
|
||||
|
||||
-- Test: processTouchEvents() returns early if no onEvent
|
||||
function TestEventHandler:test_processTouchEvents_no_onEvent()
|
||||
local handler = createEventHandler()
|
||||
local element = createMockElement()
|
||||
handler:initialize(element)
|
||||
|
||||
-- Should not error (no onEvent callback)
|
||||
handler:processTouchEvents()
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
@@ -24,20 +24,20 @@ function TestGridLayout:test_default_grid_single_child()
|
||||
y = 0,
|
||||
width = 400,
|
||||
height = 300,
|
||||
positioning = "grid"
|
||||
positioning = "grid",
|
||||
-- Default: gridRows=1, gridColumns=1
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 50, -- Will be stretched by grid
|
||||
height = 50
|
||||
width = 50, -- Will be stretched by grid
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Child should be stretched to fill the entire grid cell
|
||||
luaunit.assertEquals(child.x, 0, "Child should be at x=0")
|
||||
luaunit.assertEquals(child.y, 0, "Child should be at y=0")
|
||||
@@ -55,37 +55,37 @@ function TestGridLayout:test_2x2_grid_four_children()
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
local children = {}
|
||||
for i = 1, 4 do
|
||||
children[i] = FlexLove.new({
|
||||
id = "child" .. i,
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Each cell should be 200x200
|
||||
-- Child 1: top-left (0, 0)
|
||||
luaunit.assertEquals(children[1].x, 0, "Child 1 should be at x=0")
|
||||
luaunit.assertEquals(children[1].y, 0, "Child 1 should be at y=0")
|
||||
luaunit.assertEquals(children[1].width, 200, "Cell width should be 200")
|
||||
luaunit.assertEquals(children[1].height, 200, "Cell height should be 200")
|
||||
|
||||
|
||||
-- Child 2: top-right (200, 0)
|
||||
luaunit.assertEquals(children[2].x, 200, "Child 2 should be at x=200")
|
||||
luaunit.assertEquals(children[2].y, 0, "Child 2 should be at y=0")
|
||||
|
||||
|
||||
-- Child 3: bottom-left (0, 200)
|
||||
luaunit.assertEquals(children[3].x, 0, "Child 3 should be at x=0")
|
||||
luaunit.assertEquals(children[3].y, 200, "Child 3 should be at y=200")
|
||||
|
||||
|
||||
-- Child 4: bottom-right (200, 200)
|
||||
luaunit.assertEquals(children[4].x, 200, "Child 4 should be at x=200")
|
||||
luaunit.assertEquals(children[4].y, 200, "Child 4 should be at y=200")
|
||||
@@ -97,36 +97,36 @@ function TestGridLayout:test_grid_with_gaps()
|
||||
id = "grid",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 420, -- 2 cells * 200 + 1 gap * 20
|
||||
height = 320, -- 2 cells * 150 + 1 gap * 20
|
||||
width = 420, -- 2 cells * 200 + 1 gap * 20
|
||||
height = 320, -- 2 cells * 150 + 1 gap * 20
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2,
|
||||
columnGap = 20,
|
||||
rowGap = 20
|
||||
rowGap = 20,
|
||||
})
|
||||
|
||||
|
||||
local children = {}
|
||||
for i = 1, 4 do
|
||||
children[i] = FlexLove.new({
|
||||
id = "child" .. i,
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Cell size: (420 - 20) / 2 = 200, (320 - 20) / 2 = 150
|
||||
luaunit.assertEquals(children[1].width, 200, "Cell width should be 200")
|
||||
luaunit.assertEquals(children[1].height, 150, "Cell height should be 150")
|
||||
|
||||
|
||||
-- Child 2 should be offset by cell width + gap
|
||||
luaunit.assertEquals(children[2].x, 220, "Child 2 x = 200 + 20 gap")
|
||||
luaunit.assertEquals(children[2].y, 0, "Child 2 should be at y=0")
|
||||
|
||||
|
||||
-- Child 3 should be offset by cell height + gap
|
||||
luaunit.assertEquals(children[3].x, 0, "Child 3 should be at x=0")
|
||||
luaunit.assertEquals(children[3].y, 170, "Child 3 y = 150 + 20 gap")
|
||||
@@ -142,27 +142,27 @@ function TestGridLayout:test_grid_overflow_children()
|
||||
height = 200,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
-- Only 4 cells available
|
||||
})
|
||||
|
||||
|
||||
local children = {}
|
||||
for i = 1, 6 do -- 6 children, but only 4 cells
|
||||
for i = 1, 6 do -- 6 children, but only 4 cells
|
||||
children[i] = FlexLove.new({
|
||||
id = "child" .. i,
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- First 4 children should be positioned
|
||||
luaunit.assertNotNil(children[1].x, "Child 1 should be positioned")
|
||||
luaunit.assertNotNil(children[4].x, "Child 4 should be positioned")
|
||||
|
||||
|
||||
-- Children 5 and 6 should NOT be positioned (or positioned at 0,0 by default)
|
||||
-- This tests the overflow behavior: row >= rows breaks the loop
|
||||
end
|
||||
@@ -178,19 +178,19 @@ function TestGridLayout:test_grid_align_center()
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2,
|
||||
alignItems = "center"
|
||||
alignItems = "center",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Cell is 200x200, child is 100x100, should be centered
|
||||
-- Center position: (200 - 100) / 2 = 50
|
||||
luaunit.assertEquals(child.x, 50, "Child should be centered horizontally in cell")
|
||||
@@ -210,19 +210,19 @@ function TestGridLayout:test_grid_align_flex_start()
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2,
|
||||
alignItems = "flex-start"
|
||||
alignItems = "flex-start",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Child should be at top-left of cell
|
||||
luaunit.assertEquals(child.x, 0, "Child should be at left of cell")
|
||||
luaunit.assertEquals(child.y, 0, "Child should be at top of cell")
|
||||
@@ -241,19 +241,19 @@ function TestGridLayout:test_grid_align_flex_end()
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2,
|
||||
alignItems = "flex-end"
|
||||
alignItems = "flex-end",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Cell is 200x200, child is 100x100, should be at bottom-right
|
||||
luaunit.assertEquals(child.x, 100, "Child should be at right of cell (200 - 100)")
|
||||
luaunit.assertEquals(child.y, 100, "Child should be at bottom of cell (200 - 100)")
|
||||
@@ -267,24 +267,24 @@ function TestGridLayout:test_grid_with_padding()
|
||||
id = "grid",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 500, -- Total width
|
||||
width = 500, -- Total width
|
||||
height = 500,
|
||||
padding = { top = 50, right = 50, bottom = 50, left = 50 },
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Available space: 500 - 50 - 50 = 400
|
||||
-- Cell size: 400 / 2 = 200
|
||||
-- Child should be positioned at padding.left, padding.top
|
||||
@@ -304,17 +304,17 @@ function TestGridLayout:test_grid_with_absolute_child()
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
-- Regular child
|
||||
local child1 = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
-- Absolutely positioned child (should be ignored by grid layout)
|
||||
local child2 = FlexLove.new({
|
||||
id = "child2",
|
||||
@@ -323,28 +323,28 @@ function TestGridLayout:test_grid_with_absolute_child()
|
||||
x = 10,
|
||||
y = 10,
|
||||
width = 30,
|
||||
height = 30
|
||||
height = 30,
|
||||
})
|
||||
|
||||
|
||||
-- Another regular child
|
||||
local child3 = FlexLove.new({
|
||||
id = "child3",
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- child1 should be in first grid cell (0, 0)
|
||||
luaunit.assertEquals(child1.x, 0, "Child 1 should be at x=0")
|
||||
luaunit.assertEquals(child1.y, 0, "Child 1 should be at y=0")
|
||||
|
||||
|
||||
-- child2 should keep its absolute position
|
||||
luaunit.assertEquals(child2.x, 10, "Absolute child should keep x=10")
|
||||
luaunit.assertEquals(child2.y, 10, "Absolute child should keep y=10")
|
||||
|
||||
|
||||
-- child3 should be in second grid cell (200, 0), not third
|
||||
luaunit.assertEquals(child3.x, 200, "Child 3 should be in second cell at x=200")
|
||||
luaunit.assertEquals(child3.y, 0, "Child 3 should be in second cell at y=0")
|
||||
@@ -360,13 +360,13 @@ function TestGridLayout:test_empty_grid()
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
-- No children
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Should not crash
|
||||
luaunit.assertEquals(#container.children, 0, "Grid should have no children")
|
||||
end
|
||||
@@ -380,21 +380,21 @@ function TestGridLayout:test_grid_zero_dimensions()
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 0, -- Invalid: 0 rows
|
||||
gridColumns = 0 -- Invalid: 0 columns
|
||||
gridRows = 0, -- Invalid: 0 rows
|
||||
gridColumns = 0, -- Invalid: 0 columns
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
-- This might cause division by zero or other errors
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Test passes if it doesn't crash
|
||||
luaunit.assertTrue(true, "Grid with 0 dimensions should not crash")
|
||||
end
|
||||
@@ -409,9 +409,9 @@ function TestGridLayout:test_nested_grids()
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
-- First cell contains another grid
|
||||
local innerGrid = FlexLove.new({
|
||||
id = "inner",
|
||||
@@ -420,22 +420,22 @@ function TestGridLayout:test_nested_grids()
|
||||
height = 200,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
-- Add children to inner grid
|
||||
for i = 1, 4 do
|
||||
FlexLove.new({
|
||||
id = "inner_child" .. i,
|
||||
parent = innerGrid,
|
||||
width = 25,
|
||||
height = 25
|
||||
height = 25,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Inner grid should be positioned in first cell of outer grid
|
||||
luaunit.assertEquals(innerGrid.x, 0, "Inner grid should be at x=0")
|
||||
luaunit.assertEquals(innerGrid.y, 0, "Inner grid should be at y=0")
|
||||
@@ -452,9 +452,9 @@ function TestGridLayout:test_grid_with_reserved_space()
|
||||
height = 400,
|
||||
positioning = "grid",
|
||||
gridRows = 2,
|
||||
gridColumns = 2
|
||||
gridColumns = 2,
|
||||
})
|
||||
|
||||
|
||||
-- Absolute child with left positioning (reserves left space)
|
||||
FlexLove.new({
|
||||
id = "absolute_left",
|
||||
@@ -463,20 +463,20 @@ function TestGridLayout:test_grid_with_reserved_space()
|
||||
left = 0,
|
||||
top = 0,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
-- Regular grid child
|
||||
local child1 = FlexLove.new({
|
||||
id = "child1",
|
||||
parent = container,
|
||||
width = 50,
|
||||
height = 50
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Grid should account for reserved space
|
||||
-- Available width: 400 - 50 (reserved left) = 350
|
||||
-- Cell width: 350 / 2 = 175
|
||||
|
||||
166
testing/__tests__/image_cache_test.lua
Normal file
166
testing/__tests__/image_cache_test.lua
Normal file
@@ -0,0 +1,166 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local ImageCache = require("modules.ImageCache")
|
||||
|
||||
TestImageCache = {}
|
||||
|
||||
function TestImageCache:setUp()
|
||||
-- Clear cache before each test
|
||||
ImageCache.clear()
|
||||
end
|
||||
|
||||
function TestImageCache:tearDown()
|
||||
ImageCache.clear()
|
||||
end
|
||||
|
||||
-- Unhappy path tests
|
||||
|
||||
function TestImageCache:testLoadWithNilPath()
|
||||
local img, err = ImageCache.load(nil)
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "Invalid image path")
|
||||
end
|
||||
|
||||
function TestImageCache:testLoadWithEmptyString()
|
||||
local img, err = ImageCache.load("")
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "Invalid image path")
|
||||
end
|
||||
|
||||
function TestImageCache:testLoadWithInvalidType()
|
||||
local img, err = ImageCache.load(123)
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "Invalid image path")
|
||||
end
|
||||
|
||||
function TestImageCache:testLoadWithInvalidPath()
|
||||
local img, err = ImageCache.load("nonexistent/path/to/image.png")
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "Failed to load image")
|
||||
end
|
||||
|
||||
function TestImageCache:testLoadWithInvalidExtension()
|
||||
local img, err = ImageCache.load("test.txt")
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "Failed to load image")
|
||||
end
|
||||
|
||||
function TestImageCache:testGetWithNilPath()
|
||||
local img = ImageCache.get(nil)
|
||||
luaunit.assertNil(img)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetWithEmptyString()
|
||||
local img = ImageCache.get("")
|
||||
luaunit.assertNil(img)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetWithInvalidType()
|
||||
local img = ImageCache.get({})
|
||||
luaunit.assertNil(img)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetWithNonCachedPath()
|
||||
local img = ImageCache.get("some/random/path.png")
|
||||
luaunit.assertNil(img)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetImageDataWithNilPath()
|
||||
local imgData = ImageCache.getImageData(nil)
|
||||
luaunit.assertNil(imgData)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetImageDataWithEmptyString()
|
||||
local imgData = ImageCache.getImageData("")
|
||||
luaunit.assertNil(imgData)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetImageDataWithInvalidType()
|
||||
local imgData = ImageCache.getImageData(123)
|
||||
luaunit.assertNil(imgData)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetImageDataWithNonCachedPath()
|
||||
local imgData = ImageCache.getImageData("some/path.png")
|
||||
luaunit.assertNil(imgData)
|
||||
end
|
||||
|
||||
function TestImageCache:testRemoveWithNilPath()
|
||||
local removed = ImageCache.remove(nil)
|
||||
luaunit.assertFalse(removed)
|
||||
end
|
||||
|
||||
function TestImageCache:testRemoveWithEmptyString()
|
||||
local removed = ImageCache.remove("")
|
||||
luaunit.assertFalse(removed)
|
||||
end
|
||||
|
||||
function TestImageCache:testRemoveWithInvalidType()
|
||||
local removed = ImageCache.remove(123)
|
||||
luaunit.assertFalse(removed)
|
||||
end
|
||||
|
||||
function TestImageCache:testRemoveWithNonCachedPath()
|
||||
local removed = ImageCache.remove("uncached/image.png")
|
||||
luaunit.assertFalse(removed)
|
||||
end
|
||||
|
||||
function TestImageCache:testClearEmptyCache()
|
||||
-- Should not error on empty cache
|
||||
ImageCache.clear()
|
||||
local stats = ImageCache.getStats()
|
||||
luaunit.assertEquals(stats.count, 0)
|
||||
luaunit.assertEquals(stats.memoryEstimate, 0)
|
||||
end
|
||||
|
||||
function TestImageCache:testGetStatsOnEmptyCache()
|
||||
ImageCache.clear()
|
||||
local stats = ImageCache.getStats()
|
||||
luaunit.assertNotNil(stats)
|
||||
luaunit.assertEquals(stats.count, 0)
|
||||
luaunit.assertEquals(stats.memoryEstimate, 0)
|
||||
end
|
||||
|
||||
function TestImageCache:testPathNormalization()
|
||||
-- Test that paths with different slashes are normalized
|
||||
local path1 = "themes/space.png"
|
||||
local path2 = "themes\\space.png" -- Windows style
|
||||
|
||||
-- Both should normalize to the same path
|
||||
-- (If first load fails, both should fail the same way)
|
||||
local img1, err1 = ImageCache.load(path1)
|
||||
local img2, err2 = ImageCache.load(path2)
|
||||
|
||||
-- Both should have same result (either both nil or both same image)
|
||||
if img1 == nil then
|
||||
luaunit.assertNil(img2)
|
||||
else
|
||||
luaunit.assertEquals(img1, img2)
|
||||
end
|
||||
end
|
||||
|
||||
function TestImageCache:testLoadWithImageDataFlag()
|
||||
-- Test loading with imageData flag on invalid path
|
||||
local img, err = ImageCache.load("invalid/path.png", true)
|
||||
luaunit.assertNil(img)
|
||||
luaunit.assertNotNil(err)
|
||||
end
|
||||
|
||||
function TestImageCache:testMultipleClearCalls()
|
||||
-- Clear multiple times should not error
|
||||
ImageCache.clear()
|
||||
ImageCache.clear()
|
||||
ImageCache.clear()
|
||||
local stats = ImageCache.getStats()
|
||||
luaunit.assertEquals(stats.count, 0)
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
247
testing/__tests__/image_renderer_test.lua
Normal file
247
testing/__tests__/image_renderer_test.lua
Normal file
@@ -0,0 +1,247 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local ImageRenderer = require("modules.ImageRenderer")
|
||||
|
||||
TestImageRenderer = {}
|
||||
|
||||
function TestImageRenderer:setUp()
|
||||
-- Create a mock image for testing
|
||||
self.mockImage = {
|
||||
getDimensions = function()
|
||||
return 100, 100
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
-- Unhappy path tests for calculateFit
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithZeroImageWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(0, 100, 200, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithZeroImageHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 0, 200, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithNegativeImageWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(-100, 100, 200, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithNegativeImageHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, -100, 200, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithZeroBoundsWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 100, 0, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithZeroBoundsHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 100, 200, 0, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithNegativeBoundsWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 100, -200, 200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithNegativeBoundsHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 100, 200, -200, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithInvalidFitMode()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.calculateFit(100, 100, 200, 200, "invalid-mode")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithNilFitMode()
|
||||
-- Should default to "fill"
|
||||
local result = ImageRenderer.calculateFit(100, 100, 200, 200, nil)
|
||||
luaunit.assertNotNil(result)
|
||||
luaunit.assertEquals(result.dw, 200)
|
||||
luaunit.assertEquals(result.dh, 200)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitFillMode()
|
||||
local result = ImageRenderer.calculateFit(100, 100, 200, 200, "fill")
|
||||
luaunit.assertEquals(result.scaleX, 2)
|
||||
luaunit.assertEquals(result.scaleY, 2)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitContainMode()
|
||||
local result = ImageRenderer.calculateFit(100, 100, 200, 200, "contain")
|
||||
luaunit.assertEquals(result.scaleX, 2)
|
||||
luaunit.assertEquals(result.scaleY, 2)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitCoverMode()
|
||||
local result = ImageRenderer.calculateFit(100, 100, 200, 200, "cover")
|
||||
luaunit.assertEquals(result.scaleX, 2)
|
||||
luaunit.assertEquals(result.scaleY, 2)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitNoneMode()
|
||||
local result = ImageRenderer.calculateFit(100, 100, 200, 200, "none")
|
||||
luaunit.assertEquals(result.scaleX, 1)
|
||||
luaunit.assertEquals(result.scaleY, 1)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitScaleDownModeWithLargeImage()
|
||||
local result = ImageRenderer.calculateFit(300, 300, 200, 200, "scale-down")
|
||||
-- Should behave like contain for larger images
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitScaleDownModeWithSmallImage()
|
||||
local result = ImageRenderer.calculateFit(50, 50, 200, 200, "scale-down")
|
||||
-- Should behave like none for smaller images
|
||||
luaunit.assertEquals(result.scaleX, 1)
|
||||
luaunit.assertEquals(result.scaleY, 1)
|
||||
end
|
||||
|
||||
-- Unhappy path tests for _parsePosition
|
||||
|
||||
function TestImageRenderer:testParsePositionWithNil()
|
||||
local x, y = ImageRenderer._parsePosition(nil)
|
||||
luaunit.assertEquals(x, 0.5)
|
||||
luaunit.assertEquals(y, 0.5)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithEmptyString()
|
||||
local x, y = ImageRenderer._parsePosition("")
|
||||
luaunit.assertEquals(x, 0.5)
|
||||
luaunit.assertEquals(y, 0.5)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithInvalidType()
|
||||
local x, y = ImageRenderer._parsePosition(123)
|
||||
luaunit.assertEquals(x, 0.5)
|
||||
luaunit.assertEquals(y, 0.5)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithInvalidKeyword()
|
||||
local x, y = ImageRenderer._parsePosition("invalid keyword")
|
||||
-- Should default to center
|
||||
luaunit.assertEquals(x, 0.5)
|
||||
luaunit.assertEquals(y, 0.5)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithMixedValid()
|
||||
local x, y = ImageRenderer._parsePosition("left top")
|
||||
luaunit.assertEquals(x, 0)
|
||||
luaunit.assertEquals(y, 0)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithPercentage()
|
||||
local x, y = ImageRenderer._parsePosition("75% 25%")
|
||||
luaunit.assertAlmostEquals(x, 0.75, 0.01)
|
||||
luaunit.assertAlmostEquals(y, 0.25, 0.01)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithOutOfRangePercentage()
|
||||
local x, y = ImageRenderer._parsePosition("150% -50%")
|
||||
-- 150% clamps to 1, but -50% doesn't match pattern so defaults to 0.5
|
||||
luaunit.assertEquals(x, 1)
|
||||
luaunit.assertEquals(y, 0.5)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithSingleValue()
|
||||
local x, y = ImageRenderer._parsePosition("left")
|
||||
luaunit.assertEquals(x, 0)
|
||||
luaunit.assertEquals(y, 0.5) -- Should use center for Y
|
||||
end
|
||||
|
||||
function TestImageRenderer:testParsePositionWithSinglePercentage()
|
||||
local x, y = ImageRenderer._parsePosition("25%")
|
||||
luaunit.assertAlmostEquals(x, 0.25, 0.01)
|
||||
luaunit.assertAlmostEquals(y, 0.25, 0.01)
|
||||
end
|
||||
|
||||
-- Unhappy path tests for draw
|
||||
|
||||
function TestImageRenderer:testDrawWithNilImage()
|
||||
-- Should not crash, just return early
|
||||
ImageRenderer.draw(nil, 0, 0, 100, 100, "fill")
|
||||
-- If we get here without error, test passes
|
||||
luaunit.assertTrue(true)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testDrawWithZeroWidth()
|
||||
-- Should error in calculateFit
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.draw(self.mockImage, 0, 0, 0, 100, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testDrawWithZeroHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.draw(self.mockImage, 0, 0, 100, 0, "fill")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testDrawWithNegativeOpacity()
|
||||
-- Should work but render with negative opacity
|
||||
ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "fill", "center center", -0.5)
|
||||
luaunit.assertTrue(true)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testDrawWithOpacityGreaterThanOne()
|
||||
-- Should work but render with >1 opacity
|
||||
ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "fill", "center center", 2.0)
|
||||
luaunit.assertTrue(true)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testDrawWithInvalidFitMode()
|
||||
luaunit.assertError(function()
|
||||
ImageRenderer.draw(self.mockImage, 0, 0, 100, 100, "invalid")
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithVerySmallBounds()
|
||||
local result = ImageRenderer.calculateFit(1000, 1000, 1, 1, "contain")
|
||||
luaunit.assertNotNil(result)
|
||||
-- Scale should be very small
|
||||
luaunit.assertTrue(result.scaleX < 0.01)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithVeryLargeBounds()
|
||||
local result = ImageRenderer.calculateFit(10, 10, 10000, 10000, "contain")
|
||||
luaunit.assertNotNil(result)
|
||||
-- Scale should be very large
|
||||
luaunit.assertTrue(result.scaleX > 100)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitWithAspectRatioMismatch()
|
||||
-- Wide image, tall bounds
|
||||
local result = ImageRenderer.calculateFit(200, 100, 100, 200, "contain")
|
||||
luaunit.assertNotNil(result)
|
||||
-- Should maintain aspect ratio
|
||||
luaunit.assertEquals(result.scaleX, result.scaleY)
|
||||
end
|
||||
|
||||
function TestImageRenderer:testCalculateFitCoverWithAspectRatioMismatch()
|
||||
-- Wide image, tall bounds
|
||||
local result = ImageRenderer.calculateFit(200, 100, 100, 200, "cover")
|
||||
luaunit.assertNotNil(result)
|
||||
luaunit.assertEquals(result.scaleX, result.scaleY)
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
209
testing/__tests__/image_scaler_test.lua
Normal file
209
testing/__tests__/image_scaler_test.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local ImageScaler = require("modules.ImageScaler")
|
||||
|
||||
TestImageScaler = {}
|
||||
|
||||
function TestImageScaler:setUp()
|
||||
-- Create a minimal mock ImageData
|
||||
self.mockImageData = {
|
||||
getPixel = function(self, x, y)
|
||||
-- Return deterministic values based on position
|
||||
return (x % 256) / 255, (y % 256) / 255, 0.5, 1.0
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
-- Unhappy path tests for scaleNearest
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNilSource()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(nil, 0, 0, 10, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithZeroSourceWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 0, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithZeroSourceHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, 0, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNegativeSourceWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, -10, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNegativeSourceHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, -10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithZeroDestWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, 10, 0, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithZeroDestHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, 10, 20, 0)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNegativeDestWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, 10, -20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNegativeDestHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleNearest(self.mockImageData, 0, 0, 10, 10, 20, -20)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Unhappy path tests for scaleBilinear
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNilSource()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(nil, 0, 0, 10, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithZeroSourceWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 0, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithZeroSourceHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, 0, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNegativeSourceWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, -10, 10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNegativeSourceHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, -10, 20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithZeroDestWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, 10, 0, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithZeroDestHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, 10, 20, 0)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNegativeDestWidth()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, 10, -20, 20)
|
||||
end)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNegativeDestHeight()
|
||||
luaunit.assertError(function()
|
||||
ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 10, 10, 20, -20)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Edge case tests
|
||||
|
||||
function TestImageScaler:testScaleNearestWithVeryLargeUpscale()
|
||||
-- Scale 1x1 to 50x50 (extreme upscale, but fast for testing)
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 1, 1, 50, 50)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithVeryLargeDownscale()
|
||||
-- Scale 50x50 to 1x1 (extreme downscale)
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 50, 50, 1, 1)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithVeryLargeUpscale()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 1, 1, 50, 50)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithVeryLargeDownscale()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 50, 50, 1, 1)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNonIntegerDimensions()
|
||||
-- Fractional source dimensions (should work with floor/ceil)
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 5.5, 5.5, 10, 10)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNonIntegerDimensions()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 5.5, 5.5, 10, 10)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWith1x1Source()
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 1, 1, 5, 5)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWith1x1Source()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 1, 1, 5, 5)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWith1x1Dest()
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 5, 5, 1, 1)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWith1x1Dest()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 5, 5, 1, 1)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithNonZeroSourceOffset()
|
||||
-- Source region offset from 0,0
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 10, 10, 5, 5, 10, 10)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithNonZeroSourceOffset()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 10, 10, 5, 5, 10, 10)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleNearestWithAspectRatioChange()
|
||||
-- Change aspect ratio dramatically
|
||||
local result = ImageScaler.scaleNearest(self.mockImageData, 0, 0, 5, 20, 20, 5)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
function TestImageScaler:testScaleBilinearWithAspectRatioChange()
|
||||
local result = ImageScaler.scaleBilinear(self.mockImageData, 0, 0, 5, 20, 20, 5)
|
||||
luaunit.assertNotNil(result)
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
211
testing/__tests__/input_event_test.lua
Normal file
211
testing/__tests__/input_event_test.lua
Normal file
@@ -0,0 +1,211 @@
|
||||
-- Test suite for InputEvent module
|
||||
package.path = package.path .. ";./?.lua;./modules/?.lua"
|
||||
|
||||
require("testing.loveStub")
|
||||
local luaunit = require("testing.luaunit")
|
||||
local InputEvent = require("modules.InputEvent")
|
||||
|
||||
TestInputEvent = {}
|
||||
|
||||
-- Test: new() creates click event with all properties
|
||||
function TestInputEvent:test_new_creates_click_event()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 100,
|
||||
y = 200,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(event, "new() should return event")
|
||||
luaunit.assertEquals(event.type, "click")
|
||||
luaunit.assertEquals(event.button, 1)
|
||||
luaunit.assertEquals(event.x, 100)
|
||||
luaunit.assertEquals(event.y, 200)
|
||||
luaunit.assertNotNil(event.modifiers)
|
||||
luaunit.assertEquals(event.modifiers.shift, false)
|
||||
end
|
||||
|
||||
-- Test: new() creates press event
|
||||
function TestInputEvent:test_new_creates_press_event()
|
||||
local event = InputEvent.new({
|
||||
type = "press",
|
||||
button = 1,
|
||||
x = 50,
|
||||
y = 75,
|
||||
modifiers = { shift = true, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.type, "press")
|
||||
luaunit.assertEquals(event.button, 1)
|
||||
luaunit.assertEquals(event.x, 50)
|
||||
luaunit.assertEquals(event.y, 75)
|
||||
luaunit.assertEquals(event.modifiers.shift, true)
|
||||
end
|
||||
|
||||
-- Test: new() creates release event
|
||||
function TestInputEvent:test_new_creates_release_event()
|
||||
local event = InputEvent.new({
|
||||
type = "release",
|
||||
button = 1,
|
||||
x = 150,
|
||||
y = 250,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.type, "release")
|
||||
end
|
||||
|
||||
-- Test: new() creates rightclick event with button 2
|
||||
function TestInputEvent:test_new_creates_rightclick_event()
|
||||
local event = InputEvent.new({
|
||||
type = "rightclick",
|
||||
button = 2,
|
||||
x = 100,
|
||||
y = 100,
|
||||
modifiers = { shift = false, ctrl = true, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.type, "rightclick")
|
||||
luaunit.assertEquals(event.button, 2)
|
||||
luaunit.assertEquals(event.modifiers.ctrl, true)
|
||||
end
|
||||
|
||||
-- Test: new() creates middleclick event with button 3
|
||||
function TestInputEvent:test_new_creates_middleclick_event()
|
||||
local event = InputEvent.new({
|
||||
type = "middleclick",
|
||||
button = 3,
|
||||
x = 200,
|
||||
y = 300,
|
||||
modifiers = { shift = false, ctrl = false, alt = true, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.type, "middleclick")
|
||||
luaunit.assertEquals(event.button, 3)
|
||||
luaunit.assertEquals(event.modifiers.alt, true)
|
||||
end
|
||||
|
||||
-- Test: new() creates drag event with dx and dy
|
||||
function TestInputEvent:test_new_creates_drag_event_with_deltas()
|
||||
local event = InputEvent.new({
|
||||
type = "drag",
|
||||
button = 1,
|
||||
x = 100,
|
||||
y = 100,
|
||||
dx = 20,
|
||||
dy = -15,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.type, "drag")
|
||||
luaunit.assertEquals(event.dx, 20)
|
||||
luaunit.assertEquals(event.dy, -15)
|
||||
end
|
||||
|
||||
-- Test: new() defaults clickCount to 1 if not provided
|
||||
function TestInputEvent:test_new_defaults_clickCount_to_one()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.clickCount, 1)
|
||||
end
|
||||
|
||||
-- Test: new() accepts custom clickCount for double-click
|
||||
function TestInputEvent:test_new_accepts_custom_clickCount()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
clickCount = 2,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.clickCount, 2)
|
||||
end
|
||||
|
||||
-- Test: new() accepts custom clickCount for triple-click
|
||||
function TestInputEvent:test_new_accepts_triple_click()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
clickCount = 3,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.clickCount, 3)
|
||||
end
|
||||
|
||||
-- Test: new() defaults timestamp to current time if not provided
|
||||
function TestInputEvent:test_new_defaults_timestamp()
|
||||
local before = love.timer.getTime()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
local after = love.timer.getTime()
|
||||
|
||||
luaunit.assertNotNil(event.timestamp)
|
||||
luaunit.assertTrue(event.timestamp >= before)
|
||||
luaunit.assertTrue(event.timestamp <= after)
|
||||
end
|
||||
|
||||
-- Test: new() accepts custom timestamp
|
||||
function TestInputEvent:test_new_accepts_custom_timestamp()
|
||||
local customTime = 12345.678
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
timestamp = customTime,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.timestamp, customTime)
|
||||
end
|
||||
|
||||
-- Test: new() handles all modifier keys
|
||||
function TestInputEvent:test_new_handles_all_modifier_keys()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 0,
|
||||
y = 0,
|
||||
modifiers = { shift = true, ctrl = true, alt = true, super = true },
|
||||
})
|
||||
|
||||
luaunit.assertEquals(event.modifiers.shift, true)
|
||||
luaunit.assertEquals(event.modifiers.ctrl, true)
|
||||
luaunit.assertEquals(event.modifiers.alt, true)
|
||||
luaunit.assertEquals(event.modifiers.super, true)
|
||||
end
|
||||
|
||||
-- Test: new() handles nil dx/dy for non-drag events
|
||||
function TestInputEvent:test_new_handles_nil_deltas()
|
||||
local event = InputEvent.new({
|
||||
type = "click",
|
||||
button = 1,
|
||||
x = 100,
|
||||
y = 100,
|
||||
modifiers = { shift = false, ctrl = false, alt = false, super = false },
|
||||
})
|
||||
|
||||
luaunit.assertNil(event.dx)
|
||||
luaunit.assertNil(event.dy)
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
@@ -16,7 +16,7 @@ function TestLayoutEdgeCases:setUp()
|
||||
self.warnings = {}
|
||||
self.originalWarn = ErrorHandler.warn
|
||||
ErrorHandler.warn = function(module, message)
|
||||
table.insert(self.warnings, {module = module, message = message})
|
||||
table.insert(self.warnings, { module = module, message = message })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,22 +35,22 @@ function TestLayoutEdgeCases:test_percentage_width_with_auto_parent_warns()
|
||||
-- width not specified - auto-sizing width
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal"
|
||||
flexDirection = "horizontal",
|
||||
})
|
||||
|
||||
|
||||
FlexLove.new({
|
||||
id = "child_with_percentage",
|
||||
parent = container,
|
||||
width = "50%", -- Percentage width with auto-sizing parent - should warn
|
||||
height = 100
|
||||
width = "50%", -- Percentage width with auto-sizing parent - should warn
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Check that a warning was issued
|
||||
luaunit.assertTrue(#self.warnings > 0, "Should issue warning for percentage width with auto-sizing parent")
|
||||
|
||||
|
||||
local found = false
|
||||
for _, warning in ipairs(self.warnings) do
|
||||
if warning.message:match("percentage width") and warning.message:match("auto%-sizing") then
|
||||
@@ -58,7 +58,7 @@ function TestLayoutEdgeCases:test_percentage_width_with_auto_parent_warns()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
luaunit.assertTrue(found, "Warning should mention percentage width and auto-sizing")
|
||||
end
|
||||
|
||||
@@ -71,22 +71,22 @@ function TestLayoutEdgeCases:test_percentage_height_with_auto_parent_warns()
|
||||
width = 200,
|
||||
-- height not specified - auto-sizing height
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical"
|
||||
flexDirection = "vertical",
|
||||
})
|
||||
|
||||
|
||||
FlexLove.new({
|
||||
id = "child_with_percentage",
|
||||
parent = container,
|
||||
width = 100,
|
||||
height = "50%" -- Percentage height with auto-sizing parent - should warn
|
||||
height = "50%", -- Percentage height with auto-sizing parent - should warn
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Check that a warning was issued
|
||||
luaunit.assertTrue(#self.warnings > 0, "Should issue warning for percentage height with auto-sizing parent")
|
||||
|
||||
|
||||
local found = false
|
||||
for _, warning in ipairs(self.warnings) do
|
||||
if warning.message:match("percentage height") and warning.message:match("auto%-sizing") then
|
||||
@@ -94,7 +94,7 @@ function TestLayoutEdgeCases:test_percentage_height_with_auto_parent_warns()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
luaunit.assertTrue(found, "Warning should mention percentage height and auto-sizing")
|
||||
end
|
||||
|
||||
@@ -107,19 +107,19 @@ function TestLayoutEdgeCases:test_pixel_width_with_auto_parent_no_warn()
|
||||
-- width not specified - auto-sizing
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal"
|
||||
flexDirection = "horizontal",
|
||||
})
|
||||
|
||||
|
||||
FlexLove.new({
|
||||
id = "child_with_pixels",
|
||||
parent = container,
|
||||
width = 100, -- Pixel width - should NOT warn
|
||||
height = 100
|
||||
width = 100, -- Pixel width - should NOT warn
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Check that NO warning was issued about percentage sizing
|
||||
for _, warning in ipairs(self.warnings) do
|
||||
local hasPercentageWarning = warning.message:match("percentage") and warning.message:match("auto%-sizing")
|
||||
@@ -135,23 +135,23 @@ function TestLayoutEdgeCases:test_css_positioning_top_offset()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
top = 50, -- 50px from top
|
||||
top = 50, -- 50px from top
|
||||
left = 0,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
-- Trigger layout by ending and restarting frame
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Child should be positioned 50px from container's top edge (accounting for padding)
|
||||
local expectedY = container.y + container.padding.top + 50
|
||||
luaunit.assertEquals(child.y, expectedY, "Child should be positioned with top offset")
|
||||
@@ -165,22 +165,22 @@ function TestLayoutEdgeCases:test_css_positioning_bottom_offset()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
bottom = 50, -- 50px from bottom
|
||||
bottom = 50, -- 50px from bottom
|
||||
left = 0,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Child should be positioned 50px from container's bottom edge
|
||||
local expectedY = container.y + container.padding.top + container.height - 50 - child:getBorderBoxHeight()
|
||||
luaunit.assertEquals(child.y, expectedY, "Child should be positioned with bottom offset")
|
||||
@@ -194,22 +194,22 @@ function TestLayoutEdgeCases:test_css_positioning_left_offset()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
top = 0,
|
||||
left = 50, -- 50px from left
|
||||
left = 50, -- 50px from left
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Child should be positioned 50px from container's left edge
|
||||
local expectedX = container.x + container.padding.left + 50
|
||||
luaunit.assertEquals(child.x, expectedX, "Child should be positioned with left offset")
|
||||
@@ -223,22 +223,22 @@ function TestLayoutEdgeCases:test_css_positioning_right_offset()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
top = 0,
|
||||
right = 50, -- 50px from right
|
||||
right = 50, -- 50px from right
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Child should be positioned 50px from container's right edge
|
||||
local expectedX = container.x + container.padding.left + container.width - 50 - child:getBorderBoxWidth()
|
||||
luaunit.assertEquals(child.x, expectedX, "Child should be positioned with right offset")
|
||||
@@ -252,23 +252,23 @@ function TestLayoutEdgeCases:test_css_positioning_top_and_bottom()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
top = 10,
|
||||
bottom = 20, -- Both specified - last one wins in current implementation
|
||||
bottom = 20, -- Both specified - last one wins in current implementation
|
||||
left = 0,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Bottom should override top
|
||||
local expectedY = container.y + container.padding.top + container.height - 20 - child:getBorderBoxHeight()
|
||||
luaunit.assertEquals(child.y, expectedY, "Bottom offset should override top when both specified")
|
||||
@@ -282,23 +282,23 @@ function TestLayoutEdgeCases:test_css_positioning_left_and_right()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
positioning = "absolute",
|
||||
top = 0,
|
||||
left = 10,
|
||||
right = 20, -- Both specified - last one wins in current implementation
|
||||
right = 20, -- Both specified - last one wins in current implementation
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Right should override left
|
||||
local expectedX = container.x + container.padding.left + container.width - 20 - child:getBorderBoxWidth()
|
||||
luaunit.assertEquals(child.x, expectedX, "Right offset should override left when both specified")
|
||||
@@ -313,9 +313,9 @@ function TestLayoutEdgeCases:test_css_positioning_with_padding()
|
||||
width = 400,
|
||||
height = 400,
|
||||
padding = { top = 20, right = 20, bottom = 20, left = 20 },
|
||||
positioning = "absolute"
|
||||
positioning = "absolute",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
@@ -323,16 +323,16 @@ function TestLayoutEdgeCases:test_css_positioning_with_padding()
|
||||
top = 10,
|
||||
left = 10,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Offsets should be relative to content area (after padding)
|
||||
local expectedX = container.x + container.padding.left + 10
|
||||
local expectedY = container.y + container.padding.top + 10
|
||||
|
||||
|
||||
luaunit.assertEquals(child.x, expectedX, "Left offset should account for container padding")
|
||||
luaunit.assertEquals(child.y, expectedY, "Top offset should account for container padding")
|
||||
end
|
||||
@@ -346,21 +346,21 @@ function TestLayoutEdgeCases:test_css_positioning_ignored_in_flex()
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal"
|
||||
flexDirection = "horizontal",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
top = 100, -- This should be IGNORED in flex layout
|
||||
left = 100, -- This should be IGNORED in flex layout
|
||||
top = 100, -- This should be IGNORED in flex layout
|
||||
left = 100, -- This should be IGNORED in flex layout
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- In flex layout, child should be positioned by flex rules, not CSS offsets
|
||||
-- Child should be at (0, 0) relative to container content area
|
||||
luaunit.assertEquals(child.x, 0, "CSS offsets should be ignored in flex layout")
|
||||
@@ -375,9 +375,9 @@ function TestLayoutEdgeCases:test_css_positioning_in_relative_container()
|
||||
y = 100,
|
||||
width = 400,
|
||||
height = 400,
|
||||
positioning = "relative"
|
||||
positioning = "relative",
|
||||
})
|
||||
|
||||
|
||||
local child = FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
@@ -385,16 +385,16 @@ function TestLayoutEdgeCases:test_css_positioning_in_relative_container()
|
||||
top = 30,
|
||||
left = 30,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame()
|
||||
|
||||
|
||||
-- Should work the same as absolute container
|
||||
local expectedX = container.x + container.padding.left + 30
|
||||
local expectedY = container.y + container.padding.top + 30
|
||||
|
||||
|
||||
luaunit.assertEquals(child.x, expectedX, "CSS positioning should work in relative containers")
|
||||
luaunit.assertEquals(child.y, expectedY, "CSS positioning should work in relative containers")
|
||||
end
|
||||
|
||||
@@ -716,7 +716,6 @@ local function createMockElement(props)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
-- Run tests if this file is executed directly
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
|
||||
@@ -24,9 +24,9 @@ function TestOverflowDetection:test_vertical_overflow_detected()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 100,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Add child that exceeds container height
|
||||
FlexLove.new({
|
||||
id = "tall_child",
|
||||
@@ -34,13 +34,13 @@ function TestOverflowDetection:test_vertical_overflow_detected()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 200 -- Taller than container (100)
|
||||
height = 200, -- Taller than container (100)
|
||||
})
|
||||
|
||||
|
||||
-- Force layout to trigger detectOverflow
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Check if overflow was detected
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollY > 0, "Should detect vertical overflow")
|
||||
@@ -54,22 +54,22 @@ function TestOverflowDetection:test_horizontal_overflow_detected()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Add child that exceeds container width
|
||||
FlexLove.new({
|
||||
id = "wide_child",
|
||||
parent = container,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 300, -- Wider than container (100)
|
||||
height = 50
|
||||
width = 300, -- Wider than container (100)
|
||||
height = 50,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollX > 0, "Should detect horizontal overflow")
|
||||
luaunit.assertEquals(maxScrollY, 0, "Should not have vertical overflow")
|
||||
@@ -82,9 +82,9 @@ function TestOverflowDetection:test_both_axes_overflow()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Add child that exceeds both dimensions
|
||||
FlexLove.new({
|
||||
id = "large_child",
|
||||
@@ -92,12 +92,12 @@ function TestOverflowDetection:test_both_axes_overflow()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200
|
||||
height = 200,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollX > 0, "Should detect horizontal overflow")
|
||||
luaunit.assertTrue(maxScrollY > 0, "Should detect vertical overflow")
|
||||
@@ -110,9 +110,9 @@ function TestOverflowDetection:test_no_overflow_when_content_fits()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Add child that fits within container
|
||||
FlexLove.new({
|
||||
id = "small_child",
|
||||
@@ -120,12 +120,12 @@ function TestOverflowDetection:test_no_overflow_when_content_fits()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertEquals(maxScrollX, 0, "Should not have horizontal overflow")
|
||||
luaunit.assertEquals(maxScrollY, 0, "Should not have vertical overflow")
|
||||
@@ -140,22 +140,22 @@ function TestOverflowDetection:test_overflow_with_multiple_children()
|
||||
height = 200,
|
||||
overflow = "scroll",
|
||||
positioning = "flex",
|
||||
flexDirection = "vertical"
|
||||
flexDirection = "vertical",
|
||||
})
|
||||
|
||||
|
||||
-- Add multiple children that together exceed container
|
||||
for i = 1, 5 do
|
||||
FlexLove.new({
|
||||
id = "child_" .. i,
|
||||
parent = container,
|
||||
width = 150,
|
||||
height = 60 -- 5 * 60 = 300, exceeds container height of 200
|
||||
height = 60, -- 5 * 60 = 300, exceeds container height of 200
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollY > 0, "Should detect overflow from multiple children")
|
||||
end
|
||||
@@ -168,22 +168,22 @@ function TestOverflowDetection:test_overflow_with_padding()
|
||||
width = 200,
|
||||
height = 200,
|
||||
padding = { top = 10, right = 10, bottom = 10, left = 10 },
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Child that fits in container but exceeds available content area (200 - 20 = 180)
|
||||
FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 190, -- Exceeds content width (180)
|
||||
height = 100
|
||||
width = 190, -- Exceeds content width (180)
|
||||
height = 100,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollX > 0, "Should detect overflow accounting for padding")
|
||||
end
|
||||
@@ -197,9 +197,9 @@ function TestOverflowDetection:test_overflow_with_margins()
|
||||
height = 200,
|
||||
positioning = "flex",
|
||||
flexDirection = "horizontal",
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Child with margins that contribute to overflow
|
||||
-- In flex layout, margins are properly accounted for in positioning
|
||||
FlexLove.new({
|
||||
@@ -207,12 +207,12 @@ function TestOverflowDetection:test_overflow_with_margins()
|
||||
parent = container,
|
||||
width = 180,
|
||||
height = 180,
|
||||
margin = { top = 5, right = 20, bottom = 5, left = 5 } -- Total width: 5+180+20=205, overflows 200px container
|
||||
margin = { top = 5, right = 20, bottom = 5, left = 5 }, -- Total width: 5+180+20=205, overflows 200px container
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertTrue(maxScrollX > 0, "Should include child margins in overflow calculation")
|
||||
end
|
||||
@@ -225,9 +225,9 @@ function TestOverflowDetection:test_visible_overflow_skips_detection()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
overflow = "visible" -- Should not clip or calculate overflow
|
||||
overflow = "visible", -- Should not clip or calculate overflow
|
||||
})
|
||||
|
||||
|
||||
-- Add oversized child
|
||||
FlexLove.new({
|
||||
id = "large_child",
|
||||
@@ -235,12 +235,12 @@ function TestOverflowDetection:test_visible_overflow_skips_detection()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 300,
|
||||
height = 300
|
||||
height = 300,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- With overflow="visible", maxScroll should be 0 (no scrolling)
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertEquals(maxScrollX, 0, "visible overflow should not enable scrolling")
|
||||
@@ -255,13 +255,13 @@ function TestOverflowDetection:test_empty_container_no_overflow()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
-- No children
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
luaunit.assertEquals(maxScrollX, 0, "Empty container should have no overflow")
|
||||
luaunit.assertEquals(maxScrollY, 0, "Empty container should have no overflow")
|
||||
@@ -275,9 +275,9 @@ function TestOverflowDetection:test_absolute_children_ignored_in_overflow()
|
||||
y = 0,
|
||||
width = 200,
|
||||
height = 200,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
-- Regular child that fits
|
||||
FlexLove.new({
|
||||
id = "normal_child",
|
||||
@@ -285,9 +285,9 @@ function TestOverflowDetection:test_absolute_children_ignored_in_overflow()
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 150,
|
||||
height = 150
|
||||
height = 150,
|
||||
})
|
||||
|
||||
|
||||
-- Absolutely positioned child that extends beyond (should NOT cause overflow)
|
||||
FlexLove.new({
|
||||
id = "absolute_child",
|
||||
@@ -296,12 +296,12 @@ function TestOverflowDetection:test_absolute_children_ignored_in_overflow()
|
||||
top = 0,
|
||||
left = 0,
|
||||
width = 400,
|
||||
height = 400
|
||||
height = 400,
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
-- Should not have overflow because absolute children are ignored
|
||||
luaunit.assertEquals(maxScrollX, 0, "Absolute children should not cause overflow")
|
||||
@@ -316,26 +316,26 @@ function TestOverflowDetection:test_scroll_clamped_to_max()
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 100,
|
||||
overflow = "scroll"
|
||||
overflow = "scroll",
|
||||
})
|
||||
|
||||
|
||||
FlexLove.new({
|
||||
id = "child",
|
||||
parent = container,
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 300 -- Creates 200px of vertical overflow
|
||||
height = 300, -- Creates 200px of vertical overflow
|
||||
})
|
||||
|
||||
|
||||
FlexLove.endFrame()
|
||||
FlexLove.beginFrame(1920, 1080)
|
||||
|
||||
|
||||
-- Try to scroll beyond max
|
||||
container:setScrollPosition(0, 999999)
|
||||
local scrollX, scrollY = container:getScrollPosition()
|
||||
local maxScrollX, maxScrollY = container:getMaxScroll()
|
||||
|
||||
|
||||
luaunit.assertEquals(scrollY, maxScrollY, "Scroll should be clamped to maximum")
|
||||
luaunit.assertTrue(scrollY < 999999, "Should not scroll beyond content")
|
||||
end
|
||||
|
||||
396
testing/__tests__/text_editor_test.lua
Normal file
396
testing/__tests__/text_editor_test.lua
Normal file
@@ -0,0 +1,396 @@
|
||||
-- Test suite for TextEditor module
|
||||
package.path = package.path .. ";./?.lua;./modules/?.lua"
|
||||
|
||||
require("testing.loveStub")
|
||||
local luaunit = require("testing.luaunit")
|
||||
local TextEditor = require("modules.TextEditor")
|
||||
local Color = require("modules.Color")
|
||||
local utils = require("modules.utils")
|
||||
|
||||
TestTextEditor = {}
|
||||
|
||||
-- Mock dependencies
|
||||
local MockContext = {
|
||||
_immediateMode = false,
|
||||
_focusedElement = nil,
|
||||
}
|
||||
|
||||
local MockStateManager = {
|
||||
getState = function(id)
|
||||
return nil
|
||||
end,
|
||||
saveState = function(id, state) end,
|
||||
}
|
||||
|
||||
-- Helper to create TextEditor with dependencies
|
||||
local function createTextEditor(config)
|
||||
config = config or {}
|
||||
return TextEditor.new(config, {
|
||||
Context = MockContext,
|
||||
StateManager = MockStateManager,
|
||||
Color = Color,
|
||||
utils = utils,
|
||||
})
|
||||
end
|
||||
|
||||
-- Helper to create mock element
|
||||
local function createMockElement()
|
||||
return {
|
||||
_stateId = "test-element-1",
|
||||
width = 200,
|
||||
height = 30,
|
||||
}
|
||||
end
|
||||
|
||||
-- Test: new() creates instance with defaults
|
||||
function TestTextEditor:test_new_creates_with_defaults()
|
||||
local editor = createTextEditor()
|
||||
|
||||
luaunit.assertNotNil(editor)
|
||||
luaunit.assertFalse(editor.editable)
|
||||
luaunit.assertFalse(editor.multiline)
|
||||
luaunit.assertFalse(editor.passwordMode)
|
||||
luaunit.assertEquals(editor.inputType, "text")
|
||||
luaunit.assertEquals(editor._textBuffer, "")
|
||||
luaunit.assertEquals(editor._cursorPosition, 0)
|
||||
luaunit.assertFalse(editor._focused)
|
||||
end
|
||||
|
||||
-- Test: new() accepts configuration
|
||||
function TestTextEditor:test_new_accepts_config()
|
||||
local editor = createTextEditor({
|
||||
editable = true,
|
||||
multiline = true,
|
||||
passwordMode = true,
|
||||
text = "Hello",
|
||||
placeholder = "Enter text",
|
||||
maxLength = 100,
|
||||
inputType = "email",
|
||||
})
|
||||
|
||||
luaunit.assertTrue(editor.editable)
|
||||
luaunit.assertTrue(editor.multiline)
|
||||
luaunit.assertTrue(editor.passwordMode)
|
||||
luaunit.assertEquals(editor._textBuffer, "Hello")
|
||||
luaunit.assertEquals(editor.placeholder, "Enter text")
|
||||
luaunit.assertEquals(editor.maxLength, 100)
|
||||
luaunit.assertEquals(editor.inputType, "email")
|
||||
end
|
||||
|
||||
-- Test: new() sanitizes initial text
|
||||
function TestTextEditor:test_new_sanitizes_initial_text()
|
||||
local editor = createTextEditor({
|
||||
text = "Hello\n\nWorld",
|
||||
multiline = false,
|
||||
allowNewlines = false,
|
||||
})
|
||||
|
||||
-- Newlines should be removed for single-line
|
||||
luaunit.assertNotEquals(editor._textBuffer, "Hello\n\nWorld")
|
||||
end
|
||||
|
||||
-- Test: initialize() sets element reference
|
||||
function TestTextEditor:test_initialize_sets_element()
|
||||
local editor = createTextEditor()
|
||||
local element = createMockElement()
|
||||
|
||||
editor:initialize(element)
|
||||
|
||||
luaunit.assertEquals(editor._element, element)
|
||||
end
|
||||
|
||||
-- Test: getText() returns current text
|
||||
function TestTextEditor:test_getText_returns_text()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello World")
|
||||
end
|
||||
|
||||
-- Test: getText() returns empty string for nil buffer
|
||||
function TestTextEditor:test_getText_returns_empty_for_nil()
|
||||
local editor = createTextEditor()
|
||||
editor._textBuffer = nil
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "")
|
||||
end
|
||||
|
||||
-- Test: setText() updates text buffer
|
||||
function TestTextEditor:test_setText_updates_buffer()
|
||||
local editor = createTextEditor()
|
||||
|
||||
editor:setText("New text")
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "New text")
|
||||
end
|
||||
|
||||
-- Test: setText() sanitizes text by default
|
||||
function TestTextEditor:test_setText_sanitizes()
|
||||
local editor = createTextEditor({
|
||||
multiline = false,
|
||||
allowNewlines = false,
|
||||
})
|
||||
|
||||
editor:setText("Line1\nLine2")
|
||||
|
||||
-- Should remove newlines for single-line
|
||||
local text = editor:getText()
|
||||
luaunit.assertFalse(text:find("\n") ~= nil)
|
||||
end
|
||||
|
||||
-- Test: setText() skips sanitization when requested
|
||||
function TestTextEditor:test_setText_skips_sanitization()
|
||||
local editor = createTextEditor({
|
||||
multiline = false,
|
||||
allowNewlines = false,
|
||||
})
|
||||
|
||||
editor:setText("Line1\nLine2", true) -- skipSanitization = true
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Line1\nLine2")
|
||||
end
|
||||
|
||||
-- Test: insertText() adds text at position
|
||||
function TestTextEditor:test_insertText_at_position()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
|
||||
editor:insertText(" World", 5)
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello World")
|
||||
end
|
||||
|
||||
-- Test: insertText() adds text at start
|
||||
function TestTextEditor:test_insertText_at_start()
|
||||
local editor = createTextEditor({ text = "World" })
|
||||
|
||||
editor:insertText("Hello ", 0)
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello World")
|
||||
end
|
||||
|
||||
-- Test: deleteText() removes text range
|
||||
function TestTextEditor:test_deleteText_removes_range()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
editor:deleteText(5, 11) -- Remove " World"
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello")
|
||||
end
|
||||
|
||||
-- Test: deleteText() handles reversed positions
|
||||
function TestTextEditor:test_deleteText_handles_reversed()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
editor:deleteText(11, 5) -- Reversed: should swap
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello")
|
||||
end
|
||||
|
||||
-- Test: replaceText() replaces range with new text
|
||||
function TestTextEditor:test_replaceText_replaces_range()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
editor:replaceText(6, 11, "Lua")
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello Lua")
|
||||
end
|
||||
|
||||
-- Test: setCursorPosition() sets cursor
|
||||
function TestTextEditor:test_setCursorPosition()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
|
||||
editor:setCursorPosition(3)
|
||||
|
||||
luaunit.assertEquals(editor:getCursorPosition(), 3)
|
||||
end
|
||||
|
||||
-- Test: setCursorPosition() clamps to valid range
|
||||
function TestTextEditor:test_setCursorPosition_clamps()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
|
||||
editor:setCursorPosition(100) -- Beyond text length
|
||||
|
||||
luaunit.assertEquals(editor:getCursorPosition(), 5)
|
||||
end
|
||||
|
||||
-- Test: moveCursorBy() moves cursor relative
|
||||
function TestTextEditor:test_moveCursorBy()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
editor:setCursorPosition(2)
|
||||
|
||||
editor:moveCursorBy(2)
|
||||
|
||||
luaunit.assertEquals(editor:getCursorPosition(), 4)
|
||||
end
|
||||
|
||||
-- Test: moveCursorToStart() moves to beginning
|
||||
function TestTextEditor:test_moveCursorToStart()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
editor:setCursorPosition(3)
|
||||
|
||||
editor:moveCursorToStart()
|
||||
|
||||
luaunit.assertEquals(editor:getCursorPosition(), 0)
|
||||
end
|
||||
|
||||
-- Test: moveCursorToEnd() moves to end
|
||||
function TestTextEditor:test_moveCursorToEnd()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
|
||||
editor:moveCursorToEnd()
|
||||
|
||||
luaunit.assertEquals(editor:getCursorPosition(), 5)
|
||||
end
|
||||
|
||||
-- Test: setSelection() sets selection range
|
||||
function TestTextEditor:test_setSelection()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
editor:setSelection(0, 5)
|
||||
|
||||
local start, endPos = editor:getSelection()
|
||||
luaunit.assertEquals(start, 0)
|
||||
luaunit.assertEquals(endPos, 5)
|
||||
end
|
||||
|
||||
-- Test: hasSelection() returns true when selected
|
||||
function TestTextEditor:test_hasSelection_true()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
editor:setSelection(0, 5)
|
||||
|
||||
luaunit.assertTrue(editor:hasSelection())
|
||||
end
|
||||
|
||||
-- Test: hasSelection() returns false when no selection
|
||||
function TestTextEditor:test_hasSelection_false()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
|
||||
luaunit.assertFalse(editor:hasSelection())
|
||||
end
|
||||
|
||||
-- Test: clearSelection() removes selection
|
||||
function TestTextEditor:test_clearSelection()
|
||||
local editor = createTextEditor({ text = "Hello" })
|
||||
editor:setSelection(0, 5)
|
||||
|
||||
editor:clearSelection()
|
||||
|
||||
luaunit.assertFalse(editor:hasSelection())
|
||||
end
|
||||
|
||||
-- Test: getSelectedText() returns selected text
|
||||
function TestTextEditor:test_getSelectedText()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
editor:setSelection(0, 5)
|
||||
|
||||
luaunit.assertEquals(editor:getSelectedText(), "Hello")
|
||||
end
|
||||
|
||||
-- Test: deleteSelection() removes selected text
|
||||
function TestTextEditor:test_deleteSelection()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
editor:setSelection(0, 6)
|
||||
|
||||
editor:deleteSelection()
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "World")
|
||||
luaunit.assertFalse(editor:hasSelection())
|
||||
end
|
||||
|
||||
-- Test: selectAll() selects entire text
|
||||
function TestTextEditor:test_selectAll()
|
||||
local editor = createTextEditor({ text = "Hello World" })
|
||||
|
||||
editor:selectAll()
|
||||
|
||||
local start, endPos = editor:getSelection()
|
||||
luaunit.assertEquals(start, 0)
|
||||
luaunit.assertEquals(endPos, 11)
|
||||
end
|
||||
|
||||
-- Test: sanitization with maxLength
|
||||
function TestTextEditor:test_sanitize_max_length()
|
||||
local editor = createTextEditor({
|
||||
maxLength = 5,
|
||||
})
|
||||
|
||||
editor:setText("HelloWorld")
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "Hello")
|
||||
end
|
||||
|
||||
-- Test: sanitization disabled
|
||||
function TestTextEditor:test_sanitization_disabled()
|
||||
local editor = createTextEditor({
|
||||
sanitize = false,
|
||||
multiline = false,
|
||||
allowNewlines = false,
|
||||
})
|
||||
|
||||
editor:setText("Line1\nLine2")
|
||||
|
||||
-- Should NOT sanitize newlines when disabled
|
||||
luaunit.assertEquals(editor:getText(), "Line1\nLine2")
|
||||
end
|
||||
|
||||
-- Test: customSanitizer callback
|
||||
function TestTextEditor:test_custom_sanitizer()
|
||||
local editor = createTextEditor({
|
||||
customSanitizer = function(text)
|
||||
return text:upper()
|
||||
end,
|
||||
})
|
||||
|
||||
editor:setText("hello")
|
||||
|
||||
luaunit.assertEquals(editor:getText(), "HELLO")
|
||||
end
|
||||
|
||||
-- Test: allowNewlines follows multiline setting
|
||||
function TestTextEditor:test_allowNewlines_follows_multiline()
|
||||
local editor = createTextEditor({
|
||||
multiline = true,
|
||||
})
|
||||
|
||||
luaunit.assertTrue(editor.allowNewlines)
|
||||
|
||||
editor = createTextEditor({
|
||||
multiline = false,
|
||||
})
|
||||
|
||||
luaunit.assertFalse(editor.allowNewlines)
|
||||
end
|
||||
|
||||
-- Test: allowNewlines can be overridden
|
||||
function TestTextEditor:test_allowNewlines_override()
|
||||
local editor = createTextEditor({
|
||||
multiline = true,
|
||||
allowNewlines = false,
|
||||
})
|
||||
|
||||
luaunit.assertFalse(editor.allowNewlines)
|
||||
end
|
||||
|
||||
-- Test: allowTabs defaults to true
|
||||
function TestTextEditor:test_allowTabs_default()
|
||||
local editor = createTextEditor()
|
||||
|
||||
luaunit.assertTrue(editor.allowTabs)
|
||||
end
|
||||
|
||||
-- Test: cursorBlinkRate default
|
||||
function TestTextEditor:test_cursorBlinkRate_default()
|
||||
local editor = createTextEditor()
|
||||
|
||||
luaunit.assertEquals(editor.cursorBlinkRate, 0.5)
|
||||
end
|
||||
|
||||
-- Test: selectOnFocus default
|
||||
function TestTextEditor:test_selectOnFocus_default()
|
||||
local editor = createTextEditor()
|
||||
|
||||
luaunit.assertFalse(editor.selectOnFocus)
|
||||
end
|
||||
|
||||
if not _G.RUNNING_ALL_TESTS then
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
end
|
||||
@@ -17,17 +17,25 @@ local luaunit = require("testing.luaunit")
|
||||
|
||||
-- Run all tests in the __tests__ directory
|
||||
local testFiles = {
|
||||
"testing/__tests__/utils_test.lua",
|
||||
"testing/__tests__/units_test.lua",
|
||||
"testing/__tests__/animation_test.lua",
|
||||
"testing/__tests__/color_validation_test.lua",
|
||||
"testing/__tests__/element_test.lua",
|
||||
"testing/__tests__/error_handler_test.lua",
|
||||
"testing/__tests__/event_handler_test.lua",
|
||||
"testing/__tests__/grid_test.lua",
|
||||
"testing/__tests__/image_cache_test.lua",
|
||||
"testing/__tests__/image_renderer_test.lua",
|
||||
"testing/__tests__/image_scaler_test.lua",
|
||||
"testing/__tests__/input_event_test.lua",
|
||||
"testing/__tests__/layout_edge_cases_test.lua",
|
||||
"testing/__tests__/layout_engine_test.lua",
|
||||
"testing/__tests__/overflow_test.lua",
|
||||
"testing/__tests__/path_validation_test.lua",
|
||||
"testing/__tests__/sanitization_test.lua",
|
||||
"testing/__tests__/text_editor_test.lua",
|
||||
"testing/__tests__/theme_test.lua",
|
||||
"testing/__tests__/layout_engine_test.lua",
|
||||
"testing/__tests__/element_test.lua",
|
||||
"testing/__tests__/overflow_test.lua",
|
||||
"testing/__tests__/grid_test.lua",
|
||||
"testing/__tests__/layout_edge_cases_test.lua",
|
||||
"testing/__tests__/units_test.lua",
|
||||
"testing/__tests__/utils_test.lua",
|
||||
}
|
||||
|
||||
local success = true
|
||||
@@ -58,19 +66,19 @@ if status then
|
||||
print("\n========================================")
|
||||
print("Generating coverage report...")
|
||||
print("========================================")
|
||||
|
||||
|
||||
-- Save coverage stats
|
||||
luacov.save_stats()
|
||||
|
||||
|
||||
-- Run luacov command to generate report (silent)
|
||||
os.execute("luacov 2>/dev/null")
|
||||
|
||||
|
||||
-- Read and display the summary section from the report
|
||||
local report_file = io.open("luacov.report.out", "r")
|
||||
if report_file then
|
||||
local content = report_file:read("*all")
|
||||
report_file:close()
|
||||
|
||||
|
||||
-- Extract just the Summary section
|
||||
local summary = content:match("Summary\n=+\n(.-)$")
|
||||
if summary then
|
||||
@@ -79,7 +87,7 @@ if status then
|
||||
print(summary)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
print("Full coverage report: luacov.report.out")
|
||||
print("========================================")
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user