I can't get Appium scrolling working - iOS

Hello,
Below is the code that we are using for iOS automation. In this FindElement function, we are using a while loop which will execute for 10 times and try to make the captured element in bounds(in the view) we will do this until the element haven’t is not in bounds or we are out of ScrollMaxRepeats.

What I’m trying to capture and scroll?
I’m capturing the elements of Spotify and Chrome which need scrolling

When I searched for the error( Unhandled endpoint …/wda/scroll on iOS #19816) I found this bug Unhandled endpoint .../wda/scroll on iOS · Issue #19816 · appium/appium · GitHub and according to this when I even downgraded my appium to 6.0.0, I’m still not able to scroll down

My Configuration:
Appium: v2.5.1
[email protected]
[email protected] (also checked while downgrading to 6.0.0)
iOS Phone: 17.5.1

protected override IWebElement FindElement(AppiumMobileDriver driver)
{
var strategyPartList = strategy.StrategyParts.ToList();

//TODO: Remove the application node.
if (strategyPartList[0].Value == "XCUIElementTypeApplication")
{
    strategyPartList.RemoveAt(0);
}

var iterationTimeout = TimeSpan.FromMilliseconds(options.Timeout.TotalMilliseconds / 10);
IWebElement element = null;

using (var cts = new CancellationTokenSource(options.Timeout))
{
    var token = cts.Token;
    var selector = CreateStrategiesBasedSelector(strategyPartList);
    var bounds = new Rectangle(new Point(0, 0), driver.GetScreenSize());
    var repeatsCount = 0;

    while (token.IsCancellationRequested == false && IsNotPresentedInBounds(element, bounds))
    {
        driver.Driver.Manage().Timeouts().ImplicitWait = iterationTimeout;
        element = Try(() => FindElement(driver.Driver, selector), driver.Log.Error);

        if (IsNotPresentedInBounds(element, bounds) && options.ScrollDirection != NoScrolling)
        {
            driver.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(ManualScrollImplicitWait);

            while (IsNotPresentedInBounds(element, bounds)
                && token.IsCancellationRequested == false && repeatsCount < options.ScrollMaxRepeats)
            {
                Try(() => ScrollTo(driver.Driver, options.ScrollDirection.ToLower()), driver.Log.Error);
                element = Try(() => FindElement(driver.Driver, selector), driver.Log.Error);
                repeatsCount++;
            }
        }
    }

    if (IsNotPresentedInBounds(element, bounds))
    {
        return null;
    }
}

return element;

}

private bool IsNotPresentedInBounds(IWebElement element, Rectangle bounds)
{
return element is null || (element.Displayed || bounds.Contains(element.Location)) == false;
}

private void ScrollTo(IOSDriver driver, string direction)
{
var scrollObject = new Dictionary<string, string>();
scrollObject.Add(“direction”, direction);
driver.ExecuteScript(“mobile: scroll”, scrollObject);
}

protected IWebElement Try(Func @do, Action logError)
{
try
{
return @do();
}
catch (NoSuchElementException)
{
}
catch (Exception ex)
{
logError(ex);
}
return null;
}

*** Appium Logs ***
[XCUITestDriver@7631 (6d75e87e)] Calling AppiumDriver.execute() with args: [“mobile: scroll”,[{“direction”:“down”}],“6d75e87e-3123-44f1-84be-88d0456cc2ea”]
[XCUITestDriver@7631 (6d75e87e)] Executing command ‘execute’
[XCUITestDriver@7631 (6d75e87e)] Got response with status 200: {“value”:“iVBORw0KGgoAAAANSUhEUgAABJIAAAnkCAYAAABWQUsDAAAAAXNSR0IArs4c6QAAAHhlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAKgAgAEAAAAAQAABJKgAwAEAAAAAQAACeQAAAAAdwS/PwAAAAlwSFlzAAALEwAACxMBAJqcGAAAABxpRE9UAAAAAgAAAAAAAATyAAAAKAAABPIAAATyAAOAh2g2z5MAAEAASURBVHgB7L2J22ZXVeZd/1JbVUmlMpMQCAQIECQJCRBEBQUBmec5IIq2zM1g4we2OIENyJBEVBQa+BqHbuC7bLFpRbBpRWWmpenn2/da+957nfM+71uVmuutX13XU/sM++z9POusdd/3Wmd4Dxy8+JKNP4ePXLKJz8VHY5uWve/QRZdsDh052j7ZR+vep3Yc2/aPvq1PHcP9PYbX3Y4xmB/7d5+ybwyfwv8ipog/8MexoXZgJ/gL/5ir4V/0R8ODihNaRn9t16UDQ9Gf6E/05wI70d/kv+T/1D+sHdb55wEnIQcvPjLIQ50PtXURq5zHfUy0KUyOpCAx6fbW+w7Heo7h4wYYdYGr7R6b+bG/fQH/I/7AH/AX/oF/0R/oL+sC60hrzBC16M/U0OjvsIN9g/xDuEH+ZcwILbG6wKJtxhbyT/JP+wL5573PPw+ouBPB1ogowbcHV1tfGnRe2XIAVpHnk6A2jougncd4v+dwy/zYH/9rPkD8gT8lGZA/gL+V0CaXwD9pC/h3FlmsL9Af6C/0Z/MB9Hfwp/DAH+vMwIrOteQf5B/2C+ek9g/0F/prXtBe4oh8BP019dcBBZEMUo0SSW1Ua7NKqYCKYFOrhDf2JVkZpB18ruzGeutnQtMY621jnfmxf73zbfgY/qcYIf7AH/C3CF74J7hU3Cp88CeFcGL…
[XCUITestDriver@7631 (6d75e87e)] Proxying to WDA with an unknown route: POST /wda/scroll
[XCUITestDriver@7631 (6d75e87e)] Proxying [POST /wda/scroll] to [POST http://127.0.0.1:8100/session/D47DE39E-7B70-44AB-B30F-03A16272C8BB/wda/scroll] with body: {“direction”:“down”}
[XCUITestDriver@7631 (6d75e87e)] Responding to client with driver.getScreenshot() result: “iVBORw0KGgoAAAANSUhEUgAABJIAAAnkCAYAAABWQUsDAAAAAXNSR0IArs4c6QAAAHhlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAKgAgAEAAAAAQAABJKgAwAEAAAAAQAACeQAAAAAdwS/PwAAAAlwSFlzAAALEwAACxMBAJqcGAAAABxpRE9UAAAAAgAAAAAAAATyAAAAKAAABPIAAATyAAOAh2g2z5MAAEAASURBVHgB7L2J22ZXVeZd/1JbVUmlMpMQCAQIECQJCRBEBQUBmec5IIq2zM1g4we2OIENyJBEVBQa+BqHbuC7bLFpRbBpRWWmpenn2/da+957nfM+71uVmuutX13XU/sM++z9POusdd/3Wmd4Dxy8+JKNP4ePXLKJz8VHY5uWve/QRZdsDh052j7ZR+vep3Yc2/aPvq1PHcP9PYbX3Y4xmB/7d5+ybwyfwv8ipog/8MexoXZgJ/gL/5ir4V/0R8ODihNaRn9t16UDQ9Gf6E/05wI70d/kv+T/1D+sHdb55wEnIQcvPjLIQ50PtXURq5zHfUy0KUyOpCAx6fbW+w7Heo7h4wYYdYGr7R6b+bG/fQH/I/7AH/AX/oF/0R/oL+sC60hrzBC16M/U0OjvsIN9g/xDuEH+ZcwILbG6wKJtxhbyT/JP+wL5573PPw+ouBPB1ogowbcHV1tfGnRe2XIAVpHnk6A2jougncd4v+dwy/zYH/9rPkD8gT8lGZA/gL+V0CaXwD9pC/h3FlmsL9Af6C/0Z/MB9Hfwp/DAH+vMwIrOteQf5B/2C+ek9g/0F/prXtBe4oh8BP019dcBBZEMUo0SSW1Ua7NKqYCKYFOrhDf2JVkZpB18ruzGeutnQtMY621jnfmxf73zbfgY/qcYIf7AH/C3CF74J7hU3Cp88CeFcGLm4NaeUMK/…
[XCUITestDriver@7631 (6d75e87e)] Got response with status 404: {“value”:{“error”:“unknown command”,“message”:“Unhandled endpoint: /session/D47DE39E-7B70-44AB-B30F-03A16272C8BB/wda/scroll – http://127.0.0.1:8100/ with parameters {\n wildcards = (\n "session/D47DE39E-7B70-44AB-B30F-03A16272C8BB/wda/scroll"\n );\n}”,“traceback”:””},“sessionId”:“D47DE39E-7B70-44AB-B30F-03A16272C8BB”}
[W3C] Matched W3C error code ‘unknown command’ to UnknownCommandError
[XCUITestDriver@7631 (6d75e87e)] Encountered internal error running command: UnknownCommandError: Unhandled endpoint: /session/D47DE39E-7B70-44AB-B30F-03A16272C8BB/wda/scroll – http://127.0.0.1:8100/ with parameters {
[XCUITestDriver@7631 (6d75e87e)] wildcards = (
[XCUITestDriver@7631 (6d75e87e)] “session/D47DE39E-7B70-44AB-B30F-03A16272C8BB/wda/scroll”
[XCUITestDriver@7631 (6d75e87e)] );
[XCUITestDriver@7631 (6d75e87e)] }
[XCUITestDriver@7631 (6d75e87e)] at errorFromW3CJsonCode (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/lib/protocol/errors.js:1085:25)
[XCUITestDriver@7631 (6d75e87e)] at ProxyRequestError.getActualError (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/lib/protocol/errors.js:954:14)
[XCUITestDriver@7631 (6d75e87e)] at JWProxy.command (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/node_modules/@appium/base-driver/lib/jsonwp-proxy/proxy.js:353:19)
[XCUITestDriver@7631 (6d75e87e)] at processTicksAndRejections (node:internal/process/task_queues:95:5)
[XCUITestDriver@7631 (6d75e87e)] at XCUITestDriver.proxyCommand (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/lib/commands/proxy-helper.js:109:35)
[XCUITestDriver@7631 (6d75e87e)] at XCUITestDriver.mobileScroll (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/lib/commands/gesture.js:282:12)
[XCUITestDriver@7631 (6d75e87e)] at XCUITestDriver.executeMethod (/Users/avdheshgautam/node_modules/@appium/base-driver/lib/basedriver/commands/execute.ts:36:12)
[XCUITestDriver@7631 (6d75e87e)] at XCUITestDriver.execute (/Users/avdheshgautam/.appium/node_modules/appium-xcuitest-driver/lib/commands/execute.js:118:14)
[HTTP] ← POST /wd/hub/session/6d75e87e-3123-44f1-84be-88d0456cc2ea/execute/sync 404 256 ms - 1632
[HTTP]
[HTTP] ← GET /wd/hub/session/6d75e87e-3123-44f1-84be-88d0456cc2ea/screenshot 200 266 ms - 840640
[HTTP]
[HTTP] Request idempotency key: 0093efa7-37fc-46db-823c-d4399c28f3ca
[HTTP] → POST /wd/hub/session/6d75e87e-3123-44f1-84be-88d0456cc2ea/element
[HTTP] {“using”:“-ios class chain”,“value”:“**/XCUIElementTypeButton[label == \"'Something fell on Moon': ISRO's Chandrayaan-3 discovers unusual rock fragments at Shiv Shakti Point, BUSINESS TODAY, 22h\"]”}

@ mykola-mokhnach
@ Aleksei could you please help me in this

Looks like you are using C#. I don’t know what the issue is in this code but happy to share my code which does a similar thing to what you describe - scrolls until a given element is found or the top/bottom of the screen is reached or a maximum number of attempts is made.

This method sits in my BasePageObject and I can use it whenever I need to scroll to a particular element. Works for both Android and iOS.

public void ScrollToElement(IWebElement element, Direction scrollDirection, int numberOfAttempts = 10)
        {
            var currentWaitTime = Driver.Manage().Timeouts().ImplicitWait;
            Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1);
            var windowSize = Driver.Manage().Window.Size;
            var lowerSwipeBound = windowSize.Height * 0.65;
            var upperSwipeBound = windowSize.Height * 0.35;
            var targetYCoordinate = windowSize.Height * 0.75;
            var lastKnownYCoOrdinate = 0;
            var loopCount = 0;
            var elementFound = ElementIsOnScreen(element);
            var elementIsVisible = false;
            if (elementFound)
            {
                Console.WriteLine("Element found without needing to scroll");
                elementIsVisible = element.Location.Y < targetYCoordinate;
            }

            while (!elementIsVisible && loopCount < numberOfAttempts)
            {
                var finger = new PointerInputDevice(PointerKind.Touch, "finger");
                ActionSequence actionSequence = new ActionSequence(finger);
                switch (scrollDirection)
                {
                    case Direction.Down:
                        actionSequence.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 300, Convert.ToInt32(lowerSwipeBound), TimeSpan.Zero));
                        actionSequence.AddAction(finger.CreatePointerDown(MouseButton.Touch));
                        actionSequence.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 300, Convert.ToInt32(upperSwipeBound), TimeSpan.FromMilliseconds(200)));
                        actionSequence.AddAction(finger.CreatePause(TimeSpan.FromMilliseconds(200)));
                        actionSequence.AddAction(finger.CreatePointerUp(MouseButton.Touch));
                        break;
                    case Direction.Up:
                        actionSequence.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 300, Convert.ToInt32(upperSwipeBound), TimeSpan.Zero));
                        actionSequence.AddAction(finger.CreatePointerDown(MouseButton.Touch));
                        actionSequence.AddAction(finger.CreatePointerMove(CoordinateOrigin.Viewport, 300, Convert.ToInt32(lowerSwipeBound), TimeSpan.FromMilliseconds(200)));
                        actionSequence.AddAction(finger.CreatePause(TimeSpan.FromMilliseconds(200)));
                        actionSequence.AddAction(finger.CreatePointerUp(MouseButton.Touch));
                        break;
                }
                var actions = new List<ActionSequence>
                {
                    actionSequence
                };
                _deviceDriverService.Driver.PerformActions(actions);
                loopCount++;
                Console.WriteLine("Checking if the element can be seen");

                try
                {
                    Console.Write($"ElementY: {element?.Location.Y} TargetY: {targetYCoordinate}");

                    elementIsVisible = element.Location.Y < targetYCoordinate && element.Location.Y > -100 ||
                        element.Location.Y == lastKnownYCoOrdinate; // Cannot scroll any further so no point continuing

                    lastKnownYCoOrdinate = element.Location.Y;
                }
                catch(NoSuchElementException)
                {
                    //Continue the loop as it may not be on screen yet
                }
            }

            Driver.Manage().Timeouts().ImplicitWait = currentWaitTime;
        }
1 Like
1 Like