@tdd-buffet/react

React testing for tdd-buffet

Usage no npm install needed!

<script type="module">
  import tddBuffetReact from 'https://cdn.skypack.dev/@tdd-buffet/react';
</script>

README

React testing tools for tdd-buffet

Build Status codecov npm type definitions


This package can be used independently of tdd-buffet.

Install

npm install @tdd-buffet/react

Usage

This package wraps the excellent @testing-library/react and adds jQuery for its powerful selection engine.

The following table illustrates the methods available in @testing-library/react and their equivalents in this package:

@testing-library/react @tdd-buffet/react
render $render
rerender $rerender
unmount $unmount
fireEvent.* $fireEvent.*
fireEvent.click $click
fireEvent.change $change
fireEvent.submit $submit
fireEvent.keyDown $keyDown
waitFor $wait, $waitForElement
queryBy $find
ByText $*ByText
ByTestId $*ByTestId
prettyDOM $prettyDOM

Render components

import React from 'react';
import { expect } from 'tdd-buffet/suite/chai';
import { $render } from '@tdd-buffet/react';

const $container = $render(<span>foobar</span>);

expect($container.text()).to.equal('foobar');

The returned $container is a JQuery wrapper over the container that holds the component. You can use the familiar JQuery API to query for content ($container.find('p')), get text content ($container.text()), assert visibility ($container.find('.class').is(':visible')) etc.

Find elements

Since $render returns a jQuery container you can use the $.find method to query elements by any CSS selector (ID, class name, data-* attribute etc.) or by jQuery's special selectors (:contains, :checked, :disabled etc.).

import React from 'react';
import { $render } from '@tdd-buffet/react';

const $container = $render(<div>
  <p>first paragraph</p>
  <p>second paragraph</p>
</div>);

$container.find('p:second').text() === 'second paragraph';

There are also a few convenience query methods for finding elements by test ID ($getByTestId, $getAllByTestId, $queryByTestId) and by text ($getByText, $queryByText).

import React from 'react';
import { $render, $getByText, $getByTestId } from '@tdd-buffet/react';

$render(<div>
  <p>first paragraph</p>
  <p data-testid="second">second paragraph</p>
</div>);

$getByText('first').text() === 'first paragraph';
$getByTestId('second').text() === 'second paragraph';

By default, all queries search throw the whole component that's currently rendered. You can override this by passing an optional selector to limit the search to e.g. a subtree of the component.

import React from 'react';
import { $render, $getByText, $getByTestId } from '@tdd-buffet/react';

$render(<div>
  <div data-testid="foo">
    <span>foo text</span>
  </div>
  <div data-testid="bar">
    <span>bar text</span>
  </div>
</div>);

console.log(
  $getByText('text', $getByTestId('bar'))
); // 'bar text'

Fire events

The package exposes the fireEvent object from @testing-library/react wrapped in a helper that can take a DOM element, a CSS selector or a JQuery collection:

import React from 'react';
import { $render, $fireEvent } from '@tdd-buffet/react';

$render(<button onClick={() => console.log('clicked')}>
  click me
</button>);

$fireEvent.click('button'); // will log 'clicked'

Some aliases are also exported for the most common events:

$click

Simulate click events on buttons, checkboxes, radio buttons etc.

import React from 'react';
import { $render, $click } from '@tdd-buffet/react';

$render(<button onClick={() => console.log('clicked')}>
  click me
</button>);

$click('button'); // will log 'clicked'

$change

Simulate change events on inputs. Receives the text value as the 2nd argument.

import React from 'react';
import { $render, $change } from '@tdd-buffet/react';

$render(<input onChange={e => console.log(e.target.value)} />);

$change('input', 'foobar'); // will log 'foobar'

$submit

Simulate form submissions. Can be triggered on a form or on a linked button (either inside the form or linked via the form attribute).

import React from 'react';
import { $render, $submit } from '@tdd-buffet/react';

$render(<form onSubmit={() => console.log('submit')}>
  <button>Submit me</button>
</form>);

$submit('button'); // will log 'submit'

$keyDown

Simulate pressing down a key. Receives the key character as the 2nd argument.

import React from 'react';
import { $render, $keyDown } from '@tdd-buffet/react';

$render(<div onKeyDown={(e) => console.log(e.which)} />);

$keyDown('div', 'A'); // will log 65

Wait for conditions

If your component contains async logic like waiting for a promise or for a timer you can use the $wait function to wait for a condition to be satisfied such as an element becoming visible.

import React, { useState } from 'react';
import { $render, $wait, $click } from '@tdd-buffet/react';

const MyComponent = () => {
  const [isLoading, setIsLoading] = useState(true);
  
  return <button onClick={() => setIsLoading(false)}>
    {isLoading ? 'loading' : 'done'}
  </button>;
};

$render(<MyComponent />);
$click('button');

await $wait($container => $container.text() === 'done');

Wait for elements

The package exposes a shortcut to wait for the condition that an element is present in the container.

import React, { useState } from 'react';
import { $render, $waitForElement, $click } from '@tdd-buffet/react';

const MyComponent = () => {
  const [isLoading, setIsLoading] = useState(true);
  
  return <>
    <button onClick={() => setIsLoading(false)}>Click me</button>
    {!isLoading && <span className="present">I'm here</span>}
  </>;
};

$render(<MyComponent />);
$click('button');

await $waitForElement($container => $container.find('.present'));

Unmount

If your component has cleanup logic e.g. clearing timers in componentWillUnmount you can check them in your tests by manually unmounting the component.

import React from 'react';
import { $render, $unmount } from '@tdd-buffet/react';

const $container = $render(<span>foobar</span>);
$unmount();

$container.text() === '';

Rerender

Rerendering a component with new props can be useful if you want to check that it reacts to the new props e.g. getDerivedStateFromProps.

import React from 'react';
import { $render, $rerender } from '@tdd-buffet/react';

$render(<span>foobar</span>);
$rerender(<span>potato</span>);

Print DOM

To help with debugging tests, you can use the $.html() method to get the HTML content of an element. For large DOM trees the output could be hard to read, so you can use the $prettyDOM helper instead to get a more readable representation. By default, it prints the currently rendered component's container, but you can pass a different element e.g. the result of $find.

import { $render, $prettyDOM } from '@tdd-buffet/react';
import React from 'react';

$render(<div>foobar</div>);
console.log($prettyDOM());
// <div>
//   foobar
// </div>