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 Context = req("Context")
|
||||||
local StateManager = req("StateManager")
|
local StateManager = req("StateManager")
|
||||||
local ErrorHandler = req("ErrorHandler")
|
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
|
---@type Element
|
||||||
local Element = req("Element")
|
local Element = req("Element")
|
||||||
|
|
||||||
@@ -22,6 +33,28 @@ local Color = req("Color")
|
|||||||
local Theme = req("Theme")
|
local Theme = req("Theme")
|
||||||
local enums = utils.enums
|
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
|
---@class FlexLove
|
||||||
local flexlove = Context
|
local flexlove = Context
|
||||||
|
|
||||||
@@ -630,7 +663,7 @@ function flexlove.new(props)
|
|||||||
|
|
||||||
-- If not in immediate mode, use standard Element.new
|
-- If not in immediate mode, use standard Element.new
|
||||||
if not flexlove._immediateMode then
|
if not flexlove._immediateMode then
|
||||||
return Element.new(props)
|
return Element.new(props, Element.defaultDependencies)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Auto-begin frame if not manually started (convenience feature)
|
-- Auto-begin frame if not manually started (convenience feature)
|
||||||
@@ -656,7 +689,7 @@ function flexlove.new(props)
|
|||||||
props._scrollY = state._scrollY or 0
|
props._scrollY = state._scrollY or 0
|
||||||
|
|
||||||
-- Create the element
|
-- Create the element
|
||||||
local element = Element.new(props)
|
local element = Element.new(props, Element.defaultDependencies)
|
||||||
|
|
||||||
-- Bind persistent state to element (ImmediateModeState)
|
-- Bind persistent state to element (ImmediateModeState)
|
||||||
-- Restore event handler state
|
-- 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