Skip to content

Instantly share code, notes, and snippets.

@isthatcentered
Last active June 19, 2019 09:03
Show Gist options
  • Save isthatcentered/1877e6019599220b42c754a469f01542 to your computer and use it in GitHub Desktop.
Save isthatcentered/1877e6019599220b42c754a469f01542 to your computer and use it in GitHub Desktop.
React - Testing setup
yarn add testdouble @testing-library/react jest-dom testdouble-jest jest-then enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16 -D
// MOCKING LOCALSTORAGE
Object.defineProperty( window, "localStorage", {
value: {
clear: jest.fn() as any,
getItem: jest.fn() as any,
setItem: jest.fn() as any,
removeItem: jest.fn() as any,
} as Storage,
} )
// MOCKING FETCH
describe( `Bootstrap()`, () => {
const configFromFBServer = "config_from_firebase_server"
beforeEach( () => resolveFetchCallWith( configFromFBServer ) )
test( `Initializes firebase with server config`, async () => {
await new Firebase().bootstrap()
expect( fetch ).toHaveBeenCalledWith( "/__/firebase/init.json" )
expect( firebase.initializeApp ).toHaveBeenCalledWith( configFromFBServer )
} )
} )
export function resolveFetchCallWith( data: any )
{
jest.spyOn( window, "fetch" )
.mockResolvedValue( makeFetchResponse( data ) )
}
export function makeFetchResponse( data: any ): Response
{
return {
json: () => Promise.resolve( data ),
} as Response
}
import "jest-then"
import td from "testdouble" // https://github.com/testdouble/testdouble.js
import { cleanup } from "@testing-library/react" // https://testing-library.com/docs/
import "jest-dom/extend-expect" // https://www.npmjs.com/package/jest-dom
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'// https://airbnb.io/enzyme/
// @ts-ignore
import testDoubleAdapter from "testdouble-jest" // https://github.com/testdouble/testdouble-jest
Enzyme.configure({ adapter: new Adapter() });
testDoubleAdapter( td, jest )
afterEach( () => {
cleanup() // react-testing-library-setup (will have to check performance impact without it as global)
td.reset()
jest.resetAllMocks()
} )
declare global
{
interface Array<T>
{
last(): T | undefined
first(): T | undefined
hasDuplicates: () => boolean
}
}
Object.defineProperties( Array.prototype, {
hasDuplicates: {
value: function () {
const withoutDuplicates = new Set( this )
return withoutDuplicates.size !== this.length
},
},
last: {
value: function () {
return this[ this.length - 1 ]
},
},
first: {
value: function () {
return this[ 0 ]
},
},
} )
export default undefined // otherwise ts will throw "Cannot compile namespaces when the '--isolatedModules' flag is provided." See note in https://facebook.github.io/create-react-app/docs/running-tests
import { fireEvent, render } from "react-testing-library"
import * as React from "react"
import { ReactElement } from "react"
import { createHistory, createMemorySource, LocationProvider } from "@reach/router"
import { App } from "./App"
import { ServicesContainer, ServicesContext } from "./ServicesContext"
import { object } from "testdouble"
export function Fake<T>( name: string ): T
{
return name as any as T
}
export function aside<T>( cb: ( res: any ) => void ): ( res: T ) => T
{
return ( res ) => {
cb( res )
return res
}
}
export function tick(): Promise<undefined>
{
return new Promise( resolve =>
process.nextTick( () => resolve() ) )
}
export function customRender( component: ReactElement<any>, services: Partial<ServicesContainer> = object<ServicesContainer>() )
{
const utils = render(
<ServicesContext.Provider value={services as ServicesContainer}>
{component}
</ServicesContext.Provider>,
)
const change = ( label: RegExp, value: any ) =>
fireEvent.change( utils.getByLabelText( label ), { target: { value } } )
const fill = ( label: RegExp, value: string ) => change( label, value )
const slide = ( label: RegExp, value: number ) => change( label, value )
const click = ( label: RegExp ) =>
fireEvent.click( utils.getByText( label ) )
const submit = ( label: RegExp ) =>
fireEvent.submit( (utils.getByText( label ) as HTMLElement).closest( "form" )! )
return {
...utils,
wrapper: utils.container.firstChild as HTMLElement,
change,
fill,
slide,
click,
submit,
}
}
export function appRender( route: string, services: Partial<ServicesContainer> )
{
const [ path, query ] = route.split( "?" )
const history = createHistory( createMemorySource( route ) )
history.location.search = query ?
`?${query}` :
""
const wrapper = customRender(
<LocationProvider history={history}>
<App/>
</LocationProvider>, services )
return {
...wrapper,
navigate: history.navigate,
history,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment