Skip to content

Commit

Permalink
Merge branch 'main' into testMock
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/dom/__tests__/asyncHook.test.ts
#	src/native/__tests__/asyncHook.test.ts
#	src/server/__tests__/asyncHook.test.ts
  • Loading branch information
mpeyper committed Jul 30, 2021
2 parents cd60fa4 + a54eeb3 commit a461f78
Show file tree
Hide file tree
Showing 98 changed files with 1,596 additions and 3,850 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 馃洃 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.0
uses: styfle/cancel-workflow-action@0.9.1

- name: 猬囷笍 Checkout repo
uses: actions/checkout@v2
Expand All @@ -41,7 +41,7 @@ jobs:
run: npm run validate

- name: 猬嗭笍 Upload coverage report
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v2.0.2

release:
needs: main
Expand All @@ -52,7 +52,7 @@ jobs:
github.event_name == 'push' }}
steps:
- name: 馃洃 Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.0
uses: styfle/cancel-workflow-action@0.9.1

- name: 猬囷笍 Checkout repo
uses: actions/checkout@v2
Expand Down
4 changes: 4 additions & 0 deletions jest.config.js
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']
})
19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,22 @@
"react-error-boundary": "^3.1.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"@typescript-eslint/eslint-plugin": "4.28.5",
"@typescript-eslint/parser": "4.28.5",
"all-contributors-cli": "6.20.0",
"codecov": "3.8.2",
"codecov": "3.8.3",
"docz": "2.3.1",
"docz-theme-default": "1.2.0",
"docz-utils": "2.3.0",
"eslint": "7.29.0",
"kcd-scripts": "11.1.0",
"prettier": "^2.2.1",
"eslint": "7.31.0",
"get-pkg-repo": "4.1.1",
"kcd-scripts": "11.2.0",
"prettier": "2.3.2",
"react": "17.0.2",
"react-dom": "^17.0.1",
"react-dom": "17.0.2",
"react-test-renderer": "17.0.2",
"ts-node": "^10.0.0",
"typescript": "4.3.4"
"ts-node": "10.1.0",
"typescript": "4.3.5"
},
"peerDependencies": {
"react": ">=16.9.0",
Expand Down
58 changes: 58 additions & 0 deletions src/__tests__/asyncHook.fakeTimers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
describe('async hook (fake timers) tests', () => {
beforeEach(() => {
jest.useFakeTimers()
})

afterEach(() => {
jest.useRealTimers()
})

runForRenderers(['default', 'dom', 'native', 'server/hydrated'], ({ renderHook }) => {
test('should wait for arbitrary expectation to pass when using advanceTimersByTime()', async () => {
const { waitFor } = renderHook(() => null)

let actual = 0
const expected = 1

setTimeout(() => {
actual = expected
}, 200)

let complete = false

jest.advanceTimersByTime(200)

await waitFor(() => {
expect(actual).toBe(expected)
complete = true
})

expect(complete).toBe(true)
})

test('should wait for arbitrary expectation to pass when using runOnlyPendingTimers()', async () => {
const { waitFor } = renderHook(() => null)

let actual = 0
const expected = 1

setTimeout(() => {
actual = expected
}, 200)

let complete = false

jest.runOnlyPendingTimers()

await waitFor(() => {
expect(actual).toBe(expected)
complete = true
})

expect(complete).toBe(true)
})
})
})

// eslint-disable-next-line jest/no-export
export {}
258 changes: 258 additions & 0 deletions src/__tests__/asyncHook.test.ts
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'))
})
})
})

0 comments on commit a461f78

Please sign in to comment.