mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Add new and improved keyboard
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
"""
|
||||
Graphical tests for CustomKeyboard.
|
||||
|
||||
Tests keyboard visual appearance, text input via simulated button presses,
|
||||
and mode switching. Captures screenshots for regression testing.
|
||||
|
||||
Usage:
|
||||
Desktop: ./tests/unittest.sh tests/test_graphical_custom_keyboard.py
|
||||
Device: ./tests/unittest.sh tests/test_graphical_custom_keyboard.py ondevice
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import lvgl as lv
|
||||
import sys
|
||||
import os
|
||||
from mpos.ui.keyboard import CustomKeyboard, create_keyboard
|
||||
from graphical_test_helper import (
|
||||
wait_for_render,
|
||||
capture_screenshot,
|
||||
)
|
||||
|
||||
|
||||
class TestGraphicalCustomKeyboard(unittest.TestCase):
|
||||
"""Test suite for CustomKeyboard graphical verification."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
# Determine screenshot directory
|
||||
if sys.platform == "esp32":
|
||||
self.screenshot_dir = "tests/screenshots"
|
||||
else:
|
||||
self.screenshot_dir = "../tests/screenshots"
|
||||
|
||||
# Ensure screenshots directory exists
|
||||
try:
|
||||
os.mkdir(self.screenshot_dir)
|
||||
except OSError:
|
||||
pass # Directory already exists
|
||||
|
||||
print(f"\n=== Graphical Keyboard Test Setup ===")
|
||||
print(f"Platform: {sys.platform}")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after each test method."""
|
||||
lv.screen_load(lv.obj())
|
||||
wait_for_render(5)
|
||||
print("=== Test Cleanup Complete ===\n")
|
||||
|
||||
def _create_test_keyboard_scene(self):
|
||||
"""
|
||||
Create a test scene with textarea and keyboard.
|
||||
|
||||
Returns:
|
||||
tuple: (screen, keyboard, textarea)
|
||||
"""
|
||||
# Create screen
|
||||
screen = lv.obj()
|
||||
screen.set_size(320, 240)
|
||||
|
||||
# Create textarea
|
||||
textarea = lv.textarea(screen)
|
||||
textarea.set_size(280, 40)
|
||||
textarea.align(lv.ALIGN.TOP_MID, 0, 10)
|
||||
textarea.set_placeholder_text("Type here...")
|
||||
textarea.set_one_line(True)
|
||||
|
||||
# Create custom keyboard
|
||||
keyboard = CustomKeyboard(screen)
|
||||
keyboard.set_textarea(textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
|
||||
# Load and render
|
||||
lv.screen_load(screen)
|
||||
wait_for_render(iterations=20)
|
||||
|
||||
return screen, keyboard, textarea
|
||||
|
||||
def _simulate_button_press(self, keyboard, button_index):
|
||||
"""
|
||||
Simulate pressing a keyboard button.
|
||||
|
||||
Args:
|
||||
keyboard: CustomKeyboard instance
|
||||
button_index: Index of button to press
|
||||
|
||||
Returns:
|
||||
str: Text of the pressed button
|
||||
"""
|
||||
lvgl_keyboard = keyboard.get_lvgl_obj()
|
||||
|
||||
# Get button text before pressing
|
||||
button_text = lvgl_keyboard.get_button_text(button_index)
|
||||
|
||||
# Simulate button press by setting it as selected and sending event
|
||||
# Note: This is a bit of a hack since we can't directly click in tests
|
||||
# We'll trigger the VALUE_CHANGED event which is what happens on click
|
||||
|
||||
# The keyboard has an internal handler that responds to VALUE_CHANGED
|
||||
# We need to manually trigger it
|
||||
lvgl_keyboard.send_event(lv.EVENT.VALUE_CHANGED, None)
|
||||
|
||||
wait_for_render(5)
|
||||
|
||||
return button_text
|
||||
|
||||
def test_keyboard_lowercase_appearance(self):
|
||||
"""
|
||||
Test keyboard appearance in lowercase mode.
|
||||
|
||||
Verifies that the keyboard renders correctly and captures screenshot.
|
||||
"""
|
||||
print("\n=== Testing lowercase keyboard appearance ===")
|
||||
|
||||
screen, keyboard, textarea = self._create_test_keyboard_scene()
|
||||
|
||||
# Ensure lowercase mode
|
||||
keyboard.set_mode(CustomKeyboard.MODE_LOWERCASE)
|
||||
wait_for_render(10)
|
||||
|
||||
# Capture screenshot
|
||||
screenshot_path = f"{self.screenshot_dir}/custom_keyboard_lowercase.raw"
|
||||
print(f"Capturing screenshot: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
# Verify screenshot was created
|
||||
stat = os.stat(screenshot_path)
|
||||
self.assertTrue(stat[6] > 0, "Screenshot file is empty")
|
||||
print(f"Screenshot captured: {stat[6]} bytes")
|
||||
|
||||
print("=== Lowercase appearance test PASSED ===")
|
||||
|
||||
def test_keyboard_uppercase_appearance(self):
|
||||
"""Test keyboard appearance in uppercase mode."""
|
||||
print("\n=== Testing uppercase keyboard appearance ===")
|
||||
|
||||
screen, keyboard, textarea = self._create_test_keyboard_scene()
|
||||
|
||||
# Switch to uppercase mode
|
||||
keyboard.set_mode(CustomKeyboard.MODE_UPPERCASE)
|
||||
wait_for_render(10)
|
||||
|
||||
# Capture screenshot
|
||||
screenshot_path = f"{self.screenshot_dir}/custom_keyboard_uppercase.raw"
|
||||
print(f"Capturing screenshot: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
# Verify screenshot was created
|
||||
stat = os.stat(screenshot_path)
|
||||
self.assertTrue(stat[6] > 0, "Screenshot file is empty")
|
||||
print(f"Screenshot captured: {stat[6]} bytes")
|
||||
|
||||
print("=== Uppercase appearance test PASSED ===")
|
||||
|
||||
def test_keyboard_numbers_appearance(self):
|
||||
"""Test keyboard appearance in numbers/specials mode."""
|
||||
print("\n=== Testing numbers keyboard appearance ===")
|
||||
|
||||
screen, keyboard, textarea = self._create_test_keyboard_scene()
|
||||
|
||||
# Switch to numbers mode
|
||||
keyboard.set_mode(CustomKeyboard.MODE_NUMBERS)
|
||||
wait_for_render(10)
|
||||
|
||||
# Capture screenshot
|
||||
screenshot_path = f"{self.screenshot_dir}/custom_keyboard_numbers.raw"
|
||||
print(f"Capturing screenshot: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
# Verify screenshot was created
|
||||
stat = os.stat(screenshot_path)
|
||||
self.assertTrue(stat[6] > 0, "Screenshot file is empty")
|
||||
print(f"Screenshot captured: {stat[6]} bytes")
|
||||
|
||||
print("=== Numbers appearance test PASSED ===")
|
||||
|
||||
def test_keyboard_specials_appearance(self):
|
||||
"""Test keyboard appearance in additional specials mode."""
|
||||
print("\n=== Testing specials keyboard appearance ===")
|
||||
|
||||
screen, keyboard, textarea = self._create_test_keyboard_scene()
|
||||
|
||||
# Switch to specials mode
|
||||
keyboard.set_mode(CustomKeyboard.MODE_SPECIALS)
|
||||
wait_for_render(10)
|
||||
|
||||
# Capture screenshot
|
||||
screenshot_path = f"{self.screenshot_dir}/custom_keyboard_specials.raw"
|
||||
print(f"Capturing screenshot: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
# Verify screenshot was created
|
||||
stat = os.stat(screenshot_path)
|
||||
self.assertTrue(stat[6] > 0, "Screenshot file is empty")
|
||||
print(f"Screenshot captured: {stat[6]} bytes")
|
||||
|
||||
print("=== Specials appearance test PASSED ===")
|
||||
|
||||
def test_keyboard_visibility_light_mode(self):
|
||||
"""
|
||||
Test that custom keyboard buttons are visible in light mode.
|
||||
|
||||
This verifies that the theme fix is applied.
|
||||
"""
|
||||
print("\n=== Testing keyboard visibility in light mode ===")
|
||||
|
||||
# Set light mode (should already be default)
|
||||
import mpos.config
|
||||
import mpos.ui.theme
|
||||
prefs = mpos.config.SharedPreferences("theme_settings")
|
||||
editor = prefs.edit()
|
||||
editor.put_string("theme_light_dark", "light")
|
||||
editor.commit()
|
||||
mpos.ui.theme.set_theme(prefs)
|
||||
wait_for_render(10)
|
||||
|
||||
# Create keyboard
|
||||
screen, keyboard, textarea = self._create_test_keyboard_scene()
|
||||
|
||||
# Get button background color
|
||||
lvgl_keyboard = keyboard.get_lvgl_obj()
|
||||
bg_color = lvgl_keyboard.get_style_bg_color(lv.PART.ITEMS)
|
||||
|
||||
# Extract RGB (similar to keyboard styling test)
|
||||
try:
|
||||
color_dict = {
|
||||
'r': bg_color.red() if hasattr(bg_color, 'red') else 0,
|
||||
'g': bg_color.green() if hasattr(bg_color, 'green') else 0,
|
||||
'b': bg_color.blue() if hasattr(bg_color, 'blue') else 0,
|
||||
}
|
||||
except:
|
||||
try:
|
||||
color_int = bg_color.to_int() if hasattr(bg_color, 'to_int') else 0
|
||||
color_dict = {
|
||||
'r': (color_int >> 16) & 0xFF,
|
||||
'g': (color_int >> 8) & 0xFF,
|
||||
'b': color_int & 0xFF,
|
||||
}
|
||||
except:
|
||||
color_dict = {'r': 0, 'g': 0, 'b': 0}
|
||||
|
||||
print(f"Button background: RGB({color_dict['r']}, {color_dict['g']}, {color_dict['b']})")
|
||||
|
||||
# Verify buttons are NOT pure white (which would be invisible)
|
||||
if 'r' in color_dict:
|
||||
is_white = (color_dict['r'] >= 250 and
|
||||
color_dict['g'] >= 250 and
|
||||
color_dict['b'] >= 250)
|
||||
|
||||
self.assertFalse(
|
||||
is_white,
|
||||
f"Custom keyboard buttons are pure white in light mode (invisible)!"
|
||||
)
|
||||
|
||||
print("=== Visibility test PASSED ===")
|
||||
|
||||
def test_keyboard_with_standard_comparison(self):
|
||||
"""
|
||||
Test custom keyboard alongside standard keyboard.
|
||||
|
||||
Creates both for visual comparison.
|
||||
"""
|
||||
print("\n=== Testing custom vs standard keyboard ===")
|
||||
|
||||
# Create screen with two textareas
|
||||
screen = lv.obj()
|
||||
screen.set_size(320, 240)
|
||||
|
||||
# Top textarea with standard keyboard
|
||||
ta_standard = lv.textarea(screen)
|
||||
ta_standard.set_size(280, 30)
|
||||
ta_standard.set_pos(20, 5)
|
||||
ta_standard.set_placeholder_text("Standard")
|
||||
ta_standard.set_one_line(True)
|
||||
|
||||
# Create standard keyboard (hidden initially)
|
||||
keyboard_standard = create_keyboard(screen, custom=False)
|
||||
keyboard_standard.set_textarea(ta_standard)
|
||||
keyboard_standard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
keyboard_standard.set_style_min_height(145, 0)
|
||||
|
||||
# Load and render
|
||||
lv.screen_load(screen)
|
||||
wait_for_render(20)
|
||||
|
||||
# Capture standard keyboard
|
||||
screenshot_path = f"{self.screenshot_dir}/keyboard_standard_comparison.raw"
|
||||
print(f"Capturing standard keyboard: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
# Clean up
|
||||
lv.screen_load(lv.obj())
|
||||
wait_for_render(5)
|
||||
|
||||
# Now create custom keyboard
|
||||
screen2 = lv.obj()
|
||||
screen2.set_size(320, 240)
|
||||
|
||||
ta_custom = lv.textarea(screen2)
|
||||
ta_custom.set_size(280, 30)
|
||||
ta_custom.set_pos(20, 5)
|
||||
ta_custom.set_placeholder_text("Custom")
|
||||
ta_custom.set_one_line(True)
|
||||
|
||||
keyboard_custom = create_keyboard(screen2, custom=True)
|
||||
keyboard_custom.set_textarea(ta_custom)
|
||||
keyboard_custom.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
|
||||
lv.screen_load(screen2)
|
||||
wait_for_render(20)
|
||||
|
||||
# Capture custom keyboard
|
||||
screenshot_path = f"{self.screenshot_dir}/keyboard_custom_comparison.raw"
|
||||
print(f"Capturing custom keyboard: {screenshot_path}")
|
||||
capture_screenshot(screenshot_path, width=320, height=240)
|
||||
|
||||
print("=== Comparison test PASSED ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,192 @@
|
||||
"""
|
||||
Functional tests for CustomKeyboard.
|
||||
|
||||
Tests keyboard creation, mode switching, text input, and API compatibility.
|
||||
|
||||
Usage:
|
||||
Desktop: ./tests/unittest.sh tests/test_custom_keyboard.py
|
||||
Device: ./tests/unittest.sh tests/test_custom_keyboard.py ondevice
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import lvgl as lv
|
||||
from mpos.ui.keyboard import CustomKeyboard, create_keyboard
|
||||
|
||||
|
||||
class TestCustomKeyboard(unittest.TestCase):
|
||||
"""Test suite for CustomKeyboard functionality."""
|
||||
|
||||
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):
|
||||
"""Test that CustomKeyboard can be created."""
|
||||
print("Testing keyboard creation...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
|
||||
# Verify keyboard exists
|
||||
self.assertIsNotNone(keyboard)
|
||||
self.assertIsNotNone(keyboard.get_lvgl_obj())
|
||||
|
||||
print("Keyboard created successfully")
|
||||
|
||||
def test_keyboard_factory_custom(self):
|
||||
"""Test factory function creates custom keyboard."""
|
||||
print("Testing factory function with custom=True...")
|
||||
|
||||
keyboard = create_keyboard(self.screen, custom=True)
|
||||
|
||||
# Verify it's a CustomKeyboard instance
|
||||
self.assertIsInstance(keyboard, CustomKeyboard)
|
||||
|
||||
print("Factory created CustomKeyboard successfully")
|
||||
|
||||
def test_keyboard_factory_standard(self):
|
||||
"""Test factory function creates standard keyboard."""
|
||||
print("Testing factory function with custom=False...")
|
||||
|
||||
keyboard = create_keyboard(self.screen, custom=False)
|
||||
|
||||
# Verify it's an LVGL keyboard (not CustomKeyboard)
|
||||
self.assertFalse(isinstance(keyboard, CustomKeyboard),
|
||||
"Factory with custom=False should not create CustomKeyboard")
|
||||
# It should be an lv.keyboard instance
|
||||
self.assertEqual(type(keyboard).__name__, 'keyboard')
|
||||
|
||||
print("Factory created standard keyboard successfully")
|
||||
|
||||
def test_set_textarea(self):
|
||||
"""Test setting textarea association."""
|
||||
print("Testing set_textarea...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
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...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
|
||||
# Test setting different modes
|
||||
keyboard.set_mode(CustomKeyboard.MODE_LOWERCASE)
|
||||
keyboard.set_mode(CustomKeyboard.MODE_UPPERCASE)
|
||||
keyboard.set_mode(CustomKeyboard.MODE_NUMBERS)
|
||||
keyboard.set_mode(CustomKeyboard.MODE_SPECIALS)
|
||||
|
||||
print("Mode switching successful")
|
||||
|
||||
def test_alignment(self):
|
||||
"""Test keyboard alignment."""
|
||||
print("Testing alignment...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
|
||||
print("Alignment successful")
|
||||
|
||||
def test_height_settings(self):
|
||||
"""Test height configuration."""
|
||||
print("Testing height settings...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
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...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
|
||||
# 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...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
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):
|
||||
"""Test that CustomKeyboard has same API as lv.keyboard."""
|
||||
print("Testing API compatibility...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
|
||||
# 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),
|
||||
f"CustomKeyboard missing method: {method_name}"
|
||||
)
|
||||
self.assertTrue(
|
||||
callable(getattr(keyboard, method_name)),
|
||||
f"CustomKeyboard.{method_name} is not callable"
|
||||
)
|
||||
|
||||
print("API compatibility verified")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,189 @@
|
||||
"""
|
||||
Test CustomKeyboard animation support (show/hide with mpos.ui.anim).
|
||||
|
||||
This test reproduces the bug where CustomKeyboard is missing methods
|
||||
required by mpos.ui.anim.smooth_show() and smooth_hide().
|
||||
|
||||
Usage:
|
||||
Desktop: ./tests/unittest.sh tests/test_graphical_keyboard_animation.py
|
||||
Device: ./tests/unittest.sh tests/test_graphical_keyboard_animation.py ondevice
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import lvgl as lv
|
||||
import mpos.ui.anim
|
||||
from mpos.ui.keyboard import CustomKeyboard
|
||||
|
||||
|
||||
class TestKeyboardAnimation(unittest.TestCase):
|
||||
"""Test CustomKeyboard compatibility with animation system."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
# Create a test screen
|
||||
self.screen = lv.obj()
|
||||
self.screen.set_size(320, 240)
|
||||
lv.screen_load(self.screen)
|
||||
|
||||
# Create textarea
|
||||
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("\n=== Animation Test Setup Complete ===")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after test."""
|
||||
lv.screen_load(lv.obj())
|
||||
print("=== Test Cleanup Complete ===\n")
|
||||
|
||||
def test_keyboard_has_set_style_opa(self):
|
||||
"""
|
||||
Test that CustomKeyboard has set_style_opa method.
|
||||
|
||||
This method is required by mpos.ui.anim for fade animations.
|
||||
"""
|
||||
print("Testing that CustomKeyboard has set_style_opa...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.set_textarea(self.textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
keyboard.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
|
||||
# Verify method exists
|
||||
self.assertTrue(
|
||||
hasattr(keyboard, 'set_style_opa'),
|
||||
"CustomKeyboard missing set_style_opa method"
|
||||
)
|
||||
self.assertTrue(
|
||||
callable(getattr(keyboard, 'set_style_opa')),
|
||||
"CustomKeyboard.set_style_opa is not callable"
|
||||
)
|
||||
|
||||
# Try calling it (should not raise AttributeError)
|
||||
try:
|
||||
keyboard.set_style_opa(128, 0)
|
||||
print("set_style_opa called successfully")
|
||||
except AttributeError as e:
|
||||
self.fail(f"set_style_opa raised AttributeError: {e}")
|
||||
|
||||
print("=== set_style_opa test PASSED ===")
|
||||
|
||||
def test_keyboard_smooth_show(self):
|
||||
"""
|
||||
Test that CustomKeyboard can be shown with smooth_show animation.
|
||||
|
||||
This reproduces the actual user interaction in QuasiNametag.
|
||||
"""
|
||||
print("Testing smooth_show animation...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.set_textarea(self.textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
keyboard.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
|
||||
# This should work without raising AttributeError
|
||||
try:
|
||||
mpos.ui.anim.smooth_show(keyboard)
|
||||
print("smooth_show called successfully")
|
||||
except AttributeError as e:
|
||||
self.fail(f"smooth_show raised AttributeError: {e}\n"
|
||||
"This is the bug - CustomKeyboard missing animation methods")
|
||||
|
||||
# Verify keyboard is no longer hidden
|
||||
self.assertFalse(
|
||||
keyboard.has_flag(lv.obj.FLAG.HIDDEN),
|
||||
"Keyboard should not be hidden after smooth_show"
|
||||
)
|
||||
|
||||
print("=== smooth_show test PASSED ===")
|
||||
|
||||
def test_keyboard_smooth_hide(self):
|
||||
"""
|
||||
Test that CustomKeyboard can be hidden with smooth_hide animation.
|
||||
|
||||
This reproduces the hide behavior in QuasiNametag.
|
||||
"""
|
||||
print("Testing smooth_hide animation...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.set_textarea(self.textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
# Start visible
|
||||
keyboard.remove_flag(lv.obj.FLAG.HIDDEN)
|
||||
|
||||
# This should work without raising AttributeError
|
||||
try:
|
||||
mpos.ui.anim.smooth_hide(keyboard)
|
||||
print("smooth_hide called successfully")
|
||||
except AttributeError as e:
|
||||
self.fail(f"smooth_hide raised AttributeError: {e}\n"
|
||||
"This is the bug - CustomKeyboard missing animation methods")
|
||||
|
||||
print("=== smooth_hide test PASSED ===")
|
||||
|
||||
def test_keyboard_show_hide_cycle(self):
|
||||
"""
|
||||
Test full show/hide animation cycle.
|
||||
|
||||
This mimics the actual user flow:
|
||||
1. Click textarea -> show keyboard
|
||||
2. Press Enter/Cancel -> hide keyboard
|
||||
"""
|
||||
print("Testing full show/hide cycle...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.set_textarea(self.textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
keyboard.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
|
||||
# Initial state: hidden
|
||||
self.assertTrue(keyboard.has_flag(lv.obj.FLAG.HIDDEN))
|
||||
|
||||
# Show keyboard (simulates textarea click)
|
||||
try:
|
||||
mpos.ui.anim.smooth_show(keyboard)
|
||||
except AttributeError as e:
|
||||
self.fail(f"Failed during smooth_show: {e}")
|
||||
|
||||
# Should be visible now
|
||||
self.assertFalse(keyboard.has_flag(lv.obj.FLAG.HIDDEN))
|
||||
|
||||
# Hide keyboard (simulates pressing Enter)
|
||||
try:
|
||||
mpos.ui.anim.smooth_hide(keyboard)
|
||||
except AttributeError as e:
|
||||
self.fail(f"Failed during smooth_hide: {e}")
|
||||
|
||||
print("=== Full cycle test PASSED ===")
|
||||
|
||||
def test_keyboard_has_get_y_and_set_y(self):
|
||||
"""
|
||||
Test that CustomKeyboard has get_y and set_y methods.
|
||||
|
||||
These are required for slide animations (though not currently used).
|
||||
"""
|
||||
print("Testing get_y and set_y methods...")
|
||||
|
||||
keyboard = CustomKeyboard(self.screen)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
|
||||
# Verify methods exist
|
||||
self.assertTrue(hasattr(keyboard, 'get_y'), "Missing get_y method")
|
||||
self.assertTrue(hasattr(keyboard, 'set_y'), "Missing set_y method")
|
||||
|
||||
# Try using them
|
||||
try:
|
||||
y = keyboard.get_y()
|
||||
keyboard.set_y(y + 10)
|
||||
new_y = keyboard.get_y()
|
||||
print(f"Position test: {y} -> {new_y}")
|
||||
except AttributeError as e:
|
||||
self.fail(f"Position methods raised AttributeError: {e}")
|
||||
|
||||
print("=== Position methods test PASSED ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user