2025-11-15 22:06:12 +01:00
|
|
|
"""
|
2025-11-15 23:43:12 +01:00
|
|
|
Functional tests for MposKeyboard.
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
Tests keyboard creation, mode switching, text input, and API compatibility.
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
Desktop: ./tests/unittest.sh tests/test_custom_keyboard.py
|
2025-11-16 00:29:22 +01:00
|
|
|
Device: ./tests/unittest.sh tests/test_custom_keyboard.py --ondevice
|
2025-11-15 22:06:12 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
import lvgl as lv
|
2026-01-13 00:38:17 +01:00
|
|
|
from mpos import MposKeyboard, simulate_click, wait_for_render
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
class TestMposKeyboard(unittest.TestCase):
|
|
|
|
|
"""Test suite for MposKeyboard functionality."""
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
"""Set up test fixtures before each test method."""
|
|
|
|
|
# Create a test screen
|
|
|
|
|
self.screen = lv.obj()
|
|
|
|
|
self.screen.set_size(320, 240)
|
|
|
|
|
|
|
|
|
|
# Create a textarea for testing
|
|
|
|
|
self.textarea = lv.textarea(self.screen)
|
|
|
|
|
self.textarea.set_size(280, 40)
|
|
|
|
|
self.textarea.align(lv.ALIGN.TOP_MID, 0, 10)
|
|
|
|
|
self.textarea.set_one_line(True)
|
|
|
|
|
|
|
|
|
|
print(f"\n=== Test Setup Complete ===")
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
"""Clean up after each test method."""
|
|
|
|
|
# Clean up objects
|
|
|
|
|
lv.screen_load(lv.obj())
|
|
|
|
|
print("=== Test Cleanup Complete ===\n")
|
|
|
|
|
|
|
|
|
|
def test_keyboard_creation(self):
|
2025-11-15 23:43:12 +01:00
|
|
|
"""Test that MposKeyboard can be created."""
|
2025-11-15 22:06:12 +01:00
|
|
|
print("Testing keyboard creation...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
# Verify keyboard exists
|
|
|
|
|
self.assertIsNotNone(keyboard)
|
|
|
|
|
|
|
|
|
|
print("Keyboard created successfully")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_textarea(self):
|
|
|
|
|
"""Test setting textarea association."""
|
|
|
|
|
print("Testing set_textarea...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
keyboard.set_textarea(self.textarea)
|
|
|
|
|
|
|
|
|
|
# Verify textarea is associated
|
|
|
|
|
associated_ta = keyboard.get_textarea()
|
|
|
|
|
self.assertEqual(associated_ta, self.textarea)
|
|
|
|
|
|
|
|
|
|
print("Textarea association successful")
|
|
|
|
|
|
|
|
|
|
def test_mode_switching(self):
|
|
|
|
|
"""Test keyboard mode switching."""
|
|
|
|
|
print("Testing mode switching...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
# Test setting different modes
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard.set_mode(MposKeyboard.MODE_LOWERCASE)
|
|
|
|
|
keyboard.set_mode(MposKeyboard.MODE_UPPERCASE)
|
|
|
|
|
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
|
|
|
|
|
keyboard.set_mode(MposKeyboard.MODE_SPECIALS)
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
print("Mode switching successful")
|
|
|
|
|
|
|
|
|
|
def test_alignment(self):
|
|
|
|
|
"""Test keyboard alignment."""
|
|
|
|
|
print("Testing alignment...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
|
|
|
|
|
|
|
|
|
print("Alignment successful")
|
|
|
|
|
|
|
|
|
|
def test_height_settings(self):
|
|
|
|
|
"""Test height configuration."""
|
|
|
|
|
print("Testing height settings...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
keyboard.set_style_min_height(160, 0)
|
|
|
|
|
keyboard.set_style_height(160, 0)
|
|
|
|
|
|
|
|
|
|
print("Height settings successful")
|
|
|
|
|
|
|
|
|
|
def test_flags(self):
|
|
|
|
|
"""Test object flags (show/hide)."""
|
|
|
|
|
print("Testing flags...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
# Test hiding
|
|
|
|
|
keyboard.add_flag(lv.obj.FLAG.HIDDEN)
|
|
|
|
|
self.assertTrue(keyboard.has_flag(lv.obj.FLAG.HIDDEN))
|
|
|
|
|
|
|
|
|
|
# Test showing
|
|
|
|
|
keyboard.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
|
|
|
self.assertFalse(keyboard.has_flag(lv.obj.FLAG.HIDDEN))
|
|
|
|
|
|
|
|
|
|
print("Flag operations successful")
|
|
|
|
|
|
|
|
|
|
def test_event_callback(self):
|
|
|
|
|
"""Test adding event callbacks."""
|
|
|
|
|
print("Testing event callbacks...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
callback_called = [False]
|
|
|
|
|
|
|
|
|
|
def test_callback(event):
|
|
|
|
|
callback_called[0] = True
|
|
|
|
|
|
|
|
|
|
# Add callback
|
|
|
|
|
keyboard.add_event_cb(test_callback, lv.EVENT.READY, None)
|
|
|
|
|
|
|
|
|
|
# Send READY event
|
|
|
|
|
keyboard.send_event(lv.EVENT.READY, None)
|
|
|
|
|
|
|
|
|
|
# Verify callback was called
|
|
|
|
|
self.assertTrue(callback_called[0], "Callback was not called")
|
|
|
|
|
|
|
|
|
|
print("Event callback successful")
|
|
|
|
|
|
|
|
|
|
def test_api_compatibility(self):
|
2025-11-15 23:43:12 +01:00
|
|
|
"""Test that MposKeyboard has same API as lv.keyboard."""
|
2025-11-15 22:06:12 +01:00
|
|
|
print("Testing API compatibility...")
|
|
|
|
|
|
2025-11-15 23:43:12 +01:00
|
|
|
keyboard = MposKeyboard(self.screen)
|
2025-11-15 22:06:12 +01:00
|
|
|
|
|
|
|
|
# Check that all essential methods exist
|
|
|
|
|
essential_methods = [
|
|
|
|
|
'set_textarea',
|
|
|
|
|
'get_textarea',
|
|
|
|
|
'set_mode',
|
|
|
|
|
'align',
|
|
|
|
|
'add_flag',
|
|
|
|
|
'remove_flag',
|
|
|
|
|
'has_flag',
|
|
|
|
|
'add_event_cb',
|
|
|
|
|
'send_event',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for method_name in essential_methods:
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
hasattr(keyboard, method_name),
|
2025-11-15 23:43:12 +01:00
|
|
|
f"MposKeyboard missing method: {method_name}"
|
2025-11-15 22:06:12 +01:00
|
|
|
)
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
callable(getattr(keyboard, method_name)),
|
2025-11-15 23:43:12 +01:00
|
|
|
f"MposKeyboard.{method_name} is not callable"
|
2025-11-15 22:06:12 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
print("API compatibility verified")
|
|
|
|
|
|
2025-11-17 14:41:09 +01:00
|
|
|
def test_simulate_click_on_button(self):
|
|
|
|
|
"""Test clicking keyboard buttons using simulate_click()."""
|
|
|
|
|
print("Testing simulate_click() on keyboard buttons...")
|
|
|
|
|
|
|
|
|
|
# Create keyboard and load screen
|
|
|
|
|
keyboard = MposKeyboard(self.screen)
|
|
|
|
|
keyboard.set_textarea(self.textarea)
|
|
|
|
|
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
|
|
|
|
lv.screen_load(self.screen)
|
|
|
|
|
wait_for_render(10)
|
|
|
|
|
|
|
|
|
|
# Get initial text
|
|
|
|
|
initial_text = self.textarea.get_text()
|
|
|
|
|
print(f"Initial textarea text: '{initial_text}'")
|
|
|
|
|
|
|
|
|
|
# Get keyboard area and click on it
|
|
|
|
|
# The keyboard is an lv.keyboard object (accessed via _keyboard or through __getattr__)
|
|
|
|
|
obj_area = lv.area_t()
|
|
|
|
|
keyboard.get_coords(obj_area)
|
|
|
|
|
|
|
|
|
|
# Calculate a point to click - let's click in the lower part of keyboard
|
|
|
|
|
# which should be around where letters are
|
|
|
|
|
click_x = (obj_area.x1 + obj_area.x2) // 2 # Center horizontally
|
|
|
|
|
click_y = obj_area.y1 + (obj_area.y2 - obj_area.y1) // 3 # Upper third
|
|
|
|
|
|
|
|
|
|
print(f"Keyboard area: ({obj_area.x1}, {obj_area.y1}) to ({obj_area.x2}, {obj_area.y2})")
|
|
|
|
|
print(f"Clicking keyboard at ({click_x}, {click_y})")
|
|
|
|
|
|
|
|
|
|
# Click on the keyboard using simulate_click
|
|
|
|
|
simulate_click(click_x, click_y, press_duration_ms=100)
|
|
|
|
|
wait_for_render(5)
|
|
|
|
|
|
|
|
|
|
final_text = self.textarea.get_text()
|
|
|
|
|
print(f"Final textarea text: '{final_text}'")
|
|
|
|
|
|
|
|
|
|
# The important thing is that simulate_click worked without crashing
|
|
|
|
|
# The text might have changed if we hit a letter key
|
|
|
|
|
print("simulate_click() completed successfully")
|
|
|
|
|
|
|
|
|
|
def test_click_vs_send_event_comparison(self):
|
|
|
|
|
"""Compare simulate_click() vs send_event() for triggering button actions."""
|
|
|
|
|
print("Testing simulate_click() vs send_event() comparison...")
|
|
|
|
|
|
|
|
|
|
# Create keyboard and load screen
|
|
|
|
|
keyboard = MposKeyboard(self.screen)
|
|
|
|
|
keyboard.set_textarea(self.textarea)
|
|
|
|
|
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
|
|
|
|
lv.screen_load(self.screen)
|
|
|
|
|
wait_for_render(10)
|
|
|
|
|
|
|
|
|
|
# Test 1: Use send_event() to trigger READY event
|
|
|
|
|
callback_from_send_event = [False]
|
|
|
|
|
|
|
|
|
|
def callback_send_event(event):
|
|
|
|
|
callback_from_send_event[0] = True
|
|
|
|
|
print("send_event callback triggered")
|
|
|
|
|
|
|
|
|
|
keyboard.add_event_cb(callback_send_event, lv.EVENT.READY, None)
|
|
|
|
|
keyboard.send_event(lv.EVENT.READY, None)
|
|
|
|
|
wait_for_render(3)
|
|
|
|
|
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
callback_from_send_event[0],
|
|
|
|
|
"send_event() should trigger callback"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Test 2: Use simulate_click() to click on keyboard
|
|
|
|
|
# This demonstrates that simulate_click works with real UI interaction
|
|
|
|
|
initial_text = self.textarea.get_text()
|
|
|
|
|
|
|
|
|
|
# Get keyboard area to click within it
|
|
|
|
|
obj_area = lv.area_t()
|
|
|
|
|
keyboard.get_coords(obj_area)
|
|
|
|
|
|
|
|
|
|
# Click somewhere in the middle of the keyboard
|
|
|
|
|
click_x = (obj_area.x1 + obj_area.x2) // 2
|
|
|
|
|
click_y = (obj_area.y1 + obj_area.y2) // 2
|
|
|
|
|
|
|
|
|
|
print(f"Clicking keyboard at ({click_x}, {click_y})")
|
|
|
|
|
simulate_click(click_x, click_y, press_duration_ms=100)
|
|
|
|
|
wait_for_render(5)
|
|
|
|
|
|
|
|
|
|
# Verify click completed without crashing
|
|
|
|
|
final_text = self.textarea.get_text()
|
|
|
|
|
print(f"Text before click: '{initial_text}'")
|
|
|
|
|
print(f"Text after click: '{final_text}'")
|
|
|
|
|
|
|
|
|
|
print("Both send_event() and simulate_click() work correctly")
|
|
|
|
|
|
2025-11-15 22:06:12 +01:00
|
|
|
|