diff --git a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee index c07f7e4b990bd..21463598cda43 100644 --- a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee +++ b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee @@ -5,12 +5,14 @@ This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. ### +PropTypes = null React = null ReactDOM = null -PropTypes = null +act = null describe 'ReactCoffeeScriptClass', -> container = null + root = null InnerComponent = null attachedListener = null; renderedName = null; @@ -18,8 +20,10 @@ describe 'ReactCoffeeScriptClass', -> beforeEach -> React = require 'react' ReactDOM = require 'react-dom' + act = require('react-dom/test-utils').act PropTypes = require 'prop-types' container = document.createElement 'div' + root = ReactDOM.createRoot container attachedListener = null renderedName = null InnerComponent = class extends React.Component @@ -30,11 +34,11 @@ describe 'ReactCoffeeScriptClass', -> return React.createElement('div', className: this.props.name) test = (element, expectedTag, expectedClassName) -> - instance = ReactDOM.render(element, container) + act -> + root.render(element) expect(container.firstChild).not.toBeNull() expect(container.firstChild.tagName).toBe(expectedTag) expect(container.firstChild.className).toBe(expectedClassName) - instance; it 'preserves the name of the class for use in error messages', -> class Foo extends React.Component @@ -44,14 +48,16 @@ describe 'ReactCoffeeScriptClass', -> class Foo extends React.Component expect(-> expect(-> - ReactDOM.render React.createElement(Foo), container + act -> + root.render React.createElement(Foo) ).toThrow() ).toErrorDev([ - # A failed component renders twice in DEV + # A failed component renders four times in DEV in concurrent mode + 'No `render` method found on the returned component instance', + 'No `render` method found on the returned component instance', 'No `render` method found on the returned component instance', 'No `render` method found on the returned component instance', ]) - undefined it 'renders a simple stateless component with prop', -> class Foo extends React.Component @@ -62,7 +68,6 @@ describe 'ReactCoffeeScriptClass', -> test React.createElement(Foo, bar: 'foo'), 'DIV', 'foo' test React.createElement(Foo, bar: 'bar'), 'DIV', 'bar' - undefined it 'renders based on state using initial values in this.props', -> class Foo extends React.Component @@ -76,7 +81,6 @@ describe 'ReactCoffeeScriptClass', -> ) test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'foo' - undefined it 'renders based on state using props in the constructor', -> class Foo extends React.Component @@ -95,10 +99,10 @@ describe 'ReactCoffeeScriptClass', -> className: @state.bar ) - instance = test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo' - instance.changeState() + ref = React.createRef() + test React.createElement(Foo, initialValue: 'foo', ref: ref), 'DIV', 'foo' + ref.current.changeState() test React.createElement(Foo), 'SPAN', 'bar' - undefined it 'sets initial state with value returned by static getDerivedStateFromProps', -> class Foo extends React.Component @@ -115,7 +119,6 @@ describe 'ReactCoffeeScriptClass', -> bar: 'bar' } test React.createElement(Foo, foo: 'foo'), 'DIV', 'foo bar' - undefined it 'warns if getDerivedStateFromProps is not static', -> class Foo extends React.Component @@ -124,9 +127,9 @@ describe 'ReactCoffeeScriptClass', -> getDerivedStateFromProps: -> {} expect(-> - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container) + act -> + root.render React.createElement(Foo, foo: 'foo') ).toErrorDev 'Foo: getDerivedStateFromProps() is defined as an instance method and will be ignored. Instead, declare it as a static method.' - undefined it 'warns if getDerivedStateFromError is not static', -> class Foo extends React.Component @@ -135,9 +138,9 @@ describe 'ReactCoffeeScriptClass', -> getDerivedStateFromError: -> {} expect(-> - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container) + act -> + root.render React.createElement(Foo, foo: 'foo') ).toErrorDev 'Foo: getDerivedStateFromError() is defined as an instance method and will be ignored. Instead, declare it as a static method.' - undefined it 'warns if getSnapshotBeforeUpdate is static', -> class Foo extends React.Component @@ -146,9 +149,9 @@ describe 'ReactCoffeeScriptClass', -> Foo.getSnapshotBeforeUpdate = () -> {} expect(-> - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container) + act -> + root.render React.createElement(Foo, foo: 'foo') ).toErrorDev 'Foo: getSnapshotBeforeUpdate() is defined as a static method and will be ignored. Instead, declare it as an instance method.' - undefined it 'warns if state not initialized before static getDerivedStateFromProps', -> class Foo extends React.Component @@ -162,14 +165,14 @@ describe 'ReactCoffeeScriptClass', -> bar: 'bar' } expect(-> - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container) + act -> + root.render React.createElement(Foo, foo: 'foo') ).toErrorDev ( '`Foo` uses `getDerivedStateFromProps` but its initial state is ' + 'undefined. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `Foo`. ' + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.' ) - undefined it 'updates initial state with values returned by static getDerivedStateFromProps', -> class Foo extends React.Component @@ -187,7 +190,6 @@ describe 'ReactCoffeeScriptClass', -> foo: "not-#{prevState.foo}" } test React.createElement(Foo), 'DIV', 'not-foo bar' - undefined it 'renders updated state with values returned by static getDerivedStateFromProps', -> class Foo extends React.Component @@ -207,7 +209,6 @@ describe 'ReactCoffeeScriptClass', -> return null test React.createElement(Foo, update: false), 'DIV', 'initial' test React.createElement(Foo, update: true), 'DIV', 'updated' - undefined it 'renders based on context in the constructor', -> class Foo extends React.Component @@ -239,7 +240,6 @@ describe 'ReactCoffeeScriptClass', -> React.createElement Foo test React.createElement(Outer), 'SPAN', 'foo' - undefined it 'renders only once when setting state in componentWillMount', -> renderCount = 0 @@ -255,8 +255,9 @@ describe 'ReactCoffeeScriptClass', -> React.createElement('span', className: @state.bar) test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'bar' - expect(renderCount).toBe 1 - undefined + # This is broken with deferRenderPhaseUpdateToNextBatch flag on. + # We can't use the gate feature here because this test is also in CoffeeScript and TypeScript. + expect(renderCount).toBe(if global.__WWW__ and !global.__VARIANT__ then 2 else 1) it 'should warn with non-object in the initial state property', -> [['an array'], 'a string', 1234].forEach (state) -> @@ -270,7 +271,6 @@ describe 'ReactCoffeeScriptClass', -> expect(-> test React.createElement(Foo), 'SPAN', '' ).toErrorDev('Foo.state: must be set to an object or null') - undefined it 'should render with null in the initial state property', -> class Foo extends React.Component @@ -281,7 +281,6 @@ describe 'ReactCoffeeScriptClass', -> React.createElement('span') test React.createElement(Foo), 'SPAN', '' - undefined it 'setState through an event handler', -> class Foo extends React.Component @@ -298,9 +297,9 @@ describe 'ReactCoffeeScriptClass', -> ) test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo' - attachedListener() + act -> + attachedListener() expect(renderedName).toBe 'bar' - undefined it 'should not implicitly bind event handlers', -> class Foo extends React.Component @@ -318,7 +317,6 @@ describe 'ReactCoffeeScriptClass', -> test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo' expect(attachedListener).toThrow() - undefined it 'renders using forceUpdate even when there is no state', -> class Foo extends React.Component @@ -336,9 +334,9 @@ describe 'ReactCoffeeScriptClass', -> ) test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo' - attachedListener() + act -> + attachedListener() expect(renderedName).toBe 'bar' - undefined it 'will call all the normal life cycle methods', -> lifeCycles = [] @@ -387,9 +385,9 @@ describe 'ReactCoffeeScriptClass', -> 'did-update', { value: 'foo' }, {} ] lifeCycles = [] # reset - ReactDOM.unmountComponentAtNode container + act -> + root.unmount() expect(lifeCycles).toEqual ['will-unmount'] - undefined it 'warns when classic properties are defined on the instance, but does not invoke them.', -> @@ -425,7 +423,6 @@ describe 'ReactCoffeeScriptClass', -> ]) expect(getInitialStateWasCalled).toBe false expect(getDefaultPropsWasCalled).toBe false - undefined it 'does not warn about getInitialState() on class components if state is also defined.', -> @@ -443,7 +440,6 @@ describe 'ReactCoffeeScriptClass', -> ) test React.createElement(Foo), 'SPAN', 'foo' - undefined it 'should warn when misspelling shouldComponentUpdate', -> class NamedComponent extends React.Component @@ -462,7 +458,6 @@ describe 'ReactCoffeeScriptClass', -> Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.' ) - undefined it 'should warn when misspelling componentWillReceiveProps', -> class NamedComponent extends React.Component @@ -480,7 +475,6 @@ describe 'ReactCoffeeScriptClass', -> 'Warning: NamedComponent has a method called componentWillRecieveProps(). Did you mean componentWillReceiveProps()?' ) - undefined it 'should warn when misspelling UNSAFE_componentWillReceiveProps', -> class NamedComponent extends React.Component @@ -498,24 +492,22 @@ describe 'ReactCoffeeScriptClass', -> 'Warning: NamedComponent has a method called UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?' ) - undefined it 'should throw AND warn when trying to access classic APIs', -> - instance = - test React.createElement(InnerComponent, name: 'foo'), 'DIV', 'foo' + ref = React.createRef() + test React.createElement(InnerComponent, name: 'foo', ref: ref), 'DIV', 'foo' expect(-> - expect(-> instance.replaceState {}).toThrow() + expect(-> ref.current.replaceState {}).toThrow() ).toWarnDev( 'replaceState(...) is deprecated in plain JavaScript React classes', {withoutStack: true} ) expect(-> - expect(-> instance.isMounted()).toThrow() + expect(-> ref.current.isMounted()).toThrow() ).toWarnDev( 'isMounted(...) is deprecated in plain JavaScript React classes', {withoutStack: true} ) - undefined it 'supports this.context passed via getChildContext', -> class Bar extends React.Component @@ -533,7 +525,6 @@ describe 'ReactCoffeeScriptClass', -> React.createElement Bar test React.createElement(Foo), 'DIV', 'bar-through-context' - undefined it 'supports classic refs', -> class Foo extends React.Component @@ -543,13 +534,14 @@ describe 'ReactCoffeeScriptClass', -> ref: 'inner' ) - instance = test(React.createElement(Foo), 'DIV', 'foo') - expect(instance.refs.inner.getName()).toBe 'foo' - undefined + ref = React.createRef() + test(React.createElement(Foo, ref: ref), 'DIV', 'foo') + expect(ref.current.refs.inner.getName()).toBe 'foo' it 'supports drilling through to the DOM using findDOMNode', -> - instance = test React.createElement(InnerComponent, name: 'foo'), 'DIV', 'foo' - node = ReactDOM.findDOMNode(instance) + ref = React.createRef() + test React.createElement(InnerComponent, name: 'foo', ref: ref), 'DIV', 'foo' + node = ReactDOM.findDOMNode(ref.current) expect(node).toBe container.firstChild - undefined + undefined diff --git a/packages/react/src/__tests__/ReactES6Class-test.js b/packages/react/src/__tests__/ReactES6Class-test.js index 1d554ca9f4580..3ae11bc4e74c2 100644 --- a/packages/react/src/__tests__/ReactES6Class-test.js +++ b/packages/react/src/__tests__/ReactES6Class-test.js @@ -12,9 +12,11 @@ let PropTypes; let React; let ReactDOM; +let act; describe('ReactES6Class', () => { let container; + let root; const freeze = function(expectation) { Object.freeze(expectation); return expectation; @@ -27,7 +29,9 @@ describe('ReactES6Class', () => { PropTypes = require('prop-types'); React = require('react'); ReactDOM = require('react-dom'); + act = require('react-dom/test-utils').act; container = document.createElement('div'); + root = ReactDOM.createRoot(container); attachedListener = null; renderedName = null; Inner = class extends React.Component { @@ -43,11 +47,10 @@ describe('ReactES6Class', () => { }); function test(element, expectedTag, expectedClassName) { - const instance = ReactDOM.render(element, container); + act(() => root.render(element)); expect(container.firstChild).not.toBeNull(); expect(container.firstChild.tagName).toBe(expectedTag); expect(container.firstChild.className).toBe(expectedClassName); - return instance; } it('preserves the name of the class for use in error messages', () => { @@ -57,10 +60,14 @@ describe('ReactES6Class', () => { it('throws if no render function is defined', () => { class Foo extends React.Component {} - expect(() => - expect(() => ReactDOM.render(, container)).toThrow(), - ).toErrorDev([ - // A failed component renders twice in DEV + expect(() => { + expect(() => act(() => root.render())).toThrow(); + }).toErrorDev([ + // A failed component renders four times in DEV in concurrent mode + 'Warning: Foo(...): No `render` method found on the returned component ' + + 'instance: you may have forgotten to define `render`.', + 'Warning: Foo(...): No `render` method found on the returned component ' + + 'instance: you may have forgotten to define `render`.', 'Warning: Foo(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', 'Warning: Foo(...): No `render` method found on the returned component ' + @@ -107,8 +114,9 @@ describe('ReactES6Class', () => { return ; } } - const instance = test(, 'DIV', 'foo'); - instance.changeState(); + const ref = React.createRef(); + test(, 'DIV', 'foo'); + act(() => ref.current.changeState()); test(, 'SPAN', 'bar'); }); @@ -137,7 +145,7 @@ describe('ReactES6Class', () => { return
; } } - expect(() => ReactDOM.render(, container)).toErrorDev( + expect(() => act(() => root.render())).toErrorDev( 'Foo: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', ); @@ -152,7 +160,7 @@ describe('ReactES6Class', () => { return
; } } - expect(() => ReactDOM.render(, container)).toErrorDev( + expect(() => act(() => root.render())).toErrorDev( 'Foo: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', ); @@ -165,7 +173,7 @@ describe('ReactES6Class', () => { return
; } } - expect(() => ReactDOM.render(, container)).toErrorDev( + expect(() => act(() => root.render())).toErrorDev( 'Foo: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.', ); @@ -183,7 +191,7 @@ describe('ReactES6Class', () => { return
; } } - expect(() => ReactDOM.render(, container)).toErrorDev( + expect(() => act(() => root.render())).toErrorDev( '`Foo` uses `getDerivedStateFromProps` but its initial state is ' + 'undefined. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `Foo`. ' + @@ -277,7 +285,9 @@ describe('ReactES6Class', () => { } } test(, 'SPAN', 'bar'); - expect(renderCount).toBe(1); + // This is broken with deferRenderPhaseUpdateToNextBatch flag on. + // We can't use the gate feature here because this test is also in CoffeeScript and TypeScript. + expect(renderCount).toBe(global.__WWW__ && !global.__VARIANT__ ? 2 : 1); }); it('should warn with non-object in the initial state property', () => { @@ -326,7 +336,8 @@ describe('ReactES6Class', () => { } } test(, 'DIV', 'foo'); - attachedListener(); + + act(() => attachedListener()); expect(renderedName).toBe('bar'); }); @@ -367,7 +378,7 @@ describe('ReactES6Class', () => { } } test(, 'DIV', 'foo'); - attachedListener(); + act(() => attachedListener()); expect(renderedName).toBe('bar'); }); @@ -416,7 +427,7 @@ describe('ReactES6Class', () => { 'did-update', freeze({value: 'foo'}), {}, ]); lifeCycles = []; // reset - ReactDOM.unmountComponentAtNode(container); + act(() => root.unmount()); expect(lifeCycles).toEqual(['will-unmount']); }); @@ -520,15 +531,16 @@ describe('ReactES6Class', () => { }); it('should throw AND warn when trying to access classic APIs', () => { - const instance = test(, 'DIV', 'foo'); + const ref = React.createRef(); + test(, 'DIV', 'foo'); expect(() => - expect(() => instance.replaceState({})).toThrow(), + expect(() => ref.current.replaceState({})).toThrow(), ).toWarnDev( 'replaceState(...) is deprecated in plain JavaScript React classes', {withoutStack: true}, ); expect(() => - expect(() => instance.isMounted()).toThrow(), + expect(() => ref.current.isMounted()).toThrow(), ).toWarnDev( 'isMounted(...) is deprecated in plain JavaScript React classes', {withoutStack: true}, @@ -560,13 +572,15 @@ describe('ReactES6Class', () => { return ; } } - const instance = test(, 'DIV', 'foo'); - expect(instance.refs.inner.getName()).toBe('foo'); + const ref = React.createRef(); + test(, 'DIV', 'foo'); + expect(ref.current.refs.inner.getName()).toBe('foo'); }); it('supports drilling through to the DOM using findDOMNode', () => { - const instance = test(, 'DIV', 'foo'); - const node = ReactDOM.findDOMNode(instance); + const ref = React.createRef(); + test(, 'DIV', 'foo'); + const node = ReactDOM.findDOMNode(ref.current); expect(node).toBe(container.firstChild); }); }); diff --git a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts index cce61a34aa04b..e8533b3e37af8 100644 --- a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts +++ b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts @@ -1,6 +1,7 @@ /// /// /// +/// /*! * Copyright (c) Facebook, Inc. and its affiliates. @@ -11,11 +12,13 @@ import React = require('react'); import ReactDOM = require('react-dom'); +import ReactDOMTestUtils = require('react-dom/test-utils'); import PropTypes = require('prop-types'); // Before Each let container; +let root; let attachedListener = null; let renderedName = null; @@ -31,11 +34,10 @@ class Inner extends React.Component { } function test(element, expectedTag, expectedClassName) { - const instance = ReactDOM.render(element, container); + ReactDOMTestUtils.act(() => root.render(element)); expect(container.firstChild).not.toBeNull(); expect(container.firstChild.tagName).toBe(expectedTag); expect(container.firstChild.className).toBe(expectedClassName); - return instance; } // Classes need to be declared at the top level scope, so we declare all the @@ -313,6 +315,7 @@ class ClassicRefs extends React.Component { describe('ReactTypeScriptClass', function() { beforeEach(function() { container = document.createElement('div'); + root = ReactDOM.createRoot(container); attachedListener = null; renderedName = null; }); @@ -322,12 +325,16 @@ describe('ReactTypeScriptClass', function() { }); it('throws if no render function is defined', function() { - expect(() => + expect(() => { expect(() => - ReactDOM.render(React.createElement(Empty), container) - ).toThrow() - ).toErrorDev([ - // A failed component renders twice in DEV + ReactDOMTestUtils.act(() => root.render(React.createElement(Empty))) + ).toThrow(); + }).toErrorDev([ + // A failed component renders four times in DEV in concurrent mode + 'Warning: Empty(...): No `render` method found on the returned ' + + 'component instance: you may have forgotten to define `render`.', + 'Warning: Empty(...): No `render` method found on the returned ' + + 'component instance: you may have forgotten to define `render`.', 'Warning: Empty(...): No `render` method found on the returned ' + 'component instance: you may have forgotten to define `render`.', 'Warning: Empty(...): No `render` method found on the returned ' + @@ -349,12 +356,13 @@ describe('ReactTypeScriptClass', function() { }); it('renders based on state using props in the constructor', function() { - const instance = test( - React.createElement(StateBasedOnProps, {initialValue: 'foo'}), + const ref = React.createRef(); + test( + React.createElement(StateBasedOnProps, {initialValue: 'foo', ref: ref}), 'DIV', 'foo' ); - instance.changeState(); + ReactDOMTestUtils.act(() => ref.current.changeState()); test(React.createElement(StateBasedOnProps), 'SPAN', 'bar'); }); @@ -389,7 +397,9 @@ describe('ReactTypeScriptClass', function() { } } expect(function() { - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container); + ReactDOMTestUtils.act(() => + root.render(React.createElement(Foo, {foo: 'foo'})) + ); }).toErrorDev( 'Foo: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.' @@ -406,7 +416,9 @@ describe('ReactTypeScriptClass', function() { } } expect(function() { - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container); + ReactDOMTestUtils.act(() => + root.render(React.createElement(Foo, {foo: 'foo'})) + ); }).toErrorDev( 'Foo: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.' @@ -415,14 +427,15 @@ describe('ReactTypeScriptClass', function() { it('warns if getSnapshotBeforeUpdate is static', function() { class Foo extends React.Component { - static getSnapshotBeforeUpdate() { - } + static getSnapshotBeforeUpdate() {} render() { return React.createElement('div', {}); } } expect(function() { - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container); + ReactDOMTestUtils.act(() => + root.render(React.createElement(Foo, {foo: 'foo'})) + ); }).toErrorDev( 'Foo: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.' @@ -444,12 +457,14 @@ describe('ReactTypeScriptClass', function() { } } expect(function() { - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container); + ReactDOMTestUtils.act(() => + root.render(React.createElement(Foo, {foo: 'foo'})) + ); }).toErrorDev( '`Foo` uses `getDerivedStateFromProps` but its initial state is ' + - 'undefined. This is not recommended. Instead, define the initial state by ' + - 'assigning an object to `this.state` in the constructor of `Foo`. ' + - 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.' + 'undefined. This is not recommended. Instead, define the initial state by ' + + 'assigning an object to `this.state` in the constructor of `Foo`. ' + + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.' ); }); @@ -501,7 +516,9 @@ describe('ReactTypeScriptClass', function() { it('renders only once when setting state in componentWillMount', function() { renderCount = 0; test(React.createElement(RenderOnce, {initialValue: 'foo'}), 'SPAN', 'bar'); - expect(renderCount).toBe(1); + // This is broken with deferRenderPhaseUpdateToNextBatch flag on. + // We can't use the gate feature in TypeScript. + expect(renderCount).toBe(global.__WWW__ && !global.__VARIANT__ ? 2 : 1); }); it('should warn with non-object in the initial state property', function() { @@ -526,7 +543,7 @@ describe('ReactTypeScriptClass', function() { 'DIV', 'foo' ); - attachedListener(); + ReactDOMTestUtils.act(() => attachedListener()); expect(renderedName).toBe('bar'); }); @@ -545,7 +562,7 @@ describe('ReactTypeScriptClass', function() { 'DIV', 'foo' ); - attachedListener(); + ReactDOMTestUtils.act(() => attachedListener()); expect(renderedName).toBe('bar'); }); @@ -569,7 +586,7 @@ describe('ReactTypeScriptClass', function() { {}, ]); lifeCycles = []; // reset - ReactDOM.unmountComponentAtNode(container); + ReactDOMTestUtils.act(() => root.unmount(container)); expect(lifeCycles).toEqual(['will-unmount']); }); @@ -645,19 +662,16 @@ describe('ReactTypeScriptClass', function() { }); it('should throw AND warn when trying to access classic APIs', function() { - const instance = test( - React.createElement(Inner, {name: 'foo'}), - 'DIV', - 'foo' - ); + const ref = React.createRef(); + test(React.createElement(Inner, {name: 'foo', ref: ref}), 'DIV', 'foo'); expect(() => - expect(() => instance.replaceState({})).toThrow() + expect(() => ref.current.replaceState({})).toThrow() ).toWarnDev( 'replaceState(...) is deprecated in plain JavaScript React classes', {withoutStack: true} ); expect(() => - expect(() => instance.isMounted()).toThrow() + expect(() => ref.current.isMounted()).toThrow() ).toWarnDev( 'isMounted(...) is deprecated in plain JavaScript React classes', {withoutStack: true} @@ -669,17 +683,15 @@ describe('ReactTypeScriptClass', function() { }); it('supports classic refs', function() { - const instance = test(React.createElement(ClassicRefs), 'DIV', 'foo'); - expect(instance.refs.inner.getName()).toBe('foo'); + const ref = React.createRef(); + test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo'); + expect(ref.current.refs.inner.getName()).toBe('foo'); }); it('supports drilling through to the DOM using findDOMNode', function() { - const instance = test( - React.createElement(Inner, {name: 'foo'}), - 'DIV', - 'foo' - ); - const node = ReactDOM.findDOMNode(instance); + const ref = React.createRef(); + test(React.createElement(Inner, {name: 'foo', ref: ref}), 'DIV', 'foo'); + const node = ReactDOM.findDOMNode(ref.current); expect(node).toBe(container.firstChild); }); }); diff --git a/packages/react/src/__tests__/testDefinitions/React.d.ts b/packages/react/src/__tests__/testDefinitions/React.d.ts index 0fa9fbe94a139..b1d5ceb70bba0 100644 --- a/packages/react/src/__tests__/testDefinitions/React.d.ts +++ b/packages/react/src/__tests__/testDefinitions/React.d.ts @@ -12,6 +12,8 @@ * just helpers for the unit test. */ +declare let global: any; + declare module 'react' { export class Component { props: any; @@ -24,4 +26,5 @@ declare module 'react' { } export let PropTypes : any; export function createElement(tag : any, props ?: any, ...children : any[]) : any + export function createRef(): any; } diff --git a/packages/react/src/__tests__/testDefinitions/ReactDOM.d.ts b/packages/react/src/__tests__/testDefinitions/ReactDOM.d.ts index 90ee316124d95..1d0e883ab1621 100644 --- a/packages/react/src/__tests__/testDefinitions/ReactDOM.d.ts +++ b/packages/react/src/__tests__/testDefinitions/ReactDOM.d.ts @@ -13,6 +13,7 @@ */ declare module 'react-dom' { + export function createRoot(container : any) : any export function render(element : any, container : any) : any export function unmountComponentAtNode(container : any) : void export function findDOMNode(instance : any) : any diff --git a/packages/react/src/__tests__/testDefinitions/ReactDOMTestUtils.d.ts b/packages/react/src/__tests__/testDefinitions/ReactDOMTestUtils.d.ts new file mode 100644 index 0000000000000..50ff686037671 --- /dev/null +++ b/packages/react/src/__tests__/testDefinitions/ReactDOMTestUtils.d.ts @@ -0,0 +1,17 @@ +/*! + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * TypeScript Definition File for React. + * + * Full type definitions are not yet officially supported. These are mostly + * just helpers for the unit test. + */ + +declare module 'react-dom/test-utils' { + export function act(cb : () => any) : any +} diff --git a/scripts/jest/typescript/preprocessor.js b/scripts/jest/typescript/preprocessor.js index d14471a0e5a4b..d5457ff2ccd76 100644 --- a/scripts/jest/typescript/preprocessor.js +++ b/scripts/jest/typescript/preprocessor.js @@ -42,7 +42,7 @@ function compile(content, contentFilename) { let source; const libRegex = /lib\.(.+\.)?d\.ts$/; const jestRegex = /jest\.d\.ts/; - const reactRegex = /(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$/; + const reactRegex = /(?:React|ReactDOM|ReactDOMTestUtils|PropTypes)(?:\.d)?\.ts$/; // `path.normalize` is used to turn forward slashes in // the file path into backslashes on Windows.