Skip to content

Server Rendering #387

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

Closed
wants to merge 13 commits into from
Closed
2 changes: 2 additions & 0 deletions custom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/custom'
module.exports = require('../lib/custom')
2 changes: 2 additions & 0 deletions custom/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/custom/pure'
module.exports = require('../lib/custom/pure')
2 changes: 2 additions & 0 deletions dom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/dom'
module.exports = require('../lib/dom')
2 changes: 2 additions & 0 deletions dom/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/dom/pure'
module.exports = require('../lib/dom/pure')
2 changes: 2 additions & 0 deletions native/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/native'
module.exports = require('../lib/native')
2 changes: 2 additions & 0 deletions native/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/native/pure'
module.exports = require('../lib/native/pure')
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"files": [
"lib",
"src",
"dom",
"native",
"server",
"pure.js",
"dont-cleanup-after-each.js"
],
Expand Down Expand Up @@ -61,12 +64,25 @@
"prettier-eslint": "11.0.0",
"prettier-eslint-cli": "5.0.0",
"react": "16.13.1",
"react-dom": "^16.13.1",
"react-test-renderer": "16.13.1"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0",
"react-test-renderer": ">=16.9.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-test-renderer": {
"optional": true
}
},
"jest": {
"collectCoverage": true,
"coverageDirectory": "./coverage/",
Expand Down
2 changes: 2 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/server'
module.exports = require('../lib/server')
2 changes: 2 additions & 0 deletions server/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// makes it so people can import from '@testing-library/react-hooks/server/pure'
module.exports = require('../lib/server/pure')
4 changes: 1 addition & 3 deletions src/asyncUtils.js → src/core/async-utils.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { act } from 'react-test-renderer'

function createTimeoutError(utilName, { timeout }) {
const timeoutError = new Error(`Timed out in ${utilName} after ${timeout}ms.`)
timeoutError.timeout = true
return timeoutError
}

function asyncUtils(addResolver) {
function asyncUtils(act, addResolver) {
let nextUpdatePromise = null

const waitForNextUpdate = async (options = {}) => {
Expand Down
9 changes: 9 additions & 0 deletions src/cleanup.js → src/core/cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,13 @@ function removeCleanup(callback) {
cleanupCallbacks = cleanupCallbacks.filter((cb) => cb !== callback)
}

cleanup.autoRegister = function () {
// Automatically registers cleanup in supported testing frameworks
if (typeof afterEach === 'function' && !process.env.RHTL_SKIP_AUTO_CLEANUP) {
afterEach(async () => {
await cleanup()
})
}
}

export { cleanup, addCleanup, removeCleanup }
File renamed without changes.
91 changes: 91 additions & 0 deletions src/core/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import asyncUtils from './async-utils'
import { cleanup, addCleanup, removeCleanup } from './cleanup'

function TestHook({ callback, setValue, setError, ...props }) {
try {
setValue(callback(props))
} catch (err) {
if (err.then) {
throw err
} else {
setError(err)
}
}
return null
}

function resultContainer() {
let value = null
let error = null
const resolvers = []

const result = {
get current() {
if (error) {
throw error
}
return value
},
get error() {
return error
}
}

const updateResult = (val, err) => {
value = val
error = err
resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
}

return {
result,
addResolver: (resolver) => {
resolvers.push(resolver)
},
setValue: (val) => updateResult(val),
setError: (err) => updateResult(undefined, err)
}
}

function defaultWrapper({ children }) {
return children
}

function createRenderHook(createRenderer) {
return function renderHook(callback, { initialProps, wrapper = defaultWrapper } = {}) {
const { result, setValue, setError, addResolver } = resultContainer()
const hookProps = { current: initialProps }
const props = { callback, setValue, setError }
const options = { initialProps, wrapper }

const { render, rerender, unmount, act, ...rendererUtils } = createRenderer(
TestHook,
props,
options
)

render(hookProps.current)

function rerenderHook(newProps = hookProps.current) {
hookProps.current = newProps
rerender(hookProps.current)
}

function unmountHook() {
removeCleanup(unmountHook)
unmount()
}

addCleanup(unmountHook)

return {
result,
rerender: rerenderHook,
unmount: unmountHook,
...asyncUtils(act, addResolver),
...rendererUtils
}
}
}

export { createRenderHook, cleanup }
5 changes: 5 additions & 0 deletions src/custom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createCustomRenderer, cleanup } from './pure'

cleanup.autoRegister()

export { createCustomRenderer, cleanup }
8 changes: 8 additions & 0 deletions src/custom/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createRenderHook, cleanup } from '../core'

function createCustomRenderer(createRenderer) {
const renderHook = createRenderHook(createRenderer)
return { renderHook }
}

export { createCustomRenderer, cleanup }
5 changes: 5 additions & 0 deletions src/dom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { renderHook, act, cleanup } from './pure'

cleanup.autoRegister()

export { renderHook, act, cleanup }
44 changes: 44 additions & 0 deletions src/dom/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { Suspense } from 'react'
import ReactDOM from 'react-dom'
import { act } from 'react-dom/test-utils'
import { createRenderHook, cleanup } from '../core'

function Fallback() {
return null
}

function createRenderer(TestHook, testHookProps, { wrapper: Wrapper }) {
const container = document.createElement('div')

const toRender = (props) =>
console.log(props) || (
<Wrapper {...props}>
<TestHook {...props} {...testHookProps} />
</Wrapper>
)

return {
render(props) {
document.body.appendChild(container)
act(() => {
ReactDOM.render(<Suspense fallback={<Fallback />}>{toRender(props)}</Suspense>, container)
})
},
rerender(props) {
act(() => {
ReactDOM.render(<Suspense fallback={<Fallback />}>{toRender(props)}</Suspense>, container)
})
},
unmount() {
act(() => {
ReactDOM.unmountComponentAtNode(container)
})
document.body.removeChild(container)
},
act
}
}

const renderHook = createRenderHook(createRenderer)

export { renderHook, act, cleanup }
11 changes: 3 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { cleanup } from './pure'
import { renderHook, act, cleanup } from './pure'

// Automatically registers cleanup in supported testing frameworks
if (typeof afterEach === 'function' && !process.env.RHTL_SKIP_AUTO_CLEANUP) {
afterEach(async () => {
await cleanup()
})
}
cleanup.autoRegister()

export * from './pure'
export { renderHook, act, cleanup }
5 changes: 5 additions & 0 deletions src/native/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { renderHook, act, cleanup } from './pure'

cleanup.autoRegister()

export { renderHook, act, cleanup }
39 changes: 39 additions & 0 deletions src/native/pure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { Suspense } from 'react'
import { act, create } from 'react-test-renderer'
import { createRenderHook, cleanup } from '../core'

function Fallback() {
return null
}
function createRenderer(TestHook, testHookProps, { wrapper: Wrapper }) {
let container

const toRender = (props) => (
<Wrapper {...props}>
<TestHook {...props} {...testHookProps} />
</Wrapper>
)

return {
render(props) {
act(() => {
container = create(<Suspense fallback={<Fallback />}>{toRender(props)}</Suspense>)
})
},
rerender(props) {
act(() => {
container.update(<Suspense fallback={<Fallback />}>{toRender(props)}</Suspense>)
})
},
unmount() {
act(() => {
container.unmount()
})
},
act
}
}

const renderHook = createRenderHook(createRenderer)

export { renderHook, act, cleanup }
Loading