printTestIdTree - granmoe/data-tree-snapshot GitHub Wiki
This helper assumes that you make heavy use of data-testid
attributes for testing. The reason to use them is to make your frontend and end-to-end tests resilient to change. As a bonus, they may simultaneously be used as convenient handles for things like analytics.
Test ids are a particularly good fit for you if the nature of your app is such that the text on your site is highly dynamic—you have a lot of user-specific or client-specific text, heavy internationalization, other kinds of configurability or customizability—and/or volatile (changes constantly due to legal, requirements, marketing, etc). It's extra useful if the structure of your app is similarly dynamic. In this situation, other kinds of queries make your tests far too brittle.
(Note: you may want to consider using accessible or semantic queries in your tests if they're enough to get the job done for your app (probably the case if there's little to no client-specific or user-specific wording, configurability, internationalization, etc). As with anything else, printTestIdTree
may or may not be right for your use case.)
Create the smallest possible representation of what test ids are contained in a DOM element and within other test ids within that DOM element. printTestIdTree
only preserves ancestor/descendant relationships because it is used to assert that a given test id contains another test id or test ids. This compacted tree structure is created recursively for the entire DOM tree within the given DOM element or data-testid string.
This HTML:
<div data-testid="root">
<div>
<div data-testid="parent">
<div data-testid="child">
<div>
<div>
<span data-testid="deeply-nested-child">some stuff</span>
</div>
</div>
</div>
<br data-testid="another-child" />
<div>
<br data-testid="yet-another-child" />
</div>
</div>
</div>
</div>
Will produce this concise, neatly formatted output:
parent
child
deeply-nested-child
another-child
yet-another-child
There are some good examples in the tests for this helper!
npm install data-tree-snapshot -D
import React from 'react'
import { render } from '@testing-library/react'
import { printTestIdTree } from 'data-tree-snapshot'
test('example from the README', () => {
render(
<div data-testid="root">
<div>
<div data-testid="parent">
<div data-testid="child">
<div>
<div>
<span data-testid="deeply-nested-child">some stuff</span>
</div>
</div>
</div>
<br data-testid="another-child" />
<div>
<br data-testid="yet-another-child" />
</div>
</div>
</div>
</div>,
)
// 👇 You can pass either a data-testid string or an element itself
expect(printTestIdTree('root')).toMatchInlineSnapshot(`
"
parent
child
deeply-nested-child
another-child
yet-another-child
"
`)
})
If you use data-testids a lot for testing (and possibly other things, like analytics), and your app has a high volume of complex conditional rendering across various scenarios based on multitudinous factors like feature flags, types of client / user, and responsive designs, in addition to your standard UI logic, then you may have tests that look like this:
expect(getByTestId('section-a')).toBeTruthy()
expect(getByTestId('section-b')).toBeTruthy()
expect(getByTestId('section-c')).toBeFalsy()
expect(getByTestId('section-d')).toBeTruthy()
expect(getByTestId('widget-1')).toBeTruthy()
expect(getByTestId('something-inside-widget-1')).toBeTruthy()
expect(getByTestId('something-that-should-not-be-there')).toBeFalsy()
expect(getByTestId('this-should-not-be-there-either')).toBeFalsy()
expect(getByTestId('foo-a')).toBeFalsy()
expect(getByTestId('foo-b')).toBeTruthy()
expect(getByTestId('bar')).toBeTruthy()
expect(getByTestId('baz')).toBeFalsy()
expect(getByTestId('quux')).toBeTruthy()
printTestIdTree
turns that into this:
// You can always use a normal, separate snapshot via toMatchSnapshot also
expect(printTestIdTree('wrapper')).toMatchInlineSnapshot(`
"
section-a
section-b
section-d
widget-1
something-inside-widget-1
foo-b
bar
quux
"
`)