diff --git a/examples/hover_demo.lua b/examples/hover_demo.lua deleted file mode 100644 index 72602c7..0000000 --- a/examples/hover_demo.lua +++ /dev/null @@ -1,186 +0,0 @@ --- Example demonstrating hover and unhover events in FlexLöve --- This shows how to use the new hover/unhover events for interactive UI elements - -local FlexLove = require("FlexLove") - -function love.load() - FlexLove.init({ - baseScale = { width = 1920, height = 1080 }, - immediateMode = true, - autoFrameManagement = false, - }) -end - --- State to track hover status -local hoverStatus = "Not hovering" -local hoverCount = 0 -local unhoverCount = 0 -local lastEventTime = 0 - -function love.update(dt) - FlexLove.beginFrame() - - -- Create a container - FlexLove.new({ - width = "100vw", - height = "100vh", - backgroundColor = FlexLove.Color.fromHex("#1a1a2e"), - positioning = "flex", - flexDirection = "vertical", - justifyContent = "center", - alignItems = "center", - gap = 30, - }) - - -- Title - FlexLove.new({ - text = "Hover Event Demo", - textSize = "4xl", - textColor = FlexLove.Color.fromHex("#ffffff"), - }) - - -- Instructions - FlexLove.new({ - text = "Move your mouse over the boxes below to see hover events", - textSize = "lg", - textColor = FlexLove.Color.fromHex("#a0a0a0"), - }) - - -- Status display - FlexLove.new({ - text = hoverStatus, - textSize = "xl", - textColor = FlexLove.Color.fromHex("#4ecca3"), - padding = 20, - }) - - -- Event counters - FlexLove.new({ - text = string.format("Hover events: %d | Unhover events: %d", hoverCount, unhoverCount), - textSize = "md", - textColor = FlexLove.Color.fromHex("#ffffff"), - }) - - -- Container for hover boxes - local boxContainer = FlexLove.new({ - positioning = "flex", - flexDirection = "horizontal", - gap = 30, - }) - - -- Hover Box 1 - FlexLove.new({ - parent = boxContainer, - width = 200, - height = 200, - backgroundColor = FlexLove.Color.fromHex("#e94560"), - cornerRadius = 15, - positioning = "flex", - justifyContent = "center", - alignItems = "center", - text = "Hover Me!", - textSize = "xl", - textColor = FlexLove.Color.fromHex("#ffffff"), - onEvent = function(element, event) - if event.type == "hover" then - hoverStatus = "Hovering over RED box!" - hoverCount = hoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "unhover" then - hoverStatus = "Left RED box" - unhoverCount = unhoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "click" then - print("Clicked RED box!") - end - end, - }) - - -- Hover Box 2 - FlexLove.new({ - parent = boxContainer, - width = 200, - height = 200, - backgroundColor = FlexLove.Color.fromHex("#4ecca3"), - cornerRadius = 15, - positioning = "flex", - justifyContent = "center", - alignItems = "center", - text = "Hover Me!", - textSize = "xl", - textColor = FlexLove.Color.fromHex("#1a1a2e"), - onEvent = function(element, event) - if event.type == "hover" then - hoverStatus = "Hovering over GREEN box!" - hoverCount = hoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "unhover" then - hoverStatus = "Left GREEN box" - unhoverCount = unhoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "click" then - print("Clicked GREEN box!") - end - end, - }) - - -- Hover Box 3 - FlexLove.new({ - parent = boxContainer, - width = 200, - height = 200, - backgroundColor = FlexLove.Color.fromHex("#0f3460"), - cornerRadius = 15, - positioning = "flex", - justifyContent = "center", - alignItems = "center", - text = "Hover Me!", - textSize = "xl", - textColor = FlexLove.Color.fromHex("#ffffff"), - onEvent = function(element, event) - if event.type == "hover" then - hoverStatus = "Hovering over BLUE box!" - hoverCount = hoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "unhover" then - hoverStatus = "Left BLUE box" - unhoverCount = unhoverCount + 1 - lastEventTime = love.timer.getTime() - elseif event.type == "click" then - print("Clicked BLUE box!") - end - end, - }) - - -- Reset button - FlexLove.new({ - width = 200, - height = 50, - backgroundColor = FlexLove.Color.fromHex("#e94560"), - cornerRadius = 25, - positioning = "flex", - justifyContent = "center", - alignItems = "center", - text = "Reset Counters", - textSize = "md", - textColor = FlexLove.Color.fromHex("#ffffff"), - margin = { top = 30 }, - onEvent = function(element, event) - if event.type == "click" then - hoverCount = 0 - unhoverCount = 0 - hoverStatus = "Counters reset!" - end - end, - }) - - FlexLove.endFrame() -end - -function love.draw() - FlexLove.draw() -end - -function love.resize(w, h) - FlexLove.resize(w, h) -end diff --git a/examples/mixed_mode_demo.lua b/examples/mixed_mode_demo.lua deleted file mode 100644 index e21b1b0..0000000 --- a/examples/mixed_mode_demo.lua +++ /dev/null @@ -1,219 +0,0 @@ --- Demo: Retained children with immediate parents --- Shows how retained-mode children persist when immediate-mode parents recreate each frame - -local FlexLove = require("FlexLove") - --- Track frame count for demo -local frameCount = 0 - --- Retained button state (will persist across frames) -local buttonClicks = 0 -local topLevelButtonClicks = 0 - -function love.load() - love.window.setTitle("Mixed-Mode Demo: Retained Children + Immediate Parents") - love.window.setMode(800, 600) - - FlexLove.init({ - immediateMode = true, - performanceMonitoring = true, - }) -end - -function love.update(dt) - FlexLove.update(dt) -end - -function love.draw() - FlexLove.beginFrame() - - -- Frame counter (immediate mode - recreates each frame) - local header = FlexLove.new({ - width = 800, - height = 60, - backgroundColor = { 0.1, 0.1, 0.15, 1 }, - padding = 20, - flexDirection = "horizontal", - justifyContent = "space-between", - alignItems = "center", - }) - - FlexLove.new({ - parent = header, - text = "Frame: " .. frameCount, - textColor = { 1, 1, 1, 1 }, - textSize = 20, - }) - - FlexLove.new({ - parent = header, - text = "Mixed-Mode Element Tree Demo", - textColor = { 0.8, 0.9, 1, 1 }, - textSize = 24, - }) - - -- Main content area (immediate parent) - local container = FlexLove.new({ - id = "main_container", - y = 60, - width = 800, - height = 540, - padding = 30, - gap = 20, - backgroundColor = { 0.05, 0.05, 0.08, 1 }, - flexDirection = "vertical", - }) - - -- Section 1: Retained button in immediate parent - FlexLove.new({ - parent = container, - text = "1. Retained Button (persists across frames)", - textColor = { 0.9, 0.9, 0.9, 1 }, - textSize = 18, - }) - - local retainedButton = FlexLove.new({ - id = "retained_button", - mode = "retained", -- This button will persist! - parent = container, - width = 300, - height = 50, - backgroundColor = { 0.2, 0.6, 0.9, 1 }, - cornerRadius = 8, - text = "Clicks: " .. buttonClicks, - textColor = { 1, 1, 1, 1 }, - textSize = 16, - textAlign = "center", - onEvent = function(element, event) - if event.type == "click" then - buttonClicks = buttonClicks + 1 - -- Update button text - element.text = "Clicks: " .. buttonClicks - end - end, - }) - - FlexLove.new({ - parent = container, - text = "Note: Button state persists even though parent recreates every frame", - textColor = { 0.6, 0.6, 0.6, 1 }, - textSize = 12, - }) - - -- Section 2: Top-level retained element - FlexLove.new({ - parent = container, - text = "2. Top-Level Retained Element (also persists)", - textColor = { 0.9, 0.9, 0.9, 1 }, - textSize = 18, - margin = { top = 20 }, - }) - - FlexLove.new({ - parent = container, - text = "Look at the bottom-left corner for a persistent panel", - textColor = { 0.6, 0.6, 0.6, 1 }, - textSize = 12, - }) - - -- Section 3: Comparison with immediate button - FlexLove.new({ - parent = container, - text = "3. Immediate Button (recreates every frame)", - textColor = { 0.9, 0.9, 0.9, 1 }, - textSize = 18, - margin = { top = 20 }, - }) - - FlexLove.new({ - parent = container, - width = 300, - height = 50, - backgroundColor = { 0.9, 0.3, 0.3, 1 }, - cornerRadius = 8, - text = "Can't track clicks (recreated)", - textColor = { 1, 1, 1, 1 }, - textSize = 16, - textAlign = "center", - onEvent = function(element, event) - if event.type == "click" then - print("Immediate button clicked (but counter can't persist)") - end - end, - }) - - FlexLove.new({ - parent = container, - text = "Note: This button is recreated every frame, so it can't maintain state", - textColor = { 0.6, 0.6, 0.6, 1 }, - textSize = 12, - }) - - -- Top-level retained element (persists in immediate mode!) - local topLevelPanel = FlexLove.new({ - id = "top_level_panel", - mode = "retained", - x = 10, - y = 500, - width = 250, - height = 90, - backgroundColor = { 0.15, 0.5, 0.3, 1 }, - cornerRadius = 10, - padding = 15, - gap = 10, - flexDirection = "vertical", - }) - - FlexLove.new({ - id = "panel_title", - mode = "retained", - parent = topLevelPanel, - text = "Persistent Panel", - textColor = { 1, 1, 1, 1 }, - textSize = 16, - }) - - FlexLove.new({ - id = "panel_button", - mode = "retained", - parent = topLevelPanel, - width = 220, - height = 35, - backgroundColor = { 1, 1, 1, 0.9 }, - cornerRadius = 5, - text = "Panel Clicks: " .. topLevelButtonClicks, - textColor = { 0.15, 0.5, 0.3, 1 }, - textSize = 14, - textAlign = "center", - onEvent = function(element, event) - if event.type == "click" then - topLevelButtonClicks = topLevelButtonClicks + 1 - element.text = "Panel Clicks: " .. topLevelButtonClicks - end - end, - }) - - FlexLove.endFrame() - - -- Increment frame counter AFTER drawing - frameCount = frameCount + 1 - - -- Draw all UI elements - FlexLove.draw() -end - -function love.mousepressed(x, y, button) - FlexLove.mousepressed(x, y, button) -end - -function love.mousereleased(x, y, button) - FlexLove.mousereleased(x, y, button) -end - -function love.mousemoved(x, y, dx, dy) - FlexLove.mousemoved(x, y, dx, dy) -end - -function love.wheelmoved(x, y) - FlexLove.wheelmoved(x, y) -end diff --git a/examples/mode_override_demo.lua b/examples/mode_override_demo.lua deleted file mode 100644 index 624b5b4..0000000 --- a/examples/mode_override_demo.lua +++ /dev/null @@ -1,275 +0,0 @@ --- Mode Override Demo --- Demonstrates per-element mode override in FlexLöve --- Shows how to mix immediate and retained mode elements in the same application - -package.path = package.path .. ";../?.lua;../modules/?.lua" -local FlexLove = require("FlexLove") -local Color = require("modules.Color") - --- Global state -local frameCount = 0 -local clickCount = 0 -local retainedPanelCreated = false -local retainedPanel = nil - -function love.load() - -- Initialize FlexLove in immediate mode globally - FlexLove.init({ - immediateMode = true, - theme = "space", - }) - - love.window.setTitle("Mode Override Demo - FlexLöve") - love.window.setMode(1200, 800) -end - -function love.update(dt) - FlexLove.update(dt) - frameCount = frameCount + 1 -end - -function love.draw() - love.graphics.clear(0.1, 0.1, 0.15, 1) - - FlexLove.beginFrame() - - -- Title - Immediate mode (default, recreated every frame) - FlexLove.new({ - text = "Mode Override Demo", - textSize = 32, - textColor = Color.new(1, 1, 1, 1), - width = "100vw", - height = 60, - textAlign = "center", - backgroundColor = Color.new(0.2, 0.2, 0.3, 1), - padding = { top = 15, bottom = 15 }, - }) - - -- Container for demo panels - local container = FlexLove.new({ - positioning = "flex", - flexDirection = "horizontal", - justifyContent = "center", - alignItems = "flex-start", - gap = 20, - width = "100vw", - height = "calc(100vh - 60px)", - padding = { top = 20, left = 20, right = 20, bottom = 20 }, - }) - - -- LEFT PANEL: Immediate Mode (Dynamic, recreated every frame) - local leftPanel = FlexLove.new({ - mode = "immediate", -- Explicit immediate mode (would be default anyway) - parent = container, - width = "45vw", - height = "calc(100vh - 100px)", - backgroundColor = Color.new(0.15, 0.15, 0.2, 0.95), - cornerRadius = 8, - padding = { top = 20, left = 20, right = 20, bottom = 20 }, - positioning = "flex", - flexDirection = "vertical", - gap = 15, - }) - - -- Panel title - FlexLove.new({ - parent = leftPanel, - text = "Immediate Mode Panel", - textSize = 24, - textColor = Color.new(0.4, 0.8, 1, 1), - width = "100%", - height = 40, - }) - - -- Description - FlexLove.new({ - parent = leftPanel, - text = "Elements in this panel are recreated every frame.\nState is preserved by StateManager.", - textSize = 14, - textColor = Color.new(0.8, 0.8, 0.8, 1), - width = "100%", - height = 60, - }) - - -- Live frame counter (updates automatically) - FlexLove.new({ - parent = leftPanel, - text = string.format("Frame: %d", frameCount), - textSize = 18, - textColor = Color.new(1, 1, 0.5, 1), - width = "100%", - height = 30, - backgroundColor = Color.new(0.2, 0.2, 0.3, 1), - padding = { top = 5, left = 10, right = 10, bottom = 5 }, - cornerRadius = 4, - }) - - -- Click counter (updates automatically) - FlexLove.new({ - parent = leftPanel, - text = string.format("Clicks: %d", clickCount), - textSize = 18, - textColor = Color.new(0.5, 1, 0.5, 1), - width = "100%", - height = 30, - backgroundColor = Color.new(0.2, 0.2, 0.3, 1), - padding = { top = 5, left = 10, right = 10, bottom = 5 }, - cornerRadius = 4, - }) - - -- Interactive button (state preserved across frames) - FlexLove.new({ - parent = leftPanel, - text = "Click Me! (Immediate Mode)", - textSize = 16, - textColor = Color.new(1, 1, 1, 1), - width = "100%", - height = 50, - themeComponent = "button", - onEvent = function(element, event) - if event.type == "release" then - clickCount = clickCount + 1 - end - end, - }) - - -- Info box - FlexLove.new({ - parent = leftPanel, - text = "Notice how the frame counter updates\nautomatically without any manual updates.\n\nThis is the power of immediate mode:\nUI reflects application state automatically.", - textSize = 13, - textColor = Color.new(0.7, 0.7, 0.7, 1), - width = "100%", - height = "auto", - backgroundColor = Color.new(0.1, 0.1, 0.15, 1), - padding = { top = 10, left = 10, right = 10, bottom = 10 }, - cornerRadius = 4, - }) - - -- RIGHT PANEL: Retained Mode (Static, created once) - -- Only create on first frame - if not retainedPanelCreated then - retainedPanel = FlexLove.new({ - mode = "retained", -- Explicit retained mode override - parent = container, - width = "45vw", - height = "calc(100vh - 100px)", - backgroundColor = Color.new(0.2, 0.15, 0.15, 0.95), - cornerRadius = 8, - padding = { top = 20, left = 20, right = 20, bottom = 20 }, - positioning = "flex", - flexDirection = "vertical", - gap = 15, - }) - - -- Panel title (retained) - FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = "Retained Mode Panel", - textSize = 24, - textColor = Color.new(1, 0.6, 0.4, 1), - width = "100%", - height = 40, - }) - - -- Description (retained) - FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = "Elements in this panel are created once\nand persist across frames.", - textSize = 14, - textColor = Color.new(0.8, 0.8, 0.8, 1), - width = "100%", - height = 60, - }) - - -- Static frame counter (won't update) - local staticCounter = FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = string.format("Created at frame: %d", frameCount), - textSize = 18, - textColor = Color.new(1, 1, 0.5, 1), - width = "100%", - height = 30, - backgroundColor = Color.new(0.3, 0.2, 0.2, 1), - padding = { top = 5, left = 10, right = 10, bottom = 5 }, - cornerRadius = 4, - }) - - -- Click counter placeholder (must be manually updated) - local retainedClickCounter = FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = string.format("Clicks: %d (manual update needed)", clickCount), - textSize = 18, - textColor = Color.new(0.5, 1, 0.5, 1), - width = "100%", - height = 30, - backgroundColor = Color.new(0.3, 0.2, 0.2, 1), - padding = { top = 5, left = 10, right = 10, bottom = 5 }, - cornerRadius = 4, - }) - - -- Interactive button with manual update - FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = "Click Me! (Retained Mode)", - textSize = 16, - textColor = Color.new(1, 1, 1, 1), - width = "100%", - height = 50, - themeComponent = "button", - onEvent = function(element, event) - if event.type == "release" then - clickCount = clickCount + 1 - -- In retained mode, we must manually update the UI - retainedClickCounter.text = string.format("Clicks: %d (manual update needed)", clickCount) - end - end, - }) - - -- Info box (retained) - FlexLove.new({ - mode = "retained", - parent = retainedPanel, - text = "Notice how this panel's elements\ndon't update automatically.\n\nIn retained mode, you must manually\nupdate element properties when state changes.\n\nThis gives better performance for\nstatic UI elements.", - textSize = 13, - textColor = Color.new(0.7, 0.7, 0.7, 1), - width = "100%", - height = "auto", - backgroundColor = Color.new(0.15, 0.1, 0.1, 1), - padding = { top = 10, left = 10, right = 10, bottom = 10 }, - cornerRadius = 4, - }) - - retainedPanelCreated = true - end - - FlexLove.endFrame() - - -- Bottom instructions - love.graphics.setColor(0.5, 0.5, 0.5, 1) - love.graphics.print("Global mode: Immediate | Left panel: Immediate (explicit) | Right panel: Retained (override)", 10, love.graphics.getHeight() - 30) - love.graphics.print("Press ESC to quit", 10, love.graphics.getHeight() - 15) -end - -function love.keypressed(key) - if key == "escape" then - love.event.quit() - end -end - -function love.mousepressed(x, y, button) - FlexLove.mousepressed(x, y, button) -end - -function love.mousereleased(x, y, button) - FlexLove.mousereleased(x, y, button) -end - -function love.mousemoved(x, y, dx, dy) - FlexLove.mousemoved(x, y, dx, dy) -end diff --git a/testing/__tests__/element_mode_override_test.lua b/testing/__tests__/element_mode_override_test.lua index e909264..b0f1f40 100644 --- a/testing/__tests__/element_mode_override_test.lua +++ b/testing/__tests__/element_mode_override_test.lua @@ -16,7 +16,9 @@ local originalSearchers = package.searchers or package.loaders table.insert(originalSearchers, 2, function(modname) if modname:match("^FlexLove%.modules%.") then local moduleName = modname:gsub("^FlexLove%.modules%.", "") - return function() return require("modules." .. moduleName) end + return function() + return require("modules." .. moduleName) + end end end) @@ -46,7 +48,7 @@ function TestElementModeOverride:test_modeResolution_explicitImmediate() mode = "immediate", text = "Test", }) - + luaunit.assertEquals(element._elementMode, "immediate") end @@ -56,7 +58,7 @@ function TestElementModeOverride:test_modeResolution_explicitRetained() mode = "retained", text = "Test", }) - + luaunit.assertEquals(element._elementMode, "retained") end @@ -64,22 +66,22 @@ end function TestElementModeOverride:test_modeResolution_nilUsesGlobalImmediate() FlexLove.setMode("immediate") FlexLove.beginFrame() - + local element = FlexLove.new({ text = "Test", }) - + luaunit.assertEquals(element._elementMode, "immediate") end -- Test 04: Mode resolution - nil uses global (retained) function TestElementModeOverride:test_modeResolution_nilUsesGlobalRetained() FlexLove.setMode("retained") - + local element = FlexLove.new({ text = "Test", }) - + luaunit.assertEquals(element._elementMode, "retained") end @@ -92,7 +94,7 @@ function TestElementModeOverride:test_idGeneration_onlyForImmediate() }) luaunit.assertNotNil(immediateEl.id) luaunit.assertNotEquals(immediateEl.id, "") - + -- Retained element without ID should have empty ID local retainedEl = FlexLove.new({ mode = "retained", @@ -104,13 +106,13 @@ end -- Test 06: Immediate override in retained context function TestElementModeOverride:test_immediateOverrideInRetainedContext() FlexLove.setMode("retained") - + local element = FlexLove.new({ mode = "immediate", id = "test-immediate", text = "Immediate in retained context", }) - + luaunit.assertEquals(element._elementMode, "immediate") luaunit.assertEquals(element.id, "test-immediate") end @@ -119,12 +121,12 @@ end function TestElementModeOverride:test_retainedOverrideInImmediateContext() FlexLove.setMode("immediate") FlexLove.beginFrame() - + local element = FlexLove.new({ mode = "retained", text = "Retained in immediate context", }) - + luaunit.assertEquals(element._elementMode, "retained") luaunit.assertEquals(element.id, "") -- Should not auto-generate ID end @@ -133,19 +135,19 @@ end function TestElementModeOverride:test_mixedMode_immediateParent_retainedChild() FlexLove.setMode("immediate") FlexLove.beginFrame() - + local parent = FlexLove.new({ mode = "immediate", id = "parent", text = "Parent", }) - + local child = FlexLove.new({ mode = "retained", parent = parent, text = "Child", }) - + luaunit.assertEquals(parent._elementMode, "immediate") luaunit.assertEquals(child._elementMode, "retained") -- Child should not inherit parent mode @@ -155,19 +157,19 @@ end -- Test 09: Mixed-mode parent-child (retained parent, immediate child) function TestElementModeOverride:test_mixedMode_retainedParent_immediateChild() FlexLove.setMode("retained") - + local parent = FlexLove.new({ mode = "retained", text = "Parent", }) - + local child = FlexLove.new({ mode = "immediate", id = "child", parent = parent, text = "Child", }) - + luaunit.assertEquals(parent._elementMode, "retained") luaunit.assertEquals(child._elementMode, "immediate") luaunit.assertEquals(child.id, "child") @@ -177,24 +179,24 @@ end function TestElementModeOverride:test_frameRegistration_onlyImmediate() FlexLove.setMode("immediate") FlexLove.beginFrame() - + local immediate1 = FlexLove.new({ mode = "immediate", id = "imm1", text = "Immediate 1", }) - + local retained1 = FlexLove.new({ mode = "retained", text = "Retained 1", }) - + local immediate2 = FlexLove.new({ mode = "immediate", id = "imm2", text = "Immediate 2", }) - + -- Count immediate elements in _currentFrameElements local immediateCount = 0 for _, element in ipairs(FlexLove._currentFrameElements) do @@ -202,14 +204,14 @@ function TestElementModeOverride:test_frameRegistration_onlyImmediate() immediateCount = immediateCount + 1 end end - + luaunit.assertEquals(immediateCount, 2) end -- Test 11: Layout calculation for retained parent with immediate children function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren() FlexLove.setMode("retained") - + -- Create retained parent with flex layout local parent = FlexLove.new({ mode = "retained", @@ -219,11 +221,11 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren( flexDirection = "horizontal", gap = 10, }) - + -- Switch to immediate mode and add children FlexLove.setMode("immediate") FlexLove.beginFrame() - + local child1 = FlexLove.new({ mode = "immediate", id = "child1", @@ -231,7 +233,7 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren( width = 100, height = 50, }) - + local child2 = FlexLove.new({ mode = "immediate", id = "child2", @@ -239,9 +241,9 @@ function TestElementModeOverride:test_layoutRetainedParentWithImmediateChildren( width = 100, height = 50, }) - + FlexLove.endFrame() - + -- Verify children are positioned correctly by flex layout luaunit.assertEquals(child1.x, 0) luaunit.assertEquals(child1.y, 0) @@ -253,7 +255,7 @@ end function TestElementModeOverride:test_deeplyNestedMixedModes() FlexLove.setMode("immediate") FlexLove.beginFrame() - + -- Level 1: Retained root local root = FlexLove.new({ mode = "retained", @@ -263,7 +265,7 @@ function TestElementModeOverride:test_deeplyNestedMixedModes() flexDirection = "vertical", gap = 5, }) - + -- Level 2: Immediate child of retained parent local middle = FlexLove.new({ mode = "immediate", @@ -275,7 +277,7 @@ function TestElementModeOverride:test_deeplyNestedMixedModes() flexDirection = "horizontal", gap = 10, }) - + -- Level 3: Retained grandchildren local leaf1 = FlexLove.new({ mode = "retained", @@ -283,16 +285,16 @@ function TestElementModeOverride:test_deeplyNestedMixedModes() width = 100, height = 50, }) - + local leaf2 = FlexLove.new({ mode = "retained", parent = middle, width = 100, height = 50, }) - + FlexLove.endFrame() - + -- Verify all levels are positioned correctly luaunit.assertEquals(root.x, 0) luaunit.assertEquals(root.y, 0) @@ -307,20 +309,20 @@ end -- Test 13: Immediate children of retained parents receive updates function TestElementModeOverride:test_immediateChildrenOfRetainedParentsGetUpdated() FlexLove.setMode("retained") - + local updateCount = 0 - + -- Create retained parent local parent = FlexLove.new({ mode = "retained", width = 800, height = 600, }) - + -- Switch to immediate mode for child FlexLove.setMode("immediate") FlexLove.beginFrame() - + -- Create immediate child that tracks updates local child = FlexLove.new({ mode = "immediate", @@ -329,15 +331,17 @@ function TestElementModeOverride:test_immediateChildrenOfRetainedParentsGetUpdat width = 100, height = 50, }) - + -- Manually call update on the child to simulate what endFrame should do -- In the real implementation, endFrame calls update on retained parents, -- which cascades to immediate children FlexLove.endFrame() - + -- The child should be in the state manager local state = StateManager.getState("updateTest") luaunit.assertNotNil(state) end -os.exit(luaunit.LuaUnit.run()) +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +end diff --git a/testing/__tests__/module_loader_test.lua b/testing/__tests__/module_loader_test.lua index 1b0fd39..aaa17df 100644 --- a/testing/__tests__/module_loader_test.lua +++ b/testing/__tests__/module_loader_test.lua @@ -54,12 +54,9 @@ end function TestModuleLoader:test_safeRequire_throws_error_for_missing_required_module() -- Test loading a non-existent required module should throw error - lu.assertErrorMsgContains( - "Required module", - function() - ModuleLoader.safeRequire(modulePath .. "modules.NonExistentModule", false) - end - ) + lu.assertErrorMsgContains("Required module", function() + ModuleLoader.safeRequire(modulePath .. "modules.NonExistentModule", false) + end) end function TestModuleLoader:test_stub_has_safe_init_method() @@ -116,7 +113,7 @@ function TestModuleLoader:test_stub_returns_function_for_unknown_properties() -- Unknown properties should return no-op functions for safe method calls lu.assertIsFunction(stub.unknownProperty) lu.assertIsFunction(stub.anotherUnknownProperty) - + -- Calling unknown methods should not error stub:unknownMethod() stub:anotherUnknownMethod("arg1", "arg2") @@ -197,5 +194,3 @@ end if not _G.RUNNING_ALL_TESTS then os.exit(lu.LuaUnit.run()) end - -return TestModuleLoader diff --git a/testing/__tests__/retained_in_immediate_test.lua b/testing/__tests__/retained_in_immediate_test.lua index b53d35b..4b32e50 100644 --- a/testing/__tests__/retained_in_immediate_test.lua +++ b/testing/__tests__/retained_in_immediate_test.lua @@ -1,6 +1,6 @@ --[[ Test: Retained Elements in Immediate Mode (No Duplication) - + This test verifies that retained-mode elements don't get recreated when the overall application is in immediate mode. ]] @@ -39,34 +39,34 @@ function TestRetainedInImmediateMode:test_topLevelRetainedElementPersists() }) return backdrop end - + FlexLove.beginFrame() - + -- Frame 1: Create a retained element (no explicit ID) local backdrop = createUI() - + local backdropId = backdrop.id luaunit.assertNotNil(backdropId, "Backdrop should have auto-generated ID") luaunit.assertEquals(backdrop._elementMode, "retained") - + FlexLove.endFrame() - + -- Frame 2: Call createUI() again (same function, same line numbers) FlexLove.beginFrame() - + local backdrop2 = createUI() - + -- Should return the SAME element, not create a new one luaunit.assertEquals(backdrop2.id, backdropId, "Should return existing element with same ID") luaunit.assertEquals(backdrop2, backdrop, "Should return exact same element instance") - + FlexLove.endFrame() end -- Test that retained elements with explicit IDs can be recreated function TestRetainedInImmediateMode:test_explicitIdAllowsNewElements() FlexLove.beginFrame() - + -- Create element with explicit ID local element1 = FlexLove.new({ id = "my_custom_id", @@ -75,26 +75,26 @@ function TestRetainedInImmediateMode:test_explicitIdAllowsNewElements() height = 100, backgroundColor = Color.new(1, 0, 0, 1), }) - + FlexLove.endFrame() - + FlexLove.beginFrame() - + -- Create another element with SAME explicit ID but different properties -- This should create a NEW element (user controls uniqueness) local element2 = FlexLove.new({ id = "my_custom_id", mode = "retained", - width = 200, -- Different properties + width = 200, -- Different properties height = 200, backgroundColor = Color.new(0, 1, 0, 1), }) - + -- With explicit IDs, we allow duplicates (user responsibility) luaunit.assertEquals(element2.id, "my_custom_id") -- Properties should match NEW element, not old luaunit.assertEquals(element2.width, 200) - + FlexLove.endFrame() end @@ -106,38 +106,38 @@ function TestRetainedInImmediateMode:test_multipleRetainedElementsPersist() width = "100%", height = "100%", }) - + local window = FlexLove.new({ mode = "retained", width = "90%", height = "90%", }) - + return backdrop, window end - + FlexLove.beginFrame() - + local backdrop, window = createUI() - + local backdropId = backdrop.id local windowId = window.id - + luaunit.assertNotEquals(backdropId, windowId, "Different elements should have different IDs") - + FlexLove.endFrame() - + -- Frame 2 FlexLove.beginFrame() - + local backdrop2, window2 = createUI() - + -- Both should return existing elements luaunit.assertEquals(backdrop2.id, backdropId) luaunit.assertEquals(window2.id, windowId) luaunit.assertEquals(backdrop2, backdrop) luaunit.assertEquals(window2, window) - + FlexLove.endFrame() end @@ -149,45 +149,46 @@ function TestRetainedInImmediateMode:test_retainedChildOfRetainedParentPersists( width = 400, height = 400, }) - + local child = FlexLove.new({ mode = "retained", parent = parent, width = 100, height = 100, }) - + return parent, child end - + FlexLove.beginFrame() - + local parent, child = createUI() - + local parentId = parent.id local childId = child.id - + FlexLove.endFrame() - + -- Frame 2 FlexLove.beginFrame() - + local parent2, child2 = createUI() - + -- Parent should be the same luaunit.assertEquals(parent2.id, parentId) luaunit.assertEquals(parent2, parent) - + -- Child should also be the same instance luaunit.assertEquals(child2.id, childId, "Child ID should match") luaunit.assertEquals(child2, child, "Child should be same instance") - + -- Child should still exist in parent's children luaunit.assertEquals(#parent2.children, 1, "Parent should have exactly 1 child") luaunit.assertEquals(parent2.children[1].id, childId) - + FlexLove.endFrame() end --- Run tests -os.exit(luaunit.LuaUnit.run()) +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +end diff --git a/testing/__tests__/retained_prop_stability_test.lua b/testing/__tests__/retained_prop_stability_test.lua index 2d7365f..7d0820e 100644 --- a/testing/__tests__/retained_prop_stability_test.lua +++ b/testing/__tests__/retained_prop_stability_test.lua @@ -1,6 +1,6 @@ --[[ Test: Retained Elements with Varying Props (ID Stability) - + This test verifies that retained-mode elements return the same instance across frames even when props vary slightly (e.g., different Color instances). ]] @@ -30,33 +30,33 @@ end -- Test that retained elements persist despite creating new Color instances function TestRetainedPropStability:test_retainedElementIgnoresColorInstanceChanges() FlexLove.beginFrame() - + -- Frame 1: Create retained element with Color instance local backdrop1 = FlexLove.new({ mode = "retained", width = "100%", height = "100%", - backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance + backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance }) - + local id1 = backdrop1.id - + FlexLove.endFrame() - + -- Frame 2: Same props but NEW Color instance (common pattern in user code) FlexLove.beginFrame() - + local backdrop2 = FlexLove.new({ mode = "retained", width = "100%", height = "100%", - backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance (different table) + backgroundColor = Color.new(1, 1, 1, 0.1), -- NEW Color instance (different table) }) - + -- Should return SAME element despite different Color instance luaunit.assertEquals(backdrop2.id, id1, "ID should be stable across frames") luaunit.assertEquals(backdrop2, backdrop1, "Should return same element instance") - + FlexLove.endFrame() end @@ -81,23 +81,23 @@ function TestRetainedPropStability:test_retainedElementWithComplexProps() gap = 10, }) end - + FlexLove.beginFrame() - + local window1 = createWindow() local id1 = window1.id - + FlexLove.endFrame() - + -- Frame 2: Same function, same props FlexLove.beginFrame() - + local window2 = createWindow() - + -- Should return same element luaunit.assertEquals(window2.id, id1) luaunit.assertEquals(window2, window1) - + FlexLove.endFrame() end @@ -108,27 +108,27 @@ function TestRetainedPropStability:test_retainedElementWithBackdropBlur() mode = "retained", width = "100%", height = "100%", - backdropBlur = { radius = 10 }, -- Table prop + backdropBlur = { radius = 10 }, -- Table prop backgroundColor = Color.new(1, 1, 1, 0.1), }) end - + FlexLove.beginFrame() - + local backdrop1 = createBackdrop() local id1 = backdrop1.id - + FlexLove.endFrame() - + -- Frame 2 FlexLove.beginFrame() - + local backdrop2 = createBackdrop() - + -- Should return same element luaunit.assertEquals(backdrop2.id, id1) luaunit.assertEquals(backdrop2, backdrop1) - + FlexLove.endFrame() end @@ -143,7 +143,7 @@ function TestRetainedPropStability:test_multipleRetainedElementsWithVaryingProps backdropBlur = { radius = 10 }, backgroundColor = Color.new(1, 1, 1, 0.1), }) - + local window = FlexLove.new({ mode = "retained", z = 100, @@ -154,31 +154,32 @@ function TestRetainedPropStability:test_multipleRetainedElementsWithVaryingProps themeComponent = "framev3", padding = { horizontal = "5%", vertical = "3%" }, }) - + return backdrop, window end - + FlexLove.beginFrame() - + local backdrop1, window1 = createUI() local backdropId = backdrop1.id local windowId = window1.id - + FlexLove.endFrame() - + -- Frame 2: New Color instances, new table instances for props FlexLove.beginFrame() - + local backdrop2, window2 = createUI() - + -- Both should return existing elements luaunit.assertEquals(backdrop2.id, backdropId) luaunit.assertEquals(window2.id, windowId) luaunit.assertEquals(backdrop2, backdrop1) luaunit.assertEquals(window2, window1) - + FlexLove.endFrame() end --- Run tests -os.exit(luaunit.LuaUnit.run()) +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +end diff --git a/testing/__tests__/scroll_manager_test.lua b/testing/__tests__/scroll_manager_test.lua index 3b34653..65f13a7 100644 --- a/testing/__tests__/scroll_manager_test.lua +++ b/testing/__tests__/scroll_manager_test.lua @@ -1059,7 +1059,7 @@ function TestScrollManagerEdgeCases:testScrollbarKnobOffsetDefault() -- When not provided, scrollbarKnobOffset should be nil (use theme default) local sm = createScrollManager({}) luaunit.assertNil(sm.scrollbarKnobOffset) - + -- When explicitly set to 0, it should be normalized local sm2 = createScrollManager({ scrollbarKnobOffset = 0 }) luaunit.assertNotNil(sm2.scrollbarKnobOffset) @@ -1073,7 +1073,7 @@ function TestScrollManagerEdgeCases:testScrollbarKnobOffsetStatePersistence() local sm = createScrollManager({ scrollbarKnobOffset = { x = 5, y = 10 } }) local state = sm:getState() luaunit.assertNotNil(state.scrollbarKnobOffset) - + local sm2 = createScrollManager({}) sm2:setState(state) luaunit.assertEquals(sm2.scrollbarKnobOffset.x, 5) diff --git a/testing/__tests__/theme_test.lua b/testing/__tests__/theme_test.lua index 5f1b862..495e55d 100644 --- a/testing/__tests__/theme_test.lua +++ b/testing/__tests__/theme_test.lua @@ -373,9 +373,6 @@ function TestThemeValidation:test_validate_valid_colors() luaunit.assertEquals(#errors, 0) end - - - function TestThemeValidation:test_validate_colors_non_table() local theme = { name = "Test Theme", @@ -726,7 +723,6 @@ function TestThemeValidation:test_sanitize_nil_theme() luaunit.assertEquals(sanitized.name, "Invalid Theme") end - function TestThemeValidation:test_sanitize_theme_with_non_string_name() local theme = { name = 123, @@ -735,7 +731,6 @@ function TestThemeValidation:test_sanitize_theme_with_non_string_name() luaunit.assertEquals(type(sanitized.name), "string") end - function TestThemeValidation:test_sanitize_removes_non_string_color_names() local theme = { name = "Test", @@ -774,8 +769,6 @@ end -- === Complex Theme Validation === - - -- Run tests if this file is executed directly if not _G.RUNNING_ALL_TESTS then os.exit(luaunit.LuaUnit.run()) diff --git a/testing/runAll.lua b/testing/runAll.lua index 4e70350..7db3585 100644 --- a/testing/runAll.lua +++ b/testing/runAll.lua @@ -38,32 +38,33 @@ local luaunit = require("testing.luaunit") local testFiles = { "testing/__tests__/animation_test.lua", "testing/__tests__/blur_test.lua", + "testing/__tests__/calc_test.lua", + "testing/__tests__/critical_failures_test.lua", "testing/__tests__/element_test.lua", + "testing/__tests__/element_mode_override_test.lua", "testing/__tests__/event_handler_test.lua", + "testing/__tests__/flexlove_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_engine_test.lua", - "testing/__tests__/mixed_mode_events_test.lua", "testing/__tests__/mixed_mode_children_test.lua", - "testing/__tests__/retained_in_immediate_test.lua", + "testing/__tests__/mixed_mode_events_test.lua", "testing/__tests__/module_loader_test.lua", "testing/__tests__/ninepatch_test.lua", "testing/__tests__/performance_test.lua", "testing/__tests__/renderer_test.lua", + "testing/__tests__/retained_in_immediate_test.lua", + "testing/__tests__/retained_prop_stability_test.lua", "testing/__tests__/roundedrect_test.lua", "testing/__tests__/scroll_manager_test.lua", "testing/__tests__/text_editor_test.lua", "testing/__tests__/theme_test.lua", + "testing/__tests__/touch_events_test.lua", "testing/__tests__/units_test.lua", "testing/__tests__/utils_test.lua", - "testing/__tests__/calc_test.lua", - -- Feature/Integration tests - "testing/__tests__/critical_failures_test.lua", - "testing/__tests__/flexlove_test.lua", - "testing/__tests__/touch_events_test.lua", } local success = true