""" Test for keyboard button functionality (originally created to fix "q" button bug). This test verifies that all keyboard buttons work correctly, including the 'q' button which was previously broken due to button index 0 being treated as False in Python's truthiness check. The bug was: `if not button:` would return True when button index was 0, causing the 'q' key to be ignored. Fixed by changing to `if button is None:`. Usage: Desktop: ./tests/unittest.sh tests/test_graphical_keyboard_q_button_bug.py Device: ./tests/unittest.sh tests/test_graphical_keyboard_q_button_bug.py --ondevice """ 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 ) class TestKeyboardQButton(unittest.TestCase): """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. This test verifies the fix for the bug where: - Bug: Button index 0 ('q') was treated as False in `if not button:` - Fix: Changed to `if button is None:` to properly handle index 0 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) """ 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 and connect to textarea keyboard = MposKeyboard(self.screen) keyboard.set_textarea(textarea) keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0) wait_for_render(20) # increased from 10 to 20 because on macOS this didnt work print(f"Initial textarea: '{textarea.get_text()}'") self.assertEqual(textarea.get_text(), "", "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 # Check textarea content text_after_q = textarea.get_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") # --- Test 'a' button for comparison --- print("\n--- Testing 'a' button (for comparison) ---") # Clear textarea textarea.set_text("") wait_for_render(5) 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) # Check textarea content text_after_a = textarea.get_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") print("\nSummary:") print(f" 'q' button result: '{text_after_q}' (expected 'q') ✓") print(f" 'a' button result: '{text_after_a}' (expected 'a') ✓") print(" Both buttons work correctly!") def test_keyboard_button_discovery(self): """ Debug test: Discover all buttons on the keyboard. This test helps understand the keyboard layout and button structure. It prints all found buttons and their text. """ 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) # Iterate through button indices to find all 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 if len(found_buttons) > 20: print(f" ... (showing first 20 of {len(found_buttons)} buttons)") print(f"\nTotal buttons found: {len(found_buttons)}") # Try to find specific letters letters_to_test = ['q', 'w', 'e', 'r', 'a', 's', 'd', 'f'] 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: 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")