From 48d44a1a110fa5723022c922c127711a2f082fa9 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Fri, 14 Nov 2025 21:54:01 -0500 Subject: [PATCH] more tests, fixed theme validation --- modules/Theme.lua | 267 +++++++ testing/__tests__/color_validation_test.lua | 36 +- testing/__tests__/element_test.lua | 535 ++++++++++++++ .../layout_engine_integration_test.lua | 441 ++++++++++++ testing/__tests__/layout_engine_test.lua | 661 ++++++++++++++++++ testing/__tests__/path_validation_test.lua | 2 +- .../texteditor_sanitization_test.lua | 84 +-- testing/__tests__/theme_core_test.lua | 302 ++++++++ testing/__tests__/theme_validation_test.lua | 186 ++--- testing/__tests__/units_test.lua | 18 +- testing/__tests__/utils_test.lua | 14 +- testing/runAll.lua | 3 + 12 files changed, 2380 insertions(+), 169 deletions(-) create mode 100644 testing/__tests__/element_test.lua create mode 100644 testing/__tests__/layout_engine_integration_test.lua create mode 100644 testing/__tests__/layout_engine_test.lua create mode 100644 testing/__tests__/theme_core_test.lua diff --git a/modules/Theme.lua b/modules/Theme.lua index 9dc9fe6..e9a38db 100644 --- a/modules/Theme.lua +++ b/modules/Theme.lua @@ -663,4 +663,271 @@ end -- Export both Theme and ThemeManager Theme.Manager = ThemeManager +---Validate a theme definition for structural correctness (non-aggressive) +---@param theme table? The theme to validate +---@param options table? Optional validation options {strict: boolean} +---@return boolean valid, table errors List of validation errors +function Theme.validateTheme(theme, options) + local errors = {} + options = options or {} + + -- Basic structure validation + if theme == nil then + table.insert(errors, "Theme is nil") + return false, errors + end + + if type(theme) ~= "table" then + table.insert(errors, "Theme must be a table") + return false, errors + end + + -- Name validation (only required field) + if not theme.name then + table.insert(errors, "Theme must have a 'name' field") + elseif type(theme.name) ~= "string" then + table.insert(errors, "Theme 'name' must be a string") + elseif theme.name == "" then + table.insert(errors, "Theme 'name' cannot be empty") + end + + -- Colors validation (optional, but if present must be valid) + if theme.colors ~= nil then + if type(theme.colors) ~= "table" then + table.insert(errors, "Theme 'colors' must be a table") + else + for colorName, colorValue in pairs(theme.colors) do + if type(colorName) ~= "string" then + table.insert(errors, "Color name must be a string, got " .. type(colorName)) + else + -- Accept Color objects, hex strings, or named colors + local colorType = type(colorValue) + if colorType == "table" then + -- Assume it's a Color object if it has r,g,b fields + if not (colorValue.r and colorValue.g and colorValue.b) then + table.insert(errors, "Color '" .. colorName .. "' is not a valid Color object") + end + elseif colorType == "string" then + -- Validate color string + local isValid, err = Color.validateColor(colorValue) + if not isValid then + table.insert(errors, "Color '" .. colorName .. "': " .. err) + end + else + table.insert(errors, "Color '" .. colorName .. "' must be a Color object or string") + end + end + end + end + end + + -- Fonts validation (optional) + if theme.fonts ~= nil then + if type(theme.fonts) ~= "table" then + table.insert(errors, "Theme 'fonts' must be a table") + else + for fontName, fontPath in pairs(theme.fonts) do + if type(fontName) ~= "string" then + table.insert(errors, "Font name must be a string, got " .. type(fontName)) + elseif type(fontPath) ~= "string" then + table.insert(errors, "Font '" .. fontName .. "' path must be a string") + end + end + end + end + + -- Components validation (optional) + if theme.components ~= nil then + if type(theme.components) ~= "table" then + table.insert(errors, "Theme 'components' must be a table") + else + for componentName, component in pairs(theme.components) do + if type(component) == "table" then + -- Validate atlas if present + if component.atlas ~= nil and type(component.atlas) ~= "string" then + table.insert(errors, "Component '" .. componentName .. "' atlas must be a string") + end + + -- Validate insets if present + if component.insets ~= nil then + if type(component.insets) ~= "table" then + table.insert(errors, "Component '" .. componentName .. "' insets must be a table") + else + -- If insets are provided, all 4 sides must be present + for _, side in ipairs({ "left", "top", "right", "bottom" }) do + if component.insets[side] == nil then + table.insert(errors, "Component '" .. componentName .. "' insets must have '" .. side .. "' field") + elseif type(component.insets[side]) ~= "number" then + table.insert(errors, "Component '" .. componentName .. "' insets." .. side .. " must be a number") + elseif component.insets[side] < 0 then + table.insert(errors, "Component '" .. componentName .. "' insets." .. side .. " must be non-negative") + end + end + end + end + + -- Validate states if present + if component.states ~= nil then + if type(component.states) ~= "table" then + table.insert(errors, "Component '" .. componentName .. "' states must be a table") + else + for stateName, stateComponent in pairs(component.states) do + if type(stateComponent) ~= "table" then + table.insert(errors, "Component '" .. componentName .. "' state '" .. stateName .. "' must be a table") + end + end + end + end + + -- Validate scaleCorners if present + if component.scaleCorners ~= nil then + if type(component.scaleCorners) ~= "number" then + table.insert(errors, "Component '" .. componentName .. "' scaleCorners must be a number") + elseif component.scaleCorners <= 0 then + table.insert(errors, "Component '" .. componentName .. "' scaleCorners must be positive") + end + end + + -- Validate scalingAlgorithm if present + if component.scalingAlgorithm ~= nil then + if type(component.scalingAlgorithm) ~= "string" then + table.insert(errors, "Component '" .. componentName .. "' scalingAlgorithm must be a string") + elseif component.scalingAlgorithm ~= "nearest" and component.scalingAlgorithm ~= "bilinear" then + table.insert(errors, "Component '" .. componentName .. "' scalingAlgorithm must be 'nearest' or 'bilinear'") + end + end + end + end + end + end + + -- contentAutoSizingMultiplier validation (optional) + if theme.contentAutoSizingMultiplier ~= nil then + if type(theme.contentAutoSizingMultiplier) ~= "table" then + table.insert(errors, "Theme 'contentAutoSizingMultiplier' must be a table") + else + if theme.contentAutoSizingMultiplier.width ~= nil then + if type(theme.contentAutoSizingMultiplier.width) ~= "number" then + table.insert(errors, "contentAutoSizingMultiplier.width must be a number") + elseif theme.contentAutoSizingMultiplier.width <= 0 then + table.insert(errors, "contentAutoSizingMultiplier.width must be positive") + end + end + if theme.contentAutoSizingMultiplier.height ~= nil then + if type(theme.contentAutoSizingMultiplier.height) ~= "number" then + table.insert(errors, "contentAutoSizingMultiplier.height must be a number") + elseif theme.contentAutoSizingMultiplier.height <= 0 then + table.insert(errors, "contentAutoSizingMultiplier.height must be positive") + end + end + end + end + + -- Global atlas validation (optional) + if theme.atlas ~= nil then + if type(theme.atlas) ~= "string" then + table.insert(errors, "Theme 'atlas' must be a string") + end + end + + -- Strict mode: warn about unknown fields + if options.strict then + local knownFields = { + name = true, + atlas = true, + components = true, + colors = true, + fonts = true, + contentAutoSizingMultiplier = true, + } + for field in pairs(theme) do + if not knownFields[field] then + table.insert(errors, "Unknown field '" .. field .. "' in theme") + end + end + end + + return #errors == 0, errors +end + +---Sanitize a theme definition by removing invalid values and providing defaults +---@param theme table? The theme to sanitize +---@return table sanitized The sanitized theme +function Theme.sanitizeTheme(theme) + local sanitized = {} + + -- Handle nil theme + if theme == nil then + return { name = "Invalid Theme" } + end + + -- Handle non-table theme + if type(theme) ~= "table" then + return { name = "Invalid Theme" } + end + + -- Sanitize name + if type(theme.name) == "string" and theme.name ~= "" then + sanitized.name = theme.name + else + sanitized.name = "Unnamed Theme" + end + + -- Sanitize colors + if type(theme.colors) == "table" then + sanitized.colors = {} + for colorName, colorValue in pairs(theme.colors) do + if type(colorName) == "string" then + local colorType = type(colorValue) + if colorType == "table" and colorValue.r and colorValue.g and colorValue.b then + -- Valid Color object + sanitized.colors[colorName] = colorValue + elseif colorType == "string" then + -- Try to validate color string + local isValid = Color.validateColor(colorValue) + if isValid then + sanitized.colors[colorName] = colorValue + else + -- Provide fallback color + sanitized.colors[colorName] = Color.new(0, 0, 0, 1) + end + end + end + end + end + + -- Sanitize fonts + if type(theme.fonts) == "table" then + sanitized.fonts = {} + for fontName, fontPath in pairs(theme.fonts) do + if type(fontName) == "string" and type(fontPath) == "string" then + sanitized.fonts[fontName] = fontPath + end + end + end + + -- Sanitize components (preserve as-is, they're complex) + if type(theme.components) == "table" then + sanitized.components = theme.components + end + + -- Sanitize contentAutoSizingMultiplier + if type(theme.contentAutoSizingMultiplier) == "table" then + sanitized.contentAutoSizingMultiplier = {} + if type(theme.contentAutoSizingMultiplier.width) == "number" and theme.contentAutoSizingMultiplier.width > 0 then + sanitized.contentAutoSizingMultiplier.width = theme.contentAutoSizingMultiplier.width + end + if type(theme.contentAutoSizingMultiplier.height) == "number" and theme.contentAutoSizingMultiplier.height > 0 then + sanitized.contentAutoSizingMultiplier.height = theme.contentAutoSizingMultiplier.height + end + end + + -- Sanitize atlas + if type(theme.atlas) == "string" then + sanitized.atlas = theme.atlas + end + + return sanitized +end + return Theme diff --git a/testing/__tests__/color_validation_test.lua b/testing/__tests__/color_validation_test.lua index e4089ed..ad560f7 100644 --- a/testing/__tests__/color_validation_test.lua +++ b/testing/__tests__/color_validation_test.lua @@ -22,7 +22,7 @@ end function TestColorValidation:test_validateColorChannel_valid_0to255() local valid, clamped = Color.validateColorChannel(128, 255) luaunit.assertTrue(valid) - luaunit.assertAlmostEquals(clamped, 128/255, 0.001) + luaunit.assertAlmostEquals(clamped, 128 / 255, 0.001) end function TestColorValidation:test_validateColorChannel_clamp_below_min() @@ -44,7 +44,7 @@ function TestColorValidation:test_validateColorChannel_clamp_above_255() end function TestColorValidation:test_validateColorChannel_nan() - local valid, clamped = Color.validateColorChannel(0/0, 1) + local valid, clamped = Color.validateColorChannel(0 / 0, 1) luaunit.assertFalse(valid) luaunit.assertNil(clamped) end @@ -168,7 +168,7 @@ function TestColorValidation:test_validateRGBColor_invalid_blue() end function TestColorValidation:test_validateRGBColor_invalid_alpha() - local valid, err = Color.validateRGBColor(0.5, 0.5, 0.5, 0/0, 1) + local valid, err = Color.validateRGBColor(0.5, 0.5, 0.5, 0 / 0, 1) luaunit.assertFalse(valid) luaunit.assertNotNil(err) luaunit.assertStrContains(err, "Invalid alpha channel") @@ -231,12 +231,12 @@ function TestColorValidation:test_isValidColorFormat_named() end function TestColorValidation:test_isValidColorFormat_table_array() - local format = Color.isValidColorFormat({0.5, 0.5, 0.5, 1.0}) + local format = Color.isValidColorFormat({ 0.5, 0.5, 0.5, 1.0 }) luaunit.assertEquals(format, "table") end function TestColorValidation:test_isValidColorFormat_table_named() - local format = Color.isValidColorFormat({r=0.5, g=0.5, b=0.5, a=1.0}) + local format = Color.isValidColorFormat({ r = 0.5, g = 0.5, b = 0.5, a = 1.0 }) luaunit.assertEquals(format, "table") end @@ -252,7 +252,7 @@ function TestColorValidation:test_isValidColorFormat_invalid_string() end function TestColorValidation:test_isValidColorFormat_invalid_table() - local format = Color.isValidColorFormat({invalid=true}) + local format = Color.isValidColorFormat({ invalid = true }) luaunit.assertNil(format) end @@ -281,32 +281,32 @@ function TestColorValidation:test_validateColor_named() end function TestColorValidation:test_validateColor_table_array() - local valid, err = Color.validateColor({0.5, 0.5, 0.5, 1.0}) + local valid, err = Color.validateColor({ 0.5, 0.5, 0.5, 1.0 }) luaunit.assertTrue(valid) luaunit.assertNil(err) end function TestColorValidation:test_validateColor_table_named() - local valid, err = Color.validateColor({r=0.5, g=0.5, b=0.5, a=1.0}) + local valid, err = Color.validateColor({ r = 0.5, g = 0.5, b = 0.5, a = 1.0 }) luaunit.assertTrue(valid) luaunit.assertNil(err) end function TestColorValidation:test_validateColor_named_disallowed() - local valid, err = Color.validateColor("red", {allowNamed=false}) + local valid, err = Color.validateColor("red", { allowNamed = false }) luaunit.assertFalse(valid) luaunit.assertNotNil(err) luaunit.assertStrContains(err, "Named colors not allowed") end function TestColorValidation:test_validateColor_require_alpha_8digit() - local valid, err = Color.validateColor("#FF0000AA", {requireAlpha=true}) + local valid, err = Color.validateColor("#FF0000AA", { requireAlpha = true }) luaunit.assertTrue(valid) luaunit.assertNil(err) end function TestColorValidation:test_validateColor_require_alpha_6digit() - local valid, err = Color.validateColor("#FF0000", {requireAlpha=true}) + local valid, err = Color.validateColor("#FF0000", { requireAlpha = true }) luaunit.assertFalse(valid) luaunit.assertNotNil(err) luaunit.assertStrContains(err, "Alpha channel required") @@ -377,7 +377,7 @@ function TestColorValidation:test_sanitizeColor_named_transparent() end function TestColorValidation:test_sanitizeColor_table_array() - local color = Color.sanitizeColor({0.5, 0.6, 0.7, 0.8}) + local color = Color.sanitizeColor({ 0.5, 0.6, 0.7, 0.8 }) luaunit.assertAlmostEquals(color.r, 0.5, 0.01) luaunit.assertAlmostEquals(color.g, 0.6, 0.01) luaunit.assertAlmostEquals(color.b, 0.7, 0.01) @@ -385,7 +385,7 @@ function TestColorValidation:test_sanitizeColor_table_array() end function TestColorValidation:test_sanitizeColor_table_named() - local color = Color.sanitizeColor({r=0.5, g=0.6, b=0.7, a=0.8}) + local color = Color.sanitizeColor({ r = 0.5, g = 0.6, b = 0.7, a = 0.8 }) luaunit.assertAlmostEquals(color.r, 0.5, 0.01) luaunit.assertAlmostEquals(color.g, 0.6, 0.01) luaunit.assertAlmostEquals(color.b, 0.7, 0.01) @@ -393,7 +393,7 @@ function TestColorValidation:test_sanitizeColor_table_named() end function TestColorValidation:test_sanitizeColor_table_array_clamp_high() - local color = Color.sanitizeColor({1.5, 1.5, 1.5, 1.5}) + local color = Color.sanitizeColor({ 1.5, 1.5, 1.5, 1.5 }) luaunit.assertAlmostEquals(color.r, 1.0, 0.01) luaunit.assertAlmostEquals(color.g, 1.0, 0.01) luaunit.assertAlmostEquals(color.b, 1.0, 0.01) @@ -401,7 +401,7 @@ function TestColorValidation:test_sanitizeColor_table_array_clamp_high() end function TestColorValidation:test_sanitizeColor_table_array_clamp_low() - local color = Color.sanitizeColor({-0.5, -0.5, -0.5, -0.5}) + local color = Color.sanitizeColor({ -0.5, -0.5, -0.5, -0.5 }) luaunit.assertAlmostEquals(color.r, 0.0, 0.01) luaunit.assertAlmostEquals(color.g, 0.0, 0.01) luaunit.assertAlmostEquals(color.b, 0.0, 0.01) @@ -409,7 +409,7 @@ function TestColorValidation:test_sanitizeColor_table_array_clamp_low() end function TestColorValidation:test_sanitizeColor_table_no_alpha() - local color = Color.sanitizeColor({0.5, 0.6, 0.7}) + local color = Color.sanitizeColor({ 0.5, 0.6, 0.7 }) luaunit.assertAlmostEquals(color.r, 0.5, 0.01) luaunit.assertAlmostEquals(color.g, 0.6, 0.01) luaunit.assertAlmostEquals(color.b, 0.7, 0.01) @@ -461,7 +461,7 @@ function TestColorValidation:test_parse_named() end function TestColorValidation:test_parse_table() - local color = Color.parse({0.25, 0.50, 0.75, 1.0}) + local color = Color.parse({ 0.25, 0.50, 0.75, 1.0 }) luaunit.assertAlmostEquals(color.r, 0.25, 0.01) luaunit.assertAlmostEquals(color.g, 0.50, 0.01) luaunit.assertAlmostEquals(color.b, 0.75, 0.01) @@ -501,7 +501,7 @@ function TestColorValidation:test_edge_hex_with_spaces() end function TestColorValidation:test_edge_negative_values_clamped() - local color = Color.sanitizeColor({-1, -2, -3, -4}) + local color = Color.sanitizeColor({ -1, -2, -3, -4 }) luaunit.assertAlmostEquals(color.r, 0.0, 0.01) luaunit.assertAlmostEquals(color.g, 0.0, 0.01) luaunit.assertAlmostEquals(color.b, 0.0, 0.01) diff --git a/testing/__tests__/element_test.lua b/testing/__tests__/element_test.lua new file mode 100644 index 0000000..c722f85 --- /dev/null +++ b/testing/__tests__/element_test.lua @@ -0,0 +1,535 @@ +-- 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") + +-- Load FlexLove which properly initializes all dependencies +local FlexLove = require("FlexLove") + +-- 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 + +-- Run tests if this file is executed directly +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +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 + diff --git a/testing/__tests__/layout_engine_integration_test.lua b/testing/__tests__/layout_engine_integration_test.lua new file mode 100644 index 0000000..5b8dee7 --- /dev/null +++ b/testing/__tests__/layout_engine_integration_test.lua @@ -0,0 +1,441 @@ +-- Integration tests for LayoutEngine.lua +-- Tests actual layout calculations with mock element structures + +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 = 1, + _cachedViewport = { width = 1920, height = 1080 }, +} + +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, +} + +-- Helper function to create mock element +local function createMockElement(props) + return { + id = props.id or "mock", + x = props.x or 0, + y = props.y or 0, + width = props.width or 100, + height = props.height or 100, + absoluteX = props.absoluteX or 0, + absoluteY = props.absoluteY or 0, + marginLeft = props.marginLeft or 0, + marginTop = props.marginTop or 0, + marginRight = props.marginRight or 0, + marginBottom = props.marginBottom or 0, + children = props.children or {}, + parent = props.parent, + isHidden = props.isHidden or false, + flexGrow = props.flexGrow or 0, + flexShrink = props.flexShrink or 1, + flexBasis = props.flexBasis or "auto", + alignSelf = props.alignSelf, + minWidth = props.minWidth, + maxWidth = props.maxWidth, + minHeight = props.minHeight, + maxHeight = props.maxHeight, + text = props.text, + _layout = nil, + recalculateUnits = function() end, + layoutChildren = function() end, + } +end + +-- Test suite for layoutChildren with flex layout +TestLayoutChildrenFlex = {} + +function TestLayoutChildrenFlex:test_layoutChildren_horizontal_flex_start() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.HORIZONTAL, + justifyContent = utils.enums.JustifyContent.FLEX_START, + alignItems = utils.enums.AlignItems.FLEX_START, + gap = 10, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 100, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + parent = parent, + }) + + local child2 = createMockElement({ + id = "child2", + width = 60, + height = 40, + parent = parent, + }) + + parent.children = { child1, child2 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- Verify layout was calculated (children positions should be set) + -- Child1 should be at (0, 0) + luaunit.assertEquals(child1.x, 0) + luaunit.assertEquals(child1.y, 0) + + -- Child2 should be at (50 + gap, 0) = (60, 0) + luaunit.assertEquals(child2.x, 60) + luaunit.assertEquals(child2.y, 0) +end + +function TestLayoutChildrenFlex:test_layoutChildren_vertical_flex_start() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.VERTICAL, + justifyContent = utils.enums.JustifyContent.FLEX_START, + alignItems = utils.enums.AlignItems.FLEX_START, + gap = 5, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 100, + height = 200, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + parent = parent, + }) + + local child2 = createMockElement({ + id = "child2", + width = 60, + height = 40, + parent = parent, + }) + + parent.children = { child1, child2 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- Verify layout was calculated + luaunit.assertEquals(child1.x, 0) + luaunit.assertEquals(child1.y, 0) + + -- Child2 should be below child1 with gap + luaunit.assertEquals(child2.x, 0) + luaunit.assertEquals(child2.y, 35) -- 30 + 5 +end + +function TestLayoutChildrenFlex:test_layoutChildren_with_margins() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.HORIZONTAL, + justifyContent = utils.enums.JustifyContent.FLEX_START, + gap = 0, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 100, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + marginLeft = 10, + marginRight = 5, + parent = parent, + }) + + parent.children = { child1 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- Child should be offset by left margin + luaunit.assertEquals(child1.x, 10) +end + +function TestLayoutChildrenFlex:test_layoutChildren_with_hidden_children() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.HORIZONTAL, + gap = 10, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 100, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + parent = parent, + }) + + local child2 = createMockElement({ + id = "child2", + width = 60, + height = 40, + isHidden = true, + parent = parent, + }) + + local child3 = createMockElement({ + id = "child3", + width = 70, + height = 35, + parent = parent, + }) + + parent.children = { child1, child2, child3 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- Child2 should be skipped, so child3 should be positioned after child1 + luaunit.assertEquals(child1.x, 0) + luaunit.assertEquals(child3.x, 60) -- 50 + gap (10) +end + +function TestLayoutChildrenFlex:test_layoutChildren_center() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.HORIZONTAL, + justifyContent = utils.enums.JustifyContent.CENTER, + alignItems = utils.enums.AlignItems.CENTER, + gap = 10, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 100, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + parent = parent, + }) + + local child2 = createMockElement({ + id = "child2", + width = 60, + height = 40, + parent = parent, + }) + + parent.children = { child1, child2 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- Children should be centered + -- Total width needed: 50 + 10 + 60 = 120 + -- Remaining space: 300 - 120 = 180 + -- Center offset: 180 / 2 = 90 + luaunit.assertEquals(child1.x, 90) + luaunit.assertEquals(child2.x, 150) -- 90 + 50 + 10 + + -- Vertical centering + -- Child1 height 30, container 100, offset = (100-30)/2 = 35 + luaunit.assertEquals(child1.y, 35) + -- Child2 height 40, offset = (100-40)/2 = 30 + luaunit.assertEquals(child2.y, 30) +end + +function TestLayoutChildrenFlex:test_layoutChildren_space_between() + local props = { + positioning = utils.enums.Positioning.FLEX, + flexDirection = utils.enums.FlexDirection.HORIZONTAL, + justifyContent = utils.enums.JustifyContent.SPACE_BETWEEN, + gap = 0, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 100, + }) + + local child1 = createMockElement({ + id = "child1", + width = 50, + height = 30, + parent = parent, + }) + + local child2 = createMockElement({ + id = "child2", + width = 60, + height = 40, + parent = parent, + }) + + parent.children = { child1, child2 } + parent._layout = layout + + layout:initialize(parent) + layout:layoutChildren() + + -- First child at start + luaunit.assertEquals(child1.x, 0) + + -- Last child at end: 300 - 60 = 240 + luaunit.assertEquals(child2.x, 240) +end + +-- Test suite for applyPositioningOffsets +TestApplyPositioningOffsets = {} + +function TestApplyPositioningOffsets:test_applyPositioningOffsets_relative() + local props = { + positioning = utils.enums.Positioning.FLEX, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + x = 100, + y = 50, + }) + + local child = createMockElement({ + id = "child", + x = 20, + y = 30, + parent = parent, + }) + + layout:initialize(parent) + layout:applyPositioningOffsets(child) + + -- Relative positioning: child keeps its x, y + luaunit.assertEquals(child.x, 20) + luaunit.assertEquals(child.y, 30) +end + +function TestApplyPositioningOffsets:test_applyPositioningOffsets_absolute() + local props = { + positioning = utils.enums.Positioning.ABSOLUTE, + } + local layout = LayoutEngine.new(props, deps) + + local parent = createMockElement({ + id = "parent", + absoluteX = 100, + absoluteY = 50, + width = 300, + height = 200, + }) + + local child = createMockElement({ + id = "child", + x = 20, + y = 30, + parent = parent, + }) + + layout:initialize(parent) + layout:applyPositioningOffsets(child) + + -- Absolute positioning: child.x, child.y are relative to parent + luaunit.assertEquals(child.absoluteX, 120) -- 100 + 20 + luaunit.assertEquals(child.absoluteY, 80) -- 50 + 30 +end + +-- Test suite for grid layout +TestLayoutChildrenGrid = {} + +function TestLayoutChildrenGrid:test_layoutChildren_grid_delegates_to_Grid() + local gridCalled = false + local mockGridForTest = { + layoutGridItems = function(element) + gridCalled = true + end, + } + + local depsWithMockGrid = { + utils = utils, + Grid = mockGridForTest, + Units = Units, + Context = mockContext, + ErrorHandler = mockErrorHandler, + } + + local props = { + positioning = utils.enums.Positioning.GRID, + gridRows = 2, + gridColumns = 2, + } + local layout = LayoutEngine.new(props, depsWithMockGrid) + + local parent = createMockElement({ + id = "parent", + width = 300, + height = 200, + }) + + parent._layout = layout + layout:initialize(parent) + layout:layoutChildren() + + -- Verify Grid.layoutGridItems was called + luaunit.assertTrue(gridCalled) +end + +-- Run tests if this file is executed directly +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +end diff --git a/testing/__tests__/layout_engine_test.lua b/testing/__tests__/layout_engine_test.lua new file mode 100644 index 0000000..bc9632e --- /dev/null +++ b/testing/__tests__/layout_engine_test.lua @@ -0,0 +1,661 @@ +-- 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 diff --git a/testing/__tests__/path_validation_test.lua b/testing/__tests__/path_validation_test.lua index aefa139..c61c13c 100644 --- a/testing/__tests__/path_validation_test.lua +++ b/testing/__tests__/path_validation_test.lua @@ -40,7 +40,7 @@ end function TestSanitizePath:testSanitizePath_TrailingSlash() local result = utils.sanitizePath("/path/to/dir/") luaunit.assertEquals(result, "/path/to/dir") - + -- Root should keep trailing slash result = utils.sanitizePath("/") luaunit.assertEquals(result, "/") diff --git a/testing/__tests__/texteditor_sanitization_test.lua b/testing/__tests__/texteditor_sanitization_test.lua index 870fbdc..21dafca 100644 --- a/testing/__tests__/texteditor_sanitization_test.lua +++ b/testing/__tests__/texteditor_sanitization_test.lua @@ -12,12 +12,14 @@ local Color = require("modules.Color") -- Mock dependencies local mockContext = { _immediateMode = false, - _focusedElement = nil + _focusedElement = nil, } local mockStateManager = { - getState = function() return nil end, - setState = function() end + getState = function() + return nil + end, + setState = function() end, } -- Test Suite for TextEditor Sanitization @@ -31,7 +33,7 @@ function TestTextEditorSanitization:_createEditor(config) Context = mockContext, StateManager = mockStateManager, Color = Color, - utils = utils + utils = utils, } return TextEditor.new(config, deps) end @@ -39,41 +41,41 @@ end -- === Sanitization Enabled Tests === function TestTextEditorSanitization:test_sanitization_enabled_by_default() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) luaunit.assertTrue(editor.sanitize) end function TestTextEditorSanitization:test_sanitization_can_be_disabled() - local editor = self:_createEditor({editable = true, sanitize = false}) + local editor = self:_createEditor({ editable = true, sanitize = false }) luaunit.assertFalse(editor.sanitize) end function TestTextEditorSanitization:test_setText_removes_control_characters() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello\x00World\x01Test") luaunit.assertEquals(editor:getText(), "HelloWorldTest") end function TestTextEditorSanitization:test_setText_preserves_valid_text() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello World! 123") luaunit.assertEquals(editor:getText(), "Hello World! 123") end function TestTextEditorSanitization:test_setText_removes_multiple_control_chars() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Test\x00\x01\x02\x03\x04Data") luaunit.assertEquals(editor:getText(), "TestData") end function TestTextEditorSanitization:test_setText_with_sanitization_disabled() - local editor = self:_createEditor({editable = true, sanitize = false}) + local editor = self:_createEditor({ editable = true, sanitize = false }) editor:setText("Hello\x00World") luaunit.assertEquals(editor:getText(), "Hello\x00World") end function TestTextEditorSanitization:test_setText_skip_sanitization_parameter() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello\x00World", true) -- skipSanitization = true luaunit.assertEquals(editor:getText(), "Hello\x00World") end @@ -83,7 +85,7 @@ end function TestTextEditorSanitization:test_initial_text_is_sanitized() local editor = self:_createEditor({ editable = true, - text = "Initial\x00Text\x01" + text = "Initial\x00Text\x01", }) luaunit.assertEquals(editor:getText(), "InitialText") end @@ -92,7 +94,7 @@ function TestTextEditorSanitization:test_initial_text_preserved_when_disabled() local editor = self:_createEditor({ editable = true, sanitize = false, - text = "Initial\x00Text" + text = "Initial\x00Text", }) luaunit.assertEquals(editor:getText(), "Initial\x00Text") end @@ -100,19 +102,19 @@ end -- === insertText Sanitization === function TestTextEditorSanitization:test_insertText_sanitizes_input() - local editor = self:_createEditor({editable = true, text = "Hello"}) + local editor = self:_createEditor({ editable = true, text = "Hello" }) editor:insertText("\x00World", 5) luaunit.assertEquals(editor:getText(), "HelloWorld") end function TestTextEditorSanitization:test_insertText_with_valid_text() - local editor = self:_createEditor({editable = true, text = "Hello"}) + local editor = self:_createEditor({ editable = true, text = "Hello" }) editor:insertText(" World", 5) luaunit.assertEquals(editor:getText(), "Hello World") end function TestTextEditorSanitization:test_insertText_empty_after_sanitization() - local editor = self:_createEditor({editable = true, text = "Hello"}) + local editor = self:_createEditor({ editable = true, text = "Hello" }) editor:insertText("\x00\x01\x02", 5) -- Only control chars luaunit.assertEquals(editor:getText(), "Hello") -- Should remain unchanged end @@ -120,25 +122,25 @@ end -- === Length Limiting === function TestTextEditorSanitization:test_maxLength_enforced_on_setText() - local editor = self:_createEditor({editable = true, maxLength = 10}) + local editor = self:_createEditor({ editable = true, maxLength = 10 }) editor:setText("This is a very long text") luaunit.assertEquals(#editor:getText(), 10) end function TestTextEditorSanitization:test_maxLength_enforced_on_insertText() - local editor = self:_createEditor({editable = true, text = "12345", maxLength = 10}) + local editor = self:_createEditor({ editable = true, text = "12345", maxLength = 10 }) editor:insertText("67890", 5) -- This would make it exactly 10 luaunit.assertEquals(editor:getText(), "1234567890") end function TestTextEditorSanitization:test_maxLength_truncates_excess() - local editor = self:_createEditor({editable = true, text = "12345", maxLength = 10}) + local editor = self:_createEditor({ editable = true, text = "12345", maxLength = 10 }) editor:insertText("67890EXTRA", 5) -- Would exceed limit luaunit.assertEquals(editor:getText(), "1234567890") end function TestTextEditorSanitization:test_maxLength_prevents_insert_when_full() - local editor = self:_createEditor({editable = true, text = "1234567890", maxLength = 10}) + local editor = self:_createEditor({ editable = true, text = "1234567890", maxLength = 10 }) editor:insertText("X", 10) luaunit.assertEquals(editor:getText(), "1234567890") -- Should not change end @@ -146,13 +148,13 @@ end -- === Newline Handling === function TestTextEditorSanitization:test_newlines_allowed_in_multiline() - local editor = self:_createEditor({editable = true, multiline = true}) + local editor = self:_createEditor({ editable = true, multiline = true }) editor:setText("Line1\nLine2") luaunit.assertEquals(editor:getText(), "Line1\nLine2") end function TestTextEditorSanitization:test_newlines_removed_in_singleline() - local editor = self:_createEditor({editable = true, multiline = false}) + local editor = self:_createEditor({ editable = true, multiline = false }) editor:setText("Line1\nLine2") luaunit.assertEquals(editor:getText(), "Line1Line2") end @@ -161,7 +163,7 @@ function TestTextEditorSanitization:test_allowNewlines_explicit_false() local editor = self:_createEditor({ editable = true, multiline = true, - allowNewlines = false + allowNewlines = false, }) editor:setText("Line1\nLine2") luaunit.assertEquals(editor:getText(), "Line1Line2") @@ -170,7 +172,7 @@ end -- === Tab Handling === function TestTextEditorSanitization:test_tabs_allowed_by_default() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello\tWorld") luaunit.assertEquals(editor:getText(), "Hello\tWorld") end @@ -178,7 +180,7 @@ end function TestTextEditorSanitization:test_tabs_removed_when_disabled() local editor = self:_createEditor({ editable = true, - allowTabs = false + allowTabs = false, }) editor:setText("Hello\tWorld") luaunit.assertEquals(editor:getText(), "HelloWorld") @@ -190,10 +192,10 @@ function TestTextEditorSanitization:test_custom_sanitizer_used() local customSanitizer = function(text) return text:upper() end - + local editor = self:_createEditor({ editable = true, - customSanitizer = customSanitizer + customSanitizer = customSanitizer, }) editor:setText("hello world") luaunit.assertEquals(editor:getText(), "HELLO WORLD") @@ -204,10 +206,10 @@ function TestTextEditorSanitization:test_custom_sanitizer_with_control_chars() -- Custom sanitizer that replaces control chars with * return text:gsub("[\x00-\x1F]", "*") end - + local editor = self:_createEditor({ editable = true, - customSanitizer = customSanitizer + customSanitizer = customSanitizer, }) editor:setText("Hello\x00World\x01") luaunit.assertEquals(editor:getText(), "Hello*World*") @@ -216,19 +218,19 @@ end -- === Unicode and Special Characters === function TestTextEditorSanitization:test_unicode_preserved() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello δΈ–η•Œ 🌍") luaunit.assertEquals(editor:getText(), "Hello δΈ–η•Œ 🌍") end function TestTextEditorSanitization:test_emoji_preserved() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("πŸ˜€πŸ˜ƒπŸ˜„πŸ˜") luaunit.assertEquals(editor:getText(), "πŸ˜€πŸ˜ƒπŸ˜„πŸ˜") end function TestTextEditorSanitization:test_special_chars_preserved() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("!@#$%^&*()_+-=[]{}|;':\",./<>?") luaunit.assertEquals(editor:getText(), "!@#$%^&*()_+-=[]{}|;':\",./<>?") end @@ -236,25 +238,25 @@ end -- === Edge Cases === function TestTextEditorSanitization:test_empty_string() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("") luaunit.assertEquals(editor:getText(), "") end function TestTextEditorSanitization:test_only_control_characters() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("\x00\x01\x02\x03") luaunit.assertEquals(editor:getText(), "") end function TestTextEditorSanitization:test_nil_text() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText(nil) luaunit.assertEquals(editor:getText(), "") end function TestTextEditorSanitization:test_very_long_text_with_control_chars() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) local longText = string.rep("Hello\x00World", 100) editor:setText(longText) luaunit.assertStrContains(editor:getText(), "Hello") @@ -263,7 +265,7 @@ function TestTextEditorSanitization:test_very_long_text_with_control_chars() end function TestTextEditorSanitization:test_mixed_valid_and_invalid() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Valid\x00Text\x01With\x02Control\x03Chars") luaunit.assertEquals(editor:getText(), "ValidTextWithControlChars") end @@ -271,13 +273,13 @@ end -- === Whitespace Handling === function TestTextEditorSanitization:test_spaces_preserved() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello World") luaunit.assertEquals(editor:getText(), "Hello World") end function TestTextEditorSanitization:test_leading_trailing_spaces_preserved() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText(" Hello World ") luaunit.assertEquals(editor:getText(), " Hello World ") end @@ -285,7 +287,7 @@ end -- === Integration Tests === function TestTextEditorSanitization:test_cursor_position_after_sanitization() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello") editor:insertText("\x00World", 5) -- Cursor should be at end of "HelloWorld" = position 10 @@ -293,7 +295,7 @@ function TestTextEditorSanitization:test_cursor_position_after_sanitization() end function TestTextEditorSanitization:test_multiple_operations() - local editor = self:_createEditor({editable = true}) + local editor = self:_createEditor({ editable = true }) editor:setText("Hello") editor:insertText(" ", 5) editor:insertText("World\x00", 6) diff --git a/testing/__tests__/theme_core_test.lua b/testing/__tests__/theme_core_test.lua new file mode 100644 index 0000000..09ef006 --- /dev/null +++ b/testing/__tests__/theme_core_test.lua @@ -0,0 +1,302 @@ +-- Test suite for Theme.lua core functionality +-- Tests theme creation, registration, and retrieval functions + +package.path = package.path .. ";./?.lua;./modules/?.lua" + +-- Load love stub before anything else +require("testing.loveStub") + +local luaunit = require("testing.luaunit") +local Theme = require("modules.Theme") +local Color = require("modules.Color") + +-- Test suite for Theme.new() +TestThemeNew = {} + +function TestThemeNew:setUp() + -- Clear any registered themes before each test + -- Note: We can't access the themes table directly, but we can work around it +end + +function TestThemeNew:test_new_minimal_theme() + local def = { + name = "Minimal Theme", + } + local theme = Theme.new(def) + luaunit.assertNotNil(theme) + luaunit.assertEquals(theme.name, "Minimal Theme") +end + +function TestThemeNew:test_new_theme_with_components() + local def = { + name = "Test Theme", + components = { + button = { + atlas = "path/to/button.png", + }, + }, + } + local theme = Theme.new(def) + luaunit.assertNotNil(theme) + luaunit.assertEquals(theme.name, "Test Theme") + luaunit.assertNotNil(theme.components.button) +end + +function TestThemeNew:test_new_theme_with_colors() + local def = { + name = "Colored Theme", + colors = { + primary = Color.new(1, 0, 0, 1), + secondary = Color.new(0, 1, 0, 1), + }, + } + local theme = Theme.new(def) + luaunit.assertNotNil(theme) + luaunit.assertNotNil(theme.colors.primary) + luaunit.assertNotNil(theme.colors.secondary) +end + +function TestThemeNew:test_new_theme_with_fonts() + local def = { + name = "Font Theme", + fonts = { + default = "path/to/font.ttf", + }, + } + local theme = Theme.new(def) + luaunit.assertNotNil(theme) + luaunit.assertNotNil(theme.fonts.default) + luaunit.assertEquals(theme.fonts.default, "path/to/font.ttf") +end + +function TestThemeNew:test_new_theme_with_multiplier() + local def = { + name = "Multiplier Theme", + contentAutoSizingMultiplier = { + width = 1.5, + height = 2.0, + }, + } + local theme = Theme.new(def) + luaunit.assertNotNil(theme) + luaunit.assertNotNil(theme.contentAutoSizingMultiplier) + luaunit.assertEquals(theme.contentAutoSizingMultiplier.width, 1.5) + luaunit.assertEquals(theme.contentAutoSizingMultiplier.height, 2.0) +end + +function TestThemeNew:test_new_theme_without_name_fails() + local def = {} + luaunit.assertErrorMsgContains("name", function() + Theme.new(def) + end) +end + +function TestThemeNew:test_new_theme_with_nil_fails() + luaunit.assertErrorMsgContains("nil", function() + Theme.new(nil) + end) +end + +function TestThemeNew:test_new_theme_with_non_table_fails() + luaunit.assertErrorMsgContains("table", function() + Theme.new("not a table") + end) +end + +-- Test suite for Theme registration and retrieval +TestThemeRegistration = {} + +function TestThemeRegistration:test_setActive_with_theme_object() + local def = { + name = "Active Theme", + } + local theme = Theme.new(def) + Theme.setActive(theme) + + local active = Theme.getActive() + luaunit.assertNotNil(active) + luaunit.assertEquals(active.name, "Active Theme") +end + +function TestThemeRegistration:test_getActive_returns_nil_initially() + -- This test assumes no theme is active, but other tests may have set one + -- So we'll just check that getActive returns something or nil + local active = Theme.getActive() + -- Just verify it doesn't error + luaunit.assertTrue(active == nil or type(active) == "table") +end + +function TestThemeRegistration:test_hasActive_returns_boolean() + local hasActive = Theme.hasActive() + luaunit.assertTrue(type(hasActive) == "boolean") +end + +function TestThemeRegistration:test_get_returns_nil_for_unregistered_theme() + -- Theme.get() looks up themes in the registered themes table + -- Themes created with Theme.new() and setActive() are not automatically registered + local def = { + name = "Unregistered Theme", + } + local theme = Theme.new(def) + Theme.setActive(theme) + + -- This should return nil because the theme was not loaded from a file + local retrieved = Theme.get("Unregistered Theme") + luaunit.assertNil(retrieved) +end + +function TestThemeRegistration:test_get_returns_nil_for_nonexistent() + local retrieved = Theme.get("Nonexistent Theme 12345") + luaunit.assertNil(retrieved) +end + +function TestThemeRegistration:test_getRegisteredThemes_returns_table() + local themes = Theme.getRegisteredThemes() + luaunit.assertNotNil(themes) + luaunit.assertEquals(type(themes), "table") +end + +-- Test suite for Theme.getComponent() +TestThemeComponent = {} + +function TestThemeComponent:setUp() + -- Create and set an active theme with components + local def = { + name = "Component Test Theme", + components = { + button = { + atlas = "path/to/button.png", + }, + panel = { + atlas = "path/to/panel.png", + }, + }, + } + self.theme = Theme.new(def) + Theme.setActive(self.theme) +end + +function TestThemeComponent:test_getComponent_returns_component() + local component = Theme.getComponent("button") + luaunit.assertNotNil(component) + luaunit.assertEquals(component.atlas, "path/to/button.png") +end + +function TestThemeComponent:test_getComponent_returns_nil_for_nonexistent() + local component = Theme.getComponent("nonexistent") + luaunit.assertNil(component) +end + +function TestThemeComponent:test_getComponent_with_state() + -- Add a component with states + local def = { + name = "State Test Theme", + components = { + button = { + atlas = "path/to/button.png", + states = { + hover = { + atlas = "path/to/button_hover.png", + }, + }, + }, + }, + } + local theme = Theme.new(def) + Theme.setActive(theme) + + local component = Theme.getComponent("button", "hover") + luaunit.assertNotNil(component) + luaunit.assertEquals(component.atlas, "path/to/button_hover.png") +end + +-- Test suite for Theme.getColor() +TestThemeColor = {} + +function TestThemeColor:setUp() + local def = { + name = "Color Test Theme", + colors = { + primary = Color.new(1, 0, 0, 1), + secondary = Color.new(0, 1, 0, 1), + textColor = Color.new(0.5, 0.5, 0.5, 1), + }, + } + self.theme = Theme.new(def) + Theme.setActive(self.theme) +end + +function TestThemeColor:test_getColor_returns_color() + local color = Theme.getColor("primary") + luaunit.assertNotNil(color) + luaunit.assertEquals(color.r, 1) + luaunit.assertEquals(color.g, 0) + luaunit.assertEquals(color.b, 0) +end + +function TestThemeColor:test_getColor_returns_nil_for_nonexistent() + local color = Theme.getColor("nonexistent") + luaunit.assertNil(color) +end + +function TestThemeColor:test_getColorNames_returns_table() + local names = Theme.getColorNames() + luaunit.assertNotNil(names) + luaunit.assertEquals(type(names), "table") + -- Should contain our defined colors + luaunit.assertTrue(#names >= 3) +end + +function TestThemeColor:test_getAllColors_returns_table() + local colors = Theme.getAllColors() + luaunit.assertNotNil(colors) + luaunit.assertEquals(type(colors), "table") + luaunit.assertNotNil(colors.primary) + luaunit.assertNotNil(colors.secondary) +end + +function TestThemeColor:test_getColorOrDefault_returns_color() + local color = Theme.getColorOrDefault("primary", Color.new(0, 0, 0, 1)) + luaunit.assertNotNil(color) + luaunit.assertEquals(color.r, 1) +end + +function TestThemeColor:test_getColorOrDefault_returns_fallback() + local fallback = Color.new(0.1, 0.2, 0.3, 1) + local color = Theme.getColorOrDefault("nonexistent", fallback) + luaunit.assertNotNil(color) + luaunit.assertEquals(color.r, 0.1) + luaunit.assertEquals(color.g, 0.2) + luaunit.assertEquals(color.b, 0.3) +end + +-- Test suite for Theme.getFont() +TestThemeFont = {} + +function TestThemeFont:setUp() + local def = { + name = "Font Test Theme", + fonts = { + default = "path/to/default.ttf", + heading = "path/to/heading.ttf", + }, + } + self.theme = Theme.new(def) + Theme.setActive(self.theme) +end + +function TestThemeFont:test_getFont_returns_font_path() + local font = Theme.getFont("default") + luaunit.assertNotNil(font) + luaunit.assertEquals(font, "path/to/default.ttf") +end + +function TestThemeFont:test_getFont_returns_nil_for_nonexistent() + local font = Theme.getFont("nonexistent") + luaunit.assertNil(font) +end + +-- Run tests if this file is executed directly +if not _G.RUNNING_ALL_TESTS then + os.exit(luaunit.LuaUnit.run()) +end diff --git a/testing/__tests__/theme_validation_test.lua b/testing/__tests__/theme_validation_test.lua index 5ceabd7..9e08f55 100644 --- a/testing/__tests__/theme_validation_test.lua +++ b/testing/__tests__/theme_validation_test.lua @@ -37,7 +37,7 @@ end function TestThemeValidation:test_validate_minimal_valid_theme() local theme = { - name = "Test Theme" + name = "Test Theme", } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -46,7 +46,7 @@ end function TestThemeValidation:test_validate_theme_with_empty_name() local theme = { - name = "" + name = "", } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -55,7 +55,7 @@ end function TestThemeValidation:test_validate_theme_with_non_string_name() local theme = { - name = 123 + name = 123, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -69,8 +69,8 @@ function TestThemeValidation:test_validate_valid_colors() name = "Test Theme", colors = { primary = Color.new(1, 0, 0, 1), - secondary = Color.new(0, 1, 0, 1) - } + secondary = Color.new(0, 1, 0, 1), + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -81,8 +81,8 @@ function TestThemeValidation:test_validate_colors_with_hex() local theme = { name = "Test Theme", colors = { - primary = "#FF0000" - } + primary = "#FF0000", + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -94,8 +94,8 @@ function TestThemeValidation:test_validate_colors_with_named() name = "Test Theme", colors = { primary = "red", - secondary = "blue" - } + secondary = "blue", + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -106,8 +106,8 @@ function TestThemeValidation:test_validate_invalid_color() local theme = { name = "Test Theme", colors = { - primary = "not-a-color" - } + primary = "not-a-color", + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -118,7 +118,7 @@ end function TestThemeValidation:test_validate_colors_non_table() local theme = { name = "Test Theme", - colors = "should be a table" + colors = "should be a table", } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -129,8 +129,8 @@ function TestThemeValidation:test_validate_color_with_non_string_name() local theme = { name = "Test Theme", colors = { - [123] = Color.new(1, 0, 0, 1) - } + [123] = Color.new(1, 0, 0, 1), + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -144,8 +144,8 @@ function TestThemeValidation:test_validate_valid_fonts() name = "Test Theme", fonts = { default = "path/to/font.ttf", - heading = "path/to/heading.ttf" - } + heading = "path/to/heading.ttf", + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -155,7 +155,7 @@ end function TestThemeValidation:test_validate_fonts_non_table() local theme = { name = "Test Theme", - fonts = "should be a table" + fonts = "should be a table", } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -166,8 +166,8 @@ function TestThemeValidation:test_validate_font_with_non_string_path() local theme = { name = "Test Theme", fonts = { - default = 123 - } + default = 123, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -178,8 +178,8 @@ function TestThemeValidation:test_validate_font_with_non_string_name() local theme = { name = "Test Theme", fonts = { - [123] = "path/to/font.ttf" - } + [123] = "path/to/font.ttf", + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -193,9 +193,9 @@ function TestThemeValidation:test_validate_valid_component() name = "Test Theme", components = { button = { - atlas = "path/to/button.png" - } - } + atlas = "path/to/button.png", + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -208,9 +208,9 @@ function TestThemeValidation:test_validate_component_with_insets() components = { button = { atlas = "path/to/button.png", - insets = {left = 5, top = 5, right = 5, bottom = 5} - } - } + insets = { left = 5, top = 5, right = 5, bottom = 5 }, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -223,9 +223,9 @@ function TestThemeValidation:test_validate_component_with_missing_inset() components = { button = { atlas = "path/to/button.png", - insets = {left = 5, top = 5, right = 5} -- missing bottom - } - } + insets = { left = 5, top = 5, right = 5 }, -- missing bottom + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -239,9 +239,9 @@ function TestThemeValidation:test_validate_component_with_negative_inset() components = { button = { atlas = "path/to/button.png", - insets = {left = -5, top = 5, right = 5, bottom = 5} - } - } + insets = { left = -5, top = 5, right = 5, bottom = 5 }, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -257,14 +257,14 @@ function TestThemeValidation:test_validate_component_with_states() atlas = "path/to/button.png", states = { hover = { - atlas = "path/to/button_hover.png" + atlas = "path/to/button_hover.png", }, pressed = { - atlas = "path/to/button_pressed.png" - } - } - } - } + atlas = "path/to/button_pressed.png", + }, + }, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -278,10 +278,10 @@ function TestThemeValidation:test_validate_component_with_invalid_state() button = { atlas = "path/to/button.png", states = { - hover = "should be a table" - } - } - } + hover = "should be a table", + }, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -294,9 +294,9 @@ function TestThemeValidation:test_validate_component_with_scaleCorners() components = { button = { atlas = "path/to/button.png", - scaleCorners = 2.0 - } - } + scaleCorners = 2.0, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -309,9 +309,9 @@ function TestThemeValidation:test_validate_component_with_invalid_scaleCorners() components = { button = { atlas = "path/to/button.png", - scaleCorners = -1 - } - } + scaleCorners = -1, + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -325,9 +325,9 @@ function TestThemeValidation:test_validate_component_with_valid_scalingAlgorithm components = { button = { atlas = "path/to/button.png", - scalingAlgorithm = "nearest" - } - } + scalingAlgorithm = "nearest", + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -340,9 +340,9 @@ function TestThemeValidation:test_validate_component_with_invalid_scalingAlgorit components = { button = { atlas = "path/to/button.png", - scalingAlgorithm = "invalid" - } - } + scalingAlgorithm = "invalid", + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -352,7 +352,7 @@ end function TestThemeValidation:test_validate_components_non_table() local theme = { name = "Test Theme", - components = "should be a table" + components = "should be a table", } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -364,7 +364,7 @@ end function TestThemeValidation:test_validate_valid_multiplier() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = {width = 1.1, height = 1.2} + contentAutoSizingMultiplier = { width = 1.1, height = 1.2 }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -374,7 +374,7 @@ end function TestThemeValidation:test_validate_multiplier_with_only_width() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = {width = 1.1} + contentAutoSizingMultiplier = { width = 1.1 }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -384,7 +384,7 @@ end function TestThemeValidation:test_validate_multiplier_non_table() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = 1.5 + contentAutoSizingMultiplier = 1.5, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -394,7 +394,7 @@ end function TestThemeValidation:test_validate_multiplier_with_non_number() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = {width = "not a number"} + contentAutoSizingMultiplier = { width = "not a number" }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -404,7 +404,7 @@ end function TestThemeValidation:test_validate_multiplier_with_negative() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = {width = -1} + contentAutoSizingMultiplier = { width = -1 }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -415,7 +415,7 @@ end function TestThemeValidation:test_validate_multiplier_with_zero() local theme = { name = "Test Theme", - contentAutoSizingMultiplier = {width = 0} + contentAutoSizingMultiplier = { width = 0 }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) @@ -427,7 +427,7 @@ end function TestThemeValidation:test_validate_valid_global_atlas() local theme = { name = "Test Theme", - atlas = "path/to/atlas.png" + atlas = "path/to/atlas.png", } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -439,9 +439,9 @@ end function TestThemeValidation:test_validate_unknown_field_strict() local theme = { name = "Test Theme", - unknownField = "should trigger warning" + unknownField = "should trigger warning", } - local valid, errors = Theme.validateTheme(theme, {strict = true}) + local valid, errors = Theme.validateTheme(theme, { strict = true }) luaunit.assertFalse(valid) luaunit.assertTrue(#errors > 0) luaunit.assertStrContains(errors[1], "Unknown field") @@ -450,9 +450,9 @@ end function TestThemeValidation:test_validate_unknown_field_non_strict() local theme = { name = "Test Theme", - unknownField = "should be ignored" + unknownField = "should be ignored", } - local valid, errors = Theme.validateTheme(theme, {strict = false}) + local valid, errors = Theme.validateTheme(theme, { strict = false }) luaunit.assertTrue(valid) luaunit.assertEquals(#errors, 0) end @@ -467,7 +467,7 @@ end function TestThemeValidation:test_sanitize_theme_without_name() local theme = { - colors = {primary = "red"} + colors = { primary = "red" }, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertEquals(sanitized.name, "Unnamed Theme") @@ -475,7 +475,7 @@ end function TestThemeValidation:test_sanitize_theme_with_non_string_name() local theme = { - name = 123 + name = 123, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertEquals(type(sanitized.name), "string") @@ -486,8 +486,8 @@ function TestThemeValidation:test_sanitize_colors() name = "Test", colors = { valid = "red", - invalid = "not-a-color" - } + invalid = "not-a-color", + }, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertNotNil(sanitized.colors.valid) @@ -498,8 +498,8 @@ function TestThemeValidation:test_sanitize_removes_non_string_color_names() local theme = { name = "Test", colors = { - [123] = "red" - } + [123] = "red", + }, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertNil(sanitized.colors[123]) @@ -510,8 +510,8 @@ function TestThemeValidation:test_sanitize_fonts() name = "Test", fonts = { default = "path/to/font.ttf", - invalid = 123 - } + invalid = 123, + }, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertNotNil(sanitized.fonts.default) @@ -522,8 +522,8 @@ function TestThemeValidation:test_sanitize_preserves_components() local theme = { name = "Test", components = { - button = {atlas = "path/to/button.png"} - } + button = { atlas = "path/to/button.png" }, + }, } local sanitized = Theme.sanitizeTheme(theme) luaunit.assertNotNil(sanitized.components.button) @@ -536,35 +536,35 @@ function TestThemeValidation:test_validate_complete_theme() local theme = { name = "Complete Theme", atlas = "path/to/atlas.png", - contentAutoSizingMultiplier = {width = 1.05, height = 1.1}, + contentAutoSizingMultiplier = { width = 1.05, height = 1.1 }, colors = { primary = Color.new(1, 0, 0, 1), secondary = "#00FF00", - tertiary = "blue" + tertiary = "blue", }, fonts = { default = "path/to/font.ttf", - heading = "path/to/heading.ttf" + heading = "path/to/heading.ttf", }, components = { button = { atlas = "path/to/button.png", - insets = {left = 5, top = 5, right = 5, bottom = 5}, + insets = { left = 5, top = 5, right = 5, bottom = 5 }, scaleCorners = 2, scalingAlgorithm = "nearest", states = { hover = { - atlas = "path/to/button_hover.png" + atlas = "path/to/button_hover.png", }, pressed = { - atlas = "path/to/button_pressed.png" - } - } + atlas = "path/to/button_pressed.png", + }, + }, }, panel = { - atlas = "path/to/panel.png" - } - } + atlas = "path/to/panel.png", + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertTrue(valid) @@ -576,16 +576,16 @@ function TestThemeValidation:test_validate_theme_with_multiple_errors() name = "", colors = { invalid1 = "not-a-color", - invalid2 = 123 + invalid2 = 123, }, fonts = { - bad = 456 + bad = 456, }, components = { button = { - insets = {left = -5} -- missing fields and negative - } - } + insets = { left = -5 }, -- missing fields and negative + }, + }, } local valid, errors = Theme.validateTheme(theme) luaunit.assertFalse(valid) diff --git a/testing/__tests__/units_test.lua b/testing/__tests__/units_test.lua index cb67355..d178195 100644 --- a/testing/__tests__/units_test.lua +++ b/testing/__tests__/units_test.lua @@ -251,7 +251,7 @@ function TestUnitsResolveSpacing:testResolveSpacingAllSides() top = "10px", right = "20px", bottom = "30px", - left = "40px" + left = "40px", } local result = Units.resolveSpacing(spacing, 800, 600) luaunit.assertEquals(result.top, 10) @@ -263,7 +263,7 @@ end function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontal() local spacing = { vertical = "10px", - horizontal = "20px" + horizontal = "20px", } local result = Units.resolveSpacing(spacing, 800, 600) luaunit.assertEquals(result.top, 10) @@ -275,7 +275,7 @@ end function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontalNumbers() local spacing = { vertical = 10, - horizontal = 20 + horizontal = 20, } local result = Units.resolveSpacing(spacing, 800, 600) luaunit.assertEquals(result.top, 10) @@ -289,13 +289,13 @@ function TestUnitsResolveSpacing:testResolveSpacingMixedPercentage() top = "10%", right = "5%", bottom = "10%", - left = "5%" + left = "5%", } local result = Units.resolveSpacing(spacing, 800, 600) - luaunit.assertEquals(result.top, 60) -- 10% of 600 (height) - luaunit.assertEquals(result.right, 40) -- 5% of 800 (width) + luaunit.assertEquals(result.top, 60) -- 10% of 600 (height) + luaunit.assertEquals(result.right, 40) -- 5% of 800 (width) luaunit.assertEquals(result.bottom, 60) -- 10% of 600 (height) - luaunit.assertEquals(result.left, 40) -- 5% of 800 (width) + luaunit.assertEquals(result.left, 40) -- 5% of 800 (width) end function TestUnitsResolveSpacing:testResolveSpacingOverride() @@ -303,10 +303,10 @@ function TestUnitsResolveSpacing:testResolveSpacingOverride() local spacing = { vertical = "10px", horizontal = "20px", - top = "50px" + top = "50px", } local result = Units.resolveSpacing(spacing, 800, 600) - luaunit.assertEquals(result.top, 50) -- Overridden + luaunit.assertEquals(result.top, 50) -- Overridden luaunit.assertEquals(result.right, 20) luaunit.assertEquals(result.bottom, 10) luaunit.assertEquals(result.left, 20) diff --git a/testing/__tests__/utils_test.lua b/testing/__tests__/utils_test.lua index 31d0565..bf9d430 100644 --- a/testing/__tests__/utils_test.lua +++ b/testing/__tests__/utils_test.lua @@ -286,10 +286,10 @@ function TestFontUtils:testResolveFontPath_ThemeFont() getTheme = function() return { fonts = { - mainFont = "themes/fonts/main.ttf" - } + mainFont = "themes/fonts/main.ttf", + }, } - end + end, } local result = utils.resolveFontPath("mainFont", "button", mockThemeManager) @@ -301,9 +301,9 @@ function TestFontUtils:testResolveFontPath_ThemeFontNotFound() local mockThemeManager = { getTheme = function() return { - fonts = {} + fonts = {}, } - end + end, } -- Should fall back to treating it as a direct path @@ -421,11 +421,11 @@ function TestTextSizePresets:testResolveTextSizePreset_ValidPresets() local value, unit = utils.resolveTextSizePreset("xs") luaunit.assertEquals(value, 1.25) luaunit.assertEquals(unit, "vh") - + value, unit = utils.resolveTextSizePreset("md") luaunit.assertEquals(value, 2.25) luaunit.assertEquals(unit, "vh") - + value, unit = utils.resolveTextSizePreset("xl") luaunit.assertEquals(value, 3.5) luaunit.assertEquals(unit, "vh") diff --git a/testing/runAll.lua b/testing/runAll.lua index fba8c3a..c97187b 100644 --- a/testing/runAll.lua +++ b/testing/runAll.lua @@ -26,7 +26,10 @@ local testFiles = { "testing/__tests__/color_validation_test.lua", "testing/__tests__/texteditor_sanitization_test.lua", "testing/__tests__/theme_validation_test.lua", + "testing/__tests__/theme_core_test.lua", "testing/__tests__/units_test.lua", + "testing/__tests__/layout_engine_test.lua", + "testing/__tests__/element_test.lua", } local success = true