diff --git a/.all-contributorsrc b/.all-contributorsrc index 89091cb7..518b9f23 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1261,6 +1261,15 @@ "contributions": [ "code" ] + }, + { + "login": "anpaopao", + "name": "Angus J. Pope", + "avatar_url": "https://avatars.githubusercontent.com/u/44686792?v=4", + "profile": "https://github.com/anpaopao", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index f9286936..f866879a 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -1,3 +1,4 @@ { - "sandboxes": ["new", "github/kentcdodds/react-testing-library-examples"] + "sandboxes": ["new", "github/kentcdodds/react-testing-library-examples"], + "node": "12" } diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index dc793472..191bbcc1 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -17,7 +17,7 @@ jobs: if: ${{ !contains(github.head_ref, 'all-contributors') }} strategy: matrix: - node: [10.13, 12, 14, 15, 16] + node: [12, 14, 16] react: [latest, next, experimental] runs-on: ubuntu-latest steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5876fb3..68e77e6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,6 @@ Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! [egghead]: - https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github + https://app.egghead.io/series/how-to-contribute-to-an-open-source-project-on-github [all-contributors]: https://github.com/all-contributors/all-contributors [issues]: https://github.com/testing-library/react-testing-library/issues diff --git a/README.md b/README.md index 80d7ad9e..76fc0223 100644 --- a/README.md +++ b/README.md @@ -608,6 +608,7 @@ Thanks goes to these people ([emoji key][emojis]):
Solufa

🐛 💻
Ari Perkkiö

⚠️
Johannes Ewald

💻 +
Angus J. Pope

📖 diff --git a/package.json b/package.json index fde78a35..42555be0 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "types": "types/index.d.ts", "module": "dist/@testing-library/react.esm.js", "engines": { - "node": ">=10" + "node": ">=12" }, "scripts": { "prebuild": "rimraf dist", @@ -44,13 +44,13 @@ "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^7.28.1" + "@testing-library/dom": "^8.0.0" }, "devDependencies": { "@testing-library/jest-dom": "^5.11.6", "@types/react-dom": "^17.0.0", "dotenv-cli": "^4.0.0", - "kcd-scripts": "^7.5.1", + "kcd-scripts": "^11.1.0", "npm-run-all": "^4.1.5", "react": "^17.0.1", "react-dom": "^17.0.1", @@ -68,7 +68,10 @@ "react/no-adjacent-inline-elements": "off", "import/no-unassigned-import": "off", "import/named": "off", - "testing-library/no-dom-import": "off" + "testing-library/no-container": "off", + "testing-library/no-dom-import": "off", + "testing-library/no-unnecessary-act": "off", + "testing-library/prefer-user-event": "off" } }, "eslintIgnore": [ diff --git a/src/__tests__/cleanup.js b/src/__tests__/cleanup.js index 6043ae06..9d3f52d4 100644 --- a/src/__tests__/cleanup.js +++ b/src/__tests__/cleanup.js @@ -54,15 +54,16 @@ describe('fake timers and missing act warnings', () => { jest.useRealTimers() }) - test('cleanup does not flush immediates', () => { + test('cleanup does not flush microtasks', () => { const microTaskSpy = jest.fn() function Test() { const counter = 1 const [, setDeferredCounter] = React.useState(null) React.useEffect(() => { let cancelled = false - setImmediate(() => { + Promise.resolve().then(() => { microTaskSpy() + // eslint-disable-next-line jest/no-if -- false positive if (!cancelled) { setDeferredCounter(counter) } @@ -82,7 +83,10 @@ describe('fake timers and missing act warnings', () => { expect(microTaskSpy).toHaveBeenCalledTimes(0) // console.error is mocked // eslint-disable-next-line no-console - expect(console.error).toHaveBeenCalledTimes(0) + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 1 : 0, + ) }) test('cleanup does not swallow missing act warnings', () => { @@ -92,12 +96,12 @@ describe('fake timers and missing act warnings', () => { const [, setDeferredCounter] = React.useState(null) React.useEffect(() => { let cancelled = false - setImmediate(() => { + setTimeout(() => { deferredStateUpdateSpy() if (!cancelled) { setDeferredCounter(counter) } - }) + }, 0) return () => { cancelled = true @@ -108,16 +112,22 @@ describe('fake timers and missing act warnings', () => { } render() - jest.runAllImmediates() + jest.runAllTimers() cleanup() expect(deferredStateUpdateSpy).toHaveBeenCalledTimes(1) // console.error is mocked // eslint-disable-next-line no-console - expect(console.error).toHaveBeenCalledTimes(1) - // eslint-disable-next-line no-console - expect(console.error.mock.calls[0][0]).toMatch( - 'a test was not wrapped in act(...)', + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 2 : 1, ) + // eslint-disable-next-line no-console + expect( + console.error.mock.calls[ + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 1 : 0 + ][0], + ).toMatch('a test was not wrapped in act(...)') }) }) diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js index e4e9faa0..f3aad595 100644 --- a/src/__tests__/debug.js +++ b/src/__tests__/debug.js @@ -43,8 +43,8 @@ test('allows same arguments as prettyDOM', () => { expect(console.log).toHaveBeenCalledTimes(1) expect(console.log.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "
- ...", +
+ ..., ] `) }) diff --git a/src/__tests__/new-act.js b/src/__tests__/new-act.js index 56ce4970..af81e29c 100644 --- a/src/__tests__/new-act.js +++ b/src/__tests__/new-act.js @@ -1,4 +1,4 @@ -let asyncAct +let asyncAct, consoleErrorMock jest.mock('react-dom/test-utils', () => ({ act: cb => { @@ -9,11 +9,11 @@ jest.mock('react-dom/test-utils', () => ({ beforeEach(() => { jest.resetModules() asyncAct = require('../act-compat').asyncAct - jest.spyOn(console, 'error').mockImplementation(() => {}) + consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { - console.error.mockRestore() + consoleErrorMock.mockRestore() }) test('async act works when it does not exist (older versions of react)', async () => { @@ -49,7 +49,7 @@ test('async act recovers from errors', async () => { expect(console.error.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "call console.error", + call console.error, ], ] `) @@ -67,7 +67,7 @@ test('async act recovers from sync errors', async () => { expect(console.error.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "call console.error", + call console.error, ], ] `) diff --git a/src/__tests__/no-act.js b/src/__tests__/no-act.js index 039a79ae..d739e763 100644 --- a/src/__tests__/no-act.js +++ b/src/__tests__/no-act.js @@ -1,14 +1,15 @@ -let act, asyncAct +let act, asyncAct, React, consoleErrorMock beforeEach(() => { jest.resetModules() - act = require('..').act + act = require('../pure').act asyncAct = require('../act-compat').asyncAct - jest.spyOn(console, 'error').mockImplementation(() => {}) + React = require('react') + consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { - console.error.mockRestore() + consoleErrorMock.mockRestore() }) jest.mock('react-dom/test-utils', () => ({})) @@ -17,7 +18,10 @@ test('act works even when there is no act from test utils', () => { const callback = jest.fn() act(callback) expect(callback).toHaveBeenCalledTimes(1) - expect(console.error).toHaveBeenCalledTimes(0) + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 1 : 0, + ) }) test('async act works when it does not exist (older versions of react)', async () => { @@ -26,7 +30,10 @@ test('async act works when it does not exist (older versions of react)', async ( await Promise.resolve() await callback() }) - expect(console.error).toHaveBeenCalledTimes(0) + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 2 : 0, + ) expect(callback).toHaveBeenCalledTimes(1) callback.mockClear() @@ -36,7 +43,10 @@ test('async act works when it does not exist (older versions of react)', async ( await Promise.resolve() await callback() }) - expect(console.error).toHaveBeenCalledTimes(0) + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 2 : 0, + ) expect(callback).toHaveBeenCalledTimes(1) }) @@ -49,14 +59,16 @@ test('async act recovers from errors', async () => { } catch (err) { console.error('call console.error') } - expect(console.error).toHaveBeenCalledTimes(1) - expect(console.error.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "call console.error", - ], - ] - `) + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 2 : 1, + ) + expect( + console.error.mock.calls[ + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 1 : 0 + ][0], + ).toMatch('call console.error') }) test('async act recovers from sync errors', async () => { @@ -71,7 +83,7 @@ test('async act recovers from sync errors', async () => { expect(console.error.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "call console.error", + call console.error, ], ] `) diff --git a/src/__tests__/old-act.js b/src/__tests__/old-act.js index b3de9377..6081fef8 100644 --- a/src/__tests__/old-act.js +++ b/src/__tests__/old-act.js @@ -1,13 +1,13 @@ -let asyncAct +let asyncAct, consoleErrorMock beforeEach(() => { jest.resetModules() asyncAct = require('../act-compat').asyncAct - jest.spyOn(console, 'error').mockImplementation(() => {}) + consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { - console.error.mockRestore() + consoleErrorMock.mockRestore() }) jest.mock('react-dom/test-utils', () => ({ @@ -32,18 +32,18 @@ test('async act works even when the act is an old one', async () => { console.error('sigil') }) expect(console.error.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "sigil", - ], - Array [ - "It looks like you're using a version of react-dom that supports the \\"act\\" function, but not an awaitable version of \\"act\\" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning.", - ], - Array [ - "sigil", - ], - ] - `) + Array [ + Array [ + sigil, + ], + Array [ + It looks like you're using a version of react-dom that supports the "act" function, but not an awaitable version of "act" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning., + ], + Array [ + sigil, + ], + ] + `) expect(callback).toHaveBeenCalledTimes(1) // and it doesn't warn you twice @@ -71,10 +71,10 @@ test('async act recovers from async errors', async () => { expect(console.error.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "It looks like you're using a version of react-dom that supports the \\"act\\" function, but not an awaitable version of \\"act\\" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning.", + It looks like you're using a version of react-dom that supports the "act" function, but not an awaitable version of "act" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning., ], Array [ - "call console.error", + call console.error, ], ] `) @@ -92,7 +92,7 @@ test('async act recovers from sync errors', async () => { expect(console.error.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "call console.error", + call console.error, ], ] `) @@ -109,11 +109,11 @@ test('async act can handle any sort of console.error', async () => { Array [ Array [ Object { - "error": "some error", + error: some error, }, ], Array [ - "It looks like you're using a version of react-dom that supports the \\"act\\" function, but not an awaitable version of \\"act\\" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning.", + It looks like you're using a version of react-dom that supports the "act" function, but not an awaitable version of "act" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning., ], ] `) diff --git a/src/__tests__/render.js b/src/__tests__/render.js index fdc1ff4c..fea1a649 100644 --- a/src/__tests__/render.js +++ b/src/__tests__/render.js @@ -78,14 +78,14 @@ test('renders options.wrapper around node', () => { expect(screen.getByTestId('wrapper')).toBeInTheDocument() expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
-`) +
+
+
+ `) }) test('flushes useEffect cleanup functions sync on unmount()', () => { diff --git a/src/__tests__/stopwatch.js b/src/__tests__/stopwatch.js index eeaf395c..400fce10 100644 --- a/src/__tests__/stopwatch.js +++ b/src/__tests__/stopwatch.js @@ -53,5 +53,8 @@ test('unmounts a component', async () => { // and get an error. await sleep(5) // eslint-disable-next-line no-console - expect(console.error).not.toHaveBeenCalled() + expect(console.error).toHaveBeenCalledTimes( + // ReactDOM.render is deprecated in React 18 + React.version.startsWith('18') ? 1 : 0, + ) }) diff --git a/tests/setup-env.js b/tests/setup-env.js index 264828a9..6c0b953b 100644 --- a/tests/setup-env.js +++ b/tests/setup-env.js @@ -1 +1,20 @@ import '@testing-library/jest-dom/extend-expect' + +let consoleErrorMock + +beforeEach(() => { + const originalConsoleError = console.error + consoleErrorMock = jest + .spyOn(console, 'error') + .mockImplementation((message, ...optionalParams) => { + // Ignore ReactDOM.render/ReactDOM.hydrate deprecation warning + if (message.indexOf('Use createRoot instead.') !== -1) { + return + } + originalConsoleError(message, ...optionalParams) + }) +}) + +afterEach(() => { + consoleErrorMock.mockRestore() +}) diff --git a/types/test.tsx b/types/test.tsx index 5f9575ac..239caed6 100644 --- a/types/test.tsx +++ b/types/test.tsx @@ -3,39 +3,39 @@ import {render, fireEvent, screen, waitFor} from '.' import * as pure from './pure' export async function testRender() { - const page = render(