662 lines
16 KiB
Lua
662 lines
16 KiB
Lua
-- Test suite for LayoutEngine.lua module
|
|
-- Tests layout engine initialization and basic layout calculations
|
|
|
|
package.path = package.path .. ";./?.lua;./modules/?.lua"
|
|
|
|
-- Load love stub before anything else
|
|
require("testing.loveStub")
|
|
|
|
local luaunit = require("testing.luaunit")
|
|
local LayoutEngine = require("modules.LayoutEngine")
|
|
local Units = require("modules.Units")
|
|
local utils = require("modules.utils")
|
|
|
|
-- Mock dependencies
|
|
local mockContext = {
|
|
getScaleFactors = function()
|
|
return 1, 1
|
|
end,
|
|
baseScale = nil,
|
|
_cachedViewport = nil,
|
|
}
|
|
|
|
local mockErrorHandler = {
|
|
error = function(module, msg) end,
|
|
warn = function(module, msg) end,
|
|
}
|
|
|
|
local mockGrid = {
|
|
layoutGridItems = function(element) end,
|
|
}
|
|
|
|
local deps = {
|
|
utils = utils,
|
|
Grid = mockGrid,
|
|
Units = Units,
|
|
Context = mockContext,
|
|
ErrorHandler = mockErrorHandler,
|
|
}
|
|
|
|
-- Test suite for LayoutEngine.new()
|
|
TestLayoutEngineNew = {}
|
|
|
|
function TestLayoutEngineNew:testNewWithDefaults()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
luaunit.assertNotNil(layout)
|
|
luaunit.assertEquals(layout.positioning, utils.enums.Positioning.FLEX)
|
|
luaunit.assertEquals(layout.flexDirection, utils.enums.FlexDirection.HORIZONTAL)
|
|
luaunit.assertEquals(layout.justifyContent, utils.enums.JustifyContent.FLEX_START)
|
|
luaunit.assertEquals(layout.alignItems, utils.enums.AlignItems.STRETCH)
|
|
luaunit.assertEquals(layout.alignContent, utils.enums.AlignContent.STRETCH)
|
|
luaunit.assertEquals(layout.flexWrap, utils.enums.FlexWrap.NOWRAP)
|
|
luaunit.assertEquals(layout.gap, 10)
|
|
end
|
|
|
|
function TestLayoutEngineNew:testNewWithCustomProps()
|
|
local layout = LayoutEngine.new({
|
|
positioning = utils.enums.Positioning.GRID,
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
justifyContent = utils.enums.JustifyContent.CENTER,
|
|
alignItems = utils.enums.AlignItems.CENTER,
|
|
gap = 20,
|
|
gridRows = 3,
|
|
gridColumns = 4,
|
|
}, deps)
|
|
|
|
luaunit.assertEquals(layout.positioning, utils.enums.Positioning.GRID)
|
|
luaunit.assertEquals(layout.flexDirection, utils.enums.FlexDirection.VERTICAL)
|
|
luaunit.assertEquals(layout.justifyContent, utils.enums.JustifyContent.CENTER)
|
|
luaunit.assertEquals(layout.alignItems, utils.enums.AlignItems.CENTER)
|
|
luaunit.assertEquals(layout.gap, 20)
|
|
luaunit.assertEquals(layout.gridRows, 3)
|
|
luaunit.assertEquals(layout.gridColumns, 4)
|
|
end
|
|
|
|
function TestLayoutEngineNew:testNewStoresDependencies()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
luaunit.assertNotNil(layout._Grid)
|
|
luaunit.assertNotNil(layout._Units)
|
|
luaunit.assertNotNil(layout._Context)
|
|
luaunit.assertNotNil(layout._ErrorHandler)
|
|
end
|
|
|
|
-- Test suite for LayoutEngine:initialize()
|
|
TestLayoutEngineInitialize = {}
|
|
|
|
function TestLayoutEngineInitialize:testInitialize()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
local mockElement = { id = "test" }
|
|
|
|
layout:initialize(mockElement)
|
|
luaunit.assertEquals(layout.element, mockElement)
|
|
end
|
|
|
|
-- Test suite for LayoutEngine:calculateAutoWidth()
|
|
TestLayoutEngineAutoWidth = {}
|
|
|
|
function TestLayoutEngineAutoWidth:testAutoWidthNoElement()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
local width = layout:calculateAutoWidth()
|
|
luaunit.assertEquals(width, 0)
|
|
end
|
|
|
|
function TestLayoutEngineAutoWidth:testAutoWidthNoChildren()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
}, deps)
|
|
|
|
local mockElement = {
|
|
children = {},
|
|
calculateTextWidth = function()
|
|
return 100
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
luaunit.assertEquals(width, 100) -- Just text width
|
|
end
|
|
|
|
function TestLayoutEngineAutoWidth:testAutoWidthHorizontalWithGap()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
gap = 10,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 50
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 60
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 70
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextWidth = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
-- 50 + 60 + 70 = 180, plus 2 gaps (10 each) = 200
|
|
luaunit.assertEquals(width, 200)
|
|
end
|
|
|
|
function TestLayoutEngineAutoWidth:testAutoWidthVerticalTakesMax()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
gap = 10,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 50
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 150
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 75
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextWidth = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
-- Should take maximum width (150)
|
|
luaunit.assertEquals(width, 150)
|
|
end
|
|
|
|
function TestLayoutEngineAutoWidth:testAutoWidthSkipsAbsoluteChildren()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
gap = 10,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 50
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = true, -- Should be skipped
|
|
getBorderBoxWidth = function()
|
|
return 1000
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 60
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextWidth = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
-- 50 + 60 = 110, plus 1 gap (10) = 120 (mockChild2 is skipped)
|
|
luaunit.assertEquals(width, 120)
|
|
end
|
|
|
|
-- Test suite for LayoutEngine:calculateAutoHeight()
|
|
TestLayoutEngineAutoHeight = {}
|
|
|
|
function TestLayoutEngineAutoHeight:testAutoHeightNoElement()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
local height = layout:calculateAutoHeight()
|
|
luaunit.assertEquals(height, 0)
|
|
end
|
|
|
|
function TestLayoutEngineAutoHeight:testAutoHeightNoChildren()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
}, deps)
|
|
|
|
local mockElement = {
|
|
children = {},
|
|
calculateTextHeight = function()
|
|
return 50
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local height = layout:calculateAutoHeight()
|
|
luaunit.assertEquals(height, 50) -- Just text height
|
|
end
|
|
|
|
function TestLayoutEngineAutoHeight:testAutoHeightVerticalWithGap()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
gap = 5,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 30
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 40
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 50
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextHeight = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local height = layout:calculateAutoHeight()
|
|
-- 30 + 40 + 50 = 120, plus 2 gaps (5 each) = 130
|
|
luaunit.assertEquals(height, 130)
|
|
end
|
|
|
|
function TestLayoutEngineAutoHeight:testAutoHeightHorizontalTakesMax()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
gap = 5,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 30
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 100
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 50
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextHeight = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local height = layout:calculateAutoHeight()
|
|
-- Should take maximum height (100)
|
|
luaunit.assertEquals(height, 100)
|
|
end
|
|
|
|
function TestLayoutEngineAutoHeight:testAutoHeightSkipsAbsoluteChildren()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
gap = 5,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 30
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = true, -- Should be skipped
|
|
getBorderBoxHeight = function()
|
|
return 1000
|
|
end,
|
|
}
|
|
local mockChild3 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 40
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2, mockChild3 },
|
|
calculateTextHeight = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local height = layout:calculateAutoHeight()
|
|
-- 30 + 40 = 70, plus 1 gap (5) = 75 (mockChild2 is skipped)
|
|
luaunit.assertEquals(height, 75)
|
|
end
|
|
|
|
-- Test suite for LayoutEngine:applyPositioningOffsets()
|
|
TestLayoutEnginePositioningOffsets = {}
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyOffsetsNilChild()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
-- Should not error
|
|
layout:applyPositioningOffsets(nil)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyOffsetsNoParent()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
local mockChild = {
|
|
parent = nil,
|
|
top = 10,
|
|
}
|
|
-- Should not error, just return early
|
|
layout:applyPositioningOffsets(mockChild)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyTopOffset()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
|
|
local mockParent = {
|
|
x = 100,
|
|
y = 200,
|
|
padding = { left = 10, top = 20, right = 10, bottom = 20 },
|
|
}
|
|
|
|
local mockChild = {
|
|
parent = mockParent,
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
_explicitlyAbsolute = true,
|
|
x = 0,
|
|
y = 0,
|
|
top = 30,
|
|
}
|
|
|
|
layout:applyPositioningOffsets(mockChild)
|
|
-- y should be parent.y + parent.padding.top + top
|
|
-- 200 + 20 + 30 = 250
|
|
luaunit.assertEquals(mockChild.y, 250)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyLeftOffset()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
|
|
local mockParent = {
|
|
x = 100,
|
|
y = 200,
|
|
padding = { left = 10, top = 20, right = 10, bottom = 20 },
|
|
}
|
|
|
|
local mockChild = {
|
|
parent = mockParent,
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
_explicitlyAbsolute = true,
|
|
x = 0,
|
|
y = 0,
|
|
left = 40,
|
|
}
|
|
|
|
layout:applyPositioningOffsets(mockChild)
|
|
-- x should be parent.x + parent.padding.left + left
|
|
-- 100 + 10 + 40 = 150
|
|
luaunit.assertEquals(mockChild.x, 150)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyBottomOffset()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
|
|
local mockParent = {
|
|
x = 100,
|
|
y = 200,
|
|
width = 400,
|
|
height = 300,
|
|
padding = { left = 10, top = 20, right = 10, bottom = 20 },
|
|
}
|
|
|
|
local mockChild = {
|
|
parent = mockParent,
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
_explicitlyAbsolute = true,
|
|
x = 0,
|
|
y = 0,
|
|
bottom = 50,
|
|
getBorderBoxHeight = function()
|
|
return 80
|
|
end,
|
|
}
|
|
|
|
layout:applyPositioningOffsets(mockChild)
|
|
-- y should be parent.y + parent.padding.top + parent.height - bottom - childHeight
|
|
-- 200 + 20 + 300 - 50 - 80 = 390
|
|
luaunit.assertEquals(mockChild.y, 390)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testApplyRightOffset()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
|
|
local mockParent = {
|
|
x = 100,
|
|
y = 200,
|
|
width = 400,
|
|
height = 300,
|
|
padding = { left = 10, top = 20, right = 10, bottom = 20 },
|
|
}
|
|
|
|
local mockChild = {
|
|
parent = mockParent,
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
_explicitlyAbsolute = true,
|
|
x = 0,
|
|
y = 0,
|
|
right = 60,
|
|
getBorderBoxWidth = function()
|
|
return 100
|
|
end,
|
|
}
|
|
|
|
layout:applyPositioningOffsets(mockChild)
|
|
-- x should be parent.x + parent.padding.left + parent.width - right - childWidth
|
|
-- 100 + 10 + 400 - 60 - 100 = 350
|
|
luaunit.assertEquals(mockChild.x, 350)
|
|
end
|
|
|
|
function TestLayoutEnginePositioningOffsets:testSkipsFlexChildren()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
|
|
local mockParent = {
|
|
x = 100,
|
|
y = 200,
|
|
padding = { left = 10, top = 20, right = 10, bottom = 20 },
|
|
}
|
|
|
|
local mockChild = {
|
|
parent = mockParent,
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
_explicitlyAbsolute = false, -- Participates in flex layout
|
|
x = 500,
|
|
y = 600,
|
|
top = 30,
|
|
left = 40,
|
|
}
|
|
|
|
layout:applyPositioningOffsets(mockChild)
|
|
-- Should not apply offsets for flex children
|
|
luaunit.assertEquals(mockChild.x, 500) -- Unchanged
|
|
luaunit.assertEquals(mockChild.y, 600) -- Unchanged
|
|
end
|
|
|
|
-- Test suite for LayoutEngine:layoutChildren()
|
|
TestLayoutEngineLayoutChildren = {}
|
|
|
|
function TestLayoutEngineLayoutChildren:testLayoutChildrenNoElement()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
-- Should not error
|
|
layout:layoutChildren()
|
|
end
|
|
|
|
function TestLayoutEngineLayoutChildren:testLayoutChildrenNoChildren()
|
|
local layout = LayoutEngine.new({}, deps)
|
|
local mockElement = {
|
|
children = {},
|
|
}
|
|
layout:initialize(mockElement)
|
|
-- Should not error
|
|
layout:layoutChildren()
|
|
end
|
|
|
|
function TestLayoutEngineLayoutChildren:testLayoutChildrenAbsolutePositioning()
|
|
local layout = LayoutEngine.new({
|
|
positioning = utils.enums.Positioning.ABSOLUTE,
|
|
}, deps)
|
|
|
|
local mockElement = {
|
|
children = {},
|
|
padding = { left = 0, top = 0, right = 0, bottom = 0 },
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
-- Should handle absolute positioning (doesn't layout children, just applies offsets)
|
|
layout:layoutChildren()
|
|
end
|
|
|
|
function TestLayoutEngineLayoutChildren:testLayoutChildrenRelativePositioning()
|
|
local layout = LayoutEngine.new({
|
|
positioning = utils.enums.Positioning.RELATIVE,
|
|
}, deps)
|
|
|
|
local mockElement = {
|
|
children = {},
|
|
padding = { left = 0, top = 0, right = 0, bottom = 0 },
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
-- Should handle relative positioning (doesn't layout children, just applies offsets)
|
|
layout:layoutChildren()
|
|
end
|
|
|
|
-- Edge cases
|
|
TestLayoutEngineEdgeCases = {}
|
|
|
|
function TestLayoutEngineEdgeCases:testAutoWidthWithZeroGap()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
gap = 0,
|
|
}, deps)
|
|
|
|
local mockChild1 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 50
|
|
end,
|
|
}
|
|
local mockChild2 = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 60
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild1, mockChild2 },
|
|
calculateTextWidth = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
luaunit.assertEquals(width, 110) -- 50 + 60, no gaps
|
|
end
|
|
|
|
function TestLayoutEngineEdgeCases:testAutoHeightWithSingleChild()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.VERTICAL,
|
|
gap = 10,
|
|
}, deps)
|
|
|
|
local mockChild = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxHeight = function()
|
|
return 100
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild },
|
|
calculateTextHeight = function()
|
|
return 0
|
|
end,
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local height = layout:calculateAutoHeight()
|
|
luaunit.assertEquals(height, 100) -- No gaps with single child
|
|
end
|
|
|
|
function TestLayoutEngineEdgeCases:testAutoWidthWithTextAndChildren()
|
|
local layout = LayoutEngine.new({
|
|
flexDirection = utils.enums.FlexDirection.HORIZONTAL,
|
|
gap = 10,
|
|
}, deps)
|
|
|
|
local mockChild = {
|
|
_explicitlyAbsolute = false,
|
|
getBorderBoxWidth = function()
|
|
return 50
|
|
end,
|
|
}
|
|
|
|
local mockElement = {
|
|
children = { mockChild },
|
|
calculateTextWidth = function()
|
|
return 100
|
|
end, -- Has text
|
|
}
|
|
layout:initialize(mockElement)
|
|
|
|
local width = layout:calculateAutoWidth()
|
|
-- Text width (100) + child width (50) = 150
|
|
luaunit.assertEquals(width, 150)
|
|
end
|
|
|
|
-- Run tests if not running as part of a suite
|
|
if not _G.RUNNING_ALL_TESTS then
|
|
os.exit(luaunit.LuaUnit.run())
|
|
end
|