react-testing-library: Child Component Props Not Rendered Into Snapshot

  • @testing-library/react version: @testing-library/react@11.1.0
  • Testing Framework and version:
  • DOM Environment: jest@23.6.0, node@10.19.0, react@16.11.0, react-dom@16.13.0

Relevant code or config:

The main component to be tested:

// 3rd party libs
import React from 'react';

// components
import LinkItemsAutocomplete from 'common-react/link-items-autocomplete/link-items-autocomplete';

// types
import type { AssetPdpModel } from '../../asset-curation.types';

interface PdpTabDataProps {
  data: AssetPdpModel;
  disabled: boolean;
  onSearchQueryChange: (query: string) => void;
  onAddItem: (nodeId: string) => void;
  onRemoveItem: (nodeId: string) => void;
}

export default function PdpTabData({
  data,
  disabled,
  onSearchQueryChange,
  onAddItem,
  onRemoveItem,
}: PdpTabDataProps): React.ReactElement {
  const baseClassName = 'pdp-tab-data';

  return (
    <div className={baseClassName}>
      <LinkItemsAutocomplete
        data-testid="LinkItemsAutocomplete"
        id="pdp-linked-groups"
        title="asset.linked.group.header"
        subText="asset.linked.group.subtext"
        items={data.linkedGroups.linkedItems}
        suggestions={data.linkedGroups.search.suggestions}
        maxItemsCount={1}
        disabled={disabled}
        onSearchQueryChange={onSearchQueryChange}
        onAddItem={onAddItem}
        onRemoveItem={onRemoveItem}
      />
    </div>
  );
}

The test:

import React from 'react';
import { render, cleanup } from '@testing-library/react';
import PdpTabData from './pdp-tab-data';

jest.mock('common-react/link-items-autocomplete/link-items-autocomplete');

describe('PdpTabData', () => {
  let component;
  let props;

  const defaultProps = {
    data: {
      linkedGroups: {
        linkedItems: 'linkedItems',
        search: {
          suggestions: 'suggestions',
        },
      },
    },
    disabled: false,
    onSearchQueryChange: jest.fn(),
    onAddItem: jest.fn(),
    onRemoveItem: jest.fn(),
  };

  const getDefaultProps = () => defaultProps;

  const setup = () => {
    props = getDefaultProps();
    component = render(<PdpTabData {...props} />);
  };

  beforeEach(() => {
    setup();
  });

  afterEach(() => {
    cleanup();
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('Renderings', () => {
    it('should render the component against default mockdata', () => {
      expect(component.asFragment()).toMatchSnapshot();
    });
  });
});

The rendered snapshot:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PdpTabData Renderings should render the component against default mockdata 1`] = `
<DocumentFragment>
  <div
    class="pdp-tab-data"
  >
    <linkitemsautocompletemock
      data-testid="LinkItemsAutocomplete"
      id="pdp-linked-groups"
      items="linkedItems"
      maxitemscount="1"
      subtext="asset.linked.group.subtext"
      suggestions="suggestions"
      title="asset.linked.group.header"
    />
  </div>
</DocumentFragment>
`;

What you did:

What happened:

Writing a simple first test using @testing-library/react, with the expectation that all component props for rendered child component (including function callback props?) to be rendered out into the snapshot.

Reproduction:

Problem description:

I observed three things:

  1. The disabled prop was not rendered out at all in the snapshot when it’s set to false. I’m guessing this is Intended since the snapshot is mounted off a DOM element? And so falsy attribute boolean values are generally omitted.

  2. Also when the disabled prop is set to true, it renders out the prop, but with an empty value; disabled="". Should this rather be rendered out as disabled (note without equality symbol + empty string quotes)?

  3. Callback function props (onSearchQueryChange, onAddItem, onRemoveItem) are also not rendered out into the snapshot. I’m guessing this is also intended, due to the DOM mounting?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 17 (8 by maintainers)

Most upvoted comments

Perfect! Thanks a lot for the taking the time to explain and linking to the article — Will definitely read it through.

Your explanation makes sense as, in fact, this is the way the browser works when printing an element:

>> const button = document.createElement('button');
button.title = 'Foo';
button.onclick = function () { console.log('clicked') };
>> button
<button title="Foo">

So I guess we should expect the same behaviour here, and when in ultimate need to keep track of those handlers in snapshots, we could always use react-test-renderer in order to keep original React output.

But overall, I will try to reconsider my approach and check if simply having a test case checking for a callback isn’t a better solution as opposed to having those handles included in a snapshot. (It probably is 😜 )

@zanona Well, since testing-library works directly with DOM nodes, the actual callbacks for the events aren’t being added since they are handled by event handlers through javascript (that’s the React way). I do think that the approach of snapshot testing is problematic and should be used sparingly. You can see an explanation here: http://kcd.im/snapshots

Ok, if you still think there’s an issue with the library related to custom events, please open a new issue (as this was originally about things supposedly not in the snapshot). It’d be nice if before doing so you really make sure this is potentially a bug or shortcoming of the library. Maybe asking around in a forum such as those I mentioned. Or if you’re already convinced open the issue. When doing so, having a minimal runnable example such as a small codesandbox goes a long way in having me or others here be better able to help you. Out-of-context dumps of the code in the app you’re working are more difficult to comprehend as they usually have a low signal to noise ratio.