Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Puppeteer click don't fail when actual element is overlapped #12221

Open
1 task
shivamothkuri opened this issue Apr 5, 2024 · 5 comments
Open
1 task

Comments

@shivamothkuri
Copy link

shivamothkuri commented Apr 5, 2024

Minimal, reproducible example

await this.page.waitForSelector('selector', { visible: true })
await this.page.click('selector')

Error string

no error

Bug behavior

  • [] Flaky
  • PDF

Background

I'm trying to click an element on my page after performing some action.
Previous action renders a popup like element which covers the element. When I try to click the element before the overlapping popup closes, I expect puppeteer to fail. But, it don't throw any error.

I can add explicit timeout or wait for the overlapping element to be removed. But, why is puppeteer not throwing any error when element is not actually clicked?

I see puppeteer implementation of click is, just move the cursor to that x/y coordinate and do a mouse click. But, it's not actually validating it is clicking the element or some other element which is overlapping it.

async click(
    this: ElementHandle<Element>,
    options: Readonly<ClickOptions> = {}
  ): Promise<void> {
    await this.scrollIntoViewIfNeeded();
    const {x, y} = await this.clickablePoint(options.offset);
    await this.frame.page().mouse.click(x, y, options);
  }

WDIO has proper implementation of throwing an error for similar behaviour - https://webdriver.io/docs/api/element/click/

image

Expectation

I would expect page.click fails if element is overlapped by any other element.

Reality

Nothing happens. No error thrown by puppeteer.

Puppeteer configuration file (if used)

No response

Puppeteer version

22.6.2

Node version

20.11.1

Package manager

npm

Package manager version

10.2.4

Operating system

macOS

Copy link

github-actions bot commented Apr 5, 2024

This issue was not reproducible. Please check that your example runs locally and the following:

  • Ensure the script does not rely on dependencies outside of puppeteer and puppeteer-core.
  • Ensure the error string is just the error message.
    • Bad:

      Error: something went wrong
        at Object.<anonymous> (/Users/username/repository/script.js:2:1)
        at Module._compile (node:internal/modules/cjs/loader:1159:14)
        at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
        at Module.load (node:internal/modules/cjs/loader:1037:32)
        at Module._load (node:internal/modules/cjs/loader:878:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
        at node:internal/main/run_main_module:23:47
    • Good: Error: something went wrong.

  • Ensure your configuration file (if applicable) is valid.
  • If the issue is flaky (does not reproduce all the time), make sure 'Flaky' is checked.
  • If the issue is not expected to error, make sure to write 'no error'.

Once the above checks are satisfied, please edit your issue with the changes and we will
try to reproduce the bug again.


Analyzer run

@OrKoN
Copy link
Collaborator

OrKoN commented Apr 5, 2024

Puppeteer has never implemented it so it is not a bug. It is possible to implement this client side by tracking what was actually clicked and it is a good idea to always do this to verify that the expectations of your actions are correct. We might want to include this into the locator API https://pptr.dev/guides/locators.

@shivamothkuri
Copy link
Author

shivamothkuri commented Apr 5, 2024

Thanks for the response @OrKoN
Could you please help how can we implement this check on client side, to validate what was actually clicked?
I see response from page.click() is void.

@OrKoN
Copy link
Collaborator

OrKoN commented Apr 5, 2024

One way could be to await the click event to be actually emitted on the element you click:

const elementHandle = await this.page.waitForSelector('selector', { visible: true });
const clicked = elementHandle.evaluate((el) => {
   return new Promise((resolve, reject) => {
       el.addEventListener('click', resolve, {once: true});
       setTimeout(() => reject(new Error('notClicked')), 10000);
   });
});
await elementHandle.click();
await clicked; // either resolves or throws

There could be multiple reasons why it does not receive a click.

Or you can use a modern API to check if element is covered before trying to click:

const elementHandle = await this.page.waitForSelector('selector', { visible: true });
const isCovered = elementHandle.evaluate((el) => {
   const rect = el.getBoundingClientRect(); 
   // there are many ways to detect overlap, it could be partially overlapping or completeltey.
   // you might want to check the center or something else depending on how you intend to click.
   const overlap = document.elementFromPoint(rect.left, rect.top); 
   return overlap !== el;
});
if (isCovered) { throw ... }

@shivamothkuri
Copy link
Author

Thanks @OrKoN Let me this this out

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

No branches or pull requests

2 participants