Skip to content

Hooks Testing 🎣 #274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 5, 2019
12 changes: 12 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,18 @@
"contributions": [
"code"
]
},
{
"login": "donavon",
"name": "Donavon West",
"avatar_url": "https://avatars3.githubusercontent.com/u/887639?v=4",
"profile": "http://donavon.com",
"contributions": [
"code",
"doc",
"ideas",
"test"
]
}
]
}
35 changes: 18 additions & 17 deletions README.md

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions examples/__tests__/react-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {testHook, cleanup} from 'react-testing-library'

import useCounter from '../react-hooks'

afterEach(cleanup)

test('accepts default initial values', () => {
let count
testHook(() => ({count} = useCounter()))

expect(count).toBe(0)
})

test('accepts a default initial value for `count`', () => {
let count
testHook(() => ({count} = useCounter({})))

expect(count).toBe(0)
})

test('provides an `increment` function', () => {
let count, increment
testHook(() => ({count, increment} = useCounter({step: 2})))

expect(count).toBe(0)
increment()
expect(count).toBe(2)
})

test('provides an `decrement` function', () => {
let count, decrement
testHook(() => ({count, decrement} = useCounter({step: 2})))

expect(count).toBe(0)
decrement()
expect(count).toBe(-2)
})

test('accepts a default initial value for `step`', () => {
let count, increment
testHook(() => ({count, increment} = useCounter({})))

expect(count).toBe(0)
increment()
expect(count).toBe(1)
})
10 changes: 10 additions & 0 deletions examples/react-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {useState} from 'react'

function useCounter({initialCount = 0, step = 1} = {}) {
const [count, setCount] = useState(initialCount)
const increment = () => setCount(c => c + step)
const decrement = () => setCount(c => c - step)
return {count, increment, decrement}
}

export default useCounter
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@
"jest-dom": "^2.0.4",
"jest-in-case": "^1.0.2",
"kcd-scripts": "^0.44.0",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react": "16.8.0",
"react-dom": "16.8.0",
"react-redux": "^5.0.7",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-transition-group": "^2.5.0",
"redux": "^4.0.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
},
"eslintConfig": {
Expand Down
14 changes: 14 additions & 0 deletions src/__tests__/testHook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {useState} from 'react'
import 'jest-dom/extend-expect'
import {testHook, cleanup} from '../'

afterEach(cleanup)

test('testHook calls the callback', () => {
const spy = jest.fn()
testHook(spy)
expect(spy).toHaveBeenCalledTimes(1)
})
test('confirm we can safely call a React Hook from within the callback', () => {
testHook(() => useState())
})
12 changes: 11 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react'
import ReactDOM from 'react-dom'
import {getQueriesForElement, prettyDOM, fireEvent} from 'dom-testing-library'

Expand Down Expand Up @@ -51,6 +52,15 @@ function render(
}
}

function TestHook({callback}) {
callback()
return null
}

function testHook(callback) {
render(<TestHook callback={callback} />)
}

function cleanup() {
mountedContainers.forEach(cleanupAtContainer)
}
Expand Down Expand Up @@ -92,6 +102,6 @@ fireEvent.select = (node, init) => {

// just re-export everything from dom-testing-library
export * from 'dom-testing-library'
export {render, cleanup, flushEffects}
export {render, testHook, cleanup, flushEffects}

/* eslint func-name-matching:0 */
5 changes: 5 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export function render<Q extends Queries>(
options: RenderOptions<Q>,
): RenderResult<Q>

/**
* Renders a test component that calls back to the test.
*/
export function testHook(callback: () => void): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything special to indicate that the callback will be called with the result of the custom hook (so you can get type safety there) or is TypeScript's inference good enough to know?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the callback isn't called with anything and isn't required to return anything. i'm not a TS expert, so I'll defer, but this should work.


/**
* Unmounts React trees that were mounted with render.
*/
Expand Down