17 January 2023by Jack McGregor
Any good software stack should be thoroughly tested. We use Jest for our unit tests and Playwright for our E2E / acceptance tests, however, Jest was around long before we started adding Playwright and we use the `data-testid` extensively in our project. Ideally, we have configured Playwright to use the same same ID instead of its own `data-playwright-id` but we have a backend team that had already implemented Playwright on some of their other projects and wanted to keep things consistent.
They also wanted us to follow a pattern, where we can chain IDs to make things easier to find in the DOM.
Take the following structure for example:
Beforehand, we would include `data-plaaywright-id` attributes on the individual components like so:
Unfortunately, this is cumbersome and restrictive to implement, and lacks the ability to chain IDs together to make unique, descriptive paths.
This is where React Hooks come to the rescue!
In this hook we want to do two things: pass in an initial ID as either a string (eg 'container') or as the object `{ 'data-playwright-id': 'some-id' }`. You may observe that the latter is what the hook setter (`setPlaywrightId`) returns. This is what makes it both reusable and chainable.
I styled the returned values of the hook after React's own `useState` hook as we'll see, but obviously you can do what you want. To the hook, you first need to declare it:
Since we'll be wanting to pass the root ID down, we can extend each component's prop to accept our hooks value. This is useful if you have to add IDs to many, many components and want a single source of truth for the ID format.
So we have our components, we've created our root ID using our hook and we've extended the interfaces or components that will be using it, but now we want to extend it.
To do this, we can pass the `playwrightId` prop into a new instance of the hook in a child component.
Nesting a bit further down can get tricky. Fortunately, we can use some lightweight native functionality to pass props to children
Awesome! Now each child of Section will have the `playwrightId` prop with the chained IDs from its parents. Now to need to set it in the child Feature components
Noice.
As a bonus, let's say we need both `data-playwright-id` for Playwright AND `data-testid` for Jest, BUT we don't want them created automatically in case they override other IDS. Easy. We can set an optional boolean argument in our hook and conditionally add on a property to our data attribute object
Then use in your code like so:
Playwright ID scraper31 January 2023Seed file for PayloadCMS23 January 2023Rendering languages in code blocks17 January 2023