Description
First, I'd like to thank you for getting react-testing-library
updated so quickly after the release of hooks!
Describe the feature you'd like:
I'd like to add an option to testHook()
for testing custom hooks that use useContext()
and need to be wrapped in the Provider
.
Below is a minimal (albeit silly) example of how useContext()
could be used in a custom hook. I've used useContext()
in a similar manner to this in more complicated custom hooks.
// examples/react-context-hook.js
import React from 'react'
const NameContext = React.createContext('Unknown')
function useGreeting() {
const name = useContext(NameContext)
return `Hello, ${name}!`
}
export {NameContext, useGreeting}
Suggested implementation:
[Option 1] Providing the Context
// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'
import {NameContext, useGreeting} from '../react-context-hook'
afterEach(cleanup)
test('provides the default value from context', () => {
let name
testHook(() => (name = useGreeting()), NameContext)
expect(name).toBe('Hello, Unknown!')
})
test('provides the custom value from context', () => {
let name
testHook(() => (name = useGreeting()), NameContext, 'CustomName')
expect(name).toBe('Hello, CustomName!')
})
[Option 2] Providing a Fixture
// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'
import {NameContext, useGreeting} from '../react-context-hook'
afterEach(cleanup)
test('provides the value from context', () => {
const Fixture = ({ children }) => (
<NameContext.Provider>
{children}
</NameContext.Provider>
)
let name
testHook(() => (name = useGreeting()), Fixture)
expect(name).toBe('Hello, Unknown!')
})
test('provides the custom value from context', () => {
const Fixture = ({ children }) => (
<NameContext.Provider value="CustomName">
{children}
</NameContext.Provider>
)
let name
testHook(() => (name = useGreeting()), Fixture)
expect(name).toBe('Hello, CustomName!')
})
[Option 3] Providing a Component and Props
// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'
import {NameContext, useGreeting} from '../react-context-hook'
afterEach(cleanup)
test('provides the default value from context', () => {
let name
testHook(() => (name = useGreeting()), NameContext.Provider)
expect(name).toBe('Hello, Unknown!')
})
test('provides the custom value from context', () => {
let name
testHook(() => (name = useGreeting()), NameContext.Provider, { value: 'CustomName' })
expect(name).toBe('Hello, CustomName!')
})
[Option 4] Some variation on the options above
I like the conciseness of 1 and 3, but 2 seems like it would probably be the most versatile. These were just my initial thoughts, so there's probably something that will work better than any of these.
Describe alternatives you've considered:
I'm doing something similar to this right now, but I could probably update my helper to work more like how testHook
is implemented.
import {render, cleanup} from 'react-testing-library'
import {NameContext, useGreeting} from '../react-context-hook'
function renderWithContext(node, {value, ...options}) {
return render(
<NameContext.Provider value={value}>
{node}
</NameContext.Provider>,
options
)
}
afterEach(cleanup)
test('provides the custom value from context', () => {
function Fixture() {
const greeting = useGreeting()
return `${greeting}`
}
const { queryByText } = renderWithContext(<Fixture />)
expect(queryByText('Hello, Unknown!')).toBeTruthy()
})
test('provides the custom value from context', () => {
function Fixture() {
const greeting = useGreeting()
return `${greeting}`
}
const { queryByText } = renderWithContext(<Fixture />, { value: 'CustomName' })
expect(queryByText('Hello, CustomName!')).toBeTruthy()
})
Teachability, Documentation, Adoption, Migration Strategy:
Examples from above can be used. Adding the additional argument would be a minor release probably, so existing tests wouldn't need to be updated.