@samitouri / QOS-React / commits / a8a4742f1c

Convert ES6/TypeScript/CoffeeScript Tests to createRoot + act (#21598)

* Convert ES6/TypeScript CoffeeScript Tests to createRoot + act * Change expectation for WWW+VARIANT because the deferRenderPhaseUpdateToNextBatch flag breaks this behavior

Sebastian Markbåge committed Jun 2, 2021 at 14:36 UTC a8a4742f1c54493df00da648a3f9d26e3db9c8b5
7 files changed +152 -113
packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
+43 -51
@@ -5,12 +5,14 @@ This source code is licensed under the MIT license found in the
5 LICENSE file in the root directory of this source tree.
6 ###
7
8 +PropTypes = null
9 React = null
10 ReactDOM = null
10 -PropTypes = null
11 +act = null
12
13 describe 'ReactCoffeeScriptClass', ->
14 container = null
15 + root = null
16 InnerComponent = null
17 attachedListener = null;
18 renderedName = null;
@@ -18,8 +20,10 @@ describe 'ReactCoffeeScriptClass', ->
20 beforeEach ->
21 React = require 'react'
22 ReactDOM = require 'react-dom'
23 + act = require('react-dom/test-utils').act
24 PropTypes = require 'prop-types'
25 container = document.createElement 'div'
26 + root = ReactDOM.createRoot container
27 attachedListener = null
28 renderedName = null
29 InnerComponent = class extends React.Component
@@ -30,11 +34,11 @@ describe 'ReactCoffeeScriptClass', ->
34 return React.createElement('div', className: this.props.name)
35
36 test = (element, expectedTag, expectedClassName) ->
33 - instance = ReactDOM.render(element, container)
37 + act ->
38 + root.render(element)
39 expect(container.firstChild).not.toBeNull()
40 expect(container.firstChild.tagName).toBe(expectedTag)
41 expect(container.firstChild.className).toBe(expectedClassName)
37 - instance;
42
43 it 'preserves the name of the class for use in error messages', ->
44 class Foo extends React.Component
@@ -44,14 +48,16 @@ describe 'ReactCoffeeScriptClass', ->
48 class Foo extends React.Component
49 expect(->
50 expect(->
47 - ReactDOM.render React.createElement(Foo), container
51 + act ->
52 + root.render React.createElement(Foo)
53 ).toThrow()
54 ).toErrorDev([
50 - # A failed component renders twice in DEV
55 + # A failed component renders four times in DEV in concurrent mode
56 + 'No `render` method found on the returned component instance',
57 + 'No `render` method found on the returned component instance',
58 'No `render` method found on the returned component instance',
59 'No `render` method found on the returned component instance',
60 ])
54 - undefined
61
62 it 'renders a simple stateless component with prop', ->
63 class Foo extends React.Component
@@ -62,7 +68,6 @@ describe 'ReactCoffeeScriptClass', ->
68
69 test React.createElement(Foo, bar: 'foo'), 'DIV', 'foo'
70 test React.createElement(Foo, bar: 'bar'), 'DIV', 'bar'
65 - undefined
71
72 it 'renders based on state using initial values in this.props', ->
73 class Foo extends React.Component
@@ -76,7 +81,6 @@ describe 'ReactCoffeeScriptClass', ->
81 )
82
83 test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'foo'
79 - undefined
84
85 it 'renders based on state using props in the constructor', ->
86 class Foo extends React.Component
@@ -95,10 +99,10 @@ describe 'ReactCoffeeScriptClass', ->
99 className: @state.bar
100 )
101
98 - instance = test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
99 - instance.changeState()
102 + ref = React.createRef()
103 + test React.createElement(Foo, initialValue: 'foo', ref: ref), 'DIV', 'foo'
104 + ref.current.changeState()
105 test React.createElement(Foo), 'SPAN', 'bar'
101 - undefined
106
107 it 'sets initial state with value returned by static getDerivedStateFromProps', ->
108 class Foo extends React.Component
@@ -115,7 +119,6 @@ describe 'ReactCoffeeScriptClass', ->
119 bar: 'bar'
120 }
121 test React.createElement(Foo, foo: 'foo'), 'DIV', 'foo bar'
118 - undefined
122
123 it 'warns if getDerivedStateFromProps is not static', ->
124 class Foo extends React.Component
@@ -124,9 +127,9 @@ describe 'ReactCoffeeScriptClass', ->
127 getDerivedStateFromProps: ->
128 {}
129 expect(->
127 - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container)
130 + act ->
131 + root.render React.createElement(Foo, foo: 'foo')
132 ).toErrorDev 'Foo: getDerivedStateFromProps() is defined as an instance method and will be ignored. Instead, declare it as a static method.'
129 - undefined
133
134 it 'warns if getDerivedStateFromError is not static', ->
135 class Foo extends React.Component
@@ -135,9 +138,9 @@ describe 'ReactCoffeeScriptClass', ->
138 getDerivedStateFromError: ->
139 {}
140 expect(->
138 - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container)
141 + act ->
142 + root.render React.createElement(Foo, foo: 'foo')
143 ).toErrorDev 'Foo: getDerivedStateFromError() is defined as an instance method and will be ignored. Instead, declare it as a static method.'
140 - undefined
144
145 it 'warns if getSnapshotBeforeUpdate is static', ->
146 class Foo extends React.Component
@@ -146,9 +149,9 @@ describe 'ReactCoffeeScriptClass', ->
149 Foo.getSnapshotBeforeUpdate = () ->
150 {}
151 expect(->
149 - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container)
152 + act ->
153 + root.render React.createElement(Foo, foo: 'foo')
154 ).toErrorDev 'Foo: getSnapshotBeforeUpdate() is defined as a static method and will be ignored. Instead, declare it as an instance method.'
151 - undefined
155
156 it 'warns if state not initialized before static getDerivedStateFromProps', ->
157 class Foo extends React.Component
@@ -162,14 +165,14 @@ describe 'ReactCoffeeScriptClass', ->
165 bar: 'bar'
166 }
167 expect(->
165 - ReactDOM.render(React.createElement(Foo, foo: 'foo'), container)
168 + act ->
169 + root.render React.createElement(Foo, foo: 'foo')
170 ).toErrorDev (
171 '`Foo` uses `getDerivedStateFromProps` but its initial state is ' +
172 'undefined. This is not recommended. Instead, define the initial state by ' +
173 'assigning an object to `this.state` in the constructor of `Foo`. ' +
174 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.'
175 )
172 - undefined
176
177 it 'updates initial state with values returned by static getDerivedStateFromProps', ->
178 class Foo extends React.Component
@@ -187,7 +190,6 @@ describe 'ReactCoffeeScriptClass', ->
190 foo: "not-#{prevState.foo}"
191 }
192 test React.createElement(Foo), 'DIV', 'not-foo bar'
190 - undefined
193
194 it 'renders updated state with values returned by static getDerivedStateFromProps', ->
195 class Foo extends React.Component
@@ -207,7 +209,6 @@ describe 'ReactCoffeeScriptClass', ->
209 return null
210 test React.createElement(Foo, update: false), 'DIV', 'initial'
211 test React.createElement(Foo, update: true), 'DIV', 'updated'
210 - undefined
212
213 it 'renders based on context in the constructor', ->
214 class Foo extends React.Component
@@ -239,7 +240,6 @@ describe 'ReactCoffeeScriptClass', ->
240 React.createElement Foo
241
242 test React.createElement(Outer), 'SPAN', 'foo'
242 - undefined
243
244 it 'renders only once when setting state in componentWillMount', ->
245 renderCount = 0
@@ -255,8 +255,9 @@ describe 'ReactCoffeeScriptClass', ->
255 React.createElement('span', className: @state.bar)
256
257 test React.createElement(Foo, initialValue: 'foo'), 'SPAN', 'bar'
258 - expect(renderCount).toBe 1
259 - undefined
258 + # This is broken with deferRenderPhaseUpdateToNextBatch flag on.
259 + # We can't use the gate feature here because this test is also in CoffeeScript and TypeScript.
260 + expect(renderCount).toBe(if global.__WWW__ and !global.__VARIANT__ then 2 else 1)
261
262 it 'should warn with non-object in the initial state property', ->
263 [['an array'], 'a string', 1234].forEach (state) ->
@@ -270,7 +271,6 @@ describe 'ReactCoffeeScriptClass', ->
271 expect(->
272 test React.createElement(Foo), 'SPAN', ''
273 ).toErrorDev('Foo.state: must be set to an object or null')
273 - undefined
274
275 it 'should render with null in the initial state property', ->
276 class Foo extends React.Component
@@ -281,7 +281,6 @@ describe 'ReactCoffeeScriptClass', ->
281 React.createElement('span')
282
283 test React.createElement(Foo), 'SPAN', ''
284 - undefined
284
285 it 'setState through an event handler', ->
286 class Foo extends React.Component
@@ -298,9 +297,9 @@ describe 'ReactCoffeeScriptClass', ->
297 )
298
299 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
301 - attachedListener()
300 + act ->
301 + attachedListener()
302 expect(renderedName).toBe 'bar'
303 - undefined
303
304 it 'should not implicitly bind event handlers', ->
305 class Foo extends React.Component
@@ -318,7 +317,6 @@ describe 'ReactCoffeeScriptClass', ->
317
318 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
319 expect(attachedListener).toThrow()
321 - undefined
320
321 it 'renders using forceUpdate even when there is no state', ->
322 class Foo extends React.Component
@@ -336,9 +334,9 @@ describe 'ReactCoffeeScriptClass', ->
334 )
335
336 test React.createElement(Foo, initialValue: 'foo'), 'DIV', 'foo'
339 - attachedListener()
337 + act ->
338 + attachedListener()
339 expect(renderedName).toBe 'bar'
341 - undefined
340
341 it 'will call all the normal life cycle methods', ->
342 lifeCycles = []
@@ -387,9 +385,9 @@ describe 'ReactCoffeeScriptClass', ->
385 'did-update', { value: 'foo' }, {}
386 ]
387 lifeCycles = [] # reset
390 - ReactDOM.unmountComponentAtNode container
388 + act ->
389 + root.unmount()
390 expect(lifeCycles).toEqual ['will-unmount']
392 - undefined
391
392 it 'warns when classic properties are defined on the instance,
393 but does not invoke them.', ->
@@ -425,7 +423,6 @@ describe 'ReactCoffeeScriptClass', ->
423 ])
424 expect(getInitialStateWasCalled).toBe false
425 expect(getDefaultPropsWasCalled).toBe false
428 - undefined
426
427 it 'does not warn about getInitialState() on class components
428 if state is also defined.', ->
@@ -443,7 +440,6 @@ describe 'ReactCoffeeScriptClass', ->
440 )
441
442 test React.createElement(Foo), 'SPAN', 'foo'
446 - undefined
443
444 it 'should warn when misspelling shouldComponentUpdate', ->
445 class NamedComponent extends React.Component
@@ -462,7 +458,6 @@ describe 'ReactCoffeeScriptClass', ->
458 Did you mean shouldComponentUpdate()? The name is phrased as a
459 question because the function is expected to return a value.'
460 )
465 - undefined
461
462 it 'should warn when misspelling componentWillReceiveProps', ->
463 class NamedComponent extends React.Component
@@ -480,7 +475,6 @@ describe 'ReactCoffeeScriptClass', ->
475 'Warning: NamedComponent has a method called componentWillRecieveProps().
476 Did you mean componentWillReceiveProps()?'
477 )
483 - undefined
478
479 it 'should warn when misspelling UNSAFE_componentWillReceiveProps', ->
480 class NamedComponent extends React.Component
@@ -498,24 +492,22 @@ describe 'ReactCoffeeScriptClass', ->
492 'Warning: NamedComponent has a method called UNSAFE_componentWillRecieveProps().
493 Did you mean UNSAFE_componentWillReceiveProps()?'
494 )
501 - undefined
495
496 it 'should throw AND warn when trying to access classic APIs', ->
504 - instance =
505 - test React.createElement(InnerComponent, name: 'foo'), 'DIV', 'foo'
497 + ref = React.createRef()
498 + test React.createElement(InnerComponent, name: 'foo', ref: ref), 'DIV', 'foo'
499 expect(->
507 - expect(-> instance.replaceState {}).toThrow()
500 + expect(-> ref.current.replaceState {}).toThrow()
501 ).toWarnDev(
502 'replaceState(...) is deprecated in plain JavaScript React classes',
503 {withoutStack: true}
504 )
505 expect(->
513 - expect(-> instance.isMounted()).toThrow()
506 + expect(-> ref.current.isMounted()).toThrow()
507 ).toWarnDev(
508 'isMounted(...) is deprecated in plain JavaScript React classes',
509 {withoutStack: true}
510 )
518 - undefined
511
512 it 'supports this.context passed via getChildContext', ->
513 class Bar extends React.Component
@@ -533,7 +525,6 @@ describe 'ReactCoffeeScriptClass', ->
525 React.createElement Bar
526
527 test React.createElement(Foo), 'DIV', 'bar-through-context'
536 - undefined
528
529 it 'supports classic refs', ->
530 class Foo extends React.Component
@@ -543,13 +534,14 @@ describe 'ReactCoffeeScriptClass', ->
534 ref: 'inner'
535 )
536
546 - instance = test(React.createElement(Foo), 'DIV', 'foo')
547 - expect(instance.refs.inner.getName()).toBe 'foo'
548 - undefined
537 + ref = React.createRef()
538 + test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
539 + expect(ref.current.refs.inner.getName()).toBe 'foo'
540
541 it 'supports drilling through to the DOM using findDOMNode', ->
551 - instance = test React.createElement(InnerComponent, name: 'foo'), 'DIV', 'foo'
552 - node = ReactDOM.findDOMNode(instance)
542 + ref = React.createRef()
543 + test React.createElement(InnerComponent, name: 'foo', ref: ref), 'DIV', 'foo'
544 + node = ReactDOM.findDOMNode(ref.current)
545 expect(node).toBe container.firstChild
554 - undefined
546 +
547 undefined
packages/react/src/__tests__/ReactES6Class-test.js
+37 -23
@@ -12,9 +12,11 @@
12 let PropTypes;
13 let React;
14 let ReactDOM;
15 +let act;
16
17 describe('ReactES6Class', () => {
18 let container;
19 + let root;
20 const freeze = function(expectation) {
21 Object.freeze(expectation);
22 return expectation;
@@ -27,7 +29,9 @@ describe('ReactES6Class', () => {
29 PropTypes = require('prop-types');
30 React = require('react');
31 ReactDOM = require('react-dom');
32 + act = require('react-dom/test-utils').act;
33 container = document.createElement('div');
34 + root = ReactDOM.createRoot(container);
35 attachedListener = null;
36 renderedName = null;
37 Inner = class extends React.Component {
@@ -43,11 +47,10 @@ describe('ReactES6Class', () => {
47 });
48
49 function test(element, expectedTag, expectedClassName) {
46 - const instance = ReactDOM.render(element, container);
50 + act(() => root.render(element));
51 expect(container.firstChild).not.toBeNull();
52 expect(container.firstChild.tagName).toBe(expectedTag);
53 expect(container.firstChild.className).toBe(expectedClassName);
50 - return instance;
54 }
55
56 it('preserves the name of the class for use in error messages', () => {
@@ -57,10 +60,14 @@ describe('ReactES6Class', () => {
60
61 it('throws if no render function is defined', () => {
62 class Foo extends React.Component {}
60 - expect(() =>
61 - expect(() => ReactDOM.render(<Foo />, container)).toThrow(),
62 - ).toErrorDev([
63 - // A failed component renders twice in DEV
63 + expect(() => {
64 + expect(() => act(() => root.render(<Foo />))).toThrow();
65 + }).toErrorDev([
66 + // A failed component renders four times in DEV in concurrent mode
67 + 'Warning: Foo(...): No `render` method found on the returned component ' +
68 + 'instance: you may have forgotten to define `render`.',
69 + 'Warning: Foo(...): No `render` method found on the returned component ' +
70 + 'instance: you may have forgotten to define `render`.',
71 'Warning: Foo(...): No `render` method found on the returned component ' +
72 'instance: you may have forgotten to define `render`.',
73 'Warning: Foo(...): No `render` method found on the returned component ' +
@@ -107,8 +114,9 @@ describe('ReactES6Class', () => {
114 return <span className={this.state.bar} />;
115 }
116 }
110 - const instance = test(<Foo initialValue="foo" />, 'DIV', 'foo');
111 - instance.changeState();
117 + const ref = React.createRef();
118 + test(<Foo initialValue="foo" ref={ref} />, 'DIV', 'foo');
119 + act(() => ref.current.changeState());
120 test(<Foo />, 'SPAN', 'bar');
121 });
122
@@ -137,7 +145,7 @@ describe('ReactES6Class', () => {
145 return <div />;
146 }
147 }
140 - expect(() => ReactDOM.render(<Foo foo="foo" />, container)).toErrorDev(
148 + expect(() => act(() => root.render(<Foo foo="foo" />))).toErrorDev(
149 'Foo: getDerivedStateFromProps() is defined as an instance method ' +
150 'and will be ignored. Instead, declare it as a static method.',
151 );
@@ -152,7 +160,7 @@ describe('ReactES6Class', () => {
160 return <div />;
161 }
162 }
155 - expect(() => ReactDOM.render(<Foo foo="foo" />, container)).toErrorDev(
163 + expect(() => act(() => root.render(<Foo foo="foo" />))).toErrorDev(
164 'Foo: getDerivedStateFromError() is defined as an instance method ' +
165 'and will be ignored. Instead, declare it as a static method.',
166 );
@@ -165,7 +173,7 @@ describe('ReactES6Class', () => {
173 return <div />;
174 }
175 }
168 - expect(() => ReactDOM.render(<Foo foo="foo" />, container)).toErrorDev(
176 + expect(() => act(() => root.render(<Foo foo="foo" />))).toErrorDev(
177 'Foo: getSnapshotBeforeUpdate() is defined as a static method ' +
178 'and will be ignored. Instead, declare it as an instance method.',
179 );
@@ -183,7 +191,7 @@ describe('ReactES6Class', () => {
191 return <div className={`${this.state.foo} ${this.state.bar}`} />;
192 }
193 }
186 - expect(() => ReactDOM.render(<Foo foo="foo" />, container)).toErrorDev(
194 + expect(() => act(() => root.render(<Foo foo="foo" />))).toErrorDev(
195 '`Foo` uses `getDerivedStateFromProps` but its initial state is ' +
196 'undefined. This is not recommended. Instead, define the initial state by ' +
197 'assigning an object to `this.state` in the constructor of `Foo`. ' +
@@ -277,7 +285,9 @@ describe('ReactES6Class', () => {
285 }
286 }
287 test(<Foo initialValue="foo" />, 'SPAN', 'bar');
280 - expect(renderCount).toBe(1);
288 + // This is broken with deferRenderPhaseUpdateToNextBatch flag on.
289 + // We can't use the gate feature here because this test is also in CoffeeScript and TypeScript.
290 + expect(renderCount).toBe(global.__WWW__ && !global.__VARIANT__ ? 2 : 1);
291 });
292
293 it('should warn with non-object in the initial state property', () => {
@@ -326,7 +336,8 @@ describe('ReactES6Class', () => {
336 }
337 }
338 test(<Foo initialValue="foo" />, 'DIV', 'foo');
329 - attachedListener();
339 +
340 + act(() => attachedListener());
341 expect(renderedName).toBe('bar');
342 });
343
@@ -367,7 +378,7 @@ describe('ReactES6Class', () => {
378 }
379 }
380 test(<Foo initialValue="foo" />, 'DIV', 'foo');
370 - attachedListener();
381 + act(() => attachedListener());
382 expect(renderedName).toBe('bar');
383 });
384
@@ -416,7 +427,7 @@ describe('ReactES6Class', () => {
427 'did-update', freeze({value: 'foo'}), {},
428 ]);
429 lifeCycles = []; // reset
419 - ReactDOM.unmountComponentAtNode(container);
430 + act(() => root.unmount());
431 expect(lifeCycles).toEqual(['will-unmount']);
432 });
433
@@ -520,15 +531,16 @@ describe('ReactES6Class', () => {
531 });
532
533 it('should throw AND warn when trying to access classic APIs', () => {
523 - const instance = test(<Inner name="foo" />, 'DIV', 'foo');
534 + const ref = React.createRef();
535 + test(<Inner name="foo" ref={ref} />, 'DIV', 'foo');
536 expect(() =>
525 - expect(() => instance.replaceState({})).toThrow(),
537 + expect(() => ref.current.replaceState({})).toThrow(),
538 ).toWarnDev(
539 'replaceState(...) is deprecated in plain JavaScript React classes',
540 {withoutStack: true},
541 );
542 expect(() =>
531 - expect(() => instance.isMounted()).toThrow(),
543 + expect(() => ref.current.isMounted()).toThrow(),
544 ).toWarnDev(
545 'isMounted(...) is deprecated in plain JavaScript React classes',
546 {withoutStack: true},
@@ -560,13 +572,15 @@ describe('ReactES6Class', () => {
572 return <Inner name="foo" ref="inner" />;
573 }
574 }
563 - const instance = test(<Foo />, 'DIV', 'foo');
564 - expect(instance.refs.inner.getName()).toBe('foo');
575 + const ref = React.createRef();
576 + test(<Foo ref={ref} />, 'DIV', 'foo');
577 + expect(ref.current.refs.inner.getName()).toBe('foo');
578 });
579
580 it('supports drilling through to the DOM using findDOMNode', () => {
568 - const instance = test(<Inner name="foo" />, 'DIV', 'foo');
569 - const node = ReactDOM.findDOMNode(instance);
581 + const ref = React.createRef();
582 + test(<Inner name="foo" ref={ref} />, 'DIV', 'foo');
583 + const node = ReactDOM.findDOMNode(ref.current);
584 expect(node).toBe(container.firstChild);
585 });
586 });
packages/react/src/__tests__/ReactTypeScriptClass-test.ts
+50 -38
@@ -1,6 +1,7 @@
1 /// <reference path="./testDefinitions/PropTypes.d.ts" />
2 /// <reference path="./testDefinitions/React.d.ts" />
3 /// <reference path="./testDefinitions/ReactDOM.d.ts" />
4 +/// <reference path="./testDefinitions/ReactDOMTestUtils.d.ts" />
5
6 /*!
7 * Copyright (c) Facebook, Inc. and its affiliates.
@@ -11,11 +12,13 @@
12
13 import React = require('react');
14 import ReactDOM = require('react-dom');
15 +import ReactDOMTestUtils = require('react-dom/test-utils');
16 import PropTypes = require('prop-types');
17
18 // Before Each
19
20 let container;
21 +let root;
22 let attachedListener = null;
23 let renderedName = null;
24
@@ -31,11 +34,10 @@ class Inner extends React.Component {
34 }
35
36 function test(element, expectedTag, expectedClassName) {
34 - const instance = ReactDOM.render(element, container);
37 + ReactDOMTestUtils.act(() => root.render(element));
38 expect(container.firstChild).not.toBeNull();
39 expect(container.firstChild.tagName).toBe(expectedTag);
40 expect(container.firstChild.className).toBe(expectedClassName);
38 - return instance;
41 }
42
43 // Classes need to be declared at the top level scope, so we declare all the
@@ -313,6 +315,7 @@ class ClassicRefs extends React.Component {
315 describe('ReactTypeScriptClass', function() {
316 beforeEach(function() {
317 container = document.createElement('div');
318 + root = ReactDOM.createRoot(container);
319 attachedListener = null;
320 renderedName = null;
321 });
@@ -322,12 +325,16 @@ describe('ReactTypeScriptClass', function() {
325 });
326
327 it('throws if no render function is defined', function() {
325 - expect(() =>
328 + expect(() => {
329 expect(() =>
327 - ReactDOM.render(React.createElement(Empty), container)
328 - ).toThrow()
329 - ).toErrorDev([
330 - // A failed component renders twice in DEV
330 + ReactDOMTestUtils.act(() => root.render(React.createElement(Empty)))
331 + ).toThrow();
332 + }).toErrorDev([
333 + // A failed component renders four times in DEV in concurrent mode
334 + 'Warning: Empty(...): No `render` method found on the returned ' +
335 + 'component instance: you may have forgotten to define `render`.',
336 + 'Warning: Empty(...): No `render` method found on the returned ' +
337 + 'component instance: you may have forgotten to define `render`.',
338 'Warning: Empty(...): No `render` method found on the returned ' +
339 'component instance: you may have forgotten to define `render`.',
340 'Warning: Empty(...): No `render` method found on the returned ' +
@@ -349,12 +356,13 @@ describe('ReactTypeScriptClass', function() {
356 });
357
358 it('renders based on state using props in the constructor', function() {
352 - const instance = test(
353 - React.createElement(StateBasedOnProps, {initialValue: 'foo'}),
359 + const ref = React.createRef();
360 + test(
361 + React.createElement(StateBasedOnProps, {initialValue: 'foo', ref: ref}),
362 'DIV',
363 'foo'
364 );
357 - instance.changeState();
365 + ReactDOMTestUtils.act(() => ref.current.changeState());
366 test(React.createElement(StateBasedOnProps), 'SPAN', 'bar');
367 });
368
@@ -389,7 +397,9 @@ describe('ReactTypeScriptClass', function() {
397 }
398 }
399 expect(function() {
392 - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container);
400 + ReactDOMTestUtils.act(() =>
401 + root.render(React.createElement(Foo, {foo: 'foo'}))
402 + );
403 }).toErrorDev(
404 'Foo: getDerivedStateFromProps() is defined as an instance method ' +
405 'and will be ignored. Instead, declare it as a static method.'
@@ -406,7 +416,9 @@ describe('ReactTypeScriptClass', function() {
416 }
417 }
418 expect(function() {
409 - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container);
419 + ReactDOMTestUtils.act(() =>
420 + root.render(React.createElement(Foo, {foo: 'foo'}))
421 + );
422 }).toErrorDev(
423 'Foo: getDerivedStateFromError() is defined as an instance method ' +
424 'and will be ignored. Instead, declare it as a static method.'
@@ -415,14 +427,15 @@ describe('ReactTypeScriptClass', function() {
427
428 it('warns if getSnapshotBeforeUpdate is static', function() {
429 class Foo extends React.Component {
418 - static getSnapshotBeforeUpdate() {
419 - }
430 + static getSnapshotBeforeUpdate() {}
431 render() {
432 return React.createElement('div', {});
433 }
434 }
435 expect(function() {
425 - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container);
436 + ReactDOMTestUtils.act(() =>
437 + root.render(React.createElement(Foo, {foo: 'foo'}))
438 + );
439 }).toErrorDev(
440 'Foo: getSnapshotBeforeUpdate() is defined as a static method ' +
441 'and will be ignored. Instead, declare it as an instance method.'
@@ -444,12 +457,14 @@ describe('ReactTypeScriptClass', function() {
457 }
458 }
459 expect(function() {
447 - ReactDOM.render(React.createElement(Foo, {foo: 'foo'}), container);
460 + ReactDOMTestUtils.act(() =>
461 + root.render(React.createElement(Foo, {foo: 'foo'}))
462 + );
463 }).toErrorDev(
464 '`Foo` uses `getDerivedStateFromProps` but its initial state is ' +
450 - 'undefined. This is not recommended. Instead, define the initial state by ' +
451 - 'assigning an object to `this.state` in the constructor of `Foo`. ' +
452 - 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.'
465 + 'undefined. This is not recommended. Instead, define the initial state by ' +
466 + 'assigning an object to `this.state` in the constructor of `Foo`. ' +
467 + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.'
468 );
469 });
470
@@ -501,7 +516,9 @@ describe('ReactTypeScriptClass', function() {
516 it('renders only once when setting state in componentWillMount', function() {
517 renderCount = 0;
518 test(React.createElement(RenderOnce, {initialValue: 'foo'}), 'SPAN', 'bar');
504 - expect(renderCount).toBe(1);
519 + // This is broken with deferRenderPhaseUpdateToNextBatch flag on.
520 + // We can't use the gate feature in TypeScript.
521 + expect(renderCount).toBe(global.__WWW__ && !global.__VARIANT__ ? 2 : 1);
522 });
523
524 it('should warn with non-object in the initial state property', function() {
@@ -526,7 +543,7 @@ describe('ReactTypeScriptClass', function() {
543 'DIV',
544 'foo'
545 );
529 - attachedListener();
546 + ReactDOMTestUtils.act(() => attachedListener());
547 expect(renderedName).toBe('bar');
548 });
549
@@ -545,7 +562,7 @@ describe('ReactTypeScriptClass', function() {
562 'DIV',
563 'foo'
564 );
548 - attachedListener();
565 + ReactDOMTestUtils.act(() => attachedListener());
566 expect(renderedName).toBe('bar');
567 });
568
@@ -569,7 +586,7 @@ describe('ReactTypeScriptClass', function() {
586 {},
587 ]);
588 lifeCycles = []; // reset
572 - ReactDOM.unmountComponentAtNode(container);
589 + ReactDOMTestUtils.act(() => root.unmount(container));
590 expect(lifeCycles).toEqual(['will-unmount']);
591 });
592
@@ -645,19 +662,16 @@ describe('ReactTypeScriptClass', function() {
662 });
663
664 it('should throw AND warn when trying to access classic APIs', function() {
648 - const instance = test(
649 - React.createElement(Inner, {name: 'foo'}),
650 - 'DIV',
651 - 'foo'
652 - );
665 + const ref = React.createRef();
666 + test(React.createElement(Inner, {name: 'foo', ref: ref}), 'DIV', 'foo');
667 expect(() =>
654 - expect(() => instance.replaceState({})).toThrow()
668 + expect(() => ref.current.replaceState({})).toThrow()
669 ).toWarnDev(
670 'replaceState(...) is deprecated in plain JavaScript React classes',
671 {withoutStack: true}
672 );
673 expect(() =>
660 - expect(() => instance.isMounted()).toThrow()
674 + expect(() => ref.current.isMounted()).toThrow()
675 ).toWarnDev(
676 'isMounted(...) is deprecated in plain JavaScript React classes',
677 {withoutStack: true}
@@ -669,17 +683,15 @@ describe('ReactTypeScriptClass', function() {
683 });
684
685 it('supports classic refs', function() {
672 - const instance = test(React.createElement(ClassicRefs), 'DIV', 'foo');
673 - expect(instance.refs.inner.getName()).toBe('foo');
686 + const ref = React.createRef();
687 + test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
688 + expect(ref.current.refs.inner.getName()).toBe('foo');
689 });
690
691 it('supports drilling through to the DOM using findDOMNode', function() {
677 - const instance = test(
678 - React.createElement(Inner, {name: 'foo'}),
679 - 'DIV',
680 - 'foo'
681 - );
682 - const node = ReactDOM.findDOMNode(instance);
692 + const ref = React.createRef();
693 + test(React.createElement(Inner, {name: 'foo', ref: ref}), 'DIV', 'foo');
694 + const node = ReactDOM.findDOMNode(ref.current);
695 expect(node).toBe(container.firstChild);
696 });
697 });
packages/react/src/__tests__/testDefinitions/React.d.ts
+3
@@ -12,6 +12,8 @@
12 * just helpers for the unit test.
13 */
14
15 +declare let global: any;
16 +
17 declare module 'react' {
18 export class Component {
19 props: any;
@@ -24,4 +26,5 @@ declare module 'react' {
26 }
27 export let PropTypes : any;
28 export function createElement(tag : any, props ?: any, ...children : any[]) : any
29 + export function createRef(): any;
30 }
packages/react/src/__tests__/testDefinitions/ReactDOM.d.ts
+1
@@ -13,6 +13,7 @@
13 */
14
15 declare module 'react-dom' {
16 + export function createRoot(container : any) : any
17 export function render(element : any, container : any) : any
18 export function unmountComponentAtNode(container : any) : void
19 export function findDOMNode(instance : any) : any
packages/react/src/__tests__/testDefinitions/ReactDOMTestUtils.d.ts new
+17
@@ -0,0 +1,17 @@
1 +/*!
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +/**
9 + * TypeScript Definition File for React.
10 + *
11 + * Full type definitions are not yet officially supported. These are mostly
12 + * just helpers for the unit test.
13 + */
14 +
15 +declare module 'react-dom/test-utils' {
16 + export function act(cb : () => any) : any
17 +}
scripts/jest/typescript/preprocessor.js
+1 -1
@@ -42,7 +42,7 @@ function compile(content, contentFilename) {
42 let source;
43 const libRegex = /lib\.(.+\.)?d\.ts$/;
44 const jestRegex = /jest\.d\.ts/;
45 - const reactRegex = /(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$/;
45 + const reactRegex = /(?:React|ReactDOM|ReactDOMTestUtils|PropTypes)(?:\.d)?\.ts$/;
46
47 // `path.normalize` is used to turn forward slashes in
48 // the file path into backslashes on Windows.