From 914dd8458f73f2ba5db5e120c2e4cfffa4f9f592 Mon Sep 17 00:00:00 2001 From: MattL1989 Date: Thu, 29 Oct 2026 22:19:28 +0000 Subject: [PATCH] fix(flutter): accept a FlutterFinder as scrollView in scroll_till_visible The scroll_till_visible docstring described scrollView as a string with the default 'Scrollable', but the Flutter integration server parses scrollView as a finder map (FindElementModel), so a string fails on the server. Passing a FlutterFinder, which is what the other Flutter commands take, failed on the client with "Object of type FlutterFinder is not JSON serializable"; only a hand-built dict from FlutterFinder.to_dict() worked. Convert a FlutterFinder scrollView to its dict form, keep accepting dicts, and document the argument correctly. Co-Authored-By: Claude Opus 5.5 --- .../flutter_integration/flutter_commands.py | 4 +++- .../flutter_actions_test.py | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/appium/webdriver/extensions/flutter_integration/flutter_commands.py b/appium/webdriver/extensions/flutter_integration/flutter_commands.py index 365ee29f..fea368c8 100644 --- a/appium/webdriver/extensions/flutter_integration/flutter_commands.py +++ b/appium/webdriver/extensions/flutter_integration/flutter_commands.py @@ -131,7 +131,7 @@ def scroll_till_visible( scroll_direction (ScrollDirection): The direction to scroll up or down. Defaults to `ScrollDirection.DOWN`. KeywordArgs: - scrollView (str): The view of the scroll. Default value is 'Scrollable' + scrollView (FlutterFinder | dict): Finder of the scrollable view. Defaults to the first `Scrollable` widget delta (int): delta for the scroll. Default value is 64 maxScrolls (int): Max times to scroll. Default value is 15 settleBetweenScrollsTimeout (float): settle timeout in milliseconds. Default value is 5000 @@ -142,6 +142,8 @@ def scroll_till_visible( """ opts['finder'] = scroll_to.to_dict() opts['scrollDirection'] = scroll_direction.value + if isinstance(opts.get('scrollView'), FlutterFinder): + opts['scrollView'] = opts['scrollView'].to_dict() return self.execute_flutter_command('scrollTillVisible', opts) def inject_mock_image(self, value: str) -> str: diff --git a/test/unit/webdriver/flutter_integration/flutter_actions_test.py b/test/unit/webdriver/flutter_integration/flutter_actions_test.py index 8c54a357..c22ed5ef 100644 --- a/test/unit/webdriver/flutter_integration/flutter_actions_test.py +++ b/test/unit/webdriver/flutter_integration/flutter_actions_test.py @@ -120,6 +120,28 @@ def test_scroll_till_visible_with_kwargs(self): } assert arguments == expected_arguments + @httpretty.activate + def test_scroll_till_visible_with_scroll_view_finder(self): + driver = flutter_w3c_driver() + flutter = FlutterCommand(driver) + + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/execute/sync'), + ) + + finder = FlutterFinder.by_key('message_field') + flutter.scroll_till_visible(finder, scrollView=FlutterFinder.by_key('message_list')) + + request_body = get_httpretty_request_body(httpretty.last_request()) + arguments = request_body['args'][0] + assert request_body['script'] == 'flutter: scrollTillVisible' + assert arguments == { + 'finder': {'using': '-flutter key', 'value': 'message_field'}, + 'scrollView': {'using': '-flutter key', 'value': 'message_list'}, + 'scrollDirection': 'down', + } + @httpretty.activate def test_inject_mock_image_with_file(self): driver = flutter_w3c_driver()