element di migration
This commit is contained in:
37
FlexLove.lua
37
FlexLove.lua
@@ -10,6 +10,17 @@ local Units = req("Units")
|
||||
local Context = req("Context")
|
||||
local StateManager = req("StateManager")
|
||||
local ErrorHandler = req("ErrorHandler")
|
||||
local ImageRenderer = req("ImageRenderer")
|
||||
local NinePatch = req("NinePatch")
|
||||
local RoundedRect = req("RoundedRect")
|
||||
local ImageCache = req("ImageCache")
|
||||
local Grid = req("Grid")
|
||||
local InputEvent = req("InputEvent")
|
||||
local TextEditor = req("TextEditor")
|
||||
local LayoutEngine = req("LayoutEngine")
|
||||
local Renderer = req("Renderer")
|
||||
local EventHandler = req("EventHandler")
|
||||
local ScrollManager = req("ScrollManager")
|
||||
---@type Element
|
||||
local Element = req("Element")
|
||||
|
||||
@@ -22,6 +33,28 @@ local Color = req("Color")
|
||||
local Theme = req("Theme")
|
||||
local enums = utils.enums
|
||||
|
||||
Element.defaultDependencies = {
|
||||
Context = Context,
|
||||
Theme = Theme,
|
||||
Color = Color,
|
||||
Units = Units,
|
||||
Blur = Blur,
|
||||
ImageRenderer = ImageRenderer,
|
||||
NinePatch = NinePatch,
|
||||
RoundedRect = RoundedRect,
|
||||
ImageCache = ImageCache,
|
||||
utils = utils,
|
||||
Grid = Grid,
|
||||
InputEvent = InputEvent,
|
||||
StateManager = StateManager,
|
||||
TextEditor = TextEditor,
|
||||
LayoutEngine = LayoutEngine,
|
||||
Renderer = Renderer,
|
||||
EventHandler = EventHandler,
|
||||
ScrollManager = ScrollManager,
|
||||
ErrorHandler = ErrorHandler,
|
||||
}
|
||||
|
||||
---@class FlexLove
|
||||
local flexlove = Context
|
||||
|
||||
@@ -630,7 +663,7 @@ function flexlove.new(props)
|
||||
|
||||
-- If not in immediate mode, use standard Element.new
|
||||
if not flexlove._immediateMode then
|
||||
return Element.new(props)
|
||||
return Element.new(props, Element.defaultDependencies)
|
||||
end
|
||||
|
||||
-- Auto-begin frame if not manually started (convenience feature)
|
||||
@@ -656,7 +689,7 @@ function flexlove.new(props)
|
||||
props._scrollY = state._scrollY or 0
|
||||
|
||||
-- Create the element
|
||||
local element = Element.new(props)
|
||||
local element = Element.new(props, Element.defaultDependencies)
|
||||
|
||||
-- Bind persistent state to element (ImmediateModeState)
|
||||
-- Restore event handler state
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
94
scripts/add_dependency_injection.lua
Executable file
94
scripts/add_dependency_injection.lua
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env lua
|
||||
-- Script to add dependency injection to Element.lua
|
||||
|
||||
local function read_file(path)
|
||||
local f = io.open(path, "r")
|
||||
if not f then
|
||||
error("Could not open file: " .. path)
|
||||
end
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
local function write_file(path, content)
|
||||
local f = io.open(path, "w")
|
||||
if not f then
|
||||
error("Could not write file: " .. path)
|
||||
end
|
||||
f:write(content)
|
||||
f:close()
|
||||
end
|
||||
|
||||
local element_path = "modules/Element.lua"
|
||||
print("Reading " .. element_path)
|
||||
local content = read_file(element_path)
|
||||
|
||||
-- Step 1: Add defaultDependencies after Element table definition
|
||||
print("Step 1: Adding default dependencies...")
|
||||
local element_def = "local Element = {}\nElement.__index = Element\n"
|
||||
local new_element_def = [[local Element = {}
|
||||
Element.__index = Element
|
||||
|
||||
-- Default dependencies (can be overridden for testing)
|
||||
Element.defaultDependencies = {
|
||||
Context = Context,
|
||||
Theme = Theme,
|
||||
Color = Color,
|
||||
Units = Units,
|
||||
Blur = Blur,
|
||||
ImageRenderer = ImageRenderer,
|
||||
NinePatch = NinePatch,
|
||||
RoundedRect = RoundedRect,
|
||||
ImageCache = ImageCache,
|
||||
utils = utils,
|
||||
Grid = Grid,
|
||||
InputEvent = InputEvent,
|
||||
StateManager = StateManager,
|
||||
TextEditor = TextEditor,
|
||||
LayoutEngine = LayoutEngine,
|
||||
Renderer = Renderer,
|
||||
EventHandler = EventHandler,
|
||||
ScrollManager = ScrollManager,
|
||||
ErrorHandler = ErrorHandler,
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
if not content:find("Element.defaultDependencies") then
|
||||
content = content:gsub(element_def:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"), new_element_def)
|
||||
print(" ✓ Added defaultDependencies")
|
||||
else
|
||||
print(" - Already has defaultDependencies")
|
||||
end
|
||||
|
||||
-- Step 2: Update Element.new signature
|
||||
print("Step 2: Updating Element.new signature...")
|
||||
local old_signature = "function Element.new%(props%)\n local self = setmetatable%({}, Element%)"
|
||||
local new_signature = [[function Element.new(props, deps)
|
||||
local self = setmetatable({}, Element)
|
||||
|
||||
-- Initialize dependencies (allow injection for testing)
|
||||
self._deps = deps or Element.defaultDependencies]]
|
||||
|
||||
if not content:find("self._deps") then
|
||||
content = content:gsub(old_signature, new_signature)
|
||||
print(" ✓ Updated signature and added deps initialization")
|
||||
else
|
||||
print(" - Already has deps initialization")
|
||||
end
|
||||
|
||||
-- Step 3: Update comment for Element.new
|
||||
print("Step 3: Updating function documentation...")
|
||||
content = content:gsub(
|
||||
"%-%-%-@param props ElementProps\n%-%-%-@return Element",
|
||||
"---@param props ElementProps\n---@param deps table? Optional dependency injection (defaults to Element.defaultDependencies)\n---@return Element"
|
||||
)
|
||||
|
||||
print("Writing changes to " .. element_path)
|
||||
write_file(element_path, content)
|
||||
print("✓ Done!")
|
||||
print("\nNext steps:")
|
||||
print("1. Run tests to ensure nothing broke")
|
||||
print("2. Gradually replace module references with self._deps.ModuleName")
|
||||
print("3. Create mock dependencies for testing")
|
||||
186
testing/__tests__/element_di_test.lua
Normal file
186
testing/__tests__/element_di_test.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
-- Test that demonstrates dependency injection with mocked dependencies
|
||||
package.path = package.path .. ";../../?.lua;../?.lua"
|
||||
local luaunit = require("testing.luaunit")
|
||||
local Element = require("modules.Element")
|
||||
|
||||
-- Mock dependencies
|
||||
local function createMockDeps()
|
||||
return {
|
||||
Context = {
|
||||
_immediateMode = false,
|
||||
defaultTheme = "test",
|
||||
scaleFactors = { x = 1, y = 1 },
|
||||
registerElement = function() end,
|
||||
topElements = {},
|
||||
},
|
||||
Theme = {
|
||||
Manager = {
|
||||
new = function()
|
||||
return {
|
||||
getThemeState = function() return "normal" end,
|
||||
update = function() end,
|
||||
}
|
||||
end
|
||||
},
|
||||
},
|
||||
Color = {
|
||||
new = function(r, g, b, a)
|
||||
return { r = r or 0, g = g or 0, b = b or 0, a = a or 1 }
|
||||
end,
|
||||
},
|
||||
Units = {
|
||||
getViewport = function() return 1920, 1080 end,
|
||||
parse = function(value)
|
||||
if type(value) == "number" then
|
||||
return value, "px"
|
||||
end
|
||||
return 100, "px"
|
||||
end,
|
||||
},
|
||||
Blur = {},
|
||||
ImageRenderer = {},
|
||||
NinePatch = {},
|
||||
RoundedRect = {},
|
||||
ImageCache = {},
|
||||
utils = {
|
||||
enums = {
|
||||
Positioning = { RELATIVE = "relative", ABSOLUTE = "absolute", FLEX = "flex", GRID = "grid" },
|
||||
FlexDirection = { HORIZONTAL = "horizontal", VERTICAL = "vertical" },
|
||||
JustifyContent = { FLEX_START = "flex-start", FLEX_END = "flex-end", CENTER = "center" },
|
||||
AlignContent = { STRETCH = "stretch", FLEX_START = "flex-start", FLEX_END = "flex-end" },
|
||||
AlignItems = { STRETCH = "stretch", FLEX_START = "flex-start", FLEX_END = "flex-end", CENTER = "center" },
|
||||
TextAlign = { LEFT = "left", CENTER = "center", RIGHT = "right" },
|
||||
AlignSelf = { AUTO = "auto", STRETCH = "stretch", FLEX_START = "flex-start" },
|
||||
JustifySelf = { AUTO = "auto", FLEX_START = "flex-start", FLEX_END = "flex-end" },
|
||||
FlexWrap = { NOWRAP = "nowrap", WRAP = "wrap" },
|
||||
},
|
||||
validateEnum = function() end,
|
||||
validateRange = function() end,
|
||||
validateType = function() end,
|
||||
resolveTextSizePreset = function(size) return size end,
|
||||
getModifiers = function() return false, false, false, false end,
|
||||
},
|
||||
Grid = {},
|
||||
InputEvent = {
|
||||
new = function() return {} end,
|
||||
},
|
||||
StateManager = {
|
||||
generateID = function() return "test-id" end,
|
||||
},
|
||||
TextEditor = {
|
||||
new = function()
|
||||
return {
|
||||
initialize = function() end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
LayoutEngine = {
|
||||
new = function()
|
||||
return {
|
||||
initialize = function() end,
|
||||
calculateLayout = function() end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
Renderer = {
|
||||
new = function()
|
||||
return {
|
||||
initialize = function() end,
|
||||
draw = function() end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
EventHandler = {
|
||||
new = function()
|
||||
return {
|
||||
initialize = function() end,
|
||||
getState = function() return {} end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
ScrollManager = {
|
||||
new = function()
|
||||
return {
|
||||
initialize = function() end,
|
||||
}
|
||||
end,
|
||||
},
|
||||
ErrorHandler = {
|
||||
handle = function() end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
TestDependencyInjection = {}
|
||||
|
||||
function TestDependencyInjection:test_element_with_mocked_dependencies()
|
||||
-- Create mock dependencies
|
||||
local mockDeps = createMockDeps()
|
||||
|
||||
-- Track if Context.registerElement was called
|
||||
local registerCalled = false
|
||||
mockDeps.Context.registerElement = function()
|
||||
registerCalled = true
|
||||
end
|
||||
|
||||
-- Create element with mocked dependencies
|
||||
local element = Element.new({
|
||||
id = "test-element",
|
||||
width = 100,
|
||||
height = 100,
|
||||
x = 0,
|
||||
y = 0,
|
||||
}, mockDeps)
|
||||
|
||||
-- Verify element was created
|
||||
luaunit.assertNotNil(element)
|
||||
luaunit.assertEquals(element.id, "test-element")
|
||||
luaunit.assertEquals(element.width, 100)
|
||||
luaunit.assertEquals(element.height, 100)
|
||||
|
||||
-- Verify the element is using our mocked dependencies
|
||||
luaunit.assertEquals(element._deps, mockDeps)
|
||||
|
||||
-- Verify Context.registerElement was called
|
||||
luaunit.assertTrue(registerCalled)
|
||||
end
|
||||
|
||||
function TestDependencyInjection:test_element_without_deps_should_error()
|
||||
-- Element.new now requires deps parameter
|
||||
local success, err = pcall(function()
|
||||
Element.new({
|
||||
id = "test-element",
|
||||
width = 100,
|
||||
height = 100,
|
||||
})
|
||||
end)
|
||||
|
||||
luaunit.assertFalse(success)
|
||||
luaunit.assertNotNil(err)
|
||||
luaunit.assertStrContains(err, "deps")
|
||||
end
|
||||
|
||||
function TestDependencyInjection:test_can_mock_specific_module_behavior()
|
||||
local mockDeps = createMockDeps()
|
||||
|
||||
-- Mock Units.parse to return specific values
|
||||
local parseCallCount = 0
|
||||
mockDeps.Units.parse = function(value)
|
||||
parseCallCount = parseCallCount + 1
|
||||
return 200, "px" -- Always return 200px
|
||||
end
|
||||
|
||||
-- Create element (this will call Units.parse)
|
||||
local element = Element.new({
|
||||
id = "test",
|
||||
width = "50%", -- This should be parsed by our mock
|
||||
height = 100,
|
||||
x = 0,
|
||||
y = 0,
|
||||
}, mockDeps)
|
||||
|
||||
-- Verify our mock was called
|
||||
luaunit.assertTrue(parseCallCount > 0, "Units.parse should have been called")
|
||||
end
|
||||
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
Reference in New Issue
Block a user