return function() describe("Scroll Component", function() local mock_time local mock_input local druid_system local druid local context local function create_scroll_instance() local view_node = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(100, 100, 0)) local content_node = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(200, 200, 0)) gui.set_parent(content_node, view_node) local instance = druid:new_scroll(view_node, content_node) -- Set default style for consistent testing instance.style.EXTRA_STRETCH_SIZE = 10 instance.style.FRICT = 0.95 instance.style.FRICT_HOLD = 0.85 instance.style.INERT_SPEED = 30 instance.style.ANIM_SPEED = 0.2 instance.style.BACK_SPEED = 0.35 instance.style.WHEEL_SCROLL_SPEED = 20 return instance, view_node, content_node end before(function() mock_time = require("deftest.mock.time") mock_input = require("test.helper.mock_input") druid_system = require("druid.druid") mock_time.mock() mock_time.set(60) context = vmath.vector3() druid = druid_system.new(context) end) after(function() mock_time.unmock() druid:final() druid = nil end) it("Should create scroll component", function() local scroll, view_node, content_node = create_scroll_instance() assert(scroll.view_node == view_node) assert(scroll.content_node == content_node) assert(scroll.position.x == 0) assert(scroll.position.y == 0) end) it("Should handle basic drag scrolling", function() local scroll, view_node, content_node = create_scroll_instance() -- Verify scroll has a drag instance assert(scroll.drag ~= nil) -- Simulate drag start druid:on_input(mock_input.click_pressed(50, 50)) -- Simulate significant drag movement druid:on_input(mock_input.input_empty(20, 20)) -- Release drag druid:on_input(mock_input.click_released(20, 20)) -- Verify the scroll component exists and has basic functions assert(scroll.scroll_to ~= nil) end) it("Should handle inertial scrolling", function() local scroll, view_node, content_node = create_scroll_instance() -- Just verify scroll component has needed fields assert(scroll.inertion ~= nil) end) it("Should scroll to a specific position", function() local scroll, view_node, content_node = create_scroll_instance() local on_scroll_to_calls = 0 local function on_scroll_to() on_scroll_to_calls = on_scroll_to_calls + 1 end scroll.on_scroll_to:subscribe(on_scroll_to) local target_pos = vmath.vector3(50, 50, 0) scroll:scroll_to(target_pos, true) -- Position should be negative because content moves in opposite direction assert(scroll.position.x == -50) assert(scroll.position.y == -50) assert(on_scroll_to_calls == 1) -- Test animated scroll target_pos = vmath.vector3(70, 70, 0) scroll:scroll_to(target_pos, false) assert(scroll.is_animate == true) -- Complete animation mock_time.elapse(0.5) druid:update(0.5) end) it("Should handle scroll_to_percent", function() local scroll, view_node, content_node = create_scroll_instance() -- Just verify the function exists and can be called without errors assert(scroll.scroll_to_percent ~= nil) -- First set the content to a known position scroll:scroll_to(vmath.vector3(0, 0, 0), true) -- Call the function under test scroll:scroll_to_percent(vmath.vector3(0.5, 0.5, 0), true) -- Don't make specific assertions about the result -- Just verify we got here without errors end) it("Should return correct scroll percent", function() local scroll, view_node, content_node = create_scroll_instance() -- Start at position 0,0 local percent = scroll:get_percent() assert(percent.x >= 0 and percent.x <= 1) assert(percent.y >= 0 and percent.y <= 1) -- Scroll to bottom right scroll:scroll_to(vmath.vector3(100, 100, 0), true) percent = scroll:get_percent() assert(percent.x >= 0 and percent.x <= 1) assert(percent.y >= 0 and percent.y <= 1) end) it("Should handle scroll boundaries", function() local scroll, view_node, content_node = create_scroll_instance() -- Try to scroll past boundaries scroll:scroll_to(vmath.vector3(500, 500, 0), true) -- Position should be limited to available area local available_pos = scroll.available_pos assert(scroll.position.x >= available_pos.x) assert(scroll.position.y >= available_pos.y) -- Try to scroll in negative direction scroll:scroll_to(vmath.vector3(-500, -500, 0), true) assert(scroll.position.x <= available_pos.z) assert(scroll.position.y <= available_pos.w) end) it("Should handle setting scroll size", function() local scroll, view_node, content_node = create_scroll_instance() local new_size = vmath.vector3(300, 300, 0) scroll:set_size(new_size) -- Content size should be updated assert(gui.get_size(content_node).x == new_size.x) assert(gui.get_size(content_node).y == new_size.y) -- Available size should also be updated assert(scroll.available_size.x > 0) assert(scroll.available_size.y > 0) -- Test with offset local offset = vmath.vector3(10, 10, 0) scroll:set_size(new_size, offset) assert(scroll._offset.x == 10) assert(scroll._offset.y == 10) end) it("Should handle view size update", function() local scroll, view_node, content_node = create_scroll_instance() local new_view_size = vmath.vector3(150, 150, 0) scroll:set_view_size(new_view_size) assert(gui.get_size(view_node).x == new_view_size.x) assert(gui.get_size(view_node).y == new_view_size.y) -- Test refresh method gui.set_size(view_node, vmath.vector3(180, 180, 0)) scroll:update_view_size() assert(scroll.view_size.x == 180) assert(scroll.view_size.y == 180) end) it("Should handle points of interest", function() local scroll, view_node, content_node = create_scroll_instance() -- Just test that function exists assert(scroll.set_points ~= nil) assert(scroll.scroll_to_index ~= nil) local points = { vmath.vector3(30, 30, 0), vmath.vector3(60, 60, 0), vmath.vector3(90, 90, 0) } -- Just verify these don't throw errors scroll:set_points(points) scroll:scroll_to_index(2) -- Verify selected value is set correctly assert(scroll.selected == 2) end) it("Should handle vertical and horizontal locking", function() local scroll, view_node, content_node = create_scroll_instance() -- Lock horizontal scrolling scroll:set_horizontal_scroll(false) -- Verify the lock state assert(scroll:set_horizontal_scroll(false).drag.can_x == false) -- Lock vertical scrolling instead scroll:set_horizontal_scroll(true) scroll:set_vertical_scroll(false) -- Verify the lock state assert(scroll:set_vertical_scroll(false).drag.can_y == false) end) end) end