Files
FlexLove/testing/__tests__/24_keyboard_input_tests.lua
2025-11-09 12:00:42 -05:00

731 lines
16 KiB
Lua

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", "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
-- ====================
-- 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()
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,
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()