more tests, fixed theme validation
This commit is contained in:
@@ -663,4 +663,271 @@ end
|
|||||||
-- Export both Theme and ThemeManager
|
-- Export both Theme and ThemeManager
|
||||||
Theme.Manager = 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
|
return Theme
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ end
|
|||||||
function TestColorValidation:test_validateColorChannel_valid_0to255()
|
function TestColorValidation:test_validateColorChannel_valid_0to255()
|
||||||
local valid, clamped = Color.validateColorChannel(128, 255)
|
local valid, clamped = Color.validateColorChannel(128, 255)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
luaunit.assertAlmostEquals(clamped, 128/255, 0.001)
|
luaunit.assertAlmostEquals(clamped, 128 / 255, 0.001)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColorChannel_clamp_below_min()
|
function TestColorValidation:test_validateColorChannel_clamp_below_min()
|
||||||
@@ -44,7 +44,7 @@ function TestColorValidation:test_validateColorChannel_clamp_above_255()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColorChannel_nan()
|
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.assertFalse(valid)
|
||||||
luaunit.assertNil(clamped)
|
luaunit.assertNil(clamped)
|
||||||
end
|
end
|
||||||
@@ -168,7 +168,7 @@ function TestColorValidation:test_validateRGBColor_invalid_blue()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateRGBColor_invalid_alpha()
|
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.assertFalse(valid)
|
||||||
luaunit.assertNotNil(err)
|
luaunit.assertNotNil(err)
|
||||||
luaunit.assertStrContains(err, "Invalid alpha channel")
|
luaunit.assertStrContains(err, "Invalid alpha channel")
|
||||||
@@ -231,12 +231,12 @@ function TestColorValidation:test_isValidColorFormat_named()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_isValidColorFormat_table_array()
|
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")
|
luaunit.assertEquals(format, "table")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_isValidColorFormat_table_named()
|
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")
|
luaunit.assertEquals(format, "table")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ function TestColorValidation:test_isValidColorFormat_invalid_string()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_isValidColorFormat_invalid_table()
|
function TestColorValidation:test_isValidColorFormat_invalid_table()
|
||||||
local format = Color.isValidColorFormat({invalid=true})
|
local format = Color.isValidColorFormat({ invalid = true })
|
||||||
luaunit.assertNil(format)
|
luaunit.assertNil(format)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -281,32 +281,32 @@ function TestColorValidation:test_validateColor_named()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColor_table_array()
|
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.assertTrue(valid)
|
||||||
luaunit.assertNil(err)
|
luaunit.assertNil(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColor_table_named()
|
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.assertTrue(valid)
|
||||||
luaunit.assertNil(err)
|
luaunit.assertNil(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColor_named_disallowed()
|
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.assertFalse(valid)
|
||||||
luaunit.assertNotNil(err)
|
luaunit.assertNotNil(err)
|
||||||
luaunit.assertStrContains(err, "Named colors not allowed")
|
luaunit.assertStrContains(err, "Named colors not allowed")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColor_require_alpha_8digit()
|
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.assertTrue(valid)
|
||||||
luaunit.assertNil(err)
|
luaunit.assertNil(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_validateColor_require_alpha_6digit()
|
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.assertFalse(valid)
|
||||||
luaunit.assertNotNil(err)
|
luaunit.assertNotNil(err)
|
||||||
luaunit.assertStrContains(err, "Alpha channel required")
|
luaunit.assertStrContains(err, "Alpha channel required")
|
||||||
@@ -377,7 +377,7 @@ function TestColorValidation:test_sanitizeColor_named_transparent()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_sanitizeColor_table_array()
|
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.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
||||||
@@ -385,7 +385,7 @@ function TestColorValidation:test_sanitizeColor_table_array()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_sanitizeColor_table_named()
|
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.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
||||||
@@ -393,7 +393,7 @@ function TestColorValidation:test_sanitizeColor_table_named()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_sanitizeColor_table_array_clamp_high()
|
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.r, 1.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 1.0, 0.01)
|
luaunit.assertAlmostEquals(color.g, 1.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 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
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_sanitizeColor_table_array_clamp_low()
|
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.r, 0.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.0, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 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
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_sanitizeColor_table_no_alpha()
|
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.r, 0.5, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.6, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
luaunit.assertAlmostEquals(color.b, 0.7, 0.01)
|
||||||
@@ -461,7 +461,7 @@ function TestColorValidation:test_parse_named()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_parse_table()
|
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.r, 0.25, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.50, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.50, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 0.75, 0.01)
|
luaunit.assertAlmostEquals(color.b, 0.75, 0.01)
|
||||||
@@ -501,7 +501,7 @@ function TestColorValidation:test_edge_hex_with_spaces()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestColorValidation:test_edge_negative_values_clamped()
|
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.r, 0.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.g, 0.0, 0.01)
|
luaunit.assertAlmostEquals(color.g, 0.0, 0.01)
|
||||||
luaunit.assertAlmostEquals(color.b, 0.0, 0.01)
|
luaunit.assertAlmostEquals(color.b, 0.0, 0.01)
|
||||||
|
|||||||
535
testing/__tests__/element_test.lua
Normal file
535
testing/__tests__/element_test.lua
Normal file
@@ -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
|
||||||
|
|
||||||
441
testing/__tests__/layout_engine_integration_test.lua
Normal file
441
testing/__tests__/layout_engine_integration_test.lua
Normal file
@@ -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
|
||||||
661
testing/__tests__/layout_engine_test.lua
Normal file
661
testing/__tests__/layout_engine_test.lua
Normal file
@@ -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
|
||||||
@@ -12,12 +12,14 @@ local Color = require("modules.Color")
|
|||||||
-- Mock dependencies
|
-- Mock dependencies
|
||||||
local mockContext = {
|
local mockContext = {
|
||||||
_immediateMode = false,
|
_immediateMode = false,
|
||||||
_focusedElement = nil
|
_focusedElement = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
local mockStateManager = {
|
local mockStateManager = {
|
||||||
getState = function() return nil end,
|
getState = function()
|
||||||
setState = function() end
|
return nil
|
||||||
|
end,
|
||||||
|
setState = function() end,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Test Suite for TextEditor Sanitization
|
-- Test Suite for TextEditor Sanitization
|
||||||
@@ -31,7 +33,7 @@ function TestTextEditorSanitization:_createEditor(config)
|
|||||||
Context = mockContext,
|
Context = mockContext,
|
||||||
StateManager = mockStateManager,
|
StateManager = mockStateManager,
|
||||||
Color = Color,
|
Color = Color,
|
||||||
utils = utils
|
utils = utils,
|
||||||
}
|
}
|
||||||
return TextEditor.new(config, deps)
|
return TextEditor.new(config, deps)
|
||||||
end
|
end
|
||||||
@@ -39,41 +41,41 @@ end
|
|||||||
-- === Sanitization Enabled Tests ===
|
-- === Sanitization Enabled Tests ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_sanitization_enabled_by_default()
|
function TestTextEditorSanitization:test_sanitization_enabled_by_default()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
luaunit.assertTrue(editor.sanitize)
|
luaunit.assertTrue(editor.sanitize)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_sanitization_can_be_disabled()
|
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)
|
luaunit.assertFalse(editor.sanitize)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_setText_removes_control_characters()
|
function TestTextEditorSanitization:test_setText_removes_control_characters()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello\x00World\x01Test")
|
editor:setText("Hello\x00World\x01Test")
|
||||||
luaunit.assertEquals(editor:getText(), "HelloWorldTest")
|
luaunit.assertEquals(editor:getText(), "HelloWorldTest")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_setText_preserves_valid_text()
|
function TestTextEditorSanitization:test_setText_preserves_valid_text()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello World! 123")
|
editor:setText("Hello World! 123")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello World! 123")
|
luaunit.assertEquals(editor:getText(), "Hello World! 123")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_setText_removes_multiple_control_chars()
|
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")
|
editor:setText("Test\x00\x01\x02\x03\x04Data")
|
||||||
luaunit.assertEquals(editor:getText(), "TestData")
|
luaunit.assertEquals(editor:getText(), "TestData")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_setText_with_sanitization_disabled()
|
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")
|
editor:setText("Hello\x00World")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello\x00World")
|
luaunit.assertEquals(editor:getText(), "Hello\x00World")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_setText_skip_sanitization_parameter()
|
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
|
editor:setText("Hello\x00World", true) -- skipSanitization = true
|
||||||
luaunit.assertEquals(editor:getText(), "Hello\x00World")
|
luaunit.assertEquals(editor:getText(), "Hello\x00World")
|
||||||
end
|
end
|
||||||
@@ -83,7 +85,7 @@ end
|
|||||||
function TestTextEditorSanitization:test_initial_text_is_sanitized()
|
function TestTextEditorSanitization:test_initial_text_is_sanitized()
|
||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
text = "Initial\x00Text\x01"
|
text = "Initial\x00Text\x01",
|
||||||
})
|
})
|
||||||
luaunit.assertEquals(editor:getText(), "InitialText")
|
luaunit.assertEquals(editor:getText(), "InitialText")
|
||||||
end
|
end
|
||||||
@@ -92,7 +94,7 @@ function TestTextEditorSanitization:test_initial_text_preserved_when_disabled()
|
|||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
sanitize = false,
|
sanitize = false,
|
||||||
text = "Initial\x00Text"
|
text = "Initial\x00Text",
|
||||||
})
|
})
|
||||||
luaunit.assertEquals(editor:getText(), "Initial\x00Text")
|
luaunit.assertEquals(editor:getText(), "Initial\x00Text")
|
||||||
end
|
end
|
||||||
@@ -100,19 +102,19 @@ end
|
|||||||
-- === insertText Sanitization ===
|
-- === insertText Sanitization ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_insertText_sanitizes_input()
|
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)
|
editor:insertText("\x00World", 5)
|
||||||
luaunit.assertEquals(editor:getText(), "HelloWorld")
|
luaunit.assertEquals(editor:getText(), "HelloWorld")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_insertText_with_valid_text()
|
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)
|
editor:insertText(" World", 5)
|
||||||
luaunit.assertEquals(editor:getText(), "Hello World")
|
luaunit.assertEquals(editor:getText(), "Hello World")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_insertText_empty_after_sanitization()
|
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
|
editor:insertText("\x00\x01\x02", 5) -- Only control chars
|
||||||
luaunit.assertEquals(editor:getText(), "Hello") -- Should remain unchanged
|
luaunit.assertEquals(editor:getText(), "Hello") -- Should remain unchanged
|
||||||
end
|
end
|
||||||
@@ -120,25 +122,25 @@ end
|
|||||||
-- === Length Limiting ===
|
-- === Length Limiting ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_maxLength_enforced_on_setText()
|
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")
|
editor:setText("This is a very long text")
|
||||||
luaunit.assertEquals(#editor:getText(), 10)
|
luaunit.assertEquals(#editor:getText(), 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_maxLength_enforced_on_insertText()
|
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
|
editor:insertText("67890", 5) -- This would make it exactly 10
|
||||||
luaunit.assertEquals(editor:getText(), "1234567890")
|
luaunit.assertEquals(editor:getText(), "1234567890")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_maxLength_truncates_excess()
|
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
|
editor:insertText("67890EXTRA", 5) -- Would exceed limit
|
||||||
luaunit.assertEquals(editor:getText(), "1234567890")
|
luaunit.assertEquals(editor:getText(), "1234567890")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_maxLength_prevents_insert_when_full()
|
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)
|
editor:insertText("X", 10)
|
||||||
luaunit.assertEquals(editor:getText(), "1234567890") -- Should not change
|
luaunit.assertEquals(editor:getText(), "1234567890") -- Should not change
|
||||||
end
|
end
|
||||||
@@ -146,13 +148,13 @@ end
|
|||||||
-- === Newline Handling ===
|
-- === Newline Handling ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_newlines_allowed_in_multiline()
|
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")
|
editor:setText("Line1\nLine2")
|
||||||
luaunit.assertEquals(editor:getText(), "Line1\nLine2")
|
luaunit.assertEquals(editor:getText(), "Line1\nLine2")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_newlines_removed_in_singleline()
|
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")
|
editor:setText("Line1\nLine2")
|
||||||
luaunit.assertEquals(editor:getText(), "Line1Line2")
|
luaunit.assertEquals(editor:getText(), "Line1Line2")
|
||||||
end
|
end
|
||||||
@@ -161,7 +163,7 @@ function TestTextEditorSanitization:test_allowNewlines_explicit_false()
|
|||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
multiline = true,
|
multiline = true,
|
||||||
allowNewlines = false
|
allowNewlines = false,
|
||||||
})
|
})
|
||||||
editor:setText("Line1\nLine2")
|
editor:setText("Line1\nLine2")
|
||||||
luaunit.assertEquals(editor:getText(), "Line1Line2")
|
luaunit.assertEquals(editor:getText(), "Line1Line2")
|
||||||
@@ -170,7 +172,7 @@ end
|
|||||||
-- === Tab Handling ===
|
-- === Tab Handling ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_tabs_allowed_by_default()
|
function TestTextEditorSanitization:test_tabs_allowed_by_default()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello\tWorld")
|
editor:setText("Hello\tWorld")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello\tWorld")
|
luaunit.assertEquals(editor:getText(), "Hello\tWorld")
|
||||||
end
|
end
|
||||||
@@ -178,7 +180,7 @@ end
|
|||||||
function TestTextEditorSanitization:test_tabs_removed_when_disabled()
|
function TestTextEditorSanitization:test_tabs_removed_when_disabled()
|
||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
allowTabs = false
|
allowTabs = false,
|
||||||
})
|
})
|
||||||
editor:setText("Hello\tWorld")
|
editor:setText("Hello\tWorld")
|
||||||
luaunit.assertEquals(editor:getText(), "HelloWorld")
|
luaunit.assertEquals(editor:getText(), "HelloWorld")
|
||||||
@@ -193,7 +195,7 @@ function TestTextEditorSanitization:test_custom_sanitizer_used()
|
|||||||
|
|
||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
customSanitizer = customSanitizer
|
customSanitizer = customSanitizer,
|
||||||
})
|
})
|
||||||
editor:setText("hello world")
|
editor:setText("hello world")
|
||||||
luaunit.assertEquals(editor:getText(), "HELLO WORLD")
|
luaunit.assertEquals(editor:getText(), "HELLO WORLD")
|
||||||
@@ -207,7 +209,7 @@ function TestTextEditorSanitization:test_custom_sanitizer_with_control_chars()
|
|||||||
|
|
||||||
local editor = self:_createEditor({
|
local editor = self:_createEditor({
|
||||||
editable = true,
|
editable = true,
|
||||||
customSanitizer = customSanitizer
|
customSanitizer = customSanitizer,
|
||||||
})
|
})
|
||||||
editor:setText("Hello\x00World\x01")
|
editor:setText("Hello\x00World\x01")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello*World*")
|
luaunit.assertEquals(editor:getText(), "Hello*World*")
|
||||||
@@ -216,19 +218,19 @@ end
|
|||||||
-- === Unicode and Special Characters ===
|
-- === Unicode and Special Characters ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_unicode_preserved()
|
function TestTextEditorSanitization:test_unicode_preserved()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello 世界 🌍")
|
editor:setText("Hello 世界 🌍")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello 世界 🌍")
|
luaunit.assertEquals(editor:getText(), "Hello 世界 🌍")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_emoji_preserved()
|
function TestTextEditorSanitization:test_emoji_preserved()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("😀😃😄😁")
|
editor:setText("😀😃😄😁")
|
||||||
luaunit.assertEquals(editor:getText(), "😀😃😄😁")
|
luaunit.assertEquals(editor:getText(), "😀😃😄😁")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_special_chars_preserved()
|
function TestTextEditorSanitization:test_special_chars_preserved()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
editor:setText("!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
||||||
luaunit.assertEquals(editor:getText(), "!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
luaunit.assertEquals(editor:getText(), "!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
||||||
end
|
end
|
||||||
@@ -236,25 +238,25 @@ end
|
|||||||
-- === Edge Cases ===
|
-- === Edge Cases ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_empty_string()
|
function TestTextEditorSanitization:test_empty_string()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("")
|
editor:setText("")
|
||||||
luaunit.assertEquals(editor:getText(), "")
|
luaunit.assertEquals(editor:getText(), "")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_only_control_characters()
|
function TestTextEditorSanitization:test_only_control_characters()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("\x00\x01\x02\x03")
|
editor:setText("\x00\x01\x02\x03")
|
||||||
luaunit.assertEquals(editor:getText(), "")
|
luaunit.assertEquals(editor:getText(), "")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_nil_text()
|
function TestTextEditorSanitization:test_nil_text()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText(nil)
|
editor:setText(nil)
|
||||||
luaunit.assertEquals(editor:getText(), "")
|
luaunit.assertEquals(editor:getText(), "")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_very_long_text_with_control_chars()
|
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)
|
local longText = string.rep("Hello\x00World", 100)
|
||||||
editor:setText(longText)
|
editor:setText(longText)
|
||||||
luaunit.assertStrContains(editor:getText(), "Hello")
|
luaunit.assertStrContains(editor:getText(), "Hello")
|
||||||
@@ -263,7 +265,7 @@ function TestTextEditorSanitization:test_very_long_text_with_control_chars()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_mixed_valid_and_invalid()
|
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")
|
editor:setText("Valid\x00Text\x01With\x02Control\x03Chars")
|
||||||
luaunit.assertEquals(editor:getText(), "ValidTextWithControlChars")
|
luaunit.assertEquals(editor:getText(), "ValidTextWithControlChars")
|
||||||
end
|
end
|
||||||
@@ -271,13 +273,13 @@ end
|
|||||||
-- === Whitespace Handling ===
|
-- === Whitespace Handling ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_spaces_preserved()
|
function TestTextEditorSanitization:test_spaces_preserved()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello World")
|
editor:setText("Hello World")
|
||||||
luaunit.assertEquals(editor:getText(), "Hello World")
|
luaunit.assertEquals(editor:getText(), "Hello World")
|
||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_leading_trailing_spaces_preserved()
|
function TestTextEditorSanitization:test_leading_trailing_spaces_preserved()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText(" Hello World ")
|
editor:setText(" Hello World ")
|
||||||
luaunit.assertEquals(editor:getText(), " Hello World ")
|
luaunit.assertEquals(editor:getText(), " Hello World ")
|
||||||
end
|
end
|
||||||
@@ -285,7 +287,7 @@ end
|
|||||||
-- === Integration Tests ===
|
-- === Integration Tests ===
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_cursor_position_after_sanitization()
|
function TestTextEditorSanitization:test_cursor_position_after_sanitization()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello")
|
editor:setText("Hello")
|
||||||
editor:insertText("\x00World", 5)
|
editor:insertText("\x00World", 5)
|
||||||
-- Cursor should be at end of "HelloWorld" = position 10
|
-- Cursor should be at end of "HelloWorld" = position 10
|
||||||
@@ -293,7 +295,7 @@ function TestTextEditorSanitization:test_cursor_position_after_sanitization()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TestTextEditorSanitization:test_multiple_operations()
|
function TestTextEditorSanitization:test_multiple_operations()
|
||||||
local editor = self:_createEditor({editable = true})
|
local editor = self:_createEditor({ editable = true })
|
||||||
editor:setText("Hello")
|
editor:setText("Hello")
|
||||||
editor:insertText(" ", 5)
|
editor:insertText(" ", 5)
|
||||||
editor:insertText("World\x00", 6)
|
editor:insertText("World\x00", 6)
|
||||||
|
|||||||
302
testing/__tests__/theme_core_test.lua
Normal file
302
testing/__tests__/theme_core_test.lua
Normal file
@@ -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
|
||||||
@@ -37,7 +37,7 @@ end
|
|||||||
|
|
||||||
function TestThemeValidation:test_validate_minimal_valid_theme()
|
function TestThemeValidation:test_validate_minimal_valid_theme()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme"
|
name = "Test Theme",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -46,7 +46,7 @@ end
|
|||||||
|
|
||||||
function TestThemeValidation:test_validate_theme_with_empty_name()
|
function TestThemeValidation:test_validate_theme_with_empty_name()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = ""
|
name = "",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -55,7 +55,7 @@ end
|
|||||||
|
|
||||||
function TestThemeValidation:test_validate_theme_with_non_string_name()
|
function TestThemeValidation:test_validate_theme_with_non_string_name()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = 123
|
name = 123,
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -69,8 +69,8 @@ function TestThemeValidation:test_validate_valid_colors()
|
|||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = {
|
colors = {
|
||||||
primary = Color.new(1, 0, 0, 1),
|
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)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -81,8 +81,8 @@ function TestThemeValidation:test_validate_colors_with_hex()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = {
|
colors = {
|
||||||
primary = "#FF0000"
|
primary = "#FF0000",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -94,8 +94,8 @@ function TestThemeValidation:test_validate_colors_with_named()
|
|||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = {
|
colors = {
|
||||||
primary = "red",
|
primary = "red",
|
||||||
secondary = "blue"
|
secondary = "blue",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -106,8 +106,8 @@ function TestThemeValidation:test_validate_invalid_color()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = {
|
colors = {
|
||||||
primary = "not-a-color"
|
primary = "not-a-color",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -118,7 +118,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_colors_non_table()
|
function TestThemeValidation:test_validate_colors_non_table()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = "should be a table"
|
colors = "should be a table",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -129,8 +129,8 @@ function TestThemeValidation:test_validate_color_with_non_string_name()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
colors = {
|
colors = {
|
||||||
[123] = Color.new(1, 0, 0, 1)
|
[123] = Color.new(1, 0, 0, 1),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -144,8 +144,8 @@ function TestThemeValidation:test_validate_valid_fonts()
|
|||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
fonts = {
|
fonts = {
|
||||||
default = "path/to/font.ttf",
|
default = "path/to/font.ttf",
|
||||||
heading = "path/to/heading.ttf"
|
heading = "path/to/heading.ttf",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -155,7 +155,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_fonts_non_table()
|
function TestThemeValidation:test_validate_fonts_non_table()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
fonts = "should be a table"
|
fonts = "should be a table",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -166,8 +166,8 @@ function TestThemeValidation:test_validate_font_with_non_string_path()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
fonts = {
|
fonts = {
|
||||||
default = 123
|
default = 123,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -178,8 +178,8 @@ function TestThemeValidation:test_validate_font_with_non_string_name()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
fonts = {
|
fonts = {
|
||||||
[123] = "path/to/font.ttf"
|
[123] = "path/to/font.ttf",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -193,9 +193,9 @@ function TestThemeValidation:test_validate_valid_component()
|
|||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png"
|
atlas = "path/to/button.png",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -208,9 +208,9 @@ function TestThemeValidation:test_validate_component_with_insets()
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
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)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -223,9 +223,9 @@ function TestThemeValidation:test_validate_component_with_missing_inset()
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
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)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -239,9 +239,9 @@ function TestThemeValidation:test_validate_component_with_negative_inset()
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
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)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -257,14 +257,14 @@ function TestThemeValidation:test_validate_component_with_states()
|
|||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
states = {
|
states = {
|
||||||
hover = {
|
hover = {
|
||||||
atlas = "path/to/button_hover.png"
|
atlas = "path/to/button_hover.png",
|
||||||
},
|
},
|
||||||
pressed = {
|
pressed = {
|
||||||
atlas = "path/to/button_pressed.png"
|
atlas = "path/to/button_pressed.png",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -278,10 +278,10 @@ function TestThemeValidation:test_validate_component_with_invalid_state()
|
|||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
states = {
|
states = {
|
||||||
hover = "should be a table"
|
hover = "should be a table",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -294,9 +294,9 @@ function TestThemeValidation:test_validate_component_with_scaleCorners()
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
scaleCorners = 2.0
|
scaleCorners = 2.0,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -309,9 +309,9 @@ function TestThemeValidation:test_validate_component_with_invalid_scaleCorners()
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
scaleCorners = -1
|
scaleCorners = -1,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -325,9 +325,9 @@ function TestThemeValidation:test_validate_component_with_valid_scalingAlgorithm
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
scalingAlgorithm = "nearest"
|
scalingAlgorithm = "nearest",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -340,9 +340,9 @@ function TestThemeValidation:test_validate_component_with_invalid_scalingAlgorit
|
|||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
atlas = "path/to/button.png",
|
||||||
scalingAlgorithm = "invalid"
|
scalingAlgorithm = "invalid",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -352,7 +352,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_components_non_table()
|
function TestThemeValidation:test_validate_components_non_table()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
components = "should be a table"
|
components = "should be a table",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -364,7 +364,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_valid_multiplier()
|
function TestThemeValidation:test_validate_valid_multiplier()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = {width = 1.1, height = 1.2}
|
contentAutoSizingMultiplier = { width = 1.1, height = 1.2 },
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -374,7 +374,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_multiplier_with_only_width()
|
function TestThemeValidation:test_validate_multiplier_with_only_width()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = {width = 1.1}
|
contentAutoSizingMultiplier = { width = 1.1 },
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -384,7 +384,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_multiplier_non_table()
|
function TestThemeValidation:test_validate_multiplier_non_table()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = 1.5
|
contentAutoSizingMultiplier = 1.5,
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -394,7 +394,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_multiplier_with_non_number()
|
function TestThemeValidation:test_validate_multiplier_with_non_number()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = {width = "not a number"}
|
contentAutoSizingMultiplier = { width = "not a number" },
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -404,7 +404,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_multiplier_with_negative()
|
function TestThemeValidation:test_validate_multiplier_with_negative()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = {width = -1}
|
contentAutoSizingMultiplier = { width = -1 },
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -415,7 +415,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_multiplier_with_zero()
|
function TestThemeValidation:test_validate_multiplier_with_zero()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
contentAutoSizingMultiplier = {width = 0}
|
contentAutoSizingMultiplier = { width = 0 },
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
@@ -427,7 +427,7 @@ end
|
|||||||
function TestThemeValidation:test_validate_valid_global_atlas()
|
function TestThemeValidation:test_validate_valid_global_atlas()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test Theme",
|
name = "Test Theme",
|
||||||
atlas = "path/to/atlas.png"
|
atlas = "path/to/atlas.png",
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -439,9 +439,9 @@ end
|
|||||||
function TestThemeValidation:test_validate_unknown_field_strict()
|
function TestThemeValidation:test_validate_unknown_field_strict()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test 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.assertFalse(valid)
|
||||||
luaunit.assertTrue(#errors > 0)
|
luaunit.assertTrue(#errors > 0)
|
||||||
luaunit.assertStrContains(errors[1], "Unknown field")
|
luaunit.assertStrContains(errors[1], "Unknown field")
|
||||||
@@ -450,9 +450,9 @@ end
|
|||||||
function TestThemeValidation:test_validate_unknown_field_non_strict()
|
function TestThemeValidation:test_validate_unknown_field_non_strict()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = "Test 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.assertTrue(valid)
|
||||||
luaunit.assertEquals(#errors, 0)
|
luaunit.assertEquals(#errors, 0)
|
||||||
end
|
end
|
||||||
@@ -467,7 +467,7 @@ end
|
|||||||
|
|
||||||
function TestThemeValidation:test_sanitize_theme_without_name()
|
function TestThemeValidation:test_sanitize_theme_without_name()
|
||||||
local theme = {
|
local theme = {
|
||||||
colors = {primary = "red"}
|
colors = { primary = "red" },
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertEquals(sanitized.name, "Unnamed Theme")
|
luaunit.assertEquals(sanitized.name, "Unnamed Theme")
|
||||||
@@ -475,7 +475,7 @@ end
|
|||||||
|
|
||||||
function TestThemeValidation:test_sanitize_theme_with_non_string_name()
|
function TestThemeValidation:test_sanitize_theme_with_non_string_name()
|
||||||
local theme = {
|
local theme = {
|
||||||
name = 123
|
name = 123,
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertEquals(type(sanitized.name), "string")
|
luaunit.assertEquals(type(sanitized.name), "string")
|
||||||
@@ -486,8 +486,8 @@ function TestThemeValidation:test_sanitize_colors()
|
|||||||
name = "Test",
|
name = "Test",
|
||||||
colors = {
|
colors = {
|
||||||
valid = "red",
|
valid = "red",
|
||||||
invalid = "not-a-color"
|
invalid = "not-a-color",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertNotNil(sanitized.colors.valid)
|
luaunit.assertNotNil(sanitized.colors.valid)
|
||||||
@@ -498,8 +498,8 @@ function TestThemeValidation:test_sanitize_removes_non_string_color_names()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test",
|
name = "Test",
|
||||||
colors = {
|
colors = {
|
||||||
[123] = "red"
|
[123] = "red",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertNil(sanitized.colors[123])
|
luaunit.assertNil(sanitized.colors[123])
|
||||||
@@ -510,8 +510,8 @@ function TestThemeValidation:test_sanitize_fonts()
|
|||||||
name = "Test",
|
name = "Test",
|
||||||
fonts = {
|
fonts = {
|
||||||
default = "path/to/font.ttf",
|
default = "path/to/font.ttf",
|
||||||
invalid = 123
|
invalid = 123,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertNotNil(sanitized.fonts.default)
|
luaunit.assertNotNil(sanitized.fonts.default)
|
||||||
@@ -522,8 +522,8 @@ function TestThemeValidation:test_sanitize_preserves_components()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Test",
|
name = "Test",
|
||||||
components = {
|
components = {
|
||||||
button = {atlas = "path/to/button.png"}
|
button = { atlas = "path/to/button.png" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local sanitized = Theme.sanitizeTheme(theme)
|
local sanitized = Theme.sanitizeTheme(theme)
|
||||||
luaunit.assertNotNil(sanitized.components.button)
|
luaunit.assertNotNil(sanitized.components.button)
|
||||||
@@ -536,35 +536,35 @@ function TestThemeValidation:test_validate_complete_theme()
|
|||||||
local theme = {
|
local theme = {
|
||||||
name = "Complete Theme",
|
name = "Complete Theme",
|
||||||
atlas = "path/to/atlas.png",
|
atlas = "path/to/atlas.png",
|
||||||
contentAutoSizingMultiplier = {width = 1.05, height = 1.1},
|
contentAutoSizingMultiplier = { width = 1.05, height = 1.1 },
|
||||||
colors = {
|
colors = {
|
||||||
primary = Color.new(1, 0, 0, 1),
|
primary = Color.new(1, 0, 0, 1),
|
||||||
secondary = "#00FF00",
|
secondary = "#00FF00",
|
||||||
tertiary = "blue"
|
tertiary = "blue",
|
||||||
},
|
},
|
||||||
fonts = {
|
fonts = {
|
||||||
default = "path/to/font.ttf",
|
default = "path/to/font.ttf",
|
||||||
heading = "path/to/heading.ttf"
|
heading = "path/to/heading.ttf",
|
||||||
},
|
},
|
||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
atlas = "path/to/button.png",
|
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,
|
scaleCorners = 2,
|
||||||
scalingAlgorithm = "nearest",
|
scalingAlgorithm = "nearest",
|
||||||
states = {
|
states = {
|
||||||
hover = {
|
hover = {
|
||||||
atlas = "path/to/button_hover.png"
|
atlas = "path/to/button_hover.png",
|
||||||
},
|
},
|
||||||
pressed = {
|
pressed = {
|
||||||
atlas = "path/to/button_pressed.png"
|
atlas = "path/to/button_pressed.png",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
panel = {
|
panel = {
|
||||||
atlas = "path/to/panel.png"
|
atlas = "path/to/panel.png",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertTrue(valid)
|
luaunit.assertTrue(valid)
|
||||||
@@ -576,16 +576,16 @@ function TestThemeValidation:test_validate_theme_with_multiple_errors()
|
|||||||
name = "",
|
name = "",
|
||||||
colors = {
|
colors = {
|
||||||
invalid1 = "not-a-color",
|
invalid1 = "not-a-color",
|
||||||
invalid2 = 123
|
invalid2 = 123,
|
||||||
},
|
},
|
||||||
fonts = {
|
fonts = {
|
||||||
bad = 456
|
bad = 456,
|
||||||
},
|
},
|
||||||
components = {
|
components = {
|
||||||
button = {
|
button = {
|
||||||
insets = {left = -5} -- missing fields and negative
|
insets = { left = -5 }, -- missing fields and negative
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
local valid, errors = Theme.validateTheme(theme)
|
local valid, errors = Theme.validateTheme(theme)
|
||||||
luaunit.assertFalse(valid)
|
luaunit.assertFalse(valid)
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ function TestUnitsResolveSpacing:testResolveSpacingAllSides()
|
|||||||
top = "10px",
|
top = "10px",
|
||||||
right = "20px",
|
right = "20px",
|
||||||
bottom = "30px",
|
bottom = "30px",
|
||||||
left = "40px"
|
left = "40px",
|
||||||
}
|
}
|
||||||
local result = Units.resolveSpacing(spacing, 800, 600)
|
local result = Units.resolveSpacing(spacing, 800, 600)
|
||||||
luaunit.assertEquals(result.top, 10)
|
luaunit.assertEquals(result.top, 10)
|
||||||
@@ -263,7 +263,7 @@ end
|
|||||||
function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontal()
|
function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontal()
|
||||||
local spacing = {
|
local spacing = {
|
||||||
vertical = "10px",
|
vertical = "10px",
|
||||||
horizontal = "20px"
|
horizontal = "20px",
|
||||||
}
|
}
|
||||||
local result = Units.resolveSpacing(spacing, 800, 600)
|
local result = Units.resolveSpacing(spacing, 800, 600)
|
||||||
luaunit.assertEquals(result.top, 10)
|
luaunit.assertEquals(result.top, 10)
|
||||||
@@ -275,7 +275,7 @@ end
|
|||||||
function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontalNumbers()
|
function TestUnitsResolveSpacing:testResolveSpacingVerticalHorizontalNumbers()
|
||||||
local spacing = {
|
local spacing = {
|
||||||
vertical = 10,
|
vertical = 10,
|
||||||
horizontal = 20
|
horizontal = 20,
|
||||||
}
|
}
|
||||||
local result = Units.resolveSpacing(spacing, 800, 600)
|
local result = Units.resolveSpacing(spacing, 800, 600)
|
||||||
luaunit.assertEquals(result.top, 10)
|
luaunit.assertEquals(result.top, 10)
|
||||||
@@ -289,7 +289,7 @@ function TestUnitsResolveSpacing:testResolveSpacingMixedPercentage()
|
|||||||
top = "10%",
|
top = "10%",
|
||||||
right = "5%",
|
right = "5%",
|
||||||
bottom = "10%",
|
bottom = "10%",
|
||||||
left = "5%"
|
left = "5%",
|
||||||
}
|
}
|
||||||
local result = Units.resolveSpacing(spacing, 800, 600)
|
local result = Units.resolveSpacing(spacing, 800, 600)
|
||||||
luaunit.assertEquals(result.top, 60) -- 10% of 600 (height)
|
luaunit.assertEquals(result.top, 60) -- 10% of 600 (height)
|
||||||
@@ -303,7 +303,7 @@ function TestUnitsResolveSpacing:testResolveSpacingOverride()
|
|||||||
local spacing = {
|
local spacing = {
|
||||||
vertical = "10px",
|
vertical = "10px",
|
||||||
horizontal = "20px",
|
horizontal = "20px",
|
||||||
top = "50px"
|
top = "50px",
|
||||||
}
|
}
|
||||||
local result = Units.resolveSpacing(spacing, 800, 600)
|
local result = Units.resolveSpacing(spacing, 800, 600)
|
||||||
luaunit.assertEquals(result.top, 50) -- Overridden
|
luaunit.assertEquals(result.top, 50) -- Overridden
|
||||||
|
|||||||
@@ -286,10 +286,10 @@ function TestFontUtils:testResolveFontPath_ThemeFont()
|
|||||||
getTheme = function()
|
getTheme = function()
|
||||||
return {
|
return {
|
||||||
fonts = {
|
fonts = {
|
||||||
mainFont = "themes/fonts/main.ttf"
|
mainFont = "themes/fonts/main.ttf",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
end,
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local result = utils.resolveFontPath("mainFont", "button", mockThemeManager)
|
local result = utils.resolveFontPath("mainFont", "button", mockThemeManager)
|
||||||
@@ -301,9 +301,9 @@ function TestFontUtils:testResolveFontPath_ThemeFontNotFound()
|
|||||||
local mockThemeManager = {
|
local mockThemeManager = {
|
||||||
getTheme = function()
|
getTheme = function()
|
||||||
return {
|
return {
|
||||||
fonts = {}
|
fonts = {},
|
||||||
}
|
}
|
||||||
end
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Should fall back to treating it as a direct path
|
-- Should fall back to treating it as a direct path
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ local testFiles = {
|
|||||||
"testing/__tests__/color_validation_test.lua",
|
"testing/__tests__/color_validation_test.lua",
|
||||||
"testing/__tests__/texteditor_sanitization_test.lua",
|
"testing/__tests__/texteditor_sanitization_test.lua",
|
||||||
"testing/__tests__/theme_validation_test.lua",
|
"testing/__tests__/theme_validation_test.lua",
|
||||||
|
"testing/__tests__/theme_core_test.lua",
|
||||||
"testing/__tests__/units_test.lua",
|
"testing/__tests__/units_test.lua",
|
||||||
|
"testing/__tests__/layout_engine_test.lua",
|
||||||
|
"testing/__tests__/element_test.lua",
|
||||||
}
|
}
|
||||||
|
|
||||||
local success = true
|
local success = true
|
||||||
|
|||||||
Reference in New Issue
Block a user