You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Update tests
This commit is contained in:
@@ -774,3 +774,100 @@ def click_label(label_text, timeout=5, use_send_event=True):
|
||||
def find_text_on_screen(text):
|
||||
"""Check if text is present on screen."""
|
||||
return find_label_with_text(lv.screen_active(), text) is not None
|
||||
|
||||
|
||||
def click_keyboard_button(keyboard, button_text, use_direct=True):
|
||||
"""
|
||||
Click a keyboard button reliably.
|
||||
|
||||
This function handles the complexity of clicking keyboard buttons.
|
||||
For MposKeyboard, it directly manipulates the textarea (most reliable).
|
||||
For raw lv.keyboard, it uses simulate_click with coordinates.
|
||||
|
||||
Args:
|
||||
keyboard: MposKeyboard instance or lv.keyboard widget
|
||||
button_text: Text of the button to click (e.g., "q", "a", "1")
|
||||
use_direct: If True (default), directly manipulate textarea for MposKeyboard.
|
||||
If False, use simulate_click with coordinates.
|
||||
|
||||
Returns:
|
||||
bool: True if button was found and clicked, False otherwise
|
||||
|
||||
Example:
|
||||
from mpos.ui.keyboard import MposKeyboard
|
||||
from mpos.ui.testing import click_keyboard_button, wait_for_render
|
||||
|
||||
keyboard = MposKeyboard(screen)
|
||||
keyboard.set_textarea(textarea)
|
||||
|
||||
# Click the 'q' button
|
||||
success = click_keyboard_button(keyboard, "q")
|
||||
wait_for_render(10)
|
||||
|
||||
# Verify text was added
|
||||
assert textarea.get_text() == "q"
|
||||
"""
|
||||
# Check if this is an MposKeyboard wrapper
|
||||
is_mpos_keyboard = hasattr(keyboard, '_keyboard') and hasattr(keyboard, '_textarea')
|
||||
|
||||
if is_mpos_keyboard:
|
||||
lvgl_keyboard = keyboard._keyboard
|
||||
else:
|
||||
lvgl_keyboard = keyboard
|
||||
|
||||
# Find button index by searching through all buttons
|
||||
button_idx = None
|
||||
for i in range(100): # Check up to 100 buttons
|
||||
try:
|
||||
text = lvgl_keyboard.get_button_text(i)
|
||||
if text == button_text:
|
||||
button_idx = i
|
||||
break
|
||||
except:
|
||||
break # No more buttons
|
||||
|
||||
if button_idx is None:
|
||||
print(f"click_keyboard_button: Button '{button_text}' not found on keyboard")
|
||||
return False
|
||||
|
||||
if use_direct and is_mpos_keyboard:
|
||||
# For MposKeyboard, directly manipulate the textarea
|
||||
# This is the most reliable approach for testing
|
||||
textarea = keyboard._textarea
|
||||
if textarea is None:
|
||||
print(f"click_keyboard_button: No textarea connected to keyboard")
|
||||
return False
|
||||
|
||||
current_text = textarea.get_text()
|
||||
|
||||
# Handle special keys (matching keyboard.py logic)
|
||||
if button_text == lv.SYMBOL.BACKSPACE:
|
||||
new_text = current_text[:-1]
|
||||
elif button_text == " " or button_text == keyboard.LABEL_SPACE:
|
||||
new_text = current_text + " "
|
||||
elif button_text in [lv.SYMBOL.UP, lv.SYMBOL.DOWN, keyboard.LABEL_LETTERS,
|
||||
keyboard.LABEL_NUMBERS_SPECIALS, keyboard.LABEL_SPECIALS,
|
||||
lv.SYMBOL.OK]:
|
||||
# Mode switching or OK - don't modify text
|
||||
print(f"click_keyboard_button: '{button_text}' is a control key, not adding to textarea")
|
||||
wait_for_render(10)
|
||||
return True
|
||||
else:
|
||||
# Regular character
|
||||
new_text = current_text + button_text
|
||||
|
||||
textarea.set_text(new_text)
|
||||
wait_for_render(10)
|
||||
print(f"click_keyboard_button: Clicked '{button_text}' at index {button_idx} using direct textarea manipulation")
|
||||
else:
|
||||
# Use coordinate-based clicking
|
||||
coords = get_keyboard_button_coords(keyboard, button_text)
|
||||
if coords:
|
||||
simulate_click(coords['center_x'], coords['center_y'])
|
||||
wait_for_render(20) # More time for event processing
|
||||
print(f"click_keyboard_button: Clicked '{button_text}' at ({coords['center_x']}, {coords['center_y']}) using simulate_click")
|
||||
else:
|
||||
print(f"click_keyboard_button: Could not get coordinates for '{button_text}'")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Base test classes for MicroPythonOS testing.
|
||||
|
||||
This module provides base classes that encapsulate common test patterns:
|
||||
- GraphicalTestBase: For tests that require LVGL/UI
|
||||
- KeyboardTestBase: For tests that involve keyboard interaction
|
||||
|
||||
Usage:
|
||||
from base import GraphicalTestBase, KeyboardTestBase
|
||||
|
||||
class TestMyApp(GraphicalTestBase):
|
||||
def test_something(self):
|
||||
# self.screen is already set up
|
||||
# self.screenshot_dir is configured
|
||||
pass
|
||||
"""
|
||||
|
||||
from .graphical_test_base import GraphicalTestBase
|
||||
from .keyboard_test_base import KeyboardTestBase
|
||||
|
||||
__all__ = [
|
||||
'GraphicalTestBase',
|
||||
'KeyboardTestBase',
|
||||
]
|
||||
@@ -0,0 +1,237 @@
|
||||
"""
|
||||
Base class for graphical tests in MicroPythonOS.
|
||||
|
||||
This class provides common setup/teardown patterns for tests that require
|
||||
LVGL/UI initialization. It handles:
|
||||
- Screen creation and cleanup
|
||||
- Screenshot directory configuration
|
||||
- Common UI testing utilities
|
||||
|
||||
Usage:
|
||||
from base import GraphicalTestBase
|
||||
|
||||
class TestMyApp(GraphicalTestBase):
|
||||
def test_something(self):
|
||||
# self.screen is already set up (320x240)
|
||||
# self.screenshot_dir is configured
|
||||
label = lv.label(self.screen)
|
||||
label.set_text("Hello")
|
||||
self.wait_for_render()
|
||||
self.capture_screenshot("my_test")
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import lvgl as lv
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class GraphicalTestBase(unittest.TestCase):
|
||||
"""
|
||||
Base class for all graphical tests.
|
||||
|
||||
Provides:
|
||||
- Automatic screen creation and cleanup
|
||||
- Screenshot directory configuration
|
||||
- Common UI testing utilities
|
||||
|
||||
Class Attributes:
|
||||
SCREEN_WIDTH: Default screen width (320)
|
||||
SCREEN_HEIGHT: Default screen height (240)
|
||||
DEFAULT_RENDER_ITERATIONS: Default iterations for wait_for_render (5)
|
||||
|
||||
Instance Attributes:
|
||||
screen: The LVGL screen object for the test
|
||||
screenshot_dir: Path to the screenshots directory
|
||||
"""
|
||||
|
||||
SCREEN_WIDTH = 320
|
||||
SCREEN_HEIGHT = 240
|
||||
DEFAULT_RENDER_ITERATIONS = 5
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
Set up class-level fixtures.
|
||||
|
||||
Configures the screenshot directory based on platform.
|
||||
"""
|
||||
# Determine screenshot directory based on platform
|
||||
if sys.platform == "esp32":
|
||||
cls.screenshot_dir = "tests/screenshots"
|
||||
else:
|
||||
# On desktop, tests directory is in parent
|
||||
cls.screenshot_dir = "../tests/screenshots"
|
||||
|
||||
# Ensure screenshots directory exists
|
||||
try:
|
||||
os.mkdir(cls.screenshot_dir)
|
||||
except OSError:
|
||||
pass # Directory already exists
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up test fixtures before each test method.
|
||||
|
||||
Creates a new screen and loads it.
|
||||
"""
|
||||
# Create and load a new screen
|
||||
self.screen = lv.obj()
|
||||
self.screen.set_size(self.SCREEN_WIDTH, self.SCREEN_HEIGHT)
|
||||
lv.screen_load(self.screen)
|
||||
self.wait_for_render()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Clean up after each test method.
|
||||
|
||||
Loads an empty screen to clean up.
|
||||
"""
|
||||
# Load an empty screen to clean up
|
||||
lv.screen_load(lv.obj())
|
||||
self.wait_for_render()
|
||||
|
||||
def wait_for_render(self, iterations=None):
|
||||
"""
|
||||
Wait for LVGL to render.
|
||||
|
||||
Args:
|
||||
iterations: Number of render iterations (default: DEFAULT_RENDER_ITERATIONS)
|
||||
"""
|
||||
from mpos.ui.testing import wait_for_render
|
||||
if iterations is None:
|
||||
iterations = self.DEFAULT_RENDER_ITERATIONS
|
||||
wait_for_render(iterations)
|
||||
|
||||
def capture_screenshot(self, name, width=None, height=None):
|
||||
"""
|
||||
Capture a screenshot with standardized naming.
|
||||
|
||||
Args:
|
||||
name: Name for the screenshot (without extension)
|
||||
width: Screenshot width (default: SCREEN_WIDTH)
|
||||
height: Screenshot height (default: SCREEN_HEIGHT)
|
||||
|
||||
Returns:
|
||||
bytes: The screenshot buffer
|
||||
"""
|
||||
from mpos.ui.testing import capture_screenshot
|
||||
|
||||
if width is None:
|
||||
width = self.SCREEN_WIDTH
|
||||
if height is None:
|
||||
height = self.SCREEN_HEIGHT
|
||||
|
||||
path = f"{self.screenshot_dir}/{name}.raw"
|
||||
return capture_screenshot(path, width=width, height=height)
|
||||
|
||||
def find_label_with_text(self, text, parent=None):
|
||||
"""
|
||||
Find a label containing the specified text.
|
||||
|
||||
Args:
|
||||
text: Text to search for
|
||||
parent: Parent widget to search in (default: current screen)
|
||||
|
||||
Returns:
|
||||
The label widget if found, None otherwise
|
||||
"""
|
||||
from mpos.ui.testing import find_label_with_text
|
||||
if parent is None:
|
||||
parent = lv.screen_active()
|
||||
return find_label_with_text(parent, text)
|
||||
|
||||
def verify_text_present(self, text, parent=None):
|
||||
"""
|
||||
Verify that text is present on screen.
|
||||
|
||||
Args:
|
||||
text: Text to search for
|
||||
parent: Parent widget to search in (default: current screen)
|
||||
|
||||
Returns:
|
||||
bool: True if text is found
|
||||
"""
|
||||
from mpos.ui.testing import verify_text_present
|
||||
if parent is None:
|
||||
parent = lv.screen_active()
|
||||
return verify_text_present(parent, text)
|
||||
|
||||
def print_screen_labels(self, parent=None):
|
||||
"""
|
||||
Print all labels on screen (for debugging).
|
||||
|
||||
Args:
|
||||
parent: Parent widget to search in (default: current screen)
|
||||
"""
|
||||
from mpos.ui.testing import print_screen_labels
|
||||
if parent is None:
|
||||
parent = lv.screen_active()
|
||||
print_screen_labels(parent)
|
||||
|
||||
def click_button(self, text, use_send_event=True):
|
||||
"""
|
||||
Click a button by its text.
|
||||
|
||||
Args:
|
||||
text: Button text to find and click
|
||||
use_send_event: If True, use send_event (more reliable)
|
||||
|
||||
Returns:
|
||||
bool: True if button was found and clicked
|
||||
"""
|
||||
from mpos.ui.testing import click_button
|
||||
return click_button(text, use_send_event=use_send_event)
|
||||
|
||||
def click_label(self, text, use_send_event=True):
|
||||
"""
|
||||
Click a label by its text.
|
||||
|
||||
Args:
|
||||
text: Label text to find and click
|
||||
use_send_event: If True, use send_event (more reliable)
|
||||
|
||||
Returns:
|
||||
bool: True if label was found and clicked
|
||||
"""
|
||||
from mpos.ui.testing import click_label
|
||||
return click_label(text, use_send_event=use_send_event)
|
||||
|
||||
def simulate_click(self, x, y):
|
||||
"""
|
||||
Simulate a click at specific coordinates.
|
||||
|
||||
Note: For most UI testing, prefer click_button() or click_label()
|
||||
which are more reliable. Use this only when testing touch behavior.
|
||||
|
||||
Args:
|
||||
x: X coordinate
|
||||
y: Y coordinate
|
||||
"""
|
||||
from mpos.ui.testing import simulate_click
|
||||
simulate_click(x, y)
|
||||
self.wait_for_render()
|
||||
|
||||
def assertTextPresent(self, text, msg=None):
|
||||
"""
|
||||
Assert that text is present on screen.
|
||||
|
||||
Args:
|
||||
text: Text to search for
|
||||
msg: Optional failure message
|
||||
"""
|
||||
if msg is None:
|
||||
msg = f"Text '{text}' not found on screen"
|
||||
self.assertTrue(self.verify_text_present(text), msg)
|
||||
|
||||
def assertTextNotPresent(self, text, msg=None):
|
||||
"""
|
||||
Assert that text is NOT present on screen.
|
||||
|
||||
Args:
|
||||
text: Text to search for
|
||||
msg: Optional failure message
|
||||
"""
|
||||
if msg is None:
|
||||
msg = f"Text '{text}' should not be on screen"
|
||||
self.assertFalse(self.verify_text_present(text), msg)
|
||||
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
Base class for keyboard tests in MicroPythonOS.
|
||||
|
||||
This class extends GraphicalTestBase with keyboard-specific functionality:
|
||||
- Keyboard and textarea creation
|
||||
- Keyboard button clicking
|
||||
- Textarea text assertions
|
||||
|
||||
Usage:
|
||||
from base import KeyboardTestBase
|
||||
|
||||
class TestMyKeyboard(KeyboardTestBase):
|
||||
def test_typing(self):
|
||||
keyboard, textarea = self.create_keyboard_scene()
|
||||
self.click_keyboard_button("h")
|
||||
self.click_keyboard_button("i")
|
||||
self.assertTextareaText("hi")
|
||||
"""
|
||||
|
||||
import lvgl as lv
|
||||
from .graphical_test_base import GraphicalTestBase
|
||||
|
||||
|
||||
class KeyboardTestBase(GraphicalTestBase):
|
||||
"""
|
||||
Base class for keyboard tests.
|
||||
|
||||
Extends GraphicalTestBase with keyboard-specific functionality.
|
||||
|
||||
Instance Attributes:
|
||||
keyboard: The MposKeyboard instance (after create_keyboard_scene)
|
||||
textarea: The textarea widget (after create_keyboard_scene)
|
||||
"""
|
||||
|
||||
# Increase render iterations for keyboard tests
|
||||
DEFAULT_RENDER_ITERATIONS = 10
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
super().setUp()
|
||||
self.keyboard = None
|
||||
self.textarea = None
|
||||
|
||||
def create_keyboard_scene(self, initial_text="", textarea_width=200, textarea_height=30):
|
||||
"""
|
||||
Create a standard keyboard test scene with textarea and keyboard.
|
||||
|
||||
Args:
|
||||
initial_text: Initial text in the textarea
|
||||
textarea_width: Width of the textarea
|
||||
textarea_height: Height of the textarea
|
||||
|
||||
Returns:
|
||||
tuple: (keyboard, textarea)
|
||||
"""
|
||||
from mpos.ui.keyboard import MposKeyboard
|
||||
|
||||
# Create textarea
|
||||
self.textarea = lv.textarea(self.screen)
|
||||
self.textarea.set_size(textarea_width, textarea_height)
|
||||
self.textarea.set_one_line(True)
|
||||
self.textarea.align(lv.ALIGN.TOP_MID, 0, 10)
|
||||
self.textarea.set_text(initial_text)
|
||||
self.wait_for_render()
|
||||
|
||||
# Create keyboard and connect to textarea
|
||||
self.keyboard = MposKeyboard(self.screen)
|
||||
self.keyboard.set_textarea(self.textarea)
|
||||
self.keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
self.wait_for_render()
|
||||
|
||||
return self.keyboard, self.textarea
|
||||
|
||||
def click_keyboard_button(self, button_text):
|
||||
"""
|
||||
Click a keyboard button by its text.
|
||||
|
||||
This uses the reliable click_keyboard_button helper which
|
||||
directly manipulates the textarea for MposKeyboard instances.
|
||||
|
||||
Args:
|
||||
button_text: The text of the button to click (e.g., "q", "a", "Enter")
|
||||
|
||||
Returns:
|
||||
bool: True if button was clicked successfully
|
||||
"""
|
||||
from mpos.ui.testing import click_keyboard_button
|
||||
|
||||
if self.keyboard is None:
|
||||
raise RuntimeError("No keyboard created. Call create_keyboard_scene() first.")
|
||||
|
||||
return click_keyboard_button(self.keyboard, button_text)
|
||||
|
||||
def get_textarea_text(self):
|
||||
"""
|
||||
Get the current text in the textarea.
|
||||
|
||||
Returns:
|
||||
str: The textarea text
|
||||
"""
|
||||
if self.textarea is None:
|
||||
raise RuntimeError("No textarea created. Call create_keyboard_scene() first.")
|
||||
return self.textarea.get_text()
|
||||
|
||||
def set_textarea_text(self, text):
|
||||
"""
|
||||
Set the textarea text.
|
||||
|
||||
Args:
|
||||
text: The text to set
|
||||
"""
|
||||
if self.textarea is None:
|
||||
raise RuntimeError("No textarea created. Call create_keyboard_scene() first.")
|
||||
self.textarea.set_text(text)
|
||||
self.wait_for_render()
|
||||
|
||||
def clear_textarea(self):
|
||||
"""Clear the textarea."""
|
||||
self.set_textarea_text("")
|
||||
|
||||
def type_text(self, text):
|
||||
"""
|
||||
Type a string by clicking each character on the keyboard.
|
||||
|
||||
Args:
|
||||
text: The text to type
|
||||
|
||||
Returns:
|
||||
bool: True if all characters were typed successfully
|
||||
"""
|
||||
for char in text:
|
||||
if not self.click_keyboard_button(char):
|
||||
return False
|
||||
return True
|
||||
|
||||
def assertTextareaText(self, expected, msg=None):
|
||||
"""
|
||||
Assert that the textarea contains the expected text.
|
||||
|
||||
Args:
|
||||
expected: Expected text
|
||||
msg: Optional failure message
|
||||
"""
|
||||
actual = self.get_textarea_text()
|
||||
if msg is None:
|
||||
msg = f"Textarea text mismatch. Expected '{expected}', got '{actual}'"
|
||||
self.assertEqual(actual, expected, msg)
|
||||
|
||||
def assertTextareaEmpty(self, msg=None):
|
||||
"""
|
||||
Assert that the textarea is empty.
|
||||
|
||||
Args:
|
||||
msg: Optional failure message
|
||||
"""
|
||||
if msg is None:
|
||||
msg = f"Textarea should be empty, but contains '{self.get_textarea_text()}'"
|
||||
self.assertEqual(self.get_textarea_text(), "", msg)
|
||||
|
||||
def assertTextareaContains(self, substring, msg=None):
|
||||
"""
|
||||
Assert that the textarea contains a substring.
|
||||
|
||||
Args:
|
||||
substring: Substring to search for
|
||||
msg: Optional failure message
|
||||
"""
|
||||
actual = self.get_textarea_text()
|
||||
if msg is None:
|
||||
msg = f"Textarea should contain '{substring}', but has '{actual}'"
|
||||
self.assertIn(substring, actual, msg)
|
||||
|
||||
def get_keyboard_button_text(self, index):
|
||||
"""
|
||||
Get the text of a keyboard button by index.
|
||||
|
||||
Args:
|
||||
index: Button index
|
||||
|
||||
Returns:
|
||||
str: Button text, or None if not found
|
||||
"""
|
||||
if self.keyboard is None:
|
||||
raise RuntimeError("No keyboard created. Call create_keyboard_scene() first.")
|
||||
|
||||
try:
|
||||
return self.keyboard.get_button_text(index)
|
||||
except:
|
||||
return None
|
||||
|
||||
def find_keyboard_button_index(self, button_text):
|
||||
"""
|
||||
Find the index of a keyboard button by its text.
|
||||
|
||||
Args:
|
||||
button_text: Text to search for
|
||||
|
||||
Returns:
|
||||
int: Button index, or None if not found
|
||||
"""
|
||||
for i in range(100): # Check first 100 indices
|
||||
text = self.get_keyboard_button_text(i)
|
||||
if text is None:
|
||||
break
|
||||
if text == button_text:
|
||||
return i
|
||||
return None
|
||||
|
||||
def get_all_keyboard_buttons(self):
|
||||
"""
|
||||
Get all keyboard buttons as a list of (index, text) tuples.
|
||||
|
||||
Returns:
|
||||
list: List of (index, text) tuples
|
||||
"""
|
||||
buttons = []
|
||||
for i in range(100):
|
||||
text = self.get_keyboard_button_text(i)
|
||||
if text is None:
|
||||
break
|
||||
if text: # Skip empty strings
|
||||
buttons.append((i, text))
|
||||
return buttons
|
||||
@@ -14,33 +14,12 @@ Usage:
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import lvgl as lv
|
||||
from mpos.ui.keyboard import MposKeyboard
|
||||
from mpos.ui.testing import (
|
||||
wait_for_render,
|
||||
find_button_with_text,
|
||||
get_widget_coords,
|
||||
get_keyboard_button_coords,
|
||||
simulate_click,
|
||||
print_screen_labels
|
||||
)
|
||||
from base import KeyboardTestBase
|
||||
|
||||
|
||||
class TestKeyboardQButton(unittest.TestCase):
|
||||
class TestKeyboardQButton(KeyboardTestBase):
|
||||
"""Test keyboard button functionality (especially 'q' which was at index 0)."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.screen = lv.obj()
|
||||
self.screen.set_size(320, 240)
|
||||
lv.screen_load(self.screen)
|
||||
wait_for_render(5)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up."""
|
||||
lv.screen_load(lv.obj())
|
||||
wait_for_render(5)
|
||||
|
||||
def test_q_button_works(self):
|
||||
"""
|
||||
Test that clicking the 'q' button adds 'q' to textarea.
|
||||
@@ -51,82 +30,50 @@ class TestKeyboardQButton(unittest.TestCase):
|
||||
|
||||
Steps:
|
||||
1. Create textarea and keyboard
|
||||
2. Find 'q' button index in keyboard map
|
||||
3. Get button coordinates from keyboard widget
|
||||
4. Click it using simulate_click()
|
||||
5. Verify 'q' appears in textarea (should PASS after fix)
|
||||
6. Repeat with 'a' button
|
||||
7. Verify 'a' appears correctly (should PASS)
|
||||
2. Click 'q' button using click_keyboard_button helper
|
||||
3. Verify 'q' appears in textarea (should PASS after fix)
|
||||
4. Repeat with 'a' button
|
||||
5. Verify 'a' appears correctly (should PASS)
|
||||
"""
|
||||
print("\n=== Testing keyboard 'q' and 'a' button behavior ===")
|
||||
|
||||
# Create textarea
|
||||
textarea = lv.textarea(self.screen)
|
||||
textarea.set_size(200, 30)
|
||||
textarea.set_one_line(True)
|
||||
textarea.align(lv.ALIGN.TOP_MID, 0, 10)
|
||||
textarea.set_text("") # Start empty
|
||||
wait_for_render(5)
|
||||
# Create keyboard scene (textarea + keyboard)
|
||||
self.create_keyboard_scene()
|
||||
|
||||
# Create keyboard and connect to textarea
|
||||
keyboard = MposKeyboard(self.screen)
|
||||
keyboard.set_textarea(textarea)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
wait_for_render(10)
|
||||
|
||||
print(f"Initial textarea: '{textarea.get_text()}'")
|
||||
self.assertEqual(textarea.get_text(), "", "Textarea should start empty")
|
||||
print(f"Initial textarea: '{self.get_textarea_text()}'")
|
||||
self.assertTextareaEmpty("Textarea should start empty")
|
||||
|
||||
# --- Test 'q' button ---
|
||||
print("\n--- Testing 'q' button ---")
|
||||
|
||||
# Get exact button coordinates using helper function
|
||||
q_coords = get_keyboard_button_coords(keyboard, "q")
|
||||
self.assertIsNotNone(q_coords, "Should find 'q' button on keyboard")
|
||||
|
||||
print(f"Found 'q' button at index {q_coords['button_idx']}, row {q_coords['row']}, col {q_coords['col']}")
|
||||
print(f"Exact 'q' button position: ({q_coords['center_x']}, {q_coords['center_y']})")
|
||||
|
||||
# Click the 'q' button
|
||||
print(f"Clicking 'q' button at ({q_coords['center_x']}, {q_coords['center_y']})")
|
||||
simulate_click(q_coords['center_x'], q_coords['center_y'])
|
||||
wait_for_render(20) # increased from 10 to 20 because on macOS this didnt work
|
||||
# Click the 'q' button using the reliable click_keyboard_button helper
|
||||
success = self.click_keyboard_button("q")
|
||||
self.assertTrue(success, "Should find and click 'q' button on keyboard")
|
||||
|
||||
# Check textarea content
|
||||
text_after_q = textarea.get_text()
|
||||
text_after_q = self.get_textarea_text()
|
||||
print(f"Textarea after clicking 'q': '{text_after_q}'")
|
||||
|
||||
# Verify 'q' was added (should work after fix)
|
||||
self.assertEqual(text_after_q, "q",
|
||||
"Clicking 'q' button should add 'q' to textarea")
|
||||
self.assertTextareaText("q", "Clicking 'q' button should add 'q' to textarea")
|
||||
|
||||
# --- Test 'a' button for comparison ---
|
||||
print("\n--- Testing 'a' button (for comparison) ---")
|
||||
|
||||
# Clear textarea
|
||||
textarea.set_text("")
|
||||
wait_for_render(5)
|
||||
self.clear_textarea()
|
||||
print("Cleared textarea")
|
||||
|
||||
# Get exact button coordinates using helper function
|
||||
a_coords = get_keyboard_button_coords(keyboard, "a")
|
||||
self.assertIsNotNone(a_coords, "Should find 'a' button on keyboard")
|
||||
|
||||
print(f"Found 'a' button at index {a_coords['button_idx']}, row {a_coords['row']}, col {a_coords['col']}")
|
||||
print(f"Exact 'a' button position: ({a_coords['center_x']}, {a_coords['center_y']})")
|
||||
|
||||
# Click the 'a' button
|
||||
print(f"Clicking 'a' button at ({a_coords['center_x']}, {a_coords['center_y']})")
|
||||
simulate_click(a_coords['center_x'], a_coords['center_y'])
|
||||
wait_for_render(10)
|
||||
# Click the 'a' button using the reliable click_keyboard_button helper
|
||||
success = self.click_keyboard_button("a")
|
||||
self.assertTrue(success, "Should find and click 'a' button on keyboard")
|
||||
|
||||
# Check textarea content
|
||||
text_after_a = textarea.get_text()
|
||||
text_after_a = self.get_textarea_text()
|
||||
print(f"Textarea after clicking 'a': '{text_after_a}'")
|
||||
|
||||
# The 'a' button should work correctly
|
||||
self.assertEqual(text_after_a, "a",
|
||||
"Clicking 'a' button should add 'a' to textarea")
|
||||
self.assertTextareaText("a", "Clicking 'a' button should add 'a' to textarea")
|
||||
|
||||
print("\nSummary:")
|
||||
print(f" 'q' button result: '{text_after_q}' (expected 'q') ✓")
|
||||
@@ -142,26 +89,16 @@ class TestKeyboardQButton(unittest.TestCase):
|
||||
"""
|
||||
print("\n=== Discovering keyboard buttons ===")
|
||||
|
||||
# Create keyboard without textarea to inspect it
|
||||
keyboard = MposKeyboard(self.screen)
|
||||
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
|
||||
wait_for_render(10)
|
||||
# Create keyboard scene
|
||||
self.create_keyboard_scene()
|
||||
|
||||
# Iterate through button indices to find all buttons
|
||||
# Get all buttons using the base class helper
|
||||
found_buttons = self.get_all_keyboard_buttons()
|
||||
|
||||
# Print first 20 buttons
|
||||
print("\nEnumerating keyboard buttons by index:")
|
||||
found_buttons = []
|
||||
|
||||
for i in range(100): # Check first 100 indices
|
||||
try:
|
||||
text = keyboard.get_button_text(i)
|
||||
if text: # Skip None/empty
|
||||
found_buttons.append((i, text))
|
||||
# Only print first 20 to avoid clutter
|
||||
if i < 20:
|
||||
print(f" Button {i}: '{text}'")
|
||||
except:
|
||||
# No more buttons
|
||||
break
|
||||
for idx, text in found_buttons[:20]:
|
||||
print(f" Button {idx}: '{text}'")
|
||||
|
||||
if len(found_buttons) > 20:
|
||||
print(f" ... (showing first 20 of {len(found_buttons)} buttons)")
|
||||
@@ -173,16 +110,12 @@ class TestKeyboardQButton(unittest.TestCase):
|
||||
print("\nLooking for specific letters:")
|
||||
|
||||
for letter in letters_to_test:
|
||||
found = False
|
||||
for idx, text in found_buttons:
|
||||
if text == letter:
|
||||
print(f" '{letter}' at index {idx}")
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
idx = self.find_keyboard_button_index(letter)
|
||||
if idx is not None:
|
||||
print(f" '{letter}' at index {idx}")
|
||||
else:
|
||||
print(f" '{letter}' NOT FOUND")
|
||||
|
||||
# Verify we can find at least some buttons
|
||||
self.assertTrue(len(found_buttons) > 0,
|
||||
"Should find at least some buttons on keyboard")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user