diff --git a/internal_filesystem/builtin/apps/com.micropythonos.osupdate/assets/osupdate.py b/internal_filesystem/builtin/apps/com.micropythonos.osupdate/assets/osupdate.py index 11390c27..2da08e13 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.osupdate/assets/osupdate.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.osupdate/assets/osupdate.py @@ -708,20 +708,7 @@ class UpdateChecker: self.json = json_module if json_module else ujson def get_update_url(self, hardware_id): - """Determine the update JSON URL based on hardware ID. - - Args: - hardware_id: Hardware identifier string - - Returns: - str: Full URL to update JSON file - """ - if hardware_id == "waveshare_esp32_s3_touch_lcd_2": - # First supported device - no hardware ID in URL - infofile = "osupdate.json" - else: - infofile = f"osupdate_{hardware_id}.json" - return f"https://updates.micropythonos.com/{infofile}" + return f"https://updates.micropythonos.com/osupdate_{hardware_id}.json" async def fetch_update_info(self, hardware_id): """Fetch and parse update information from server. diff --git a/internal_filesystem/lib/mpos/net/wifi_service.py b/internal_filesystem/lib/mpos/net/wifi_service.py index 8cbf1fa0..9d83dc19 100644 --- a/internal_filesystem/lib/mpos/net/wifi_service.py +++ b/internal_filesystem/lib/mpos/net/wifi_service.py @@ -258,7 +258,7 @@ class WifiService: time_mod = time_module if time_module else time if WifiService.is_hotspot_enabled(network_module=network_module): - WifiService._needs_hotspot_restore = False + WifiService._needs_hotspot_restore = True WifiService.disable_hotspot(network_module=network_module) # Desktop mode - simulate successful connection diff --git a/internal_filesystem/lib/mpos/ui/testing.py b/internal_filesystem/lib/mpos/ui/testing.py index 2cfc600b..fbc2163b 100644 --- a/internal_filesystem/lib/mpos/ui/testing.py +++ b/internal_filesystem/lib/mpos/ui/testing.py @@ -44,6 +44,11 @@ Usage in apps: import lvgl as lv import time +try: + import unittest +except ImportError: # pragma: no cover - fallback for device builds without unittest + unittest = None + # Simulation globals for touch input _touch_x = 0 _touch_y = 0 @@ -51,6 +56,227 @@ _touch_pressed = False _touch_indev = None +class GraphicalTestCase(unittest.TestCase if unittest else object): + """ + Base class for graphical tests. + + Provides: + - Automatic screen creation and cleanup + - 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 + """ + + SCREEN_WIDTH = 320 + SCREEN_HEIGHT = 240 + DEFAULT_RENDER_ITERATIONS = 5 + + def setUp(self): + """Set up test fixtures before each test method.""" + 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.""" + lv.screen_load(lv.obj()) + self.wait_for_render() + + def wait_for_render(self, iterations=None): + """Wait for LVGL to render.""" + if iterations is None: + iterations = self.DEFAULT_RENDER_ITERATIONS + wait_for_render(iterations) + + def find_label_with_text(self, text, parent=None): + """Find a label containing the specified 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.""" + 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).""" + 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.""" + 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.""" + return click_label(text, use_send_event=use_send_event) + + def simulate_click(self, x, y): + """Simulate a click at specific coordinates.""" + simulate_click(x, y) + self.wait_for_render() + + def assertTextPresent(self, text, msg=None): + """Assert that text is present on screen.""" + 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.""" + if msg is None: + msg = f"Text '{text}' should not be on screen" + self.assertFalse(self.verify_text_present(text), msg) + + +class KeyboardTestCase(GraphicalTestCase): + """ + Base class for keyboard tests. + + Extends GraphicalTestCase with keyboard-specific functionality. + + Instance Attributes: + keyboard: The MposKeyboard instance (after create_keyboard_scene) + textarea: The textarea widget (after create_keyboard_scene) + """ + + 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 import MposKeyboard + + 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() + + 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. + + Args: + button_text: The text of the button to click (e.g., "q", "a", "Enter") + + Returns: + bool: True if button was clicked successfully + """ + 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.""" + 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.""" + 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.""" + 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.""" + 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.""" + 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.""" + 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.""" + 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.""" + for i in range(100): + 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.""" + buttons = [] + for i in range(100): + text = self.get_keyboard_button_text(i) + if text is None: + break + if text: + buttons.append((i, text)) + return buttons + + def wait_for_render(iterations=10): """ Wait for LVGL to process UI events and render. diff --git a/tests/base/__init__.py b/tests/base/__init__.py deleted file mode 100644 index 0d7c5b19..00000000 --- a/tests/base/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -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 - pass -""" - -from .graphical_test_base import GraphicalTestBase -from .keyboard_test_base import KeyboardTestBase - -__all__ = [ - 'GraphicalTestBase', - 'KeyboardTestBase', -] diff --git a/tests/base/graphical_test_base.py b/tests/base/graphical_test_base.py deleted file mode 100644 index b2497060..00000000 --- a/tests/base/graphical_test_base.py +++ /dev/null @@ -1,189 +0,0 @@ -""" -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 -- Common UI testing utilities - -Usage: - from base import GraphicalTestBase - - class TestMyApp(GraphicalTestBase): - def test_something(self): - # self.screen is already set up (320x240) - label = lv.label(self.screen) - label.set_text("Hello") - self.wait_for_render() -""" - -import unittest -import lvgl as lv - - -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 - """ - - SCREEN_WIDTH = 320 - SCREEN_HEIGHT = 240 - DEFAULT_RENDER_ITERATIONS = 5 - - 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 import wait_for_render - if iterations is None: - iterations = self.DEFAULT_RENDER_ITERATIONS - wait_for_render(iterations) - - 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 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 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 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 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 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 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) diff --git a/tests/base/keyboard_test_base.py b/tests/base/keyboard_test_base.py deleted file mode 100644 index 86670ec8..00000000 --- a/tests/base/keyboard_test_base.py +++ /dev/null @@ -1,223 +0,0 @@ -""" -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 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 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 diff --git a/tests/test_graphical_keyboard_animation.py b/tests/test_graphical_keyboard_animation.py index 569049fe..31e415d6 100644 --- a/tests/test_graphical_keyboard_animation.py +++ b/tests/test_graphical_keyboard_animation.py @@ -13,10 +13,10 @@ import unittest import lvgl as lv import time from mpos.ui.widget_animator import WidgetAnimator -from base import KeyboardTestBase +from mpos.ui.testing import KeyboardTestCase -class TestKeyboardAnimation(KeyboardTestBase): +class TestKeyboardAnimation(KeyboardTestCase): """Test MposKeyboard compatibility with animation system.""" def test_keyboard_has_set_style_opa(self): diff --git a/tests/test_graphical_keyboard_default_vs_custom.py b/tests/test_graphical_keyboard_default_vs_custom.py index ffc1976c..4751ae0d 100644 --- a/tests/test_graphical_keyboard_default_vs_custom.py +++ b/tests/test_graphical_keyboard_default_vs_custom.py @@ -10,23 +10,20 @@ Usage: import unittest import lvgl as lv -from mpos import MposKeyboard, wait_for_render +from mpos import MposKeyboard +from mpos.ui.testing import GraphicalTestCase -class TestDefaultVsCustomKeyboard(unittest.TestCase): +class TestDefaultVsCustomKeyboard(GraphicalTestCase): """Compare default LVGL keyboard with custom MposKeyboard.""" 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) + super().setUp() def tearDown(self): """Clean up.""" - lv.screen_load(lv.obj()) - wait_for_render(5) + super().tearDown() def test_default_lvgl_keyboard_layout(self): """ @@ -41,13 +38,13 @@ class TestDefaultVsCustomKeyboard(unittest.TestCase): textarea.set_size(280, 40) textarea.align(lv.ALIGN.TOP_MID, 0, 10) textarea.set_one_line(True) - wait_for_render(5) + self.wait_for_render(5) # Create DEFAULT LVGL keyboard keyboard = lv.keyboard(self.screen) keyboard.set_textarea(textarea) keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0) - wait_for_render(10) + self.wait_for_render(10) print("\nDefault LVGL keyboard buttons (first 40):") found_special_labels = {} @@ -87,13 +84,13 @@ class TestDefaultVsCustomKeyboard(unittest.TestCase): textarea.set_size(280, 40) textarea.align(lv.ALIGN.TOP_MID, 0, 10) textarea.set_one_line(True) - wait_for_render(5) + self.wait_for_render(5) # Create CUSTOM MposKeyboard keyboard = MposKeyboard(self.screen) keyboard.set_textarea(textarea) keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0) - wait_for_render(10) + self.wait_for_render(10) print("\nCustom MposKeyboard buttons (first 40):") found_special_labels = {} @@ -130,12 +127,12 @@ class TestDefaultVsCustomKeyboard(unittest.TestCase): textarea.set_size(280, 40) textarea.align(lv.ALIGN.TOP_MID, 0, 10) textarea.set_one_line(True) - wait_for_render(5) + self.wait_for_render(5) keyboard = MposKeyboard(self.screen) keyboard.set_textarea(textarea) keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0) - wait_for_render(10) + self.wait_for_render(10) # Step 1: Start in lowercase print("\nStep 1: Initial lowercase mode") @@ -146,7 +143,7 @@ class TestDefaultVsCustomKeyboard(unittest.TestCase): # Step 2: Switch to numbers print("\nStep 2: Switch to numbers mode") keyboard.set_mode(MposKeyboard.MODE_NUMBERS) - wait_for_render(5) + self.wait_for_render(5) labels_step2 = self._get_special_labels(keyboard) print(f" Labels: {list(labels_step2.keys())}") self.assertIn("Abc", labels_step2, "Should have 'Abc' in numbers mode") @@ -154,7 +151,7 @@ class TestDefaultVsCustomKeyboard(unittest.TestCase): # Step 3: Switch back to lowercase (this is where bug might happen) print("\nStep 3: Switch back to lowercase via set_mode()") keyboard.set_mode(MposKeyboard.MODE_LOWERCASE) - wait_for_render(5) + self.wait_for_render(5) labels_step3 = self._get_special_labels(keyboard) print(f" Labels: {list(labels_step3.keys())}") diff --git a/tests/test_graphical_keyboard_q_button_bug.py b/tests/test_graphical_keyboard_q_button_bug.py index f9de244f..18620d30 100644 --- a/tests/test_graphical_keyboard_q_button_bug.py +++ b/tests/test_graphical_keyboard_q_button_bug.py @@ -14,10 +14,10 @@ Usage: """ import unittest -from base import KeyboardTestBase +from mpos.ui.testing import KeyboardTestCase -class TestKeyboardQButton(KeyboardTestBase): +class TestKeyboardQButton(KeyboardTestCase): """Test keyboard button functionality (especially 'q' which was at index 0).""" def test_q_button_works(self): diff --git a/tests/test_osupdate.py b/tests/test_osupdate.py index f618ecdf..2dc2b2dd 100644 --- a/tests/test_osupdate.py +++ b/tests/test_osupdate.py @@ -66,7 +66,7 @@ class TestUpdateChecker(unittest.TestCase): """Test URL generation for waveshare hardware.""" url = self.checker.get_update_url("waveshare_esp32_s3_touch_lcd_2") - self.assertEqual(url, "https://updates.micropythonos.com/osupdate.json") + self.assertEqual(url, "https://updates.micropythonos.com/osupdate_waveshare_esp32_s3_touch_lcd_2.json") def test_get_update_url_other_hardware(self): """Test URL generation for other hardware."""