diff --git a/.all-contributorsrc b/.all-contributorsrc
index fd2fd14a..d7d137c5 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -212,6 +212,64 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "nobrayner",
+ "name": "Braydon Hall",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/40751395?v=4",
+ "profile": "https://github.com/nobrayner",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "JacobMGEvans",
+ "name": "Jacob M-G Evans",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/27247160?v=4",
+ "profile": "https://dev.to/jacobmgevans",
+ "contributions": [
+ "code",
+ "test"
+ ]
+ },
+ {
+ "login": "tigerabrodi",
+ "name": "Tiger Abrodi",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/49603590?v=4",
+ "profile": "https://tigerabrodi.dev/",
+ "contributions": [
+ "code",
+ "test"
+ ]
+ },
+ {
+ "login": "merodiro",
+ "name": "Amr A.Mohammed",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/17033502?v=4",
+ "profile": "https://github.com/merodiro",
+ "contributions": [
+ "code",
+ "test"
+ ]
+ },
+ {
+ "login": "juhanakristian",
+ "name": "Juhana Jauhiainen",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/544386?v=4",
+ "profile": "https://github.com/juhanakristian",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "jensmeindertsma",
+ "name": "Jens Meindertsma",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/64677517?v=4",
+ "profile": "https://github.com/jensmeindertsma",
+ "contributions": [
+ "code",
+ "test"
+ ]
}
],
"commitConvention": "none"
diff --git a/.eslintrc b/.eslintrc
index d6c4554f..052c9caa 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,10 +1,16 @@
{
- "extends": "./node_modules/kcd-scripts/eslint.js",
+ "extends": ["./node_modules/kcd-scripts/eslint.js"],
"rules": {
"max-lines-per-function": "off",
"no-constant-condition": "off",
"no-await-in-loop": "off",
+ "no-console": "off",
+ "import/no-unresolved": "off",
"react-hooks/rules-of-hooks": "off",
- "no-console": "off"
+ "@typescript-eslint/no-floating-promises": "off",
+ "@typescript-eslint/no-unnecessary-condition": "off"
+ },
+ "parserOptions": {
+ "project": ["./tsconfig.json", "./test/tsconfig.json"]
}
}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8924985f..24384b04 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,11 +27,6 @@ select the added contribution type.
Please make sure to run the tests before you commit your changes. You can do so by running
`npm test`.
-### Update Typings
-
-The TypeScript type definitions can be found in the
-[DefinitelyTyped repo](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/testing-library__react-hooks).
-
## Help needed
Please check out the
diff --git a/README.md b/README.md
index 72ef55a9..b62c279d 100644
--- a/README.md
+++ b/README.md
@@ -192,6 +192,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Josh 📖 |
Na'aman Hirschfeld 💻 |
+
+ Braydon Hall 💻 |
+ Jacob M-G Evans 💻 ⚠️ |
+ Tiger Abrodi 💻 ⚠️ |
+ Amr A.Mohammed 💻 ⚠️ |
+ Juhana Jauhiainen 💻 |
+ Jens Meindertsma 💻 ⚠️ |
+
diff --git a/docs/api-reference.md b/docs/api-reference.md
index a3cc05e5..fa7daf4b 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -259,28 +259,3 @@ The maximum amount of time in milliseconds (ms) to wait. By default, no timeout
If this option is set to `true`, any errors that occur while waiting are treated as a failed check.
If this option is set to `false`, any errors that occur while waiting cause the promise to be
rejected. By default, errors are not suppressed for this utility.
-
-### `wait`
-
-_(DEPRECATED, use [`waitFor`](/reference/api#waitfor) instead)_
-
-```js
-function wait(callback: function(): boolean|void, options?: {
- timeout?: number,
- suppressErrors?: boolean
-}): Promise
-```
-
-Returns a `Promise` that resolves if the provided callback executes without exception and returns a
-truthy or `undefined` value. It is safe to use the [`result` of `renderHook`](/reference/api#result)
-in the callback to perform assertion or to test values.
-
-#### `timeout`
-
-The maximum amount of time in milliseconds (ms) to wait. By default, no timeout is applied.
-
-#### `suppressErrors`
-
-If this option is set to `true`, any errors that occur while waiting are treated as a failed check.
-If this option is set to `false`, any errors that occur while waiting cause the promise to be
-rejected. By default, errors are suppressed for this utility.
diff --git a/jest.config.js b/jest.config.js
index c2e6a069..bb6a1d85 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,6 +1,7 @@
+// eslint-disable-next-line
const { jest: jestConfig } = require('kcd-scripts/config')
module.exports = Object.assign(jestConfig, {
roots: ['/src', '/test'],
- testMatch: ['/test/*.js']
+ testMatch: ['/test/*.(ts|tsx|js)']
})
diff --git a/package.json b/package.json
index cb1f5f57..5e90940a 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.0.0-semantically-released",
"description": "Simple and complete React hooks testing utilities that encourage good testing practices.",
"main": "lib/index.js",
+ "types": "lib/index.d.ts",
"keywords": [
"testing",
"react",
@@ -27,29 +28,34 @@
"validate": "kcd-scripts validate",
"prepare": "npm run build",
"build": "kcd-scripts build --out-dir lib",
+ "test": "kcd-scripts test",
+ "typecheck": "kcd-scripts typecheck",
"lint": "kcd-scripts lint",
"format": "kcd-scripts format",
"coverage": "codecov",
- "test": "kcd-scripts test",
"docs:dev": "docz dev",
"docs:build": "docz build",
"contributors:add": "all-contributors add"
},
"dependencies": {
"@babel/runtime": "^7.12.5",
- "@types/testing-library__react-hooks": "^3.4.0"
+ "@types/react": ">=16.9.0",
+ "@types/react-test-renderer": ">=16.9.0"
},
"devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^4.9.1",
+ "@typescript-eslint/parser": "^4.9.1",
"all-contributors-cli": "6.19.0",
"codecov": "3.8.1",
"docz": "2.3.1",
"docz-theme-default": "1.2.0",
"docz-utils": "2.3.0",
+ "eslint": "7.15.0",
"kcd-scripts": "7.5.2",
+ "prettier": "^2.2.1",
"react": "17.0.1",
"react-test-renderer": "17.0.1",
- "typescript": "4.1.2",
- "eslint": "7.15.0"
+ "typescript": "4.1.2"
},
"peerDependencies": {
"react": ">=16.9.0",
diff --git a/src/asyncUtils.js b/src/asyncUtils.js
deleted file mode 100644
index afb5ac7e..00000000
--- a/src/asyncUtils.js
+++ /dev/null
@@ -1,123 +0,0 @@
-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 resolveAfter(ms) {
- return new Promise((resolve) => {
- setTimeout(resolve, ms)
- })
-}
-
-let hasWarnedDeprecatedWait = false
-
-function asyncUtils(addResolver) {
- let nextUpdatePromise = null
-
- const waitForNextUpdate = async (options = {}) => {
- if (!nextUpdatePromise) {
- nextUpdatePromise = new Promise((resolve, reject) => {
- let timeoutId
- if (options.timeout > 0) {
- timeoutId = setTimeout(
- () => reject(createTimeoutError('waitForNextUpdate', options)),
- options.timeout
- )
- }
- addResolver(() => {
- clearTimeout(timeoutId)
- nextUpdatePromise = null
- resolve()
- })
- })
- await act(() => nextUpdatePromise)
- }
- await nextUpdatePromise
- }
-
- const waitFor = async (callback, { interval, timeout, suppressErrors = true } = {}) => {
- // eslint-disable-next-line consistent-return
- const checkResult = () => {
- try {
- const callbackResult = callback()
- return callbackResult || callbackResult === undefined
- } catch (e) {
- if (!suppressErrors) {
- throw e
- }
- }
- }
-
- const waitForResult = async () => {
- const initialTimeout = timeout
- while (true) {
- const startTime = Date.now()
- try {
- const nextCheck = interval
- ? Promise.race([waitForNextUpdate({ timeout }), resolveAfter(interval)])
- : waitForNextUpdate({ timeout })
-
- await nextCheck
-
- if (checkResult()) {
- return
- }
- } catch (e) {
- if (e.timeout) {
- throw createTimeoutError('waitFor', { timeout: initialTimeout })
- }
- throw e
- }
- timeout -= Date.now() - startTime
- }
- }
-
- if (!checkResult()) {
- await waitForResult()
- }
- }
-
- const waitForValueToChange = async (selector, options = {}) => {
- const initialValue = selector()
- try {
- await waitFor(() => selector() !== initialValue, {
- suppressErrors: false,
- ...options
- })
- } catch (e) {
- if (e.timeout) {
- throw createTimeoutError('waitForValueToChange', options)
- }
- throw e
- }
- }
-
- const wait = async (callback, { timeout, suppressErrors } = {}) => {
- if (!hasWarnedDeprecatedWait) {
- hasWarnedDeprecatedWait = true
- console.warn(
- '`wait` has been deprecated. Use `waitFor` instead: https://react-hooks-testing-library.com/reference/api#waitfor.'
- )
- }
- try {
- await waitFor(callback, { timeout, suppressErrors })
- } catch (e) {
- if (e.timeout) {
- throw createTimeoutError('wait', { timeout })
- }
- throw e
- }
- }
-
- return {
- wait,
- waitFor,
- waitForNextUpdate,
- waitForValueToChange
- }
-}
-
-export default asyncUtils
diff --git a/src/asyncUtils.ts b/src/asyncUtils.ts
new file mode 100644
index 00000000..22921721
--- /dev/null
+++ b/src/asyncUtils.ts
@@ -0,0 +1,112 @@
+import { act } from 'react-test-renderer'
+
+export interface WaitOptions {
+ interval?: number
+ timeout?: number
+ suppressErrors?: boolean
+}
+
+class TimeoutError extends Error {
+ constructor(utilName: string, { timeout }: Pick) {
+ super(`Timed out in ${utilName} after ${timeout as number}ms.`)
+ }
+}
+
+function resolveAfter(ms: number) {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms)
+ })
+}
+
+function asyncUtils(addResolver: (callback: () => void) => void) {
+ let nextUpdatePromise: Promise | null = null
+
+ const waitForNextUpdate = async (options: Pick = {}) => {
+ if (!nextUpdatePromise) {
+ nextUpdatePromise = new Promise((resolve, reject) => {
+ let timeoutId: ReturnType
+ if (options.timeout && options.timeout > 0) {
+ timeoutId = setTimeout(
+ () => reject(new TimeoutError('waitForNextUpdate', options)),
+ options.timeout
+ )
+ }
+ addResolver(() => {
+ clearTimeout(timeoutId)
+ nextUpdatePromise = null
+ resolve()
+ })
+ })
+ await act(() => nextUpdatePromise as Promise)
+ }
+ await nextUpdatePromise
+ }
+
+ const waitFor = async (
+ callback: () => T | Promise,
+ { interval, timeout, suppressErrors = true }: WaitOptions = {}
+ ) => {
+ const checkResult = () => {
+ try {
+ const callbackResult = callback()
+ return callbackResult || callbackResult === undefined
+ } catch (error: unknown) {
+ if (!suppressErrors) {
+ throw error as Error
+ }
+ return undefined
+ }
+ }
+
+ const waitForResult = async () => {
+ const initialTimeout = timeout
+ while (true) {
+ const startTime = Date.now()
+ try {
+ const nextCheck = interval
+ ? Promise.race([waitForNextUpdate({ timeout }), resolveAfter(interval)])
+ : waitForNextUpdate({ timeout })
+
+ await nextCheck
+
+ if (checkResult()) {
+ return
+ }
+ } catch (error: unknown) {
+ if (error instanceof TimeoutError) {
+ throw new TimeoutError('waitFor', { timeout: initialTimeout })
+ }
+ throw error as Error
+ }
+ if (timeout) timeout -= Date.now() - startTime
+ }
+ }
+
+ if (!checkResult()) {
+ await waitForResult()
+ }
+ }
+
+ const waitForValueToChange = async (selector: () => unknown, options: WaitOptions = {}) => {
+ const initialValue = selector()
+ try {
+ await waitFor(() => selector() !== initialValue, {
+ suppressErrors: false,
+ ...options
+ })
+ } catch (error: unknown) {
+ if (error instanceof TimeoutError) {
+ throw new TimeoutError('waitForValueToChange', options)
+ }
+ throw error as Error
+ }
+ }
+
+ return {
+ waitFor,
+ waitForNextUpdate,
+ waitForValueToChange
+ }
+}
+
+export default asyncUtils
diff --git a/src/cleanup.js b/src/cleanup.ts
similarity index 55%
rename from src/cleanup.js
rename to src/cleanup.ts
index d3172c46..8309bd04 100644
--- a/src/cleanup.js
+++ b/src/cleanup.ts
@@ -1,4 +1,4 @@
-let cleanupCallbacks = []
+let cleanupCallbacks: (() => Promise | void)[] = []
async function cleanup() {
for (const callback of cleanupCallbacks) {
@@ -7,12 +7,12 @@ async function cleanup() {
cleanupCallbacks = []
}
-function addCleanup(callback) {
- cleanupCallbacks.unshift(callback)
+function addCleanup(callback: () => Promise | void) {
+ cleanupCallbacks = [callback, ...cleanupCallbacks]
return () => removeCleanup(callback)
}
-function removeCleanup(callback) {
+function removeCleanup(callback: () => Promise | void) {
cleanupCallbacks = cleanupCallbacks.filter((cb) => cb !== callback)
}
diff --git a/src/index.js b/src/index.ts
similarity index 100%
rename from src/index.js
rename to src/index.ts
diff --git a/src/pure.js b/src/pure.js
deleted file mode 100644
index 435d0736..00000000
--- a/src/pure.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import React, { Suspense } from 'react'
-import { act, create } from 'react-test-renderer'
-import asyncUtils from './asyncUtils'
-import { cleanup, addCleanup, removeCleanup } from './cleanup'
-
-function TestHook({ callback, hookProps, onError, children }) {
- try {
- children(callback(hookProps))
- } catch (err) {
- if (err.then) {
- throw err
- } else {
- onError(err)
- }
- }
- return null
-}
-
-function Fallback() {
- return null
-}
-
-function resultContainer() {
- const results = []
- const resolvers = []
-
- const result = {
- get all() {
- return results.map(({ value, error }) => error || value)
- },
- get current() {
- const { value, error } = results[results.length - 1]
- if (error) {
- throw error
- }
- return value
- },
- get error() {
- const { error } = results[results.length - 1]
- return error
- }
- }
-
- const updateResult = (value, error) => {
- results.push({ value, error })
- resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
- }
-
- return {
- result,
- addResolver: (resolver) => {
- resolvers.push(resolver)
- },
- setValue: (value) => updateResult(value),
- setError: (error) => updateResult(undefined, error)
- }
-}
-
-function renderHook(callback, { initialProps, wrapper } = {}) {
- const { result, setValue, setError, addResolver } = resultContainer()
- const hookProps = { current: initialProps }
-
- const wrapUiIfNeeded = (innerElement) =>
- wrapper ? React.createElement(wrapper, hookProps.current, innerElement) : innerElement
-
- const toRender = () =>
- wrapUiIfNeeded(
- }>
-
- {setValue}
-
-
- )
-
- let testRenderer
- act(() => {
- testRenderer = create(toRender())
- })
- const { unmount, update } = testRenderer
-
- function rerenderHook(newProps = hookProps.current) {
- hookProps.current = newProps
- act(() => {
- update(toRender())
- })
- }
-
- function unmountHook() {
- act(() => {
- removeCleanup(unmountHook)
- unmount()
- })
- }
-
- addCleanup(unmountHook)
-
- return {
- result,
- rerender: rerenderHook,
- unmount: unmountHook,
- ...asyncUtils(addResolver)
- }
-}
-
-export { renderHook, cleanup, addCleanup, removeCleanup, act }
diff --git a/src/pure.tsx b/src/pure.tsx
new file mode 100644
index 00000000..a1c14897
--- /dev/null
+++ b/src/pure.tsx
@@ -0,0 +1,124 @@
+import React, { ReactElement, ReactNode, Suspense } from 'react'
+import { act, create, ReactTestRenderer } from 'react-test-renderer'
+import asyncUtils from './asyncUtils'
+import { cleanup, addCleanup, removeCleanup } from './cleanup'
+
+function isPromise(value: unknown): boolean {
+ return typeof (value as PromiseLike).then === 'function'
+}
+
+type TestHookProps = {
+ callback: (props: TProps) => TResult
+ hookProps: TProps | undefined
+ onError: (error: Error) => void
+ children: (value: TResult) => void
+}
+
+function TestHook({
+ callback,
+ hookProps,
+ onError,
+ children
+}: TestHookProps) {
+ try {
+ // coerce undefined into TProps, so it maintains the previous behaviour
+ children(callback(hookProps as TProps))
+ } catch (err: unknown) {
+ if (isPromise(err)) {
+ throw err
+ } else {
+ onError(err as Error)
+ }
+ }
+ return null
+}
+
+function Fallback() {
+ return null
+}
+
+function resultContainer() {
+ const results: Array<{ value?: TValue; error?: Error }> = []
+ const resolvers: Array<() => void> = []
+
+ const result = {
+ get all() {
+ return results.map(({ value, error }) => error ?? value)
+ },
+ get current() {
+ const { value, error } = results[results.length - 1]
+ if (error) {
+ throw error
+ }
+ return value as TValue
+ },
+ get error() {
+ const { error } = results[results.length - 1]
+ return error
+ }
+ }
+
+ const updateResult = (value?: TValue, error?: Error) => {
+ results.push({ value, error })
+ resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
+ }
+
+ return {
+ result,
+ addResolver: (resolver: () => void) => {
+ resolvers.push(resolver)
+ },
+ setValue: (value: TValue) => updateResult(value),
+ setError: (error: Error) => updateResult(undefined, error)
+ }
+}
+
+function renderHook(
+ callback: (props: TProps) => TResult,
+ { initialProps, wrapper }: { initialProps?: TProps; wrapper?: React.ComponentType } = {}
+) {
+ const { result, setValue, setError, addResolver } = resultContainer()
+ const hookProps = { current: initialProps }
+
+ const wrapUiIfNeeded = (innerElement: ReactNode) =>
+ wrapper ? React.createElement(wrapper, hookProps.current, innerElement) : innerElement
+
+ const toRender = () =>
+ wrapUiIfNeeded(
+ }>
+
+ {setValue}
+
+
+ ) as ReactElement
+
+ let testRenderer: ReactTestRenderer
+ act(() => {
+ testRenderer = create(toRender())
+ })
+
+ function rerenderHook(newProps: typeof initialProps = hookProps.current) {
+ hookProps.current = newProps
+ act(() => {
+ testRenderer.update(toRender())
+ })
+ }
+
+ function unmountHook() {
+ act(() => {
+ removeCleanup(unmountHook)
+ testRenderer.unmount()
+ })
+ }
+
+ addCleanup(unmountHook)
+
+ return {
+ result,
+ rerender: rerenderHook,
+ unmount: unmountHook,
+ ...asyncUtils(addResolver)
+ }
+}
+
+export { renderHook, cleanup, addCleanup, removeCleanup, act }
diff --git a/test/asyncHook.js b/test/asyncHook.ts
similarity index 74%
rename from test/asyncHook.js
rename to test/asyncHook.ts
index 74d321a6..5479db82 100644
--- a/test/asyncHook.js
+++ b/test/asyncHook.ts
@@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from 'react'
import { renderHook } from '../src'
describe('async hook tests', () => {
- const useSequence = (...values) => {
+ const useSequence = (...values: string[]) => {
const [first, ...otherValues] = values
const [value, setValue] = useState(first)
const index = useRef(0)
@@ -266,92 +266,4 @@ describe('async hook tests', () => {
expect(result.current).toBe('third')
})
-
- test('should wait for expectation to pass (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second', 'third'))
-
- expect(result.current).toBe('first')
-
- let complete = false
- await wait(() => {
- expect(result.current).toBe('third')
- complete = true
- })
- expect(complete).toBe(true)
- })
-
- test('should not hang if expectation is already passing (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second'))
-
- expect(result.current).toBe('first')
-
- let complete = false
- await wait(() => {
- expect(result.current).toBe('first')
- complete = true
- })
- expect(complete).toBe(true)
- })
-
- test('should reject if callback throws error (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second', 'third'))
-
- expect(result.current).toBe('first')
-
- await expect(
- wait(
- () => {
- if (result.current === 'second') {
- throw new Error('Something Unexpected')
- }
- return result.current === 'third'
- },
- {
- suppressErrors: false
- }
- )
- ).rejects.toThrow(Error('Something Unexpected'))
- })
-
- test('should reject if callback immediately throws error (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second', 'third'))
-
- expect(result.current).toBe('first')
-
- await expect(
- wait(
- () => {
- throw new Error('Something Unexpected')
- },
- {
- suppressErrors: false
- }
- )
- ).rejects.toThrow(Error('Something Unexpected'))
- })
-
- test('should wait for truthy value (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second', 'third'))
-
- expect(result.current).toBe('first')
-
- await wait(() => result.current === 'third')
-
- expect(result.current).toBe('third')
- })
-
- test('should reject if timeout exceeded when waiting for expectation to pass (deprecated)', async () => {
- const { result, wait } = renderHook(() => useSequence('first', 'second', 'third'))
-
- expect(result.current).toBe('first')
-
- await expect(
- wait(
- () => {
- expect(result.current).toBe('third')
- },
- { timeout: 75 }
- )
- ).rejects.toThrow(Error('Timed out in wait after 75ms.'))
- })
})
diff --git a/test/autoCleanup.disabled.js b/test/autoCleanup.disabled.ts
similarity index 89%
rename from test/autoCleanup.disabled.js
rename to test/autoCleanup.disabled.ts
index d11f9314..35cbf91a 100644
--- a/test/autoCleanup.disabled.js
+++ b/test/autoCleanup.disabled.ts
@@ -4,10 +4,11 @@ import { useEffect } from 'react'
// then we DON'T auto-wire up the afterEach for folks
describe('skip auto cleanup (disabled) tests', () => {
let cleanupCalled = false
- let renderHook
+ let renderHook: (arg0: () => void) => void
beforeAll(() => {
process.env.RHTL_SKIP_AUTO_CLEANUP = 'true'
+ // eslint-disable-next-line
renderHook = require('../src').renderHook
})
diff --git a/test/autoCleanup.noAfterEach.js b/test/autoCleanup.noAfterEach.ts
similarity index 80%
rename from test/autoCleanup.noAfterEach.js
rename to test/autoCleanup.noAfterEach.ts
index 9b894e00..cd30a841 100644
--- a/test/autoCleanup.noAfterEach.js
+++ b/test/autoCleanup.noAfterEach.ts
@@ -4,11 +4,13 @@ import { useEffect } from 'react'
// then we DON'T auto-wire up the afterEach for folks
describe('skip auto cleanup (no afterEach) tests', () => {
let cleanupCalled = false
- let renderHook
+ let renderHook: (arg0: () => void) => void
beforeAll(() => {
+ // @ts-expect-error Turning off AfterEach -- ignore Jest LifeCycle Type
// eslint-disable-next-line no-global-assign
afterEach = false
+ // eslint-disable-next-line
renderHook = require('../').renderHook
})
diff --git a/test/autoCleanup.js b/test/autoCleanup.ts
similarity index 100%
rename from test/autoCleanup.js
rename to test/autoCleanup.ts
diff --git a/test/cleanup.js b/test/cleanup.ts
similarity index 92%
rename from test/cleanup.js
rename to test/cleanup.ts
index 05dba6dc..1eafffbf 100644
--- a/test/cleanup.js
+++ b/test/cleanup.ts
@@ -21,8 +21,8 @@ describe('cleanup tests', () => {
})
test('should cleanup all rendered hooks', async () => {
- const cleanupCalled = []
- const hookWithCleanup = (id) => {
+ const cleanupCalled: boolean[] = []
+ const hookWithCleanup = (id: number) => {
useEffect(() => {
return () => {
cleanupCalled[id] = true
@@ -40,7 +40,7 @@ describe('cleanup tests', () => {
})
test('should call cleanups in reverse order', async () => {
- const callSequence = []
+ const callSequence: string[] = []
addCleanup(() => {
callSequence.push('cleanup')
})
@@ -62,7 +62,7 @@ describe('cleanup tests', () => {
})
test('should wait for async cleanup', async () => {
- const callSequence = []
+ const callSequence: string[] = []
addCleanup(() => {
callSequence.push('cleanup')
})
@@ -85,7 +85,7 @@ describe('cleanup tests', () => {
})
test('should remove cleanup using removeCleanup', async () => {
- const callSequence = []
+ const callSequence: string[] = []
addCleanup(() => {
callSequence.push('cleanup')
})
@@ -110,7 +110,7 @@ describe('cleanup tests', () => {
})
test('should remove cleanup using returned handler', async () => {
- const callSequence = []
+ const callSequence: string[] = []
addCleanup(() => {
callSequence.push('cleanup')
})
diff --git a/test/customHook.js b/test/customHook.ts
similarity index 100%
rename from test/customHook.js
rename to test/customHook.ts
diff --git a/test/errorHook.js b/test/errorHook.ts
similarity index 95%
rename from test/errorHook.js
rename to test/errorHook.ts
index 55e425e2..e507bb92 100644
--- a/test/errorHook.js
+++ b/test/errorHook.ts
@@ -2,15 +2,15 @@ import { useState, useEffect } from 'react'
import { renderHook } from '../src'
describe('error hook tests', () => {
- function useError(throwError) {
+ function useError(throwError: boolean) {
if (throwError) {
throw new Error('expected')
}
return true
}
- function useAsyncError(throwError) {
- const [value, setValue] = useState()
+ function useAsyncError(throwError: boolean) {
+ const [value, setValue] = useState()
useEffect(() => {
const timeout = setTimeout(() => setValue(throwError), 100)
return () => clearTimeout(timeout)
@@ -18,7 +18,7 @@ describe('error hook tests', () => {
return useError(value)
}
- function useEffectError(throwError) {
+ function useEffectError(throwError: boolean) {
useEffect(() => {
useError(throwError)
}, [throwError])
diff --git a/test/resultHistory.js b/test/resultHistory.ts
similarity index 100%
rename from test/resultHistory.js
rename to test/resultHistory.ts
diff --git a/test/suspenseHook.js b/test/suspenseHook.ts
similarity index 76%
rename from test/suspenseHook.js
rename to test/suspenseHook.ts
index 6dcfdeae..8d696927 100644
--- a/test/suspenseHook.js
+++ b/test/suspenseHook.ts
@@ -1,10 +1,10 @@
import { renderHook } from '../src'
describe('suspense hook tests', () => {
- const cache = {}
- const fetchName = (isSuccessful) => {
+ const cache: { value?: Promise | string | Error } = {}
+ const fetchName = (isSuccessful: boolean) => {
if (!cache.value) {
- cache.value = new Promise((resolve, reject) => {
+ cache.value = new Promise((resolve, reject) => {
setTimeout(() => {
if (isSuccessful) {
resolve('Bob')
@@ -14,15 +14,15 @@ describe('suspense hook tests', () => {
}, 50)
})
.then((value) => (cache.value = value))
- .catch((e) => (cache.value = e))
+ .catch((e: Error) => (cache.value = e))
}
return cache.value
}
const useFetchName = (isSuccessful = true) => {
const name = fetchName(isSuccessful)
- if (typeof name.then === 'function' || name instanceof Error) {
- throw name
+ if (name instanceof Promise || name instanceof Error) {
+ throw name as unknown
}
return name
}
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 00000000..48209b56
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "declaration": false
+ },
+ "exclude": [],
+ "include": ["."]
+}
diff --git a/test/useContext.js b/test/useContext.tsx
similarity index 89%
rename from test/useContext.js
rename to test/useContext.tsx
index 4bcbe774..03bc19f4 100644
--- a/test/useContext.js
+++ b/test/useContext.tsx
@@ -15,7 +15,7 @@ describe('useContext tests', () => {
test('should get value from context provider', () => {
const TestContext = createContext('foo')
- const wrapper = ({ children }) => (
+ const wrapper: React.FC = ({ children } ) => (
{children}
)
@@ -29,7 +29,7 @@ describe('useContext tests', () => {
const value = { current: 'bar' }
- const wrapper = ({ children }) => (
+ const wrapper: React.FC = ({ children }) => (
{children}
)
@@ -45,7 +45,8 @@ describe('useContext tests', () => {
test('should update value in context when props are updated', () => {
const TestContext = createContext('foo')
- const wrapper = ({ current, children }) => (
+
+ const wrapper: React.FC<{current: string}> = ({ current, children }) => (
{children}
)
diff --git a/test/useEffect.js b/test/useEffect.ts
similarity index 89%
rename from test/useEffect.js
rename to test/useEffect.ts
index 9e120e07..cad9d0f3 100644
--- a/test/useEffect.js
+++ b/test/useEffect.ts
@@ -3,7 +3,7 @@ import { renderHook } from '../src'
describe('useEffect tests', () => {
test('should handle useEffect hook', () => {
- const sideEffect = { 1: false, 2: false }
+ const sideEffect: { [key: number]: boolean } = { 1: false, 2: false }
const { rerender, unmount } = renderHook(
({ id }) => {
@@ -32,7 +32,7 @@ describe('useEffect tests', () => {
})
test('should handle useLayoutEffect hook', () => {
- const sideEffect = { 1: false, 2: false }
+ const sideEffect: { [key: number]: boolean } = { 1: false, 2: false }
const { rerender, unmount } = renderHook(
({ id }) => {
diff --git a/test/useMemo.js b/test/useMemo.ts
similarity index 100%
rename from test/useMemo.js
rename to test/useMemo.ts
diff --git a/test/useReducer.js b/test/useReducer.ts
similarity index 79%
rename from test/useReducer.js
rename to test/useReducer.ts
index 114f579b..7b98431a 100644
--- a/test/useReducer.js
+++ b/test/useReducer.ts
@@ -3,7 +3,8 @@ import { renderHook, act } from '../src'
describe('useReducer tests', () => {
test('should handle useReducer hook', () => {
- const reducer = (state, action) => (action.type === 'inc' ? state + 1 : state)
+ const reducer = (state: number, action: { type: string }) =>
+ action.type === 'inc' ? state + 1 : state
const { result } = renderHook(() => useReducer(reducer, 0))
const [initialState, dispatch] = result.current
diff --git a/test/useRef.js b/test/useRef.ts
similarity index 92%
rename from test/useRef.js
rename to test/useRef.ts
index b9dbefe3..9d3851ff 100644
--- a/test/useRef.js
+++ b/test/useRef.ts
@@ -13,7 +13,7 @@ describe('useHook tests', () => {
test('should handle useImperativeHandle hook', () => {
const { result } = renderHook(() => {
- const ref = useRef()
+ const ref = useRef boolean>>({})
useImperativeHandle(ref, () => ({
fakeImperativeMethod: () => true
}))
diff --git a/test/useState.js b/test/useState.ts
similarity index 100%
rename from test/useState.js
rename to test/useState.ts
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..1337ac30
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./node_modules/kcd-scripts/shared-tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "target": "ES6"
+ },
+ "exclude": ["./test"]
+}