-- Test suite for Element.lua -- Tests element creation, size calculations, and basic functionality package.path = package.path .. ";./?.lua;./modules/?.lua" -- Load love stub before anything else require("testing.loveStub") local luaunit = require("testing.luaunit") local ErrorHandler = require("modules.ErrorHandler") -- Initialize ErrorHandler ErrorHandler.init({}) -- Load FlexLove which properly initializes all dependencies local FlexLove = require("FlexLove") local ErrorHandler = require("modules.ErrorHandler") -- Initialize ErrorHandler ErrorHandler.init({}) -- Test suite for Element creation TestElementCreation = {} function TestElementCreation:setUp() -- Initialize FlexLove for each test FlexLove.beginFrame(1920, 1080) end function TestElementCreation:tearDown() FlexLove.endFrame() end function TestElementCreation:test_create_minimal_element() local element = FlexLove.new({ id = "test1", x = 10, y = 20, width = 100, height = 50, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.id, "test1") luaunit.assertEquals(element.x, 10) luaunit.assertEquals(element.y, 20) luaunit.assertEquals(element.width, 100) luaunit.assertEquals(element.height, 50) end function TestElementCreation:test_element_with_text() local element = FlexLove.new({ id = "text1", x = 0, y = 0, width = 200, height = 100, text = "Hello World", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.text, "Hello World") end function TestElementCreation:test_element_with_backgroundColor() local element = FlexLove.new({ id = "colored1", x = 0, y = 0, width = 100, height = 100, backgroundColor = { 1, 0, 0, 1 }, }) luaunit.assertNotNil(element) luaunit.assertNotNil(element.backgroundColor) end function TestElementCreation:test_element_with_children() local parent = FlexLove.new({ id = "parent1", x = 0, y = 0, width = 300, height = 200, }) local child = FlexLove.new({ id = "child1", x = 10, y = 10, width = 50, height = 50, parent = parent, }) luaunit.assertNotNil(parent) luaunit.assertNotNil(child) luaunit.assertEquals(child.parent, parent) luaunit.assertEquals(#parent.children, 1) luaunit.assertEquals(parent.children[1], child) end function TestElementCreation:test_element_with_padding() local element = FlexLove.new({ id = "padded1", x = 0, y = 0, width = 200, height = 100, padding = { horizontal = 10, vertical = 10 }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.padding.left, 10) luaunit.assertEquals(element.padding.top, 10) luaunit.assertEquals(element.padding.right, 10) luaunit.assertEquals(element.padding.bottom, 10) end function TestElementCreation:test_element_with_margin() local element = FlexLove.new({ id = "margined1", x = 0, y = 0, width = 200, height = 100, margin = { horizontal = 5, vertical = 5 }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.margin.left, 5) luaunit.assertEquals(element.margin.top, 5) luaunit.assertEquals(element.margin.right, 5) luaunit.assertEquals(element.margin.bottom, 5) end -- Test suite for Element sizing TestElementSizing = {} function TestElementSizing:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementSizing:tearDown() FlexLove.endFrame() end function TestElementSizing:test_getBorderBoxWidth() local element = FlexLove.new({ id = "sized1", x = 0, y = 0, width = 100, height = 50, }) local borderBoxWidth = element:getBorderBoxWidth() luaunit.assertEquals(borderBoxWidth, 100) end function TestElementSizing:test_getBorderBoxHeight() local element = FlexLove.new({ id = "sized2", x = 0, y = 0, width = 100, height = 50, }) local borderBoxHeight = element:getBorderBoxHeight() luaunit.assertEquals(borderBoxHeight, 50) end function TestElementSizing:test_getBounds() local element = FlexLove.new({ id = "bounds1", x = 10, y = 20, width = 100, height = 50, }) local bounds = element:getBounds() luaunit.assertEquals(bounds.x, 10) luaunit.assertEquals(bounds.y, 20) luaunit.assertEquals(bounds.width, 100) luaunit.assertEquals(bounds.height, 50) end function TestElementSizing:test_contains_point_inside() local element = FlexLove.new({ id = "contains1", x = 10, y = 20, width = 100, height = 50, }) local contains = element:contains(50, 40) luaunit.assertTrue(contains) end function TestElementSizing:test_contains_point_outside() local element = FlexLove.new({ id = "contains2", x = 10, y = 20, width = 100, height = 50, }) local contains = element:contains(150, 100) luaunit.assertFalse(contains) end function TestElementSizing:test_contains_point_on_edge() local element = FlexLove.new({ id = "contains3", x = 10, y = 20, width = 100, 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) end -- Test suite for Element with units (units are resolved immediately after creation) TestElementUnits = {} function TestElementUnits:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementUnits:tearDown() FlexLove.endFrame() end function TestElementUnits:test_element_with_percentage_width() local parent = FlexLove.new({ id = "parent_pct", x = 0, y = 0, width = 1000, height = 500, }) local child = FlexLove.new({ id = "child_pct", x = 0, y = 0, width = "50%", height = 100, parent = parent, }) luaunit.assertNotNil(child) -- Width should be resolved to 500 (50% of parent's 1000) luaunit.assertEquals(child.width, 500) end function TestElementUnits:test_element_with_viewport_units() local element = FlexLove.new({ id = "viewport1", x = 0, y = 0, 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") luaunit.assertEquals(type(element.height), "number") -- Should be positive values luaunit.assertTrue(element.width > 0) luaunit.assertTrue(element.height > 0) end -- Test suite for Element positioning TestElementPositioning = {} function TestElementPositioning:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementPositioning:tearDown() FlexLove.endFrame() end function TestElementPositioning:test_element_absolute_position() local element = FlexLove.new({ id = "abs1", x = 100, y = 200, width = 50, height = 50, positioning = "absolute", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.positioning, "absolute") end function TestElementPositioning:test_nested_element_positions() local parent = FlexLove.new({ id = "nest_parent", x = 100, y = 100, width = 300, height = 200, }) local child = FlexLove.new({ id = "nest_child", x = 20, y = 30, width = 50, height = 50, parent = parent, }) luaunit.assertNotNil(parent) luaunit.assertNotNil(child) -- Child positions are absolute in FlexLove, not relative to parent -- So child.x = parent.x + relative_x = 100 + 20 = 120 luaunit.assertEquals(child.x, 120) luaunit.assertEquals(child.y, 130) end -- Test suite for Element flex layout TestElementFlex = {} function TestElementFlex:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementFlex:tearDown() FlexLove.endFrame() end function TestElementFlex:test_element_with_flex_direction() local element = FlexLove.new({ id = "flex1", x = 0, y = 0, width = 300, height = 200, positioning = "flex", flexDirection = "horizontal", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.flexDirection, "horizontal") end function TestElementFlex:test_element_with_flex_properties() local parent = FlexLove.new({ id = "flex_parent", x = 0, y = 0, width = 300, height = 200, positioning = "flex", flexDirection = "horizontal", }) local element = FlexLove.new({ id = "flex2", parent = parent, width = 100, height = 100, flexGrow = 1, flexShrink = 0, flexBasis = "auto", }) luaunit.assertNotNil(element) -- Just check element was created successfully -- Flex properties are handled by LayoutEngine, not stored on element luaunit.assertNotNil(element) luaunit.assertEquals(element.parent, parent) end function TestElementFlex:test_element_with_gap() local element = FlexLove.new({ id = "gap1", x = 0, y = 0, width = 300, height = 200, positioning = "flex", gap = 10, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.gap, 10) end -- Test suite for Element styling properties TestElementStyling = {} function TestElementStyling:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementStyling:tearDown() FlexLove.endFrame() end function TestElementStyling:test_element_with_border() local element = FlexLove.new({ id = "bordered1", x = 0, y = 0, width = 100, height = 100, border = 2, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.border, 2) end function TestElementStyling:test_element_with_corner_radius() local element = FlexLove.new({ id = "rounded1", x = 0, y = 0, width = 100, height = 100, cornerRadius = 10, }) luaunit.assertNotNil(element) -- Corner radius might be stored as a table luaunit.assertNotNil(element.cornerRadius) end function TestElementStyling:test_element_with_text_align() local element = FlexLove.new({ id = "aligned1", x = 0, y = 0, width = 200, height = 100, text = "Centered Text", textAlign = "center", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.textAlign, "center") end function TestElementStyling:test_element_with_opacity() local element = FlexLove.new({ id = "transparent1", x = 0, y = 0, width = 100, height = 100, opacity = 0.5, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.opacity, 0.5) end function TestElementStyling:test_element_with_border_color() local element = FlexLove.new({ id = "colored_border", x = 0, y = 0, width = 100, height = 100, border = 2, borderColor = { 1, 0, 0, 1 }, }) luaunit.assertNotNil(element) luaunit.assertNotNil(element.borderColor) end -- Test suite for Element methods TestElementMethods = {} function TestElementMethods:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementMethods:tearDown() FlexLove.endFrame() end function TestElementMethods:test_element_setText() local element = FlexLove.new({ id = "textual1", x = 0, y = 0, width = 200, height = 100, text = "Initial", }) element:setText("Updated") luaunit.assertEquals(element.text, "Updated") end function TestElementMethods:test_element_addChild() local parent = FlexLove.new({ id = "parent_add", x = 0, y = 0, width = 300, height = 200, }) local child = FlexLove.new({ id = "child_add", x = 10, y = 10, width = 50, height = 50, }) parent:addChild(child) luaunit.assertEquals(#parent.children, 1) luaunit.assertEquals(parent.children[1], child) luaunit.assertEquals(child.parent, parent) end -- Test suite for scroll-related functions TestElementScroll = {} function TestElementScroll:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementScroll:tearDown() FlexLove.endFrame() end function TestElementScroll:test_scrollable_element_with_overflow() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.overflow, "scroll") luaunit.assertNotNil(element._scrollManager) end function TestElementScroll:test_setScrollPosition() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, 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) end function TestElementScroll:test_scrollBy() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) local initialX, initialY = element:getScrollPosition() element:scrollBy(10, 20) local newX, newY = element:getScrollPosition() luaunit.assertNotNil(newX) luaunit.assertNotNil(newY) end function TestElementScroll:test_scrollToTop() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) element:scrollToTop() local _, scrollY = element:getScrollPosition() luaunit.assertEquals(scrollY, 0) end function TestElementScroll:test_scrollToBottom() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) element:scrollToBottom() -- Bottom position depends on content, just verify it doesn't error local _, scrollY = element:getScrollPosition() luaunit.assertNotNil(scrollY) end function TestElementScroll:test_scrollToLeft() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) element:scrollToLeft() local scrollX, _ = element:getScrollPosition() luaunit.assertEquals(scrollX, 0) end function TestElementScroll:test_scrollToRight() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) element:scrollToRight() local scrollX, _ = element:getScrollPosition() luaunit.assertNotNil(scrollX) end function TestElementScroll:test_getMaxScroll() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) local maxX, maxY = element:getMaxScroll() luaunit.assertNotNil(maxX) luaunit.assertNotNil(maxY) end function TestElementScroll:test_getScrollPercentage() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) local percentX, percentY = element:getScrollPercentage() luaunit.assertNotNil(percentX) luaunit.assertNotNil(percentY) luaunit.assertTrue(percentX >= 0 and percentX <= 1) luaunit.assertTrue(percentY >= 0 and percentY <= 1) end function TestElementScroll:test_hasOverflow() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) local hasOverflowX, hasOverflowY = element:hasOverflow() luaunit.assertNotNil(hasOverflowX) luaunit.assertNotNil(hasOverflowY) end function TestElementScroll:test_getContentSize() local element = FlexLove.new({ id = "scrollable", x = 0, y = 0, width = 200, height = 200, overflow = "scroll", }) local contentWidth, contentHeight = element:getContentSize() luaunit.assertNotNil(contentWidth) luaunit.assertNotNil(contentHeight) end -- Test suite for element geometry and bounds TestElementGeometry = {} function TestElementGeometry:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementGeometry:tearDown() FlexLove.endFrame() end function TestElementGeometry:test_getBounds() local element = FlexLove.new({ id = "test", x = 10, y = 20, width = 100, height = 50, }) local bounds = element:getBounds() luaunit.assertEquals(bounds.x, 10) luaunit.assertEquals(bounds.y, 20) luaunit.assertEquals(bounds.width, 100) luaunit.assertEquals(bounds.height, 50) end function TestElementGeometry:test_contains_point_inside() local element = FlexLove.new({ id = "test", x = 10, y = 20, width = 100, height = 50, }) luaunit.assertTrue(element:contains(50, 40)) end function TestElementGeometry:test_contains_point_outside() local element = FlexLove.new({ id = "test", x = 10, y = 20, width = 100, height = 50, }) luaunit.assertFalse(element:contains(200, 200)) end function TestElementGeometry:test_getBorderBoxWidth_no_border() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 50, }) local borderBoxWidth = element:getBorderBoxWidth() luaunit.assertEquals(borderBoxWidth, 100) end function TestElementGeometry:test_getBorderBoxHeight_no_border() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 50, }) local borderBoxHeight = element:getBorderBoxHeight() luaunit.assertEquals(borderBoxHeight, 50) end function TestElementGeometry:test_getBorderBoxWidth_with_border() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 50, border = { left = 2, right = 2, top = 0, bottom = 0 }, }) local borderBoxWidth = element:getBorderBoxWidth() -- Width includes left + right borders luaunit.assertTrue(borderBoxWidth >= 100) end function TestElementGeometry:test_getAvailableContentWidth() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 200, height = 100, 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 luaunit.assertTrue(availWidth <= 200) end function TestElementGeometry:test_getAvailableContentHeight() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 200, height = 100, 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 luaunit.assertTrue(availHeight <= 100) end function TestElementGeometry:test_getScaledContentPadding() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 200, height = 100, 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 luaunit.assertNotNil(padding.top) luaunit.assertNotNil(padding.right) luaunit.assertNotNil(padding.bottom) luaunit.assertNotNil(padding.left) end end -- Test suite for child management TestElementChildren = {} function TestElementChildren:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementChildren:tearDown() FlexLove.endFrame() end function TestElementChildren:test_addChild() local parent = FlexLove.new({ id = "parent", x = 0, y = 0, width = 200, height = 200, }) local child = FlexLove.new({ id = "child", x = 10, y = 10, width = 50, height = 50, }) parent:addChild(child) luaunit.assertEquals(#parent.children, 1) luaunit.assertEquals(parent.children[1], child) luaunit.assertEquals(child.parent, parent) end function TestElementChildren:test_removeChild() local parent = FlexLove.new({ id = "parent", x = 0, y = 0, width = 200, height = 200, }) local child = FlexLove.new({ id = "child", x = 10, y = 10, width = 50, height = 50, }) parent:addChild(child) parent:removeChild(child) luaunit.assertEquals(#parent.children, 0) luaunit.assertNil(child.parent) end function TestElementChildren:test_clearChildren() local parent = FlexLove.new({ id = "parent", x = 0, y = 0, width = 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 function TestElementChildren:test_getChildCount() local parent = FlexLove.new({ id = "parent", x = 0, y = 0, width = 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 -- Test suite for element visibility and opacity TestElementVisibility = {} function TestElementVisibility:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementVisibility:tearDown() FlexLove.endFrame() end function TestElementVisibility:test_visibility_visible() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, visibility = "visible", }) luaunit.assertEquals(element.visibility, "visible") end function TestElementVisibility:test_visibility_hidden() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, visibility = "hidden", }) luaunit.assertEquals(element.visibility, "hidden") end function TestElementVisibility:test_opacity_default() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, }) luaunit.assertEquals(element.opacity, 1) end function TestElementVisibility:test_opacity_custom() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, opacity = 0.5, }) luaunit.assertEquals(element.opacity, 0.5) end -- Test suite for text editing TestElementTextEditing = {} function TestElementTextEditing:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementTextEditing:tearDown() FlexLove.endFrame() end function TestElementTextEditing:test_editable_element() local element = FlexLove.new({ id = "input", x = 0, y = 0, width = 200, height = 40, editable = true, text = "Edit me", }) luaunit.assertTrue(element.editable) luaunit.assertNotNil(element._textEditor) end function TestElementTextEditing:test_placeholder_text() local element = FlexLove.new({ id = "input", x = 0, y = 0, width = 200, height = 40, editable = true, placeholder = "Enter text...", }) luaunit.assertEquals(element.placeholder, "Enter text...") end -- Test suite for additional element features TestElementAdditional = {} function TestElementAdditional:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementAdditional:tearDown() FlexLove.endFrame() end function TestElementAdditional:test_element_with_z_index() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, z = 10, }) luaunit.assertEquals(element.z, 10) end function TestElementAdditional:test_element_with_text() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, 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, y = 0, width = 100, height = 100, text = "Red text", 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, }) luaunit.assertEquals(element.backgroundColor, bgColor) end function TestElementAdditional:test_element_with_corner_radius() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, cornerRadius = 10, }) luaunit.assertNotNil(element.cornerRadius) luaunit.assertEquals(element.cornerRadius.topLeft, 10) luaunit.assertEquals(element.cornerRadius.topRight, 10) luaunit.assertEquals(element.cornerRadius.bottomLeft, 10) luaunit.assertEquals(element.cornerRadius.bottomRight, 10) end function TestElementAdditional:test_element_with_margin() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, 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) luaunit.assertEquals(element.margin.bottom, 5) luaunit.assertEquals(element.margin.left, 10) end function TestElementAdditional:test_element_destroy() local parent = FlexLove.new({ id = "parent", x = 0, y = 0, width = 200, height = 200, }) local child = FlexLove.new({ id = "child", parent = parent, x = 0, y = 0, width = 50, height = 50, }) luaunit.assertEquals(#parent.children, 1) child:destroy() luaunit.assertNil(child.parent) end function TestElementAdditional:test_element_with_disabled() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, disabled = true, }) luaunit.assertTrue(element.disabled) end function TestElementAdditional:test_element_with_active() local element = FlexLove.new({ id = "test", x = 0, y = 0, width = 100, height = 100, 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, }) luaunit.assertEquals(element.userdata, customData) luaunit.assertEquals(element.userdata.foo, "bar") luaunit.assertEquals(element.userdata.count, 42) end -- ========================================== -- UNHAPPY PATH TESTS -- ========================================== TestElementUnhappyPaths = {} function TestElementUnhappyPaths:setUp() FlexLove.beginFrame(1920, 1080) end function TestElementUnhappyPaths:tearDown() FlexLove.endFrame() end -- Test: Element with missing deps parameter function TestElementUnhappyPaths:test_element_with_init() -- Test that Element.new() works after FlexLove.init() is called -- Element now uses module-level dependencies initialized via Element.init() FlexLove.init() -- Ensure FlexLove is initialized local Element = require("modules.Element") local success = pcall(function() Element.new({}) end) luaunit.assertTrue(success) -- Should work after Element.init() is called by FlexLove end -- Test: Element with negative dimensions function TestElementUnhappyPaths:test_element_negative_dimensions() local element = FlexLove.new({ id = "negative", x = 0, y = 0, width = -100, height = -50, }) luaunit.assertNotNil(element) -- Element should still be created (negative values handled) end -- Test: Element with zero dimensions function TestElementUnhappyPaths:test_element_zero_dimensions() local element = FlexLove.new({ id = "zero", x = 0, y = 0, width = 0, height = 0, }) luaunit.assertNotNil(element) end -- Test: Element with invalid opacity values function TestElementUnhappyPaths:test_element_invalid_opacity() -- Opacity > 1 local success = pcall(function() FlexLove.new({ id = "high_opacity", width = 100, height = 100, opacity = 2.5, }) end) luaunit.assertFalse(success) -- Should error (validateRange) -- Negative opacity success = pcall(function() FlexLove.new({ id = "negative_opacity", width = 100, height = 100, opacity = -0.5, }) end) luaunit.assertFalse(success) -- Should error (validateRange) end -- Test: Element with invalid imageOpacity values function TestElementUnhappyPaths:test_element_invalid_image_opacity() -- imageOpacity > 1 local success = pcall(function() FlexLove.new({ id = "high_img_opacity", width = 100, height = 100, imageOpacity = 3.0, }) end) luaunit.assertFalse(success) -- Negative imageOpacity success = pcall(function() FlexLove.new({ id = "negative_img_opacity", width = 100, height = 100, imageOpacity = -1.0, }) end) luaunit.assertFalse(success) end -- Test: Element with invalid textSize function TestElementUnhappyPaths:test_element_invalid_text_size() -- Zero textSize local success = pcall(function() FlexLove.new({ id = "zero_text", width = 100, height = 100, textSize = 0, }) end) luaunit.assertFalse(success) -- Negative textSize success = pcall(function() FlexLove.new({ id = "negative_text", width = 100, height = 100, textSize = -12, }) end) luaunit.assertFalse(success) end -- Test: Element with invalid textAlign enum function TestElementUnhappyPaths:test_element_invalid_text_align() local success = pcall(function() FlexLove.new({ id = "invalid_align", width = 100, height = 100, textAlign = "invalid_value", }) end) luaunit.assertFalse(success) -- Should error (validateEnum) end -- Test: Element with invalid positioning enum function TestElementUnhappyPaths:test_element_invalid_positioning() local success = pcall(function() FlexLove.new({ id = "invalid_pos", width = 100, height = 100, positioning = "invalid_positioning", }) end) luaunit.assertFalse(success) -- Should error (validateEnum) end -- Test: Element with invalid flexDirection enum function TestElementUnhappyPaths:test_element_invalid_flex_direction() local success = pcall(function() FlexLove.new({ id = "invalid_flex", width = 100, height = 100, positioning = "flex", flexDirection = "diagonal", }) end) luaunit.assertFalse(success) -- Should error (validateEnum) end -- Test: Element with invalid objectFit enum function TestElementUnhappyPaths:test_element_invalid_object_fit() local success = pcall(function() FlexLove.new({ id = "invalid_fit", width = 100, height = 100, objectFit = "stretch", }) end) luaunit.assertFalse(success) -- Should error (validateEnum) end -- Test: Element with nonexistent image path function TestElementUnhappyPaths:test_element_nonexistent_image() local element = FlexLove.new({ id = "no_image", width = 100, height = 100, imagePath = "/nonexistent/path/to/image.png", }) luaunit.assertNotNil(element) luaunit.assertNil(element._loadedImage) -- Image should fail to load silently end -- Test: Element with passwordMode and multiline (conflicting) function TestElementUnhappyPaths:test_element_password_multiline_conflict() local element = FlexLove.new({ id = "conflict", width = 200, height = 100, editable = true, passwordMode = true, multiline = true, -- Should be disabled by passwordMode }) luaunit.assertNotNil(element) luaunit.assertFalse(element.multiline) -- multiline should be forced to false end -- Test: Element addChild with nil child function TestElementUnhappyPaths:test_add_nil_child() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) local success = pcall(function() parent:addChild(nil) end) luaunit.assertFalse(success) -- Should error end -- Test: Element removeChild that doesn't exist function TestElementUnhappyPaths:test_remove_nonexistent_child() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) local notAChild = FlexLove.new({ id = "orphan", width = 50, height = 50, }) parent:removeChild(notAChild) -- Should not crash luaunit.assertEquals(#parent.children, 0) end -- Test: Element removeChild with nil function TestElementUnhappyPaths:test_remove_nil_child() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) parent:removeChild(nil) -- Should not crash luaunit.assertTrue(true) end -- Test: Element clearChildren on empty parent function TestElementUnhappyPaths:test_clear_children_empty() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) parent:clearChildren() -- Should not crash luaunit.assertEquals(#parent.children, 0) end -- Test: Element clearChildren called twice function TestElementUnhappyPaths:test_clear_children_twice() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) local child = FlexLove.new({ id = "child", width = 50, height = 50, parent = parent, }) parent:clearChildren() parent:clearChildren() -- Call again luaunit.assertEquals(#parent.children, 0) end -- Test: Element contains with NaN coordinates function TestElementUnhappyPaths:test_contains_nan_coordinates() local element = FlexLove.new({ id = "test", x = 10, y = 20, width = 100, height = 50, }) local nan = 0 / 0 local result = element:contains(nan, nan) -- NaN comparisons return false, so this should be false luaunit.assertFalse(result) end -- Test: Element setScrollPosition without ScrollManager function TestElementUnhappyPaths:test_scroll_without_manager() local element = FlexLove.new({ id = "no_scroll", width = 100, height = 100, -- No overflow property, so no ScrollManager }) element:setScrollPosition(50, 50) -- Should not crash luaunit.assertTrue(true) end -- Test: Element scrollBy with nil values function TestElementUnhappyPaths:test_scroll_by_nil() local element = FlexLove.new({ id = "scrollable", width = 200, height = 200, overflow = "scroll", }) element:scrollBy(nil, nil) -- Should use current position luaunit.assertTrue(true) end -- Test: Element destroy on already destroyed element function TestElementUnhappyPaths:test_destroy_twice() local element = FlexLove.new({ id = "destroyable", width = 100, height = 100, }) element:destroy() element:destroy() -- Call again - should not crash luaunit.assertTrue(true) end -- Test: Element destroy with circular reference (parent-child) function TestElementUnhappyPaths:test_destroy_with_children() local parent = FlexLove.new({ id = "parent", width = 200, height = 200, }) local child = FlexLove.new({ id = "child", width = 50, height = 50, parent = parent, }) parent:destroy() -- Should destroy all children too luaunit.assertEquals(#parent.children, 0) end -- Test: Element update with nil dt function TestElementUnhappyPaths:test_update_nil_dt() local element = FlexLove.new({ id = "test", width = 100, height = 100, }) local success = pcall(function() element:update(nil) end) -- May or may not error depending on implementation end -- Test: Element update with negative dt function TestElementUnhappyPaths:test_update_negative_dt() local element = FlexLove.new({ id = "test", width = 100, height = 100, }) element:update(-0.016) -- Should not crash luaunit.assertTrue(true) end -- Test: Element draw with nil backdropCanvas function TestElementUnhappyPaths:test_draw_nil_backdrop() local element = FlexLove.new({ id = "test", width = 100, height = 100, }) element:draw(nil) -- Should not crash luaunit.assertTrue(true) end -- Test: Element with invalid cornerRadius types function TestElementUnhappyPaths:test_invalid_corner_radius() -- String cornerRadius local element = FlexLove.new({ id = "test", width = 100, height = 100, cornerRadius = "invalid", }) luaunit.assertNotNil(element) -- Negative cornerRadius element = FlexLove.new({ id = "test2", width = 100, height = 100, cornerRadius = -10, }) luaunit.assertNotNil(element) end -- Test: Element with partial cornerRadius table function TestElementUnhappyPaths:test_partial_corner_radius() local element = FlexLove.new({ id = "test", width = 100, height = 100, cornerRadius = { topLeft = 10, -- Missing other corners }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.cornerRadius.topLeft, 10) luaunit.assertEquals(element.cornerRadius.topRight, 0) end -- Test: Element with invalid border types function TestElementUnhappyPaths:test_invalid_border() -- String border local element = FlexLove.new({ id = "test", width = 100, height = 100, border = "invalid", }) luaunit.assertNotNil(element) -- Negative border element = FlexLove.new({ id = "test2", width = 100, height = 100, border = -5, }) luaunit.assertNotNil(element) end -- Test: Element with partial border table function TestElementUnhappyPaths:test_partial_border() local element = FlexLove.new({ id = "test", width = 100, height = 100, border = { top = 2, left = 3, -- Missing right and bottom }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.border.top, 2) luaunit.assertEquals(element.border.left, 3) luaunit.assertFalse(element.border.right) luaunit.assertFalse(element.border.bottom) end -- Test: Element with invalid padding types function TestElementUnhappyPaths:test_invalid_padding() -- String padding local element = FlexLove.new({ id = "test", width = 100, height = 100, padding = "invalid", }) luaunit.assertNotNil(element) -- Negative padding element = FlexLove.new({ id = "test2", width = 100, height = 100, padding = { top = -10, left = -10, right = -10, bottom = -10 }, }) luaunit.assertNotNil(element) end -- Test: Element with invalid margin types function TestElementUnhappyPaths:test_invalid_margin() -- String margin local element = FlexLove.new({ id = "test", width = 100, height = 100, margin = "invalid", }) luaunit.assertNotNil(element) end -- Test: Element with invalid gap value function TestElementUnhappyPaths:test_invalid_gap() -- Negative gap local element = FlexLove.new({ id = "test", width = 300, height = 200, positioning = "flex", gap = -10, }) luaunit.assertNotNil(element) -- Negative rows/columns element = FlexLove.new({ id = "test2", width = 300, height = 200, positioning = "grid", gridRows = -5, gridColumns = -5, }) luaunit.assertNotNil(element) end -- Test: Element setText on non-text element function TestElementUnhappyPaths:test_set_text_on_non_text() local element = FlexLove.new({ id = "no_text", width = 100, height = 100, }) element:setText("New text") -- Should not crash luaunit.assertEquals(element.text, "New text") end -- Test: Element setText with nil function TestElementUnhappyPaths:test_set_text_nil() local element = FlexLove.new({ id = "text", width = 100, height = 100, text = "Initial", }) element:setText(nil) luaunit.assertNil(element.text) end -- Test: Element with conflicting size constraints function TestElementUnhappyPaths:test_conflicting_size_constraints() -- Width less than padding local element = FlexLove.new({ id = "conflict", width = 10, height = 10, padding = { top = 20, left = 20, right = 20, bottom = 20 }, }) luaunit.assertNotNil(element) -- Content width should be clamped to 0 or handled gracefully end -- Test: Element textinput on non-editable element function TestElementUnhappyPaths:test_textinput_non_editable() local element = FlexLove.new({ id = "not_editable", width = 100, height = 100, editable = false, }) local success = pcall(function() element:textinput("a") end) -- Should either do nothing or handle gracefully end -- Test: Element keypressed on non-editable element function TestElementUnhappyPaths:test_keypressed_non_editable() local element = FlexLove.new({ id = "not_editable", width = 100, height = 100, editable = false, }) local success = pcall(function() element:keypressed("return", "return", false) end) -- Should either do nothing or handle gracefully end -- Test: Element with invalid blur configuration function TestElementUnhappyPaths:test_invalid_blur_config() -- Negative intensity local element = FlexLove.new({ id = "blur", width = 100, height = 100, contentBlur = { intensity = -10, quality = 5 }, }) luaunit.assertNotNil(element) -- Intensity > 100 element = FlexLove.new({ id = "blur2", width = 100, height = 100, backdropBlur = { intensity = 150, quality = 5 }, }) luaunit.assertNotNil(element) -- Invalid quality element = FlexLove.new({ id = "blur3", width = 100, height = 100, contentBlur = { intensity = 50, quality = 0 }, }) luaunit.assertNotNil(element) end -- Test: Element getAvailableContentWidth/Height on element with no padding function TestElementUnhappyPaths:test_available_content_no_padding() local element = FlexLove.new({ id = "test", width = 100, height = 100, }) local availWidth = element:getAvailableContentWidth() local availHeight = element:getAvailableContentHeight() luaunit.assertEquals(availWidth, 100) luaunit.assertEquals(availHeight, 100) end -- Test: Element with maxLines but no multiline function TestElementUnhappyPaths:test_max_lines_without_multiline() local element = FlexLove.new({ id = "text", width = 200, height = 100, editable = true, multiline = false, maxLines = 5, -- Should be ignored for single-line }) luaunit.assertNotNil(element) end -- Test: Element with maxLength 0 function TestElementUnhappyPaths:test_max_length_zero() local element = FlexLove.new({ id = "text", width = 200, height = 40, editable = true, maxLength = 0, }) luaunit.assertNotNil(element) end -- Test: Element with negative maxLength function TestElementUnhappyPaths:test_max_length_negative() local element = FlexLove.new({ id = "text", width = 200, height = 40, editable = true, maxLength = -10, }) luaunit.assertNotNil(element) end -- Test suite for convenience API features TestConvenienceAPI = {} function TestConvenienceAPI:setUp() FlexLove.beginFrame(1920, 1080) end function TestConvenienceAPI:tearDown() FlexLove.endFrame() end -- Test: flexDirection "row" converts to "horizontal" function TestConvenienceAPI:test_flexDirection_row_converts() local element = FlexLove.new({ id = "test_row", width = 200, height = 100, positioning = "flex", flexDirection = "row", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.flexDirection, "horizontal") end -- Test: flexDirection "column" converts to "vertical" function TestConvenienceAPI:test_flexDirection_column_converts() local element = FlexLove.new({ id = "test_column", width = 200, height = 100, positioning = "flex", flexDirection = "column", }) luaunit.assertNotNil(element) luaunit.assertEquals(element.flexDirection, "vertical") end -- Test: Single number padding expands to all sides function TestConvenienceAPI:test_padding_single_number() local element = FlexLove.new({ id = "test_padding_num", width = 200, height = 100, padding = 10, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.padding.top, 10) luaunit.assertEquals(element.padding.right, 10) luaunit.assertEquals(element.padding.bottom, 10) luaunit.assertEquals(element.padding.left, 10) end -- Test: Single string padding expands to all sides function TestConvenienceAPI:test_padding_single_string() local element = FlexLove.new({ id = "test_padding_str", width = 200, height = 100, padding = "5%", }) luaunit.assertNotNil(element) -- All sides should be 5% of the element's dimensions -- For width: 5% of 200 = 10, for height: 5% of 100 = 5 luaunit.assertEquals(element.padding.left, 10) luaunit.assertEquals(element.padding.right, 10) luaunit.assertEquals(element.padding.top, 5) luaunit.assertEquals(element.padding.bottom, 5) end -- Test: Single number margin expands to all sides function TestConvenienceAPI:test_margin_single_number() local parent = FlexLove.new({ id = "parent", width = 400, height = 300, }) local element = FlexLove.new({ id = "test_margin_num", parent = parent, width = 100, height = 100, margin = 15, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.margin.top, 15) luaunit.assertEquals(element.margin.right, 15) luaunit.assertEquals(element.margin.bottom, 15) luaunit.assertEquals(element.margin.left, 15) end -- Test: Single string margin expands to all sides function TestConvenienceAPI:test_margin_single_string() local parent = FlexLove.new({ id = "parent", width = 400, height = 300, }) local element = FlexLove.new({ id = "test_margin_str", parent = parent, width = 100, height = 100, margin = "10%", }) luaunit.assertNotNil(element) -- Margin percentages are relative to parent dimensions -- 10% of parent width 400 = 40, 10% of parent height 300 = 30 luaunit.assertEquals(element.margin.left, 40) luaunit.assertEquals(element.margin.right, 40) luaunit.assertEquals(element.margin.top, 30) luaunit.assertEquals(element.margin.bottom, 30) end -- Test: Table padding still works (backward compatibility) function TestConvenienceAPI:test_padding_table_still_works() local element = FlexLove.new({ id = "test_padding_table", width = 200, height = 100, padding = { top = 5, right = 10, bottom = 15, left = 20 }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.padding.top, 5) luaunit.assertEquals(element.padding.right, 10) luaunit.assertEquals(element.padding.bottom, 15) luaunit.assertEquals(element.padding.left, 20) end -- Test: Table margin still works (backward compatibility) function TestConvenienceAPI:test_margin_table_still_works() local parent = FlexLove.new({ id = "parent", width = 400, height = 300, }) local element = FlexLove.new({ id = "test_margin_table", parent = parent, width = 100, height = 100, margin = { top = 5, right = 10, bottom = 15, left = 20 }, }) luaunit.assertNotNil(element) luaunit.assertEquals(element.margin.top, 5) luaunit.assertEquals(element.margin.right, 10) luaunit.assertEquals(element.margin.bottom, 15) luaunit.assertEquals(element.margin.left, 20) end if not _G.RUNNING_ALL_TESTS then os.exit(luaunit.LuaUnit.run()) end