From df1a41cd972ea6a8d4cc0edcd5090ace3c8ff8e6 Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Fri, 5 Apr 2019 08:50:18 -0600 Subject: [PATCH] =?UTF-8?q?feat(act):=20Support=20async=20act=20?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #281 --- .github/PULL_REQUEST_TEMPLATE.md | 5 ++-- package.json | 7 +++-- src/__tests__/old-act.js | 45 ++++++++++++++++++++++++++++++++ src/act-compat.js | 41 ++++++++++++++++++++++++++--- src/index.js | 11 +++++++- 5 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 src/__tests__/old-act.js diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index df54e83c..e3b54b33 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -34,12 +34,11 @@ merge of your pull request! -- [ ] Documentation added to the [docs site](https://github.com/alexkrolick/testing-library-docs) +- [ ] Documentation added to the + [docs site](https://github.com/alexkrolick/testing-library-docs) - [ ] Tests - [ ] Typescript definitions updated - [ ] Ready to be merged -- [ ] Added myself to contributors table - diff --git a/package.json b/package.json index 6bcb30cd..f226d339 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "node": ">=8" }, "scripts": { - "add-contributor": "kcd-scripts contributors add", "build": "kcd-scripts build && kcd-scripts build --bundle --no-clean", "lint": "kcd-scripts lint", "test": "kcd-scripts test --config=other/jest.config.js", @@ -43,7 +42,7 @@ "license": "MIT", "dependencies": { "@babel/runtime": "^7.4.2", - "dom-testing-library": "^3.18.2" + "dom-testing-library": "^3.19.0" }, "devDependencies": { "@reach/router": "^1.2.1", @@ -56,8 +55,8 @@ "jest-dom": "3.1.3", "jest-in-case": "^1.0.2", "kcd-scripts": "1.1.2", - "react": "^16.8.5", - "react-dom": "^16.8.5", + "react": "16.9.0-alpha.0", + "react-dom": "16.9.0-alpha.0", "react-intl": "^2.8.0", "react-redux": "6.0.1", "react-router": "^5.0.0", diff --git a/src/__tests__/old-act.js b/src/__tests__/old-act.js new file mode 100644 index 00000000..5fee7fe7 --- /dev/null +++ b/src/__tests__/old-act.js @@ -0,0 +1,45 @@ +import {asyncAct} from '../act-compat' + +jest.mock('react-dom/test-utils', () => ({ + act: cb => { + const promise = cb() + return { + then() { + console.error('blah, do not do this') + return promise + }, + } + }, +})) + +test('async act works even when the act is an old one', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}) + const callback = jest.fn() + await asyncAct(async () => { + await Promise.resolve() + await callback() + }) + 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.", + ], +] +`) + expect(callback).toHaveBeenCalledTimes(1) + + // and it doesn't warn you twice + callback.mockClear() + console.error.mockClear() + + await asyncAct(async () => { + await Promise.resolve() + await callback() + }) + expect(console.error).toHaveBeenCalledTimes(0) + expect(callback).toHaveBeenCalledTimes(1) + + console.error.mockRestore() +}) + +/* eslint no-console:0 */ diff --git a/src/act-compat.js b/src/act-compat.js index f6e4a680..7e4fbb9a 100644 --- a/src/act-compat.js +++ b/src/act-compat.js @@ -2,8 +2,23 @@ import React from 'react' import ReactDOM from 'react-dom' let reactAct +let actSupported = false +let asyncActSupported = false try { reactAct = require('react-dom/test-utils').act + actSupported = reactAct !== undefined + + const originalError = console.error + let errorCalled = false + console.error = () => { + errorCalled = true + } + console.error.calls = [] + reactAct(() => ({then: () => {}})).then(/* istanbul ignore next */ () => {}) + if (!errorCalled) { + asyncActSupported = true + } + console.error = originalError } catch (error) { // ignore, this is to support old versions of react } @@ -19,8 +34,28 @@ function actPolyfill(cb) { const act = reactAct || actPolyfill -function rtlAct(...args) { - return act(...args) +let youHaveBeenWarned = false +// this will not avoid warnings that react-dom 16.8.0 logs for triggering +// state updates asynchronously, but at least we can tell people they need +// to upgrade to avoid the warnings. +async function asyncActPolyfill(cb) { + if (!youHaveBeenWarned && actSupported) { + // if act is supported and async act isn't and they're trying to use async + // act, then they need to upgrade from 16.8 to 16.9. + // This is a seemless upgrade, so we'll add a warning + console.error( + `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.`, + ) + youHaveBeenWarned = true + } + await cb() + // make all effects resolve after + act(() => {}) } -export default rtlAct +const asyncAct = asyncActSupported ? reactAct : asyncActPolyfill + +export default act +export {asyncAct} + +/* eslint no-console:0 */ diff --git a/src/index.js b/src/index.js index c07bd000..fe6b259b 100644 --- a/src/index.js +++ b/src/index.js @@ -4,8 +4,13 @@ import { getQueriesForElement, prettyDOM, fireEvent as dtlFireEvent, + configure as configureDTL, } from 'dom-testing-library' -import act from './act-compat' +import act, {asyncAct} from './act-compat' + +configureDTL({ + asyncWrapper: asyncAct, +}) const mountedContainers = new Set() @@ -133,4 +138,8 @@ fireEvent.select = (node, init) => { export * from 'dom-testing-library' export {render, cleanup, fireEvent, act} +// NOTE: we're not going to export asyncAct because that's our own compatibility +// thing for people using react-dom@16.8.0. Anyone else doesn't need it and +// people should just upgrade anyway. + /* eslint func-name-matching:0 */