playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Bug]: Elements can not be clicked when using emulation

Open PlanetartRoc opened this issue 1 year ago • 1 comments

Version

1.46.0

Steps to reproduce

scripts:

from playwright.sync_api import Playwright, sync_playwright


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context(**playwright.devices["iPhone 14 Pro"])
    page = context.new_page()
    page.goto("https://www.simplytoimpress.com/baptism-invitations/design-105133")
    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)


Expected behavior

Can operate normally

Actual behavior

image None of the elements in the red box can be operated The script can not navigate to elements, nor can it click to manually manipulate them

Additional context

In the script-initiated browser window, manually switch to developer mode, and then simulate the mobile page is operational. I do not know whether can be configured to solve with the new_context arguments?

Environment

- Operating System: [windows11]
- CPU: [Inter]
- Browser: [All, Chromium, Firefox, WebKit]
- Python Version: [3.12]
- Other info:

PlanetartRoc avatar Aug 23 '24 09:08 PlanetartRoc

Moving upstream since I can repro with npx playwright open --device="Galaxy S8" https://www.simplytoimpress.com/baptism-invitations/design-105133.

mxschmitt avatar Aug 23 '24 16:08 mxschmitt

Thank you for your support.

PlanetartRoc avatar Aug 30 '24 02:08 PlanetartRoc

Investigation:

  • on desktop, they do onclick="die_cut_changed('2', false, 0); displayLightningIcon();" inside the DOM
  • on mobile, they attach on('touchend', function() { click event listeners via JavaScript
  • based on the User-Agent the site returns a different version, desktop or mobile
  • on the mobile version, they don't attach 'click' event handlers
  • inside our Codegen, we pretend to be a mobile device but never emit touch events, this is why the clicks are noops.
  • Its blocked by https://github.com/microsoft/playwright/issues/2903

mxschmitt avatar Sep 09 '24 15:09 mxschmitt

Workaround: We recommend switching to pointerdown/pointerup since touch events are deprecated as per here: https://w3c.github.io/touch-events/#:~:text=license%20rules%20apply.-,Status%20of%20This%20Document,-This%20document%20is

The Community Group considers Touch Events a legacy API – authors are strongly encouraged to adopt Pointer Events instead.

mxschmitt avatar Sep 09 '24 16:09 mxschmitt

Workaround: We recommend switching to pointerdown/pointerup since touch events are deprecated as per here: https://w3c.github.io/touch-events/#:~:text=license%20rules%20apply.-,Status%20of%20This%20Document,-This%20document%20is

The Community Group considers Touch Events a legacy API – authors are strongly encouraged to adopt Pointer Events instead.

I tried the following method but it didn't work. Is it the code?

from playwright.sync_api import Playwright, sync_playwright
def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)

    context = browser.new_context(**playwright.devices["iPhone 14 Pro"])
    page = context.new_page()
    page.goto("https://www.simplytoimpress.com/baptism-invitations/design-105133")
    selector = 'div[data-value="13"]'
    locator = page.locator(selector)
    print(locator.is_visible())

    js_code = """
        (element) => {
        const eventInit = {
            pointerId: 1,
            bubbles: true,
            cancelable: true,
            composed: true,
            pointerType: 'mouse', # nor touch
            isPrimary: true,
        };

        console.log('Creating pointerdown event');
        const pointerDownEvent = new PointerEvent('pointerdown', eventInit);
        element.dispatchEvent(pointerDownEvent);
        console.log('pointerdown event dispatched');

        console.log('Creating pointerup event');
        const pointerUpEvent = new PointerEvent('pointerup', eventInit);
        element.dispatchEvent(pointerUpEvent);
        console.log('pointerup event dispatched');
    }
        """

    page.evaluate(js_code, page.query_selector(selector))
    page.wait_for_timeout(100_000)
    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

PlanetartRoc avatar Sep 19 '24 00:09 PlanetartRoc

You can do the following:

import { chromium, devices } from '@playwright/test';

(async () => {
  // Make sure to run headed.
  const browser = await chromium.launch({ headless: false });

  // Setup context however you like.
  const context = await browser.newContext(devices['iPhone 15']);
  await context.addInitScript(() => {
    document.addEventListener('click', clickEvent => {
      // Get the target element
      const target = clickEvent.target;

      // Function to create and dispatch a touch event
      function dispatchTouchEvent(eventType) {
        const touch = new Touch({
          identifier: Date.now(),
          target: target,
          clientX: clickEvent.clientX,
          clientY: clickEvent.clientY,
          pageX: clickEvent.pageX,
          pageY: clickEvent.pageY
        });

        const touchEvent = new TouchEvent(eventType, {
          bubbles: true,
          cancelable: true,
          view: window,
          touches: [touch],
          targetTouches: [touch],
          changedTouches: [touch]
        });

        target.dispatchEvent(touchEvent);
      }

      // Dispatch touchstart and touchend events
      dispatchTouchEvent('touchstart');
      dispatchTouchEvent('touchend');
    }, true);  // Use capture phase to ensure this runs before other click handlers
  });

  // Pause the page, and start recording manually.
  const page = await context.newPage();
  await page.goto('https://www.simplytoimpress.com/baptism-invitations/design-105133');
  await page.pause();
})();

Make sure to attach the addInitScript only for mobile tho. Its in JavaScript, but the same works in Python as well (make sure to use """ when passing the script over to the add_init_script function.

Folding into https://github.com/microsoft/playwright/issues/2903

mxschmitt avatar Sep 19 '24 10:09 mxschmitt

Thank you so much!

PlanetartRoc avatar Sep 20 '24 01:09 PlanetartRoc