better input field keyboard handling
This commit is contained in:
@@ -11,7 +11,16 @@ local Gui = FlexLove.Gui
|
||||
TestEventSystem = {}
|
||||
|
||||
function TestEventSystem:setUp()
|
||||
-- Initialize GUI before each test
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
love.window.setMode(1920, 1080)
|
||||
Gui.resize(1920, 1080) -- Recalculate scale factors after setMode
|
||||
@@ -27,6 +36,8 @@ function TestEventSystem:tearDown()
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
end
|
||||
|
||||
-- Test 1: Event object structure
|
||||
|
||||
367
testing/__tests__/16_event_system_tests.lua.bak
Normal file
367
testing/__tests__/16_event_system_tests.lua.bak
Normal file
@@ -0,0 +1,367 @@
|
||||
-- Event System Tests
|
||||
-- Tests for the enhanced callback system with InputEvent objects
|
||||
|
||||
package.path = package.path .. ";?.lua"
|
||||
|
||||
local lu = require("testing.luaunit")
|
||||
require("testing.loveStub") -- Required to mock LOVE functions
|
||||
local FlexLove = require("FlexLove")
|
||||
local Gui = FlexLove.Gui
|
||||
|
||||
TestEventSystem = {}
|
||||
|
||||
function TestEventSystem:setUp()
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
love.window.setMode(1920, 1080)
|
||||
Gui.resize(1920, 1080) -- Recalculate scale factors after setMode
|
||||
end
|
||||
|
||||
function TestEventSystem:tearDown()
|
||||
-- Clean up after each test
|
||||
Gui.destroy()
|
||||
-- Reset keyboard state
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
end
|
||||
|
||||
-- Test 1: Event object structure
|
||||
function TestEventSystem:test_event_object_has_required_fields()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
eventReceived = event
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate mouse press and release
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Verify event object structure
|
||||
lu.assertNotNil(eventReceived, "Event should be received")
|
||||
lu.assertNotNil(eventReceived.type, "Event should have type field")
|
||||
lu.assertNotNil(eventReceived.button, "Event should have button field")
|
||||
lu.assertNotNil(eventReceived.x, "Event should have x field")
|
||||
lu.assertNotNil(eventReceived.y, "Event should have y field")
|
||||
lu.assertNotNil(eventReceived.modifiers, "Event should have modifiers field")
|
||||
lu.assertNotNil(eventReceived.clickCount, "Event should have clickCount field")
|
||||
lu.assertNotNil(eventReceived.timestamp, "Event should have timestamp field")
|
||||
end
|
||||
|
||||
-- Test 2: Left click event
|
||||
function TestEventSystem:test_left_click_generates_click_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, { type = event.type, button = event.button })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate left click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Should receive press, click, and release events
|
||||
lu.assertTrue(#eventsReceived >= 2, "Should receive at least 2 events")
|
||||
|
||||
-- Check for click event
|
||||
local hasClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "click" and evt.button == 1 then
|
||||
hasClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasClickEvent, "Should receive click event for left button")
|
||||
end
|
||||
|
||||
-- Test 3: Right click event
|
||||
function TestEventSystem:test_right_click_generates_rightclick_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, { type = event.type, button = event.button })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate right click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(2, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(2, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Check for rightclick event
|
||||
local hasRightClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "rightclick" and evt.button == 2 then
|
||||
hasRightClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasRightClickEvent, "Should receive rightclick event for right button")
|
||||
end
|
||||
|
||||
-- Test 4: Middle click event
|
||||
function TestEventSystem:test_middle_click_generates_middleclick_event()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, { type = event.type, button = event.button })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate middle click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(3, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(3, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Check for middleclick event
|
||||
local hasMiddleClickEvent = false
|
||||
for _, evt in ipairs(eventsReceived) do
|
||||
if evt.type == "middleclick" and evt.button == 3 then
|
||||
hasMiddleClickEvent = true
|
||||
break
|
||||
end
|
||||
end
|
||||
lu.assertTrue(hasMiddleClickEvent, "Should receive middleclick event for middle button")
|
||||
end
|
||||
|
||||
-- Test 5: Modifier keys detection
|
||||
function TestEventSystem:test_modifier_keys_are_detected()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate shift + click
|
||||
love.keyboard.setDown("lshift", true)
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertTrue(eventReceived.modifiers.shift, "Shift modifier should be detected")
|
||||
end
|
||||
|
||||
-- Test 6: Double click detection
|
||||
function TestEventSystem:test_double_click_increments_click_count()
|
||||
local clickEvents = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
table.insert(clickEvents, event.clickCount)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate first click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Simulate second click quickly (double-click)
|
||||
love.timer.setTime(love.timer.getTime() + 0.1) -- 100ms later
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertEquals(#clickEvents, 2, "Should receive 2 click events")
|
||||
lu.assertEquals(clickEvents[1], 1, "First click should have clickCount = 1")
|
||||
lu.assertEquals(clickEvents[2], 2, "Second click should have clickCount = 2")
|
||||
end
|
||||
|
||||
-- Test 7: Press and release events
|
||||
function TestEventSystem:test_press_and_release_events_are_fired()
|
||||
local eventsReceived = {}
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
table.insert(eventsReceived, event.type)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate click
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
-- Should receive press, click, and release
|
||||
lu.assertTrue(#eventsReceived >= 2, "Should receive multiple events")
|
||||
|
||||
local hasPress = false
|
||||
local hasRelease = false
|
||||
for _, eventType in ipairs(eventsReceived) do
|
||||
if eventType == "press" then
|
||||
hasPress = true
|
||||
end
|
||||
if eventType == "release" then
|
||||
hasRelease = true
|
||||
end
|
||||
end
|
||||
|
||||
lu.assertTrue(hasPress, "Should receive press event")
|
||||
lu.assertTrue(hasRelease, "Should receive release event")
|
||||
end
|
||||
|
||||
-- Test 8: Mouse position in event
|
||||
function TestEventSystem:test_event_contains_mouse_position()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate click at specific position
|
||||
local mouseX, mouseY = 175, 125
|
||||
love.mouse.setPosition(mouseX, mouseY)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertEquals(eventReceived.x, mouseX, "Event should contain correct mouse X position")
|
||||
lu.assertEquals(eventReceived.y, mouseY, "Event should contain correct mouse Y position")
|
||||
end
|
||||
|
||||
-- Test 9: No callback when mouse outside element
|
||||
function TestEventSystem:test_no_callback_when_clicking_outside_element()
|
||||
local callbackCalled = false
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
callbackCalled = true
|
||||
end,
|
||||
})
|
||||
|
||||
-- Click outside element
|
||||
love.mouse.setPosition(50, 50)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertFalse(callbackCalled, "Callback should not be called when clicking outside element")
|
||||
end
|
||||
|
||||
-- Test 10: Multiple modifiers
|
||||
function TestEventSystem:test_multiple_modifiers_detected()
|
||||
local eventReceived = nil
|
||||
|
||||
local button = Gui.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
callback = function(element, event)
|
||||
if event.type == "click" then
|
||||
eventReceived = event
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Simulate shift + ctrl + click
|
||||
love.keyboard.setDown("lshift", true)
|
||||
love.keyboard.setDown("lctrl", true)
|
||||
love.mouse.setPosition(150, 150)
|
||||
love.mouse.setDown(1, true)
|
||||
button:update(0.016)
|
||||
|
||||
love.mouse.setDown(1, false)
|
||||
button:update(0.016)
|
||||
|
||||
lu.assertNotNil(eventReceived, "Should receive click event")
|
||||
lu.assertTrue(eventReceived.modifiers.shift, "Shift modifier should be detected")
|
||||
lu.assertTrue(eventReceived.modifiers.ctrl, "Ctrl modifier should be detected")
|
||||
end
|
||||
|
||||
print("Running Event System Tests...")
|
||||
lu.LuaUnit.run()
|
||||
@@ -5,11 +5,55 @@ local Element = FlexLove.Element
|
||||
|
||||
TestKeyboardInput = {}
|
||||
|
||||
-- Helper function to ensure clean keyboard state
|
||||
local function clearModifierKeys()
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:setUp()
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
if love.keyboard.isDown("lgui", "rgui", "lalt", "ralt", "lctrl", "rctrl", "lshift", "rshift") then
|
||||
local mods = {}
|
||||
if love.keyboard.isDown("lshift") then table.insert(mods, "lshift") end
|
||||
if love.keyboard.isDown("rshift") then table.insert(mods, "rshift") end
|
||||
if love.keyboard.isDown("lctrl") then table.insert(mods, "lctrl") end
|
||||
if love.keyboard.isDown("rctrl") then table.insert(mods, "rctrl") end
|
||||
if love.keyboard.isDown("lalt") then table.insert(mods, "lalt") end
|
||||
if love.keyboard.isDown("ralt") then table.insert(mods, "ralt") end
|
||||
if love.keyboard.isDown("lgui") then table.insert(mods, "lgui") end
|
||||
if love.keyboard.isDown("rgui") then table.insert(mods, "rgui") end
|
||||
print("WARNING: Modifiers down at setUp: " .. table.concat(mods, ", "))
|
||||
end
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
end
|
||||
|
||||
function TestKeyboardInput:tearDown()
|
||||
-- Clear all keyboard modifier states
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.destroy()
|
||||
end
|
||||
|
||||
@@ -313,6 +357,186 @@ function TestKeyboardInput:testEndKey()
|
||||
lu.assertEquals(input._cursorPosition, 5)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Modifier Key Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testSuperLeftMovesToStart()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(8) -- Middle of text
|
||||
|
||||
-- Simulate Super (Cmd/Win) key being held
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperRightMovesToEnd()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(3) -- Middle of text
|
||||
|
||||
-- Simulate Super (Cmd/Win) key being held
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 11) -- End of "Hello World"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltLeftMovesByWord()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World Test",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(16) -- End of text
|
||||
|
||||
-- Simulate Alt key being held and move left by word
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 12) -- Start of "Test"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltRightMovesByWord()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World Test",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- Start of text
|
||||
|
||||
-- Simulate Alt key being held and move right by word
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 6) -- After "Hello "
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltLeftMultipleWords()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "The quick brown fox",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(19) -- End of text
|
||||
|
||||
-- Move left by word three times
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("left", "left", false) -- to "fox"
|
||||
input:keypressed("left", "left", false) -- to "brown"
|
||||
input:keypressed("left", "left", false) -- to "quick"
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 4) -- Start of "quick"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltRightMultipleWords()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "The quick brown fox",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- Start of text
|
||||
|
||||
-- Move right by word three times
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("right", "right", false) -- after "The "
|
||||
input:keypressed("right", "right", false) -- after "quick "
|
||||
input:keypressed("right", "right", false) -- after "brown "
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 16) -- After "brown "
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperLeftWithSelection()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(3, 8)
|
||||
lu.assertTrue(input:hasSelection())
|
||||
|
||||
-- Super+Left should move to start and clear selection
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperRightWithSelection()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(3, 8)
|
||||
lu.assertTrue(input:hasSelection())
|
||||
|
||||
-- Super+Right should move to end and clear selection
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 11)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testEscapeClearsSelection()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
|
||||
733
testing/__tests__/24_keyboard_input_tests.lua.bak
Normal file
733
testing/__tests__/24_keyboard_input_tests.lua.bak
Normal file
@@ -0,0 +1,733 @@
|
||||
local lu = require("testing.luaunit")
|
||||
local FlexLove = require("FlexLove")
|
||||
local Gui = FlexLove.Gui
|
||||
local Element = FlexLove.Element
|
||||
|
||||
TestKeyboardInput = {}
|
||||
|
||||
-- Helper function to ensure clean keyboard state
|
||||
local function clearModifierKeys()
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:setUp()
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
if love.keyboard.isDown("lgui", "rgui", "lalt", "ralt", "lctrl", "rctrl") then
|
||||
print("WARNING: Modifier keys were down at start of test!")
|
||||
end
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.init({ baseScale = { width = 1920, height = 1080 } })
|
||||
end
|
||||
|
||||
function TestKeyboardInput:tearDown()
|
||||
-- Clear all keyboard modifier states
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
Gui.destroy()
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Focus Management Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testFocusEditable()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
lu.assertFalse(input:isFocused())
|
||||
|
||||
input:focus()
|
||||
|
||||
lu.assertTrue(input:isFocused())
|
||||
lu.assertEquals(Gui._focusedElement, input)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testBlurEditable()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
lu.assertTrue(input:isFocused())
|
||||
|
||||
input:blur()
|
||||
|
||||
lu.assertFalse(input:isFocused())
|
||||
lu.assertNil(Gui._focusedElement)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testFocusSwitching()
|
||||
local input1 = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Input 1",
|
||||
})
|
||||
|
||||
local input2 = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Input 2",
|
||||
})
|
||||
|
||||
input1:focus()
|
||||
lu.assertTrue(input1:isFocused())
|
||||
lu.assertFalse(input2:isFocused())
|
||||
|
||||
input2:focus()
|
||||
lu.assertFalse(input1:isFocused())
|
||||
lu.assertTrue(input2:isFocused())
|
||||
lu.assertEquals(Gui._focusedElement, input2)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSelectOnFocus()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
selectOnFocus = true,
|
||||
})
|
||||
|
||||
lu.assertFalse(input:hasSelection())
|
||||
|
||||
input:focus()
|
||||
|
||||
lu.assertTrue(input:hasSelection())
|
||||
local startPos, endPos = input:getSelection()
|
||||
lu.assertEquals(startPos, 0)
|
||||
lu.assertEquals(endPos, 11) -- Length of "Hello World"
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Text Input Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testTextInput()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:textinput("H")
|
||||
input:textinput("i")
|
||||
|
||||
lu.assertEquals(input:getText(), "Hi")
|
||||
lu.assertEquals(input._cursorPosition, 2)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testTextInputAtPosition()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(2) -- After "He"
|
||||
input:textinput("X")
|
||||
|
||||
lu.assertEquals(input:getText(), "HeXllo")
|
||||
lu.assertEquals(input._cursorPosition, 3)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testTextInputWithSelection()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(0, 5) -- Select "Hello"
|
||||
input:textinput("Hi")
|
||||
|
||||
lu.assertEquals(input:getText(), "Hi World")
|
||||
lu.assertEquals(input._cursorPosition, 2)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testMaxLengthConstraint()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "",
|
||||
maxLength = 5,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:textinput("Hello")
|
||||
lu.assertEquals(input:getText(), "Hello")
|
||||
|
||||
input:textinput("X") -- Should not be added
|
||||
lu.assertEquals(input:getText(), "Hello")
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Backspace/Delete Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testBackspace()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(5) -- At end
|
||||
input:keypressed("backspace", "backspace", false)
|
||||
|
||||
lu.assertEquals(input:getText(), "Hell")
|
||||
lu.assertEquals(input._cursorPosition, 4)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testBackspaceAtStart()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- At start
|
||||
input:keypressed("backspace", "backspace", false)
|
||||
|
||||
lu.assertEquals(input:getText(), "Hello") -- No change
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testDelete()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- At start
|
||||
input:keypressed("delete", "delete", false)
|
||||
|
||||
lu.assertEquals(input:getText(), "ello")
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testDeleteAtEnd()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(5) -- At end
|
||||
input:keypressed("delete", "delete", false)
|
||||
|
||||
lu.assertEquals(input:getText(), "Hello") -- No change
|
||||
lu.assertEquals(input._cursorPosition, 5)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testBackspaceWithSelection()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(0, 5) -- Select "Hello"
|
||||
input:keypressed("backspace", "backspace", false)
|
||||
|
||||
lu.assertEquals(input:getText(), " World")
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Cursor Movement Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testArrowLeft()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(5)
|
||||
input:keypressed("left", "left", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 4)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testArrowRight()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0)
|
||||
input:keypressed("right", "right", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 1)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testHomeKey()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(5)
|
||||
input:keypressed("home", "home", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testEndKey()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0)
|
||||
input:keypressed("end", "end", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 5)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Modifier Key Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testSuperLeftMovesToStart()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(8) -- Middle of text
|
||||
|
||||
-- Simulate Super (Cmd/Win) key being held
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperRightMovesToEnd()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(3) -- Middle of text
|
||||
|
||||
-- Ensure clean state before setting modifier
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
|
||||
-- Simulate Super (Cmd/Win) key being held
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 11) -- End of "Hello World"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltLeftMovesByWord()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World Test",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(16) -- End of text
|
||||
|
||||
-- Simulate Alt key being held and move left by word
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 12) -- Start of "Test"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltRightMovesByWord()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World Test",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- Start of text
|
||||
|
||||
-- Simulate Alt key being held and move right by word
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 6) -- After "Hello "
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltLeftMultipleWords()
|
||||
clearModifierKeys()
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "The quick brown fox",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(19) -- End of text
|
||||
|
||||
-- Move left by word three times
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("left", "left", false) -- to "fox"
|
||||
input:keypressed("left", "left", false) -- to "brown"
|
||||
input:keypressed("left", "left", false) -- to "quick"
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 4) -- Start of "quick"
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testAltRightMultipleWords()
|
||||
-- Ensure clean keyboard state
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "The quick brown fox",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(0) -- Start of text
|
||||
|
||||
-- Move right by word three times
|
||||
love.keyboard.setDown("lalt", true)
|
||||
input:keypressed("right", "right", false) -- after "The "
|
||||
input:keypressed("right", "right", false) -- after "quick "
|
||||
input:keypressed("right", "right", false) -- after "brown "
|
||||
love.keyboard.setDown("lalt", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 16) -- After "brown "
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperLeftWithSelection()
|
||||
-- Ensure clean keyboard state
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(3, 8)
|
||||
lu.assertTrue(input:hasSelection())
|
||||
|
||||
-- Super+Left should move to start and clear selection
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("left", "left", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 0)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testSuperRightWithSelection()
|
||||
-- Ensure clean keyboard state
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello World",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setSelection(3, 8)
|
||||
lu.assertTrue(input:hasSelection())
|
||||
|
||||
-- Super+Right should move to end and clear selection
|
||||
love.keyboard.setDown("lgui", true)
|
||||
input:keypressed("right", "right", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
|
||||
lu.assertEquals(input._cursorPosition, 11)
|
||||
lu.assertFalse(input:hasSelection())
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testEscapeClearsSelection()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:selectAll()
|
||||
lu.assertTrue(input:hasSelection())
|
||||
|
||||
input:keypressed("escape", "escape", false)
|
||||
|
||||
lu.assertFalse(input:hasSelection())
|
||||
lu.assertTrue(input:isFocused()) -- Still focused
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testEscapeBlurs()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
lu.assertTrue(input:isFocused())
|
||||
|
||||
input:keypressed("escape", "escape", false)
|
||||
|
||||
lu.assertFalse(input:isFocused())
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Callback Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testOnTextChangeCallback()
|
||||
local changeCount = 0
|
||||
local oldTextValue = nil
|
||||
local newTextValue = nil
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
onTextChange = function(element, newText, oldText)
|
||||
changeCount = changeCount + 1
|
||||
newTextValue = newText
|
||||
oldTextValue = oldText
|
||||
end,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:textinput("X")
|
||||
|
||||
lu.assertEquals(changeCount, 1)
|
||||
lu.assertEquals(oldTextValue, "Hello")
|
||||
lu.assertEquals(newTextValue, "HelloX")
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testOnTextInputCallback()
|
||||
local inputCount = 0
|
||||
local lastChar = nil
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "",
|
||||
onTextInput = function(element, text)
|
||||
inputCount = inputCount + 1
|
||||
lastChar = text
|
||||
end,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:textinput("A")
|
||||
input:textinput("B")
|
||||
|
||||
lu.assertEquals(inputCount, 2)
|
||||
lu.assertEquals(lastChar, "B")
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testOnEnterCallback()
|
||||
local enterCalled = false
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
multiline = false,
|
||||
text = "Hello",
|
||||
onEnter = function(element)
|
||||
enterCalled = true
|
||||
end,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:keypressed("return", "return", false)
|
||||
|
||||
lu.assertTrue(enterCalled)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testOnFocusCallback()
|
||||
local focusCalled = false
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
onFocus = function(element)
|
||||
focusCalled = true
|
||||
end,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
|
||||
lu.assertTrue(focusCalled)
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testOnBlurCallback()
|
||||
local blurCalled = false
|
||||
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
onBlur = function(element)
|
||||
blurCalled = true
|
||||
end,
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:blur()
|
||||
|
||||
lu.assertTrue(blurCalled)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- GUI-level Input Forwarding Tests
|
||||
-- ====================
|
||||
|
||||
function TestKeyboardInput:testGuiTextinputForwarding()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
Gui.textinput("A")
|
||||
|
||||
lu.assertEquals(input:getText(), "A")
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testGuiKeypressedForwarding()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
input:focus()
|
||||
input:setCursorPosition(5)
|
||||
Gui.keypressed("backspace", "backspace", false)
|
||||
|
||||
lu.assertEquals(input:getText(), "Hell")
|
||||
end
|
||||
|
||||
function TestKeyboardInput:testGuiInputWithoutFocus()
|
||||
local input = Element.new({
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Hello",
|
||||
})
|
||||
|
||||
-- No focus
|
||||
Gui.textinput("X")
|
||||
|
||||
lu.assertEquals(input:getText(), "Hello") -- No change
|
||||
end
|
||||
|
||||
lu.LuaUnit.run()
|
||||
@@ -18,6 +18,16 @@ local testElement
|
||||
TestInputField = {}
|
||||
|
||||
function TestInputField:setUp()
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
-- Reset FlexLove state
|
||||
FlexLove.Gui.topElements = {}
|
||||
FlexLove.Gui._focusedElement = nil
|
||||
@@ -34,6 +44,16 @@ function TestInputField:setUp()
|
||||
end
|
||||
|
||||
function TestInputField:tearDown()
|
||||
-- Clear all keyboard modifier states
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
testElement = nil
|
||||
FlexLove.Gui.topElements = {}
|
||||
FlexLove.Gui._focusedElement = nil
|
||||
|
||||
438
testing/__tests__/34_password_mode_tests.lua
Normal file
438
testing/__tests__/34_password_mode_tests.lua
Normal file
@@ -0,0 +1,438 @@
|
||||
-- ====================
|
||||
-- Password Mode Tests
|
||||
-- ====================
|
||||
-- Test suite for password mode functionality in FlexLove input fields
|
||||
|
||||
local lu = require("testing.luaunit")
|
||||
local loveStub = require("testing.loveStub")
|
||||
|
||||
-- Setup LÖVE environment
|
||||
_G.love = loveStub
|
||||
|
||||
-- Load FlexLove after setting up love stub
|
||||
local FlexLove = require("FlexLove")
|
||||
|
||||
-- Test fixtures
|
||||
local testElement
|
||||
|
||||
TestPasswordMode = {}
|
||||
|
||||
function TestPasswordMode:setUp()
|
||||
-- Clear all keyboard modifier states at start of each test
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
-- Reset FlexLove state
|
||||
FlexLove.Gui.topElements = {}
|
||||
FlexLove.Gui._focusedElement = nil
|
||||
|
||||
-- Create a test password input element
|
||||
testElement = FlexLove.Element.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
passwordMode = true,
|
||||
text = "",
|
||||
})
|
||||
end
|
||||
|
||||
function TestPasswordMode:tearDown()
|
||||
-- Clear all keyboard modifier states
|
||||
love.keyboard.setDown("lshift", false)
|
||||
love.keyboard.setDown("rshift", false)
|
||||
love.keyboard.setDown("lctrl", false)
|
||||
love.keyboard.setDown("rctrl", false)
|
||||
love.keyboard.setDown("lalt", false)
|
||||
love.keyboard.setDown("ralt", false)
|
||||
love.keyboard.setDown("lgui", false)
|
||||
love.keyboard.setDown("rgui", false)
|
||||
|
||||
testElement = nil
|
||||
FlexLove.Gui.topElements = {}
|
||||
FlexLove.Gui._focusedElement = nil
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Property Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testPasswordModePropertyExists()
|
||||
-- Test that passwordMode property exists and can be set
|
||||
lu.assertNotNil(testElement.passwordMode)
|
||||
lu.assertTrue(testElement.passwordMode)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeDefaultIsFalse()
|
||||
-- Test that passwordMode defaults to false
|
||||
local normalElement = FlexLove.Element.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
text = "Normal text",
|
||||
})
|
||||
|
||||
lu.assertFalse(normalElement.passwordMode or false)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeIsSingleLineOnly()
|
||||
-- Password mode should only work with single-line inputs
|
||||
-- The constraint is enforced in Element.lua line 292-293
|
||||
local multilinePassword = FlexLove.Element.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 100,
|
||||
editable = true,
|
||||
multiline = true,
|
||||
passwordMode = true,
|
||||
text = "Password",
|
||||
})
|
||||
|
||||
-- Based on the constraint, multiline should be set to false
|
||||
lu.assertFalse(multilinePassword.multiline)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Text Buffer Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testActualTextContentRemains()
|
||||
-- Insert text into password field
|
||||
testElement:focus()
|
||||
testElement:insertText("S")
|
||||
testElement:insertText("e")
|
||||
testElement:insertText("c")
|
||||
testElement:insertText("r")
|
||||
testElement:insertText("e")
|
||||
testElement:insertText("t")
|
||||
|
||||
-- Verify actual text buffer contains the real text
|
||||
lu.assertEquals(testElement._textBuffer, "Secret")
|
||||
lu.assertEquals(testElement:getText(), "Secret")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordTextIsNotModified()
|
||||
-- Set initial text
|
||||
testElement:setText("MyPassword123")
|
||||
|
||||
-- The actual buffer should contain the real password
|
||||
lu.assertEquals(testElement._textBuffer, "MyPassword123")
|
||||
lu.assertEquals(testElement:getText(), "MyPassword123")
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Cursor Position Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testCursorPositionWithPasswordMode()
|
||||
testElement:setText("test")
|
||||
testElement:focus()
|
||||
|
||||
-- Set cursor to end
|
||||
testElement:setCursorPosition(4)
|
||||
lu.assertEquals(testElement._cursorPosition, 4)
|
||||
|
||||
-- Move cursor to middle
|
||||
testElement:setCursorPosition(2)
|
||||
lu.assertEquals(testElement._cursorPosition, 2)
|
||||
|
||||
-- Move cursor to start
|
||||
testElement:setCursorPosition(0)
|
||||
lu.assertEquals(testElement._cursorPosition, 0)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testCursorMovementInPasswordField()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
testElement:setCursorPosition(0)
|
||||
|
||||
-- Move right
|
||||
testElement:moveCursorBy(1)
|
||||
lu.assertEquals(testElement._cursorPosition, 1)
|
||||
|
||||
-- Move right again
|
||||
testElement:moveCursorBy(1)
|
||||
lu.assertEquals(testElement._cursorPosition, 2)
|
||||
|
||||
-- Move left
|
||||
testElement:moveCursorBy(-1)
|
||||
lu.assertEquals(testElement._cursorPosition, 1)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Text Editing Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testInsertTextInPasswordMode()
|
||||
testElement:focus()
|
||||
testElement:setCursorPosition(0)
|
||||
|
||||
testElement:insertText("a")
|
||||
lu.assertEquals(testElement._textBuffer, "a")
|
||||
|
||||
testElement:insertText("b")
|
||||
lu.assertEquals(testElement._textBuffer, "ab")
|
||||
|
||||
testElement:insertText("c")
|
||||
lu.assertEquals(testElement._textBuffer, "abc")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testBackspaceInPasswordMode()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
testElement:setCursorPosition(8) -- End of text
|
||||
|
||||
-- Delete last character
|
||||
testElement:keypressed("backspace", nil, false)
|
||||
lu.assertEquals(testElement._textBuffer, "passwor")
|
||||
|
||||
-- Delete another character
|
||||
testElement:keypressed("backspace", nil, false)
|
||||
lu.assertEquals(testElement._textBuffer, "passwo")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testDeleteInPasswordMode()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
testElement:setCursorPosition(0) -- Start of text
|
||||
|
||||
-- Delete first character
|
||||
testElement:keypressed("delete", nil, false)
|
||||
lu.assertEquals(testElement._textBuffer, "assword")
|
||||
|
||||
-- Delete another character
|
||||
testElement:keypressed("delete", nil, false)
|
||||
lu.assertEquals(testElement._textBuffer, "ssword")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testInsertTextAtPosition()
|
||||
testElement:setText("pass")
|
||||
testElement:focus()
|
||||
testElement:setCursorPosition(2) -- Between 'pa' and 'ss'
|
||||
|
||||
testElement:insertText("x")
|
||||
lu.assertEquals(testElement._textBuffer, "paxss")
|
||||
lu.assertEquals(testElement._cursorPosition, 3)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Selection Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testTextSelectionInPasswordMode()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
|
||||
-- Select from position 2 to 5
|
||||
testElement:setSelection(2, 5)
|
||||
|
||||
local selStart, selEnd = testElement:getSelection()
|
||||
lu.assertEquals(selStart, 2)
|
||||
lu.assertEquals(selEnd, 5)
|
||||
lu.assertTrue(testElement:hasSelection())
|
||||
end
|
||||
|
||||
function TestPasswordMode:testDeleteSelectionInPasswordMode()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
|
||||
-- Select "sswo" (positions 2-6)
|
||||
testElement:setSelection(2, 6)
|
||||
|
||||
-- Delete selection
|
||||
testElement:deleteSelection()
|
||||
lu.assertEquals(testElement._textBuffer, "pard")
|
||||
lu.assertFalse(testElement:hasSelection())
|
||||
end
|
||||
|
||||
function TestPasswordMode:testReplaceSelectionInPasswordMode()
|
||||
testElement:setText("password")
|
||||
testElement:focus()
|
||||
|
||||
-- Select "sswo" (positions 2-6)
|
||||
testElement:setSelection(2, 6)
|
||||
|
||||
-- Type new text (should replace selection)
|
||||
testElement:textinput("X")
|
||||
lu.assertEquals(testElement._textBuffer, "paXrd")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testSelectAllInPasswordMode()
|
||||
testElement:setText("secret")
|
||||
testElement:focus()
|
||||
|
||||
testElement:selectAll()
|
||||
|
||||
local selStart, selEnd = testElement:getSelection()
|
||||
lu.assertEquals(selStart, 0)
|
||||
lu.assertEquals(selEnd, 6)
|
||||
lu.assertTrue(testElement:hasSelection())
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Integration Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithMaxLength()
|
||||
testElement.maxLength = 5
|
||||
testElement:focus()
|
||||
|
||||
testElement:insertText("1")
|
||||
testElement:insertText("2")
|
||||
testElement:insertText("3")
|
||||
testElement:insertText("4")
|
||||
testElement:insertText("5")
|
||||
testElement:insertText("6") -- Should be rejected
|
||||
|
||||
lu.assertEquals(testElement._textBuffer, "12345")
|
||||
lu.assertEquals(utf8.len(testElement._textBuffer), 5)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithPlaceholder()
|
||||
local passwordWithPlaceholder = FlexLove.Element.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
passwordMode = true,
|
||||
placeholder = "Enter password",
|
||||
text = "",
|
||||
})
|
||||
|
||||
-- When empty and not focused, placeholder should be available
|
||||
lu.assertEquals(passwordWithPlaceholder.placeholder, "Enter password")
|
||||
lu.assertEquals(passwordWithPlaceholder._textBuffer, "")
|
||||
|
||||
-- When text is added, actual text should be stored
|
||||
passwordWithPlaceholder:focus()
|
||||
passwordWithPlaceholder:insertText("secret")
|
||||
lu.assertEquals(passwordWithPlaceholder._textBuffer, "secret")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeClearText()
|
||||
testElement:setText("password123")
|
||||
lu.assertEquals(testElement._textBuffer, "password123")
|
||||
|
||||
-- Clear text
|
||||
testElement:setText("")
|
||||
lu.assertEquals(testElement._textBuffer, "")
|
||||
lu.assertEquals(testElement:getText(), "")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeToggle()
|
||||
-- Start with password mode off
|
||||
local toggleElement = FlexLove.Element.new({
|
||||
x = 100,
|
||||
y = 100,
|
||||
width = 200,
|
||||
height = 40,
|
||||
editable = true,
|
||||
passwordMode = false,
|
||||
text = "visible",
|
||||
})
|
||||
|
||||
lu.assertEquals(toggleElement._textBuffer, "visible")
|
||||
lu.assertFalse(toggleElement.passwordMode)
|
||||
|
||||
-- Enable password mode
|
||||
toggleElement.passwordMode = true
|
||||
lu.assertTrue(toggleElement.passwordMode)
|
||||
|
||||
-- Text buffer should remain unchanged
|
||||
lu.assertEquals(toggleElement._textBuffer, "visible")
|
||||
|
||||
-- Disable password mode again
|
||||
toggleElement.passwordMode = false
|
||||
lu.assertFalse(toggleElement.passwordMode)
|
||||
lu.assertEquals(toggleElement._textBuffer, "visible")
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- UTF-8 Support Tests
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithUTF8Characters()
|
||||
testElement:focus()
|
||||
|
||||
-- Insert UTF-8 characters
|
||||
testElement:insertText("h")
|
||||
testElement:insertText("é")
|
||||
testElement:insertText("l")
|
||||
testElement:insertText("l")
|
||||
testElement:insertText("ö")
|
||||
|
||||
-- Text buffer should contain actual UTF-8 text
|
||||
lu.assertEquals(testElement._textBuffer, "héllö")
|
||||
lu.assertEquals(utf8.len(testElement._textBuffer), 5)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeCursorWithUTF8()
|
||||
testElement:setText("café")
|
||||
testElement:focus()
|
||||
|
||||
-- Move cursor through UTF-8 text
|
||||
testElement:setCursorPosition(0)
|
||||
lu.assertEquals(testElement._cursorPosition, 0)
|
||||
|
||||
testElement:moveCursorBy(1)
|
||||
lu.assertEquals(testElement._cursorPosition, 1)
|
||||
|
||||
testElement:moveCursorBy(1)
|
||||
lu.assertEquals(testElement._cursorPosition, 2)
|
||||
|
||||
testElement:setCursorPosition(4)
|
||||
lu.assertEquals(testElement._cursorPosition, 4)
|
||||
end
|
||||
|
||||
-- ====================
|
||||
-- Edge Cases
|
||||
-- ====================
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithEmptyString()
|
||||
testElement:setText("")
|
||||
lu.assertEquals(testElement._textBuffer, "")
|
||||
lu.assertEquals(testElement:getText(), "")
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithSingleCharacter()
|
||||
testElement:setText("x")
|
||||
lu.assertEquals(testElement._textBuffer, "x")
|
||||
lu.assertEquals(utf8.len(testElement._textBuffer), 1)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeWithLongPassword()
|
||||
local longPassword = string.rep("a", 100)
|
||||
testElement:setText(longPassword)
|
||||
|
||||
lu.assertEquals(testElement._textBuffer, longPassword)
|
||||
lu.assertEquals(utf8.len(testElement._textBuffer), 100)
|
||||
end
|
||||
|
||||
function TestPasswordMode:testPasswordModeSetTextUpdatesBuffer()
|
||||
testElement:setText("initial")
|
||||
lu.assertEquals(testElement._textBuffer, "initial")
|
||||
|
||||
testElement:setText("updated")
|
||||
lu.assertEquals(testElement._textBuffer, "updated")
|
||||
|
||||
testElement:setText("")
|
||||
lu.assertEquals(testElement._textBuffer, "")
|
||||
end
|
||||
|
||||
-- Run tests if executed directly
|
||||
if arg and arg[0] and arg[0]:match("34_password_mode_tests%.lua$") then
|
||||
os.exit(lu.LuaUnit.run())
|
||||
end
|
||||
|
||||
return TestPasswordMode
|
||||
@@ -37,6 +37,7 @@ local testFiles = {
|
||||
"testing/__tests__/31_immediate_mode_basic_tests.lua",
|
||||
"testing/__tests__/32_state_manager_tests.lua",
|
||||
"testing/__tests__/33_input_field_tests.lua",
|
||||
"testing/__tests__/34_password_mode_tests.lua",
|
||||
}
|
||||
|
||||
local success = true
|
||||
|
||||
Reference in New Issue
Block a user