-
Notifications
You must be signed in to change notification settings - Fork 233
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(types): fix
cleanup
return type to match async
implementation
* fix(types): fix `cleanup` return type to match `async` implementation * test: refactor tests to reduce duplication for different renderers * test: refactor `hydratedServerRenderer` and added comment for clarity about it
- Loading branch information
Showing
95 changed files
with
1,525 additions
and
3,674 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
const { jest: jestConfig } = require('kcd-scripts/config') | ||
module.exports = Object.assign(jestConfig, { | ||
setupFiles: ['<rootDir>/src/__tests__/utils/runForRenderers.ts'] | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
import { useState, useRef, useEffect } from 'react' | ||
|
||
describe('async hook tests', () => { | ||
const useSequence = (values: string[], intervalMs = 50) => { | ||
const [first, ...otherValues] = values | ||
const [value, setValue] = useState(() => first) | ||
const index = useRef(0) | ||
|
||
useEffect(() => { | ||
const interval = setInterval(() => { | ||
setValue(otherValues[index.current++]) | ||
if (index.current >= otherValues.length) { | ||
clearInterval(interval) | ||
} | ||
}, intervalMs) | ||
return () => { | ||
clearInterval(interval) | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, otherValues) | ||
|
||
return value | ||
} | ||
|
||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
test('should wait for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitForNextUpdate() | ||
|
||
expect(result.current).toBe('second') | ||
}) | ||
|
||
test('should wait for multiple updates', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitForNextUpdate() | ||
|
||
expect(result.current).toBe('second') | ||
|
||
await waitForNextUpdate() | ||
|
||
expect(result.current).toBe('third') | ||
}) | ||
|
||
test('should reject if timeout exceeded when waiting for next update', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await expect(waitForNextUpdate({ timeout: 10 })).rejects.toThrow( | ||
Error('Timed out in waitForNextUpdate after 10ms.') | ||
) | ||
}) | ||
|
||
test('should not reject when waiting for next update if timeout has been disabled', async () => { | ||
const { result, waitForNextUpdate } = renderHook(() => useSequence(['first', 'second'], 1100)) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitForNextUpdate({ timeout: false }) | ||
|
||
expect(result.current).toBe('second') | ||
}) | ||
|
||
test('should wait for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
let complete = false | ||
await waitFor(() => { | ||
expect(result.current).toBe('third') | ||
complete = true | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
|
||
test('should wait for arbitrary expectation to pass', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
|
||
let actual = 0 | ||
const expected = 1 | ||
|
||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
|
||
let complete = false | ||
await waitFor(() => { | ||
expect(actual).toBe(expected) | ||
complete = true | ||
}) | ||
|
||
expect(complete).toBe(true) | ||
}) | ||
|
||
test('should not hang if expectation is already passing', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
let complete = false | ||
await waitFor(() => { | ||
expect(result.current).toBe('first') | ||
complete = true | ||
}) | ||
expect(complete).toBe(true) | ||
}) | ||
|
||
test('should wait for truthy value', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitFor(() => result.current === 'third') | ||
|
||
expect(result.current).toBe('third') | ||
}) | ||
|
||
test('should wait for arbitrary truthy value', async () => { | ||
const { waitFor } = renderHook(() => null) | ||
|
||
let actual = 0 | ||
const expected = 1 | ||
|
||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
|
||
await waitFor(() => actual === 1) | ||
|
||
expect(actual).toBe(expected) | ||
}) | ||
|
||
test('should reject if timeout exceeded when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await expect( | ||
waitFor( | ||
() => { | ||
expect(result.current).toBe('third') | ||
}, | ||
{ timeout: 75 } | ||
) | ||
).rejects.toThrow(Error('Timed out in waitFor after 75ms.')) | ||
}) | ||
|
||
test('should not reject when waiting for expectation to pass if timeout has been disabled', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'], 550)) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitFor( | ||
() => { | ||
expect(result.current).toBe('third') | ||
}, | ||
{ timeout: false } | ||
) | ||
|
||
expect(result.current).toBe('third') | ||
}) | ||
|
||
test('should check on interval when waiting for expectation to pass', async () => { | ||
const { result, waitFor } = renderHook(() => useSequence(['first', 'second', 'third'])) | ||
|
||
let checks = 0 | ||
|
||
await waitFor( | ||
() => { | ||
checks++ | ||
return result.current === 'third' | ||
}, | ||
{ interval: 100 } | ||
) | ||
|
||
expect(checks).toBe(3) | ||
}) | ||
|
||
test('should wait for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitForValueToChange(() => result.current === 'third') | ||
|
||
expect(result.current).toBe('third') | ||
}) | ||
|
||
test('should wait for arbitrary value to change', async () => { | ||
const { waitForValueToChange } = renderHook(() => null) | ||
|
||
let actual = 0 | ||
const expected = 1 | ||
|
||
setTimeout(() => { | ||
actual = expected | ||
}, 200) | ||
|
||
await waitForValueToChange(() => actual) | ||
|
||
expect(actual).toBe(expected) | ||
}) | ||
|
||
test('should reject if timeout exceeded when waiting for value to change', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third']) | ||
) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await expect( | ||
waitForValueToChange(() => result.current === 'third', { | ||
timeout: 75 | ||
}) | ||
).rejects.toThrow(Error('Timed out in waitForValueToChange after 75ms.')) | ||
}) | ||
|
||
test('should not reject when waiting for value to change if timeout is disabled', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => | ||
useSequence(['first', 'second', 'third'], 550) | ||
) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await waitForValueToChange(() => result.current === 'third', { | ||
timeout: false | ||
}) | ||
|
||
expect(result.current).toBe('third') | ||
}) | ||
|
||
test('should reject if selector throws error', async () => { | ||
const { result, waitForValueToChange } = renderHook(() => useSequence(['first', 'second'])) | ||
|
||
expect(result.current).toBe('first') | ||
|
||
await expect( | ||
waitForValueToChange(() => { | ||
if (result.current === 'second') { | ||
throw new Error('Something Unexpected') | ||
} | ||
return result.current | ||
}) | ||
).rejects.toThrow(Error('Something Unexpected')) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { useEffect } from 'react' | ||
|
||
// This verifies that if RHTL_SKIP_AUTO_CLEANUP is set | ||
// then we DON'T auto-wire up the afterEach for folks | ||
describe('skip auto cleanup (disabled) tests', () => { | ||
process.env.RHTL_SKIP_AUTO_CLEANUP = 'true' | ||
|
||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
let cleanupCalled = false | ||
|
||
test('first', () => { | ||
const useHookWithCleanup = () => { | ||
useEffect(() => { | ||
return () => { | ||
cleanupCalled = true | ||
} | ||
}) | ||
} | ||
renderHook(() => useHookWithCleanup()) | ||
}) | ||
|
||
test('second', () => { | ||
expect(cleanupCalled).toBe(false) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { useEffect } from 'react' | ||
|
||
// This verifies that if afterEach is unavailable | ||
// then we DON'T auto-wire up the afterEach for folks | ||
describe('skip auto cleanup (no afterEach) tests', () => { | ||
// @ts-expect-error Turning off AfterEach -- ignore Jest LifeCycle Type | ||
// eslint-disable-next-line no-global-assign | ||
afterEach = false | ||
|
||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
let cleanupCalled = false | ||
|
||
test('first', () => { | ||
const useHookWithCleanup = () => { | ||
useEffect(() => { | ||
return () => { | ||
cleanupCalled = true | ||
} | ||
}) | ||
} | ||
renderHook(() => useHookWithCleanup()) | ||
}) | ||
|
||
test('second', () => { | ||
expect(cleanupCalled).toBe(false) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { useEffect } from 'react' | ||
|
||
// This verifies that if process.env is unavailable | ||
// then we still auto-wire up the afterEach for folks | ||
describe('auto cleanup (no process.env) tests', () => { | ||
process.env = { | ||
...process.env, | ||
get RHTL_SKIP_AUTO_CLEANUP(): string | undefined { | ||
throw new Error('expected') | ||
} | ||
} | ||
|
||
runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => { | ||
let cleanupCalled = false | ||
|
||
test('first', () => { | ||
const useHookWithCleanup = () => { | ||
useEffect(() => { | ||
return () => { | ||
cleanupCalled = true | ||
} | ||
}) | ||
} | ||
renderHook(() => useHookWithCleanup()) | ||
}) | ||
|
||
test('second', () => { | ||
expect(cleanupCalled).toBe(true) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { useEffect } from 'react' | ||
|
||
// This verifies that if pure imports are used | ||
// then we DON'T auto-wire up the afterEach for folks | ||
describe('skip auto cleanup (pure) tests', () => { | ||
runForRenderers( | ||
['default/pure', 'dom/pure', 'native/pure', 'server/hydrated/pure'], | ||
({ renderHook }) => { | ||
let cleanupCalled = false | ||
|
||
test('first', () => { | ||
const useHookWithCleanup = () => { | ||
useEffect(() => { | ||
return () => { | ||
cleanupCalled = true | ||
} | ||
}) | ||
} | ||
renderHook(() => useHookWithCleanup()) | ||
}) | ||
|
||
test('second', () => { | ||
expect(cleanupCalled).toBe(false) | ||
}) | ||
} | ||
) | ||
}) |
Oops, something went wrong.