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

Overriding data-testid requires a lot of work, and can't be done completely #162

Closed
RoystonS opened this issue Nov 27, 2018 · 9 comments
Closed

Comments

@RoystonS
Copy link
Contributor

Describe the feature you'd like:

An easier way to override the data-testid string.

I'm really sorry to raise this: I'm aware of #76, #128 and #136, and the current proposed solution. But they're really hard to implement in practice, fully, and a very small amount of configuration in dom-testing-library would make that significantly easier for everybody who needs to override the test id.

I definitely agree with @kentcdodds about the composability of JS, wanting to keep things simple, and the fact that it is currently possible to override most of the layers that ordinarily expose the original xxxByTestId functions, but the problem is that data-testid is baked in at a rather low level, and overriding all the things layered on top is actually quite hard/impossible. As more things are added to the framework (e.g. within), this gets harder, and consumers of the library will need to track and override more things.

Here's what you have to do right now if you need to override data-testid:

  1. Implement your own top-level wrapper for react-testing-library. (I've already done this, and would expect to do so, just as I did for Enzyme. That's not an issue.)
  2. Do a fair bit of shenanigans around overriding render (and rerender) to return a set of queries where the implementation of xxxByTestId is changed to your own implementation. This is not trivial, as shown by the number of times that sample code has been tweaked in the react-testing-library docs. These all then need maintaining as APIs change.
  3. (Not documented anywhere else afaik) Override within/getQueriesForElement because that'll return the original xxxByTestId functions too.
  4. Tell all your developers that they mustn't use any functions imported directly from dom-testing-library because those xxxByTestId functions won't work properly. (This rules out being able to use any other helper libraries that make use of dom-testing-library.)

Suggested implementation:

A simple design such as the one proposed by @davidgilbertson in #136 would solve all of this at a stroke and guarantee that whatever functions are layered on top of the library, whatever changes are made to higher level APIs, they'll all work, because we've not had to touch them, and have merely configured the low-level primitive operations directly.

Describe alternatives you've considered:

Doing it all by hand. I've not just considered this - I've been doing it. I've almost got it all working, but it's taken a good few hours, and I still haven't sorted out how to override within in a good way.

I also can't do anything about my devs using things directly imported from dom-testing-library because I can't override those unless I could inject some configuration into dom-testing-library. If more things are added to dom-testing-library like within, I'll have to go back and override those (and anything that returns them, all the way up).

Teachability, Documentation, Adoption, Migration Strategy:

Documentation for react-testing-library and dom-testing-library would be simpler, as the task of overriding data-testid would become a one-liner rather than a significant block of code that needs copying, customising and maintaining by each consumer.

It could easily be a non-breaking change, and I'd be very happy to provide a PR.

@alexkrolick
Copy link
Collaborator

alexkrolick commented Nov 27, 2018

I think there is actually an easier way to get react-testing-library's render to create the correct bindings for within, rerender, etc.

Render takes a queries option which is passed to getQueriesForElement in dom-testing-library

So, defining the custom queries and then doing this should work:

import {queries} from 'dom-testing-library'

const customQueries = {
  /* ...queries... */
  getByTestId,
  getAllByTestId,
  queryAllByTestId,
  queryByTestId
}

const allQueries = {...queries, ...customQueries}

const customRender = (ui, opts) => render(ui, {...opts, queries: allQueries})

The hard part is then mostly in actually implementing the queries, which does take a bit of work, but should be fine as documented.

EDIT: render might also need to return within/getQueriesForElement with a closure over the initial queries argument.

@RoystonS
Copy link
Contributor Author

Yes, I saw the extra queries parameter, which looked like it was intended for this purpose, but a) it didn't appear in any documentation I could find, b) the existing documentation specifically recommends the laborious approach, and c) queries isn't in the TS typings file, suggesting that it wasn't an officially-supported approach.

And, as you say, I'd need to do something with within/getQueriesForElement too. It's all a lot of messing around, and leaves me worried that with future changes to the libraries I'll have to do yet more work to maintain all this, as more functions or render options are added. e.g. if something like a user object (discussed elsewhere) or another top-level API is added, I'd have to override that API as well.

The problem is that I have to override every high-level API that exposes query functions because I can't reconfigure the low-level APIs.

Although pretty appalling to consider due to the maintenance headaches, it would probably actually be less work to have my local build scripts hack the dom-testing-library.esm.js after npm install, to replace data-testid with my testid string in the code, and to maintain that hacky patch, than to provide all the overrides on top, for all high-level query-exposing APIs, and to maintain those.

I'd be interested to better understand the strength of the reluctance to add a specific API to reconfigure the low-level test id string. For many user-space libraries I'd understand, as static configuration APIs are a nasty global stateful thing to be avoided, as they side-by-side use of different configurations hard to achieve. But for this testing library, I can't imagine such a side-by-side use case...? It's also such a specific piece of configuration that it could be made configurable without necessarily opening the flood gates to all sorts of other configurations.

@lukaszfiszer
Copy link

lukaszfiszer commented Nov 27, 2018

I second @RoystonS.

We started replacing enzyme with react-testing-library for a number of existing react application. All of them already use data-test attributes for purposes of selenium e2e tests. Sticking to the data-testid convention is not an option because we would have to rewrite it everywhere. We have implemented a wrapped, but doing this is every project is painful. Maintaining those wrappers when API changes will be even worse. We use Typescript so needed to import appropriate interfaces/type aliases - again not easy.

I think that the decision to make this dataTestId non-configurable is actually harming the adoption of this great library.

@kentcdodds
Copy link
Member

I really wanted to avoid global configuration of this library, but I can see that this is causing real problems for people.

I am willing to look at a pull request that implements this feature request. No promises it will be merged, though I'm feeling at about ~75% in favor of accepting this functionality. Still a little hesitant, but definitely willing to take a look.

@RoystonS
Copy link
Contributor Author

Yup, I do understand and respect the desire to avoid global configuration; I've been giving the high-level wrapping approach a good go as a result, and only (re)raised this reluctantly as it is so painful. Thanks for being willing to consider it, @kentcdodds.

I'll get a PR together, and understand 'no promises'.

@RoystonS
Copy link
Contributor Author

Ok, I've pushed in two alternative PRs (#163 and #164) for the price of one, to enable two alternative configuration APIs to be considered.

The PRs are identical apart from the exposed configuration API.

@alexkrolick
Copy link
Collaborator

alexkrolick commented Nov 28, 2018

I'm in favor of the config object argument in #164. It extends better to something like .dom-testing-libraryrc.js, and I can think of at least one other thing to add in there (custom queries).

@RoystonS
Copy link
Contributor Author

(For anybody reading this, @kentcdodds is looking for feedback on PR #164.)

kentcdodds pushed a commit that referenced this issue Nov 29, 2018
…generic config] (#164)

N.B. This is one of two alternative PRs with slightly differing configuration APIs.  This one contains a generic configuration API exposing one configuration function taking an object which could be extended for future use.  That may desirable or not, hence the two PRs.

**What**:

Introduce an API to allow `data-testid` to be overridden without consumers needing
to construct and maintain a set of higher level decorator functions to override the various 'xxxByTestId' functions as exposed in various places.

<!-- Why are these changes necessary? -->

**Why**:

The existing API, where `data-testid` cannot be changed at the low level, requires a significant amount of effort to customise the higher-level APIs. See issue #162 for more discussion.

**How**:

Ultimately we needed to parameterise what was previously some hardcoded strings in `queries.js`. I would have preferred to keep the changes local to `queries.js`, but we assume elsewhere that all exports from `queries.js` are queries, and I didn't want to alter that.  So I added a simple `config.js` module to store the actual configuration, and `queries.js` references that and `index.js` exposes the tiny configuration API.

<!-- Have you done all of these things?  -->

**Checklist**:

- [x] Documentation
- [x] Tests
- [x] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
- [x] Added myself to contributors table <!-- this is optional, see the contributing guidelines for instructions -->

<!-- feel free to add additional comments -->
@RoystonS
Copy link
Contributor Author

This shipped in v3.13.0. Many thanks to @kentcdodds and those who reviewed/provided input.

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

4 participants