How to Test Ordered Elements in Jest & Testing Library

Testing elements to make sure that they are ordered can be confusing, especially without using `data-testid`. Here's I do it.

By Muhammad Rizqi Ardiansyah on 2023-05-18

Summary


So I have been trying out TDD (test-driven development) lately, and I have met with some roadblock: how do I properly test ordered elements, and make sure those elements are in the correct order?

I gotta say, the approach Testing Library use on finding elements are quite unique. Instead of using class name, elements are quried by using several matchers:

This has many benefits, but also makes me stuck in a roadblock I mentioned before.

We need to be careful, as, for example, if we query using the text matcher, like this:

render(
  <article>
    <p>There's an empty space inside my heart</p>
    <p>Where the weeds take root</p>
    <p>So now I'll set you free</p>
    <p>I'll set you free</p>
  </article>
);

screen.getByText("There's an empty space inside my heart");
screen.getByText("Where the weeds take root");
screen.getByText("So now I'll set you free");
screen.getByText("I'll set you free");

it only get and assert that those elements exists, not assserting that those elements are in the correct order.

Using getAllByRole when the elements' role are defined

If the elements' role are defined, usually in li element, querying elements are simpler, by using getAllByRole method.

Let's say there's an HTML document like this:

render(
  <>
    <h1>My Top 5 Favorite Radiohead Albums</h1>
    <ol>
      <li>In Rainbows</li>
      <li>The Bends</li>
      <li>OK Computer</li>
      <li>The King Of Limbs</li>
      <li>Kid A</li>
    </ol>
  </>
);

you can query those elements like this:

// Will return every <li /> element
const listElements = screen.getAllByRole("listitem");

and, to make sure those elements are in the correct order, iterate every list element:

// Defining the expected list order
const albumList = [
  "In Rainbows",
  "The Bends",
  "OK Computer",
  "The King Of Limbs",
  "Kid A",
];

// Assert it on every element
listElements.forEach((listElement, index) => {
  expect(listElement).toHaveTextContent(albumList[index]);
});

It's also important to note that li are one of the elements you can do this--take a look at this docs.

Using the compareDocumentPosition method

Another approach of asserting the order of elements are comparing the position of elements in DOM tree, using the compareDocumentPosition method.

Let's say there exists a HTML document like this:

render(
  <>
    <p>text 1</p>
    <p>text 2</p>
    <p>text 3</p>
    <p>text 4</p>
    <p>text 5</p>
  </>
);

Unlike previous example, we actually can't use getAllByRole because, by default, the p tag doesn't have ARIA roles defined.

So, an alternative way in this case are like this:

// Define the expected order
const paragraphElements = ["text 1", "text 2", "text 3", "text 4", "text 5"];

// Iterate every element of the expected order
for (let i = 0; i < paragraphElements.length - 1; i++) {
  const currentParagraph = screen.getByText(paragraphElements[i]);
  const nextParagraph = screen.getByText(paragraphElements[i + 1]);

  // Compare the current paragraph element position relative to the expected
  // next paragraph element.
  //
  // If `compareDocumentPosition` returns value 4 or in other words equal to
  // the constant `Node.DOCUMENT_POSITION_FOLLOWING` the test should pass.
  expect(currentParagraph.compareDocumentPosition(nextParagraph)).toBe(
    Node.DOCUMENT_POSITION_FOLLOWING
  );
}

This way, the elements are assert to have the correct order.

Hope this helps.