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.