Skip to content

Commit 73b9095

Browse files
Add tests/test_graphical_hotspot_password.py
1 parent abcf4df commit 73b9095

4 files changed

Lines changed: 352 additions & 3 deletions

File tree

internal_filesystem/lib/mpos/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
wait_for_render, capture_screenshot, simulate_click, get_widget_coords,
3535
find_label_with_text, verify_text_present, print_screen_labels, find_text_on_screen,
3636
click_button, click_label, click_keyboard_button, find_button_with_text,
37-
get_all_widgets_with_text
37+
get_all_widgets_with_text, find_setting_value_label, get_setting_value_text,
38+
verify_setting_value_text, find_dropdown_widget, get_dropdown_options,
39+
find_dropdown_option_index, select_dropdown_option_by_text
3840
)
3941

4042
# UI utility functions
@@ -92,7 +94,9 @@
9294
"wait_for_render", "capture_screenshot", "simulate_click", "get_widget_coords",
9395
"find_label_with_text", "verify_text_present", "print_screen_labels", "find_text_on_screen",
9496
"click_button", "click_label", "click_keyboard_button", "find_button_with_text",
95-
"get_all_widgets_with_text",
97+
"get_all_widgets_with_text", "find_setting_value_label", "get_setting_value_text",
98+
"verify_setting_value_text", "find_dropdown_widget", "get_dropdown_options",
99+
"find_dropdown_option_index", "select_dropdown_option_by_text",
96100
# Submodules
97101
"ui", "config", "net", "content", "time", "sensor_manager",
98102
"camera_manager", "sdcard", "audio", "hardware",

internal_filesystem/lib/mpos/ui/testing.py

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ def get_screen_text_content(obj):
259259
pass # Error getting text
260260
return texts
261261

262-
263262
def verify_text_present(obj, expected_text):
264263
"""
265264
Verify that expected text is present somewhere on screen.
@@ -281,6 +280,87 @@ def verify_text_present(obj, expected_text):
281280
return find_label_with_text(obj, expected_text) is not None
282281

283282

283+
def find_setting_value_label(obj, setting_title_text):
284+
"""
285+
Find the value label associated with a SettingsActivity setting title.
286+
287+
SettingsActivity renders each setting as a container with two labels:
288+
a title label (large) and a value label (smaller) directly below it.
289+
This helper finds the title label, then returns the sibling value label.
290+
291+
Args:
292+
obj: LVGL object to search (typically lv.screen_active())
293+
setting_title_text: Text of the setting title (exact or substring)
294+
295+
Returns:
296+
LVGL label object for the value if found, None otherwise
297+
298+
Example:
299+
value_label = find_setting_value_label(lv.screen_active(), "Auth Mode")
300+
if value_label:
301+
assert value_label.get_text() == "(defaults to none)"
302+
"""
303+
title_label = find_label_with_text(obj, setting_title_text)
304+
if not title_label:
305+
return None
306+
try:
307+
parent = title_label.get_parent()
308+
if not parent:
309+
return None
310+
child_count = parent.get_child_count()
311+
for i in range(child_count):
312+
child = parent.get_child(i)
313+
if child is title_label:
314+
continue
315+
try:
316+
if hasattr(child, "get_text"):
317+
text = child.get_text()
318+
if text:
319+
return child
320+
except:
321+
pass
322+
except:
323+
pass
324+
return None
325+
326+
327+
def get_setting_value_text(obj, setting_title_text):
328+
"""
329+
Get the value text associated with a SettingsActivity setting title.
330+
331+
Args:
332+
obj: LVGL object to search (typically lv.screen_active())
333+
setting_title_text: Text of the setting title (exact or substring)
334+
335+
Returns:
336+
str or None: The value label text if found
337+
"""
338+
value_label = find_setting_value_label(obj, setting_title_text)
339+
if value_label:
340+
try:
341+
return value_label.get_text()
342+
except:
343+
return None
344+
return None
345+
346+
347+
def verify_setting_value_text(obj, setting_title_text, expected_text):
348+
"""
349+
Verify a SettingsActivity value label matches expected text.
350+
351+
Args:
352+
obj: LVGL object to search (typically lv.screen_active())
353+
setting_title_text: Text of the setting title (exact or substring)
354+
expected_text: Expected text for the value label (exact match)
355+
356+
Returns:
357+
bool: True if value label text matches expected, False otherwise
358+
"""
359+
value_text = get_setting_value_text(obj, setting_title_text)
360+
return value_text == expected_text
361+
362+
363+
284364
def text_to_hex(text):
285365
"""
286366
Convert text to hex representation for debugging.
@@ -414,6 +494,116 @@ def find_button_with_text(obj, search_text):
414494
return None
415495

416496

497+
def find_dropdown_widget(obj):
498+
"""
499+
Find a dropdown widget in the object hierarchy.
500+
501+
Args:
502+
obj: LVGL object to search (typically lv.screen_active())
503+
504+
Returns:
505+
LVGL dropdown object if found, None otherwise
506+
"""
507+
def find_dropdown_recursive(node):
508+
try:
509+
if node.__class__.__name__ == "dropdown" or hasattr(node, "get_selected"):
510+
if hasattr(node, "get_options"):
511+
return node
512+
except:
513+
pass
514+
515+
try:
516+
child_count = node.get_child_count()
517+
except:
518+
return None
519+
520+
for i in range(child_count):
521+
child = node.get_child(i)
522+
result = find_dropdown_recursive(child)
523+
if result:
524+
return result
525+
return None
526+
527+
return find_dropdown_recursive(obj)
528+
529+
530+
def get_dropdown_options(dropdown):
531+
"""
532+
Get dropdown options as a list of strings.
533+
534+
Args:
535+
dropdown: LVGL dropdown widget
536+
537+
Returns:
538+
list: List of option strings (order preserved)
539+
"""
540+
try:
541+
options = dropdown.get_options()
542+
if options:
543+
lines = options.split("\n")
544+
return [line for line in lines if line]
545+
except:
546+
pass
547+
return []
548+
549+
550+
def find_dropdown_option_index(dropdown, option_text, allow_partial=True):
551+
"""
552+
Find the index of an option in a dropdown by text.
553+
554+
Args:
555+
dropdown: LVGL dropdown widget
556+
option_text: Text to search for
557+
allow_partial: If True, match substring (default: True)
558+
559+
Returns:
560+
int or None: Index of matching option
561+
"""
562+
options = get_dropdown_options(dropdown)
563+
if options:
564+
for idx, text in enumerate(options):
565+
if (allow_partial and option_text in text) or (not allow_partial and option_text == text):
566+
return idx
567+
return None
568+
569+
try:
570+
option_count = dropdown.get_option_count()
571+
except:
572+
option_count = 0
573+
574+
for idx in range(option_count):
575+
try:
576+
text = dropdown.get_option_text(idx)
577+
if (allow_partial and option_text in text) or (not allow_partial and option_text == text):
578+
return idx
579+
except:
580+
pass
581+
582+
return None
583+
584+
585+
def select_dropdown_option_by_text(dropdown, option_text, allow_partial=True):
586+
"""
587+
Select a dropdown option by its text.
588+
589+
Args:
590+
dropdown: LVGL dropdown widget
591+
option_text: Text to select
592+
allow_partial: If True, match substring (default: True)
593+
594+
Returns:
595+
bool: True if option was found and selected
596+
"""
597+
idx = find_dropdown_option_index(dropdown, option_text, allow_partial=allow_partial)
598+
if idx is None:
599+
return False
600+
try:
601+
dropdown.set_selected(idx)
602+
return True
603+
except:
604+
return False
605+
606+
417607
def get_keyboard_button_coords(keyboard, button_text):
418608
"""
419609
Get the coordinates of a specific button on an LVGL keyboard/buttonmatrix.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""
2+
Graphical test for hotspot settings password defaults.
3+
4+
This test verifies that the hotspot settings screen shows the
5+
"(defaults to none)" value under the "Auth Mode" setting.
6+
7+
Usage:
8+
Desktop: ./tests/unittest.sh tests/test_graphical_hotspot_password.py
9+
Device: ./tests/unittest.sh tests/test_graphical_hotspot_password.py --ondevice
10+
"""
11+
12+
import unittest
13+
import lvgl as lv
14+
import mpos.ui
15+
from mpos import (
16+
AppManager,
17+
wait_for_render,
18+
print_screen_labels,
19+
click_button,
20+
verify_text_present,
21+
find_setting_value_label,
22+
get_setting_value_text,
23+
click_label,
24+
simulate_click,
25+
get_widget_coords,
26+
select_dropdown_option_by_text,
27+
find_dropdown_widget,
28+
SharedPreferences,
29+
)
30+
31+
32+
class TestGraphicalHotspotPassword(unittest.TestCase):
33+
"""Test suite for hotspot password defaults in settings UI."""
34+
35+
def _reset_hotspot_preferences(self):
36+
"""Clear hotspot preferences to ensure default values are shown."""
37+
prefs = SharedPreferences("com.micropythonos.settings.hotspot")
38+
editor = prefs.edit()
39+
editor.remove_all()
40+
editor.commit()
41+
42+
def _open_hotspot_settings_screen(self):
43+
"""Start hotspot app and open the Settings screen."""
44+
result = AppManager.start_app("com.micropythonos.settings.hotspot")
45+
self.assertTrue(result, "Failed to start hotspot settings app")
46+
wait_for_render(iterations=20)
47+
48+
screen = lv.screen_active()
49+
print("\nInitial screen labels:")
50+
print_screen_labels(screen)
51+
52+
self.assertTrue(
53+
click_button("Settings"),
54+
"Could not find Settings button in hotspot app",
55+
)
56+
wait_for_render(iterations=40)
57+
58+
screen = lv.screen_active()
59+
print("\nSettings screen labels:")
60+
print_screen_labels(screen)
61+
return screen
62+
63+
def tearDown(self):
64+
"""Clean up after each test method."""
65+
# Navigate back to launcher to close any opened apps
66+
try:
67+
mpos.ui.back_screen()
68+
wait_for_render(5)
69+
except:
70+
pass
71+
72+
def test_auth_mode_defaults_label(self):
73+
"""Verify Auth Mode shows defaults to none in hotspot settings."""
74+
print("\n=== Starting Hotspot Settings Auth Mode default test ===")
75+
76+
self._reset_hotspot_preferences()
77+
screen = self._open_hotspot_settings_screen()
78+
79+
self.assertTrue(
80+
verify_text_present(screen, "Auth Mode"),
81+
"Auth Mode setting title not found on settings screen",
82+
)
83+
84+
value_label = find_setting_value_label(screen, "Auth Mode")
85+
self.assertIsNotNone(
86+
value_label,
87+
"Could not find value label for Auth Mode setting",
88+
)
89+
90+
value_text = get_setting_value_text(screen, "Auth Mode")
91+
print(f"Auth Mode value text: {value_text}")
92+
self.assertEqual(
93+
value_text,
94+
"(defaults to none)",
95+
"Auth Mode value text did not match expected default",
96+
)
97+
98+
print("\n=== Hotspot settings Auth Mode default test completed ===")
99+
100+
def test_auth_mode_dropdown_select_wpa2(self):
101+
"""Change Auth Mode via dropdown and verify stored value label."""
102+
print("\n=== Starting Hotspot Settings Auth Mode dropdown test ===")
103+
104+
self._reset_hotspot_preferences()
105+
screen = self._open_hotspot_settings_screen()
106+
107+
self.assertTrue(
108+
click_label("Auth Mode"),
109+
"Could not click Auth Mode setting",
110+
)
111+
wait_for_render(iterations=40)
112+
113+
screen = lv.screen_active()
114+
print("\nAuth Mode edit screen labels:")
115+
print_screen_labels(screen)
116+
117+
dropdown = find_dropdown_widget(screen)
118+
self.assertIsNotNone(dropdown, "Auth Mode dropdown not found")
119+
120+
coords = get_widget_coords(dropdown)
121+
self.assertIsNotNone(coords, "Could not get dropdown coordinates")
122+
123+
print(f"Clicking dropdown at ({coords['center_x']}, {coords['center_y']})")
124+
simulate_click(coords["center_x"], coords["center_y"], press_duration_ms=100)
125+
wait_for_render(iterations=20)
126+
127+
self.assertTrue(
128+
select_dropdown_option_by_text(dropdown, "WPA2", allow_partial=True),
129+
"Could not select WPA2 option in dropdown",
130+
)
131+
wait_for_render(iterations=20)
132+
133+
self.assertTrue(
134+
click_button("Save"),
135+
"Could not click Save button in Auth Mode settings",
136+
)
137+
wait_for_render(iterations=40)
138+
139+
screen = lv.screen_active()
140+
print("\nSettings screen labels after save:")
141+
print_screen_labels(screen)
142+
143+
value_text = get_setting_value_text(screen, "Auth Mode")
144+
print(f"Auth Mode value text after save: {value_text}")
145+
self.assertEqual(
146+
value_text,
147+
"wpa2",
148+
"Auth Mode value did not update to wpa2",
149+
)
150+
151+
print("\n=== Hotspot settings Auth Mode dropdown test completed ===")
152+
153+
154+
if __name__ == "__main__":
155+
pass

tests/test_graphical_imu_calibration_ui_bug.py

100755100644
File mode changed.

0 commit comments

Comments
 (0)