appium / appium-flutter-driver

Appium Flutter Driver is a test automation tool for Flutter apps on multiple platforms/OSes. Appium Flutter Driver is part of the Appium mobile test automation tool maintained by community
MIT License
451 stars 183 forks source link

flutter:scrollIntoView, flutter:scroll, flutter:scrollUntilVisible not working #600

Open dlewis2017 opened 11 months ago

dlewis2017 commented 11 months ago

I've tried a few of the scroll methods but none of them seem to be working (python, flutter, iOS)

Environment

iPad Pro 16.6.1

flutter --version
Flutter 3.13.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision efbf63d9c6 (9 weeks ago) • 2023-08-15 21:05:06 -0500
Engine • revision 1ac611c64e
Tools • Dart 3.1.0 • DevTools 2.25.0
pip3 show Appium-Flutter-Finder
Name: Appium-Flutter-Finder
Version: 0.6.1
...
pip3 show Appium-Python-Client
Name: Appium-Python-Client
Version: 3.1.0
...

flutter:scrollIntoView Error

command:

driver = webdriver.Remote(appium_server_url, capabilities)
assert driver.execute("flutter:scrollIntoView",  finder.by_text('Begin'))

error:

fixturefunc = <function my_function at 0x10361e520>, request = <FixtureRequest for <Function test_1>>, kwargs = {}

    def call_fixture_func(
        fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs
    ) -> FixtureValue:
        if is_generator(fixturefunc):
            fixturefunc = cast(
                Callable[..., Generator[FixtureValue, None, None]], fixturefunc
            )
            generator = fixturefunc(**kwargs)
            try:
                fixture_result = next(generator)
            except StopIteration:
                raise ValueError(f"{request.fixturename} did not yield a value") from None
            finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
            request.addfinalizer(finalizer)
        else:
            fixturefunc = cast(Callable[..., FixtureValue], fixturefunc)
>           fixture_result = fixturefunc(**kwargs)

/opt/homebrew/lib/python3.11/site-packages/_pytest/fixtures.py:902: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
my_test.py:20: in my_test
    assert common.my_test(driver)
my_test.py:112: in my_test
    assert driver.execute("flutter:scrollIntoView",  finder.by_text('Begin') )
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <appium.webdriver.webdriver.WebDriver (session="55c49cf8-a75b-4381-af60-43c9ec9a9186")>, driver_command = 'flutter:scrollIntoView', params = 'eyJmaW5kZXJUeXBlIjoiQnlUZXh0IiwidGV4dCI6IkJlZ2luIFByb2NlZHVyZSJ9'

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.

        :Args:
         - driver_command: The name of the command to execute as a string.
         - params: A dictionary of named parameters to send with the command.

        :Returns:
          The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)

        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
>               params["sessionId"] = self.session_id
E               TypeError: 'str' object does not support item assignment

/opt/homebrew/lib/python3.11/site-packages/selenium/webdriver/remote/webdriver.py:340: TypeError
_______________________________________________________________________________________ my_test ________________________________________________________________________________________

fixturefunc = <function my_function at 0x10361e520>, request = <FixtureRequest for <Function my_test>>, kwargs = {}

    def call_fixture_func(
        fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs
    ) -> FixtureValue:
        if is_generator(fixturefunc):
            fixturefunc = cast(
                Callable[..., Generator[FixtureValue, None, None]], fixturefunc
            )
            generator = fixturefunc(**kwargs)
            try:
                fixture_result = next(generator)
            except StopIteration:
                raise ValueError(f"{request.fixturename} did not yield a value") from None
            finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
            request.addfinalizer(finalizer)
        else:
            fixturefunc = cast(Callable[..., FixtureValue], fixturefunc)
>           fixture_result = fixturefunc(**kwargs)

/opt/homebrew/lib/python3.11/site-packages/_pytest/fixtures.py:902: 
dlewis2017 commented 11 months ago

Is there an alternative I can use in the meantime or an alternative approach to scroll/drag? driver.execute_scrip("mobile:scroll", scroll_args) doesn't seem to be working either

denysord88 commented 11 months ago

I'm scrolling like this in Java:

import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
...
    public static void scrollByCoordinates(int xStart, int yStart, int xEnd, int yEnd) {
        switchContext("NATIVE_APP");
        PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
        Sequence dragNDrop = new Sequence(finger, 1);
        dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(0),
                PointerInput.Origin.viewport(), xStart, yStart));
        dragNDrop.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
        dragNDrop.addAction(finger.createPointerMove(Duration.ofMillis(200),
                PointerInput.Origin.viewport(), xEnd, yEnd));
        dragNDrop.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
        getAppiumInstance().getDriver().perform(List.of(dragNDrop));
        switchContext("FLUTTER");
        waitMilliseconds(100);
    }
denysord88 commented 11 months ago

Other languages here https://appium.readthedocs.io/en/latest/en/commands/interactions/actions/

dlewis2017 commented 10 months ago

So I managed to get it working but only scrollIntoView and somewhat scrolluntilVisible. The issue was syntax. I had to use: driver.execute_script("flutter:scrollIntoView", locator, {"alignment": 0}). Also, it seems you HAVE to have the map with alignment defined and set. It didn't seem to work without it which is an issue.

I also tried to use scrollUntilTapable seemed to scroll the wrong ListView even if the correct key was provided. It was also scroll indefinitely.

scrollUntilVisible I managed to get working and has similar issues as scrollUntilTapable UNLESS you specify a waitTimeoutMilliseconds property. Then it will still scroll the wrong ListView but at the last second, scroll the correct ListView.

driver.execute_script("flutter:waitFor", scrollLocator, waitTime)
driver.execute_script("flutter:scrollUntilVisible", scrollLocator, {"item":locator, "dxScroll":0, "dyScroll":-300, "waitTimeoutMilliseconds": waitTime})
sanjay7956 commented 7 months ago

Scroll until visible not working below is the version Appium version -2.21 flutter@2.4.2 [installed (npm)] scrolling continuously even the element found.

// java

{ WebElement year1 = find.byValueKey("scroll_2"); WebElement textField = find.byText(year); JavascriptExecutor executor = driver; executor.executeScript("flutter:scrollUntilVisible", year1, ImmutableMap.of( "item",textField, "dxScroll",0, "dyScroll",-300, )); } below is the error log for particular scroll "<< {"isError":false,"response":{},"type":"_extensionType","method":"ext.flutter.driver"} | previous command scroll [FlutterDriver] >>> {"command":"waitFor","text":"1999","finderType":"ByText","timeout":100} [FlutterDriver] >>> {"command":"scroll","keyValueType":"String","keyValueString":"scroll_2","finderType":"ByValueKey","dx":0,"dy":-300,"duration":100000,"frequency":60}"

@KazuCocoa