Search code examples
playwright

playwright with typescript, difference between locator.click() and page.click()


Reading the documentation, I found that using the function click() of page element is discouraged, as we can see here.

Instead, we can use the locator.click() method, as shown here.

I am interested in understanding if there's some technical difference between the two methods, and if so what's exactly this difference.

I am recently updating a bunch of old functions created a year ago or so, and for some reasons when I change for example

await this.page.click('.add-card') with await this.page.locator('.add-card').click()

the behaviour changes, and some elements are not waited anymore; so the test fails. I am pretty sure I need to change the rest of the code before trying to run everything again, since I think the issue is due not to this change but to the various old functions present. But nonetheless I am interested to better understand if there's something that changes in the click function, or if nothing really changes.


Solution

  • page.click() does auto-wait, so that's not a factor. I can offer a number of reasons to prefer locators:

    1. Strictness. page.click() doesn't enforce strictness by default, so if there are two elements on the page, it'll click the first without error, whereas locator.click() will raise an error if there's any ambiguity. This avoids regressions and helps guarantee the element you locate remains stable.
    2. Consistency and simplicity. If you always use locator-based operations, then your code will be consistent to read. I generally pretend that page.action() operations don't exist, leading to consistent test suites that are mostly comprised of getByRole and a few other key methods. Playwright has a fairly extensive and complex API, so anything you can do to set a portion of its methods off-limits is a good thing.
    3. Separation of concerns. Separating locators (the things you don't await) and actions (the things you do await) allows for locator reusability and is conducive to writing Page Object Models, the canonical way to organize locators and actions with respect to a particular page view on a website. With page.click(), you have to re-write the selector string (or use a string constant) on every call, harming reusability. With locators, you can declare the locator once in the POM constructor, then take a variety of different actions on it over time.
    4. There's a certain rhythm to seeing page.locator(selector).action(options) over and over in different forms, unbroken by the page.action(selector, options) idiom. Some Playwright users are confused by where the options go and may not be using TypeScript, so consistently sticking to the second pattern can improve clarity.
    5. The locator API is broader than just page.locator(). Other locators are page.getByRole(), page.getByLabel(), page.getByText() and similar methods that enforce user-visible best practice selections. If you're using page.click(), you're likely using CSS or XPath, which are discouraged. The API tries to steer you to use page.getByRole(), even over plain page.locator(), as well as page.click(), page.$(), etc.
    6. Ability to chain and filter. Playwright suggests using chaining and filtering with locators, but this isn't possible with page.action() methods. The lack of chaining can result in an over-reliance on CSS and XPath to disambiguate selections.
    7. Locators cover 100% of the functionality of the discouraged approaches, so the only benefit of using page.click() is saving a few keystrokes.

    You wrote

    I am recently updating a bunch of old functions created a year ago or so, and for some reasons when I change for example

    await this.page.click('.add-card') with await this.page.locator('.add-card').click()

    the behaviour changes, and some elements are not waited anymore; so the test fails.

    This doesn't make sense. locator.click() definitely auto-waits, so the only failure I can imagine is due to strictness (matching more than one element). You'd need to share a minimal, reproducible example and full stack trace or assertion failure to get help with this. A common mistake is failing to await a locator action, so check that first.