test-tube
v3.0.3
Published
Test React components from the point of view of a user
Downloads
15
Readme
test-tube
Test React components from the point of view of a user
Example
import React from 'react'
import { render } from 'test-tube'
function run() {
const { container } = render(
<div>Hello World!</div>
)
console.log(container.innerHTML)
}
run()
Installation
Install test-tube by running:
yarn add test-tube
API
render(jsx, [container])
Render a React element, returning the <div>
containing it and the
corresponding
React root.
import React from 'react'
import { render } from 'test-tube'
const { container, root } = render(
<div>Hello World!</div>
)
console.log(container.innerHTML)
render
attempts to run React components as if they were being run in a
browser for an actual user. It uses jsdom to
augment Node.js environments with support for browser APIs.
Optionally, a container (HTMLElement
) to render into can be passed in (by
default, render
creates a new <div>
). This enables usecases like
re-rendering a component with different props.
Note that JavaScript referenced using <script>
tags will be downloaded and
executed.
unrender(root)
Unmount a React element
import React, { useEffect } from 'react'
import { render, unrender } from 'test-tube'
function ComponentWithSideEffect() {
useEffect(() => {
localStorage.setItem('globalVariable', 'foo')
return () => {
localStorage.removeItem('globalVariable')
}
})
return (
<div>Side Effect</div>
)
}
const { container, root } = render(<ComponentWithSideEffect />)
console.log(localStorage.getItem('globalVariable'))
unrender(root)
console.log(localStorage.getItem('globalVariable'))
unrender
unmounts a React component, triggering any relevant configured
lifecycle hooks.
format(container)
Format an element into an HTML string to help with debugging
import React from 'react'
import { render, format } from 'test-tube'
const { container } = render(
<div>Hello World!</div>
)
console.log(format(container))
navigate(pathname)
Simulate a URL change.
import React from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import { render, format, navigate } from 'test-tube'
const { container } = render(
<Router>
<>
<Route exact path='/' render={() => 'Home'} />
<Route exact path='/one' render={() => 'One'} />
<Route exact path='/two' render={() => 'Two'} />
</>
</Router>
)
console.log(format(container))
navigate('/one')
console.log(format(container))
navigate('/two')
console.log(format(container))
navigate
changes window.location
to match the given path and then dispatches
a popstate
event, which is verified to work with react-router-dom
.
findElements(container, cssSelector)
Find all elements matching a CSS selector
import React from 'react'
import { render, findElements } from 'test-tube'
const { container } = render(
<div>
<button>Submit</button>
<button>Sign Up</button>
</div>
)
const buttons = findElement(container, 'button')
findElement(container, cssSelector, containedText)
Find the first element matching a CSS selector and optionally, containing some text
import React from 'react'
import { render, findElement } from 'test-tube'
const { container } = render(
<div>
<button>Submit</button>
<button>Sign Up</button>
</div>
)
const submitButton findElement(container, 'button')
const signUpButton = findElement(container, 'button', 'Sign Up')
If a matching element cannot be found, an exception is thrown.
findElement
searches the textContent
, aria-label
and value
for the given
text.
findInput(container, labelText)
Find an input element by its associated label
import React from 'react'
import { render, findInput } from 'test-tube'
const { container } = render(
<div>
<label htmlFor="email">Email</label>
<input id="email" />
</div>
)
const emailInput = findInput(container, 'Email')
If a matching element cannot be found, an exception is thrown.
click(container, containedText)
Click on a button or link containing the given text
import React from 'react'
import { render, click } from 'test-tube'
const { container } = render(
<div>
<button>Click Here</button>
</div>
)
click(container, 'Click Here')
Only links and buttons (with the proper tag or ARIA role) are clickable.
In addition, if the button is the submit button for a form, an onSubmit
event
will also be fired on the form.
fillIn(container, labelText, value)
Find a form field by label and change its value.
import React from 'react'
import { render, fillIn } from 'test-tube'
const { container } = render(
<div>
<label for="username">Username</label>
<input id="username" />
</div>
)
fillIn(container, 'Username', 'example-user')
check(container, labelText)
Check an unchecked checkbox or radio button
import React from 'react'
import { render, check } from 'test-tube'
const { container } = render(
<div>
<label for="confirm">Confirm</label>
<input id="confirm" type="checkbox" />
</div>
)
check(container, 'Username')
If the input is already checked, an exception will be thrown.
uncheck(container, labelText)
Check an unchecked checkbox
import React from 'react'
import { render, uncheck } from 'test-tube'
const { container } = render(
<div>
<label for="confirm">Confirm</label>
<input id="confirm" type="checkbox" checked />
</div>
)
uncheck(container, 'Username')
If the input is already unchecked, an exception will be thrown.
waitForRender(container, timeout = 5000)
Wait for the given container's HTML to be modified for upto the given timeout
import React, { useState, useEffect } from 'react'
import { render, format, waitForRender } from 'test-tube'
function Component() {
const [text, setText] = useState('Waiting')
useEffect(() => {
setTimeout(() => {
setText('Done')
}, 3000)
})
return (
<div>
{text}
</div>
)
}
async function run() {
const { container } = render(<Component />)
console.log(container.textContent)
await waitForRender()
console.log(container.textContent)
}
run()
waitForRender
uses
MutationObserver
to detect when React completes its next render cycle and updates the container's
HTML.
waitForPromises()
Wait for pending Promises to be resolved.
import React, { useState } from 'react'
import { render, format, click, waitForPromises } from 'test-tube'
function Component() {
const [text, setText] = useState('Waiting')
return (
<div>
<span>{text}</span>
<button
onClick={async () => {
await Promise.resolve()
setText('Done')
}}
>
Do Work
</button>
</div>
)
}
async function run() {
const { container } = render(<Component />)
click(container, 'Do Work')
await waitForPromises()
console.log(format(container))
}
run()