printTestIdTree - granmoe/data-tree-snapshot GitHub Wiki

Who This Tool is For

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.)

Intro

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.

Basic Example

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

Examples

There are some good examples in the tests for this helper!

Usage

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
    "
  `)
})

Motivation

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
"
`)
⚠️ **GitHub.com Fallback** ⚠️