-
Notifications
You must be signed in to change notification settings - Fork 410
Description
Describe the feature you'd like:
Matchers that work when running tests in Puppeteer.
Suggested implementation:
Puppeteer methods return ElementHandles that are proxies for elements in the DOM in the browser it's communicating with. jest-dom currently expects actual HTMLElements. So at a high level, it seems like an implementation approach could be to proxy the methods jest-dom is calling on the HTMLElements into calls to, say, ElementHandle
.evaluate. ElementHandle's methods are all async, so the matchers would need to be async -- perhaps this could be done with a parallel set of async matchers to the existing (sync) matchers.
Describe alternatives you've considered:
- Not using jest-dom with puppeteer :(
- Possibly this would belong in its own repo, something like jest-dom-pptr-adapter
Teachability, Documentation, Adoption, Migration Strategy:
This would likely be straightforward: simply mentioning in the docs that jest-dom is compatible with puppeteer, and noting that the async versions of the matchers would need to be used.
Activity
gnapse commentedon Feb 3, 2020
This seems like something that would be good to look into. I have zero experience with puppeteer, but would love to see this tackled and in the process to get to know more about it.
As for the approach to implement it, if it can be handled internally in a way that does not add significant complexity to the existing code, while allowing for this proxying to happen as transparently as possible, then I'm all for it. Alternatively, I'd also be open to this be in a separate repo.
Please, feel free to explore making a PR to add support for this as you see fit, or starting a more technical discussion here of what this would entail, if you're in doubt it would be accepted.
gnapse commentedon Feb 3, 2020
For instance, an acceptable alternative that seems viable from what you mention the issue is right now, is that we could require all matchers to not consume the received
element
argument directly, but to pass it via some helper/wrapper function that would detect if we're running in a puppeteer environment, or maybe to check if the element is a puppeteer proxy, and do the puppeteer thing, or else do what we do today.The thing that makes me wonder if it would be that simple is that you mention something along the lines of matchers being async with puppeteer, so not sure if that poses an additional difficulty in making the same matchers implementation be compatible with both the sync and async way of calling them.
gnapse commentedon Mar 15, 2020
@sandinmyjoints do you have any comments on this? I'm trying to figure out if there can be some actionable in the future, or else close the issue.
kevin940726 commentedon Dec 6, 2020
I've been playing with this idea recently, and I made a POC of this feature here.
The basic idea is to use sync then-able function to evaluate the element in regular Jest environment, but use
ElementHandle.evaluate
to evaluate the element in puppeteer environment. The hack is that the matchers can now be both a sync and an async function, so that it can be used seamlessly in both environments.Here's a simplified version of the code.
The gotcha here is that everything passed to and returned from
evaluate
should be serializable. Fortunately, I believe this is possible as most of our matchers are dealing with primitive values (or elements, which can also be handled by puppeteer viaElementHandle
).I use
typeof htmlElement.evaluate === 'function'
there to check if the environment is in puppeteer. This is intentionally vague to achieve a few things:puppeteer
as a dependencyOne thing left there is how to handle
checkHtmlElement
calls. I think it's still possible if we refactorcheckHtmlElement
andcheckHasWindow
to return boolean instead of throwing errors. So that we can do someevaluate
magic there and throw the error later in Jest environment.Another option would be to export another function like
toHaveAttributeAsync
and do theevaluate
magic there. The code would be easier to read (can use async/await for instance) and a little less magical. But would require the users to import from another endpoint (maybe@testing-library/jest-dom/puppeteer
?).WDYT? Curious to hear your thoughts :).
nickserv commentedon Dec 8, 2020
Unfortunately if we use the same API and try to hide the async Promises from non-Puppeteer users, we run into issues with
@typescript-eslint/require-await
which would requireawait
even when not using Puppeteer, and thatawait
could cause different event scheduling and crash tests. I think it would be best to have a separateasync
API (or possibly a separate package) for tools like Puppeteer. Alternatively we could have a breaking change that makes the entire APIasync
like testing-library/user-event#504, but I'm not sure it would be worth the breakages.kevin940726 commentedon Dec 9, 2020
I'm not sure I follow this. How does
@typescript-eslint/require-await
work? The implementation I showed is returning athenable
, but not apromise
, so that in theory, it shouldn't requireawait
. 🤔nickserv commentedon Dec 13, 2020
As far as I understand by looking at the code,
@typescript-eslint/require-await
does check to see if a type is thenable.