Skip to content

fix(android): include the from point in the swipe path - #42349

Open
Lazizbek Ergashev (lazerg) wants to merge 4 commits into
microsoft:mainfrom
lazerg:fix-42348
Open

fix(android): include the from point in the swipe path#42349
Lazizbek Ergashev (lazerg) wants to merge 4 commits into
microsoft:mainfrom
lazerg:fix-42348

Conversation

@lazerg

@lazerg Lazizbek Ergashev (lazerg) commented Aug 21, 2026

Copy link
Copy Markdown

Summary

  • AndroidInput.swipe() accepts a from point and then drops it: only segments reaches the protocol, so the gesture actually starts at segments[0]. from is the first point of the path, so it is prepended to segments on the client. No protocol or driver change is needed, since the driver passes the array straight to UiDevice.swipe, which starts at its first point.
  • The minimal documented call swipe(from, [to], steps) used to send a one-element array, which the driver turns into a touch down and up on the same point, so it did not move at all. Anyone who worked around this by repeating from inside segments now gets a zero-length leading segment: same path, one segment worth of extra time.
  • No test: tests/android has no coverage of AndroidInput and no channel-level harness to assert the payload against, so this would need new test infrastructure.

Fixes #42348

@lazerg Lazizbek Ergashev (lazerg) changed the title fix(android): include the 'from' point in the swipe path fix(android): include the from point in the swipe path Aug 21, 2026

@yury-s Yury Semikhatsky (yury-s) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good. Any chance you can add a test for it?

@lazerg

Copy link
Copy Markdown
Author

Added a test in c658786 (tests/android/input.spec.ts). It scrolls a long page with input.swipe(from, [to], steps) and checks scrollY moved. The old code dropped from, so the swipe never moved and this test fails on it.

It goes through the real androidDevice fixtures like the rest of tests/android, so it only runs under npm run atest against the ADB emulator, not locally here. CI's Android job triggers on push or the CQ1 label, not automatically on external PRs, so it hasn't run there yet.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "tests 2"

1 failed
❌ [android-native] › android/input.spec.ts:19 › androidInput.swipe should start from the given point

20 flaky ⚠️ [chromium-page] › page/page-screenshot.spec.ts:504 › page screenshot › should take fullPage screenshots during navigation `@tracing-chromium`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:179 › serviceWorker(), and fromServiceWorker() work `@chrome-ubuntu-22.04`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:301 › should report intercepted service worker requests in HAR `@chrome-ubuntu-22.04`
⚠️ [chromium-library] › library/beforeunload.spec.ts:130 › should support dismissing the dialog multiple times `@chromium-macos-15-large`
⚠️ [chromium-library] › library/chromium/oopif.spec.ts:179 › should take screenshot `@chromium-macos-14-xlarge`
⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@chromium-macos-14-xlarge`
⚠️ [chromium-library] › library/chromium/oopif.spec.ts:179 › should take screenshot `@chromium-macos-15-xlarge`
⚠️ [chromium-library] › library/trace-viewer.spec.ts:1893 › should not leak recorders `@chromium-macos-15-xlarge`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:179 › serviceWorker(), and fromServiceWorker() work `@chrome-beta-ubuntu-22.04`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:301 › should report intercepted service worker requests in HAR `@chrome-beta-ubuntu-22.04`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:301 › should report intercepted service worker requests in HAR `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:102 › selector generator › should not use description when name is unique `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:288 › selector generator › should use internal:has-text `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:457 › selector generator › should match in shadow dom `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:541 › selector generator › should not over-escape for CSS syntax `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:633 › selector generator › should generate exact role when necessary `@chrome-macos-latest`
⚠️ [chromium-library] › library/selector-generator.spec.ts:683 › selector generator › should generate noText: name from aria-label `@chrome-macos-latest`
⚠️ [firefox-page] › page/page-goto.spec.ts:90 › should work with Cross-Origin-Opener-Policy `@tracing-firefox`
⚠️ [firefox-library] › library/inspector/cli-codegen-aria.spec.ts:87 › should update aria snapshot highlight `@firefox-macos-15-large`
⚠️ [webkit-library] › library/screencast.spec.ts:57 › applies backpressure while async onFrame callback is pending `@webkit-macos-15-large`

105772 passed, 4611 skipped


Merge workflow run.

@yury-s

Copy link
Copy Markdown
Member

Added a test in c658786 (tests/android/input.spec.ts). It scrolls a long page with input.swipe(from, [to], steps) and checks scrollY moved. The old code dropped from, so the swipe never moved and this test fails on it.

It goes through the real androidDevice fixtures like the rest of tests/android, so it only runs under npm run atest against the ADB emulator, not locally here. CI's Android job triggers on push or the CQ1 label, not automatically on external PRs, so it hasn't run there yet.

The new test failed on CI: https://github.com/microsoft/playwright/actions/runs/32510988871/job/96862912472?pr=42349

CI showed the swipe assertion timing out with scrollY stuck at 0 on
every retry, even though the swipe call itself completed without
error. androidDevice.input.swipe() injects a raw touch event through
the OS input pipeline, independent of the CDP channel used by
setContent(), so the gesture can land before the new content is
actually composited and presented on the device screen. Wait for two
animation frames after setContent() so the swipe reliably hits the
rendered page.
@lazerg

Copy link
Copy Markdown
Author

Checked the job log. The swipe call did not throw. But window.scrollY stayed at 0 through every retry, so the gesture never scrolled the page. This looks like a race in the test, not a problem with the fix itself.

androidDevice.input.swipe() sends a raw touch event through ADB. That event does not go through the CDP channel that setContent() uses. On a real device the touch can land before the new page content is actually painted on screen, so the swipe likely hit the old blank frame.

Added a short wait for two animation frames after setContent(), before the swipe runs, in commit 9b39365, pushed to fix-42348. Still only runs under npm run atest / CQ1, so it will need another labeled run to confirm.

@Mukller

Copy link
Copy Markdown

Confirmed the root cause independently: \AndroidDispatcher.inputSwipe\ forwards the params verbatim to the on-device agent (\packages/playwright-core/src/server/dispatchers/androidDispatcher.ts), so before this change \ rom\ never reached the driver and every gesture started at \segments[0]. The fix is correct and minimal — thanks.

On the flaky test: the double-
equestAnimationFrame\ wait guarantees the new content frame is composited, but raw touch injection goes through the system input pipeline and can be silently dropped if the browser surface isn't the focused window at that moment. If failures persist after c658786, consider retrying the gesture itself inside the assertion, e.g.:

\\ s
await expect.poll(async () => {
await androidDevice.input.swipe({ x: 250, y: 1500 }, [{ x: 250, y: 500 }], 30);
return page.evaluate(() => window.scrollY);
}, { timeout: 15_000 }).toBeGreaterThan(0);
\\

so a single dropped first gesture doesn't fail the run.

Comment thread tests/android/input.spec.ts Outdated
// pipeline, independent of the CDP connection used by setContent() above. Wait for
// the new content to actually be composited and presented on the device screen,
// otherwise the swipe can land on the previous (blank) frame and never scroll anything.
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use rafraf from ../config/utils. I doubt this will help though, if the test is still flaky, let's revert it and keep just the fix.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to rafraf in 6de3e90.

Worth noting the wait has never actually run on the emulator. The only Android job on this branch is run 32510988871, which built c658786, before the wait existed. tests 2 starts only on the labeled event, so the pushes after the CQ1 label did not re-trigger it. So there is no evidence yet either way.

If you re-apply CQ1 and it still fails, I will drop the test and leave only the fix.

Comment thread tests/android/input.spec.ts Outdated
@@ -0,0 +1,31 @@
/**
* Copyright 2020 Microsoft Corporation. All rights reserved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not 2020 anymore, look at other files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, copied it from the older files in this folder. Changed to the Copyright (c) Microsoft Corporation. header in 6de3e90.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

1 failed
❌ [msedge] › mcp/annotate.spec.ts:386 › should cancel browser_annotate when the MCP request is aborted @mcp-windows-latest-msedge

8141 passed, 1361 skipped


Merge workflow run.

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "tests 1"

3 flaky ⚠️ [chromium-library] › library/video.spec.ts:736 › screencast › should work with video+trace `@chromium-ubuntu-22.04-node24`
⚠️ [chromium-library] › library/chromium/chromium.spec.ts:436 › should produce network events, routing, and annotations for Service Worker (advanced) `@chromium-ubuntu-22.04-node22`
⚠️ [chromium-library] › library/tracing.spec.ts:494 › should produce screencast frames fit `@chromium-ubuntu-22.04-node22`

51371 passed, 1238 skipped


Merge workflow run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: AndroidInput.swipe() silently ignores its documented 'from' point

3 participants