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

[Feature] Allow ElementHandle as an input to DOM expect matchers #11145

Closed
Meemaw opened this issue Dec 31, 2021 · 11 comments
Closed

[Feature] Allow ElementHandle as an input to DOM expect matchers #11145

Meemaw opened this issue Dec 31, 2021 · 11 comments

Comments

@Meemaw
Copy link

Meemaw commented Dec 31, 2021

We're using https://github.com/testing-library/playwright-testing-library for selecting elements on our page. Those return ElementHandle, which doesn't seem to be compatible with some expect matchers.

Simple example:

const buyButton = await queries.findByText($document, "Buy")
expect(buyButton).toBeDisabled()

Throws:

Error: toBeDisabled can be only used with Locator object

There might be a way to get a Locator from ElementHandle (not sure), but it would be great if those also worked with ElementHandle itself.

@pavelfeldman
Copy link
Member

Making assertions work against ElementHandle would not make much sense - underlying elements can be swapped by React at any moment:

const buyButton = await queries.findByText($document, "Buy");
// React re-render happens here, and the following line no longer makes sense.
await expect(buyButton).toBeDisabled()

Using locator objects solves this and other races.

Could you share why you are using testing library to select elements? I would imagine that Playwright's selectors are much more powerful at this point.

@Meemaw
Copy link
Author

Meemaw commented Jan 2, 2022

Main reason is that it enforces best practices and prevents bad selectors. I'm aware Playwright's builtin selectors are quite strong, but there are concepts that testing library abstracts away so you don't have to know the underlying selectors implementation/engine.

I would love if those selectors returned Locator though. I'm slightly annoyed having to deal with Promises all around.

I guess, It would be fairly trivial to implements some of the testing library selectors to return locators behind the scenes, but 2 that I'm particularly interested in are byRole and byLabelText which seem non-trivial.

@pavelfeldman
Copy link
Member

I'm not familiar with how testing library enforces best practices, but your byRole and byLabelText use cases are clear.

byLabelText would look like this:

page.locator(`label:has-text('Username')`)

starting with 1.18 it will also be available as

page.locator('label').hasText('Username'))

Although testing library claims that byRole queries the accessibility tree, it most definitely does not (it runs outside the browser and only browser can build a11y tree). So I guess there is some heuristic that checks DOM attributes instead. If so, you can do the same:

page.locator('[role=button][aria-label=Username]')

@Meemaw
Copy link
Author

Meemaw commented Jan 4, 2022

byLabelText would look like this:

page.locator(`label:has-text('Username')`)

It's not that simple. I assume the element selected by this would be the label, and not the input thats associated with it, which is what byLabelText does.

Although testing library claims that byRole queries the accessibility tree, it most definitely does not (it runs outside the browser and only browser can build a11y tree). So I guess there is some heuristic that checks DOM attributes instead. If so, you can do the same:

page.locator('[role=button][aria-label=Username]')

Yeah, I'm fairly certain they have heuristics for this. This for example wouldn't select <button>Username</button>, or am I wrong? In short, they abstract these things away. For example given the following selector:

queries.findByRole("button", { name: "Save" })

The following elements would be selected:

<button>Save</button>
<div role="button">Save</div>
<div role="button" aria-label="Save" />

@pavelfeldman
Copy link
Member

It's not that simple. I assume the element selected by this would be the label, and not the input thats associated with it, which is what byLabelText does.

It'll resolve to a label, but input methods such as fill work on label locators.

Yeah, I'm fairly certain they have heuristics for this. This for example wouldn't select Username, or am I wrong? In short, they abstract these things away.

Got it. We don't have anything like this. Let me treat this issue as a feature request for the role selectors.

@Meemaw
Copy link
Author

Meemaw commented Jan 5, 2022

@pavelfeldman thanks for considering this as a feature request. Personally, given the popularity of testing library in React (and wider) ecosystem, I think having those builtin would be very compelling for a lot of people. TBH, this was the first thing I checked for when we were evaluating Playwright.

If you think the initial feature request "allowing ElementHandle as an input to DOM expect matchers" does not make sense, fell free to close this issue.

@sebinsua
Copy link

sebinsua commented Jan 7, 2022

@petetnt and I worked on a simple script to use Locators with Testing Library here: https://gist.github.com/sebinsua/205682c7af6966fdadc103b4e15d0d8b#gistcomment-4005229

@pavelfeldman The way testing library works is more complex than you suggest, so a ByLabelText selector is not the same as [aria-label="Some Label"]. It also works with stuff like aria-labelledby and so on, and resolves to the element that is being pointed to. See implementation.

Similarly, it gets implicit roles from the DOM (see implementation) so it doesn't only match when you have explicitly set aria-role on DOM elements.

There's a lot that it abstracts away from you, but you're correct that it's not doing this via the browser (so has to re-invent the wheel a bit).

A better Playwright implementation would be very helpful. I wonder if @kentcdodds or @eps1lon might be able to fill you in on the feature set better than I can?

@eps1lon
Copy link
Contributor

eps1lon commented Jan 7, 2022

The original issue makes it sound like this issue is about assertions which I don't have much insight on. Looks like a Playwright issue.

@sebinsua
Copy link

sebinsua commented Jan 7, 2022

@eps1lon Sorry, to be clear, this issue is about making a better integration between Playwright and Testing Library. However, I @ed you because there was some confusion over what the Testing Library queries do / abstract away and whether they could just be replaced with selectors like [role=button][aria-label=Username]. I know that it does more than this, but wondered whether there is documentation for each query about what its implementation does or whether the only way to find out is by reading the code?

@pavelfeldman created an issue #11182 to track the possibility of implementing something like ByRole directly within Playwright but there are probably other queries that Testing Library has an improved implementation of (e.g. ByLabelText, etc).

Sorry for the out of context @.

@eps1lon
Copy link
Contributor

eps1lon commented Jan 7, 2022

ByRole follows ARIA and related specifications e.g. the name option in ByRole(role, { name }) implements ACCNAME.

role is tied to HTML-ARIA.

The documentation for ByRole goes into more detail.

@dgozman
Copy link
Contributor

dgozman commented Jan 10, 2022

Thanks for all the pointers! Closing in favor of #11182.

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

No branches or pull requests

5 participants