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
+ * @jest-environment node
8
+ */
9
+
10
+let React;
11
+let Scheduler;
12
+let ReactNoop;
13
+let useState;
14
+let act;
15
+
16
+// These tests are mostly concerned with concurrent roots. The legacy root
17
+// behavior is covered by other older test suites and is unchanged from
18
+// React 17.
19
+describe('act warnings', () => {
20
+ beforeEach(() => {
21
+ jest.resetModules();
22
+ React = require('react');
23
+ Scheduler = require('scheduler');
24
+ ReactNoop = require('react-noop-renderer');
25
+ act = React.unstable_act;
26
+ useState = React.useState;
27
+ });
28
+
29
+ function Text(props) {
30
+ Scheduler.unstable_yieldValue(props.text);
31
+ return props.text;
32
+ }
33
+
34
+ function withActEnvironment(value, scope) {
35
+ const prevValue = global.IS_REACT_ACT_ENVIRONMENT;
36
+ global.IS_REACT_ACT_ENVIRONMENT = value;
37
+ try {
38
+ return scope();
39
+ } finally {
40
+ global.IS_REACT_ACT_ENVIRONMENT = prevValue;
41
+ }
42
+ }
43
+
44
+ test('warns about unwrapped updates only if environment flag is enabled', () => {
45
+ let setState;
46
+ function App() {
47
+ const [state, _setState] = useState(0);
48
+ setState = _setState;
49
+ return <Text text={state} />;
50
+ }
51
+
52
+ const root = ReactNoop.createRoot();
53
+ root.render(<App />);
54
+ expect(Scheduler).toFlushAndYield([0]);
55
+ expect(root).toMatchRenderedOutput('0');
56
+
57
+ // Default behavior. Flag is undefined. No warning.
58
+ expect(global.IS_REACT_ACT_ENVIRONMENT).toBe(undefined);
59
+ setState(1);
60
+ expect(Scheduler).toFlushAndYield([1]);
61
+ expect(root).toMatchRenderedOutput('1');
62
+
63
+ // Flag is true. Warn.
64
+ withActEnvironment(true, () => {
65
+ expect(() => setState(2)).toErrorDev(
66
+ 'An update to App inside a test was not wrapped in act',
67
+ );
68
+ expect(Scheduler).toFlushAndYield([2]);
69
+ expect(root).toMatchRenderedOutput('2');
70
+ });
71
+
72
+ // Flag is false. No warning.
73
+ withActEnvironment(false, () => {
74
+ setState(3);
75
+ expect(Scheduler).toFlushAndYield([3]);
76
+ expect(root).toMatchRenderedOutput('3');
77
+ });
78
+ });
79
+
80
+ // @gate __DEV__
81
+ test('act warns if the environment flag is not enabled', () => {
82
+ let setState;
83
+ function App() {
84
+ const [state, _setState] = useState(0);
85
+ setState = _setState;
86
+ return <Text text={state} />;
87
+ }
88
+
89
+ const root = ReactNoop.createRoot();
90
+ root.render(<App />);
91
+ expect(Scheduler).toFlushAndYield([0]);
92
+ expect(root).toMatchRenderedOutput('0');
93
+
94
+ // Default behavior. Flag is undefined. Warn.
95
+ expect(global.IS_REACT_ACT_ENVIRONMENT).toBe(undefined);
96
+ expect(() => {
97
+ act(() => {
98
+ setState(1);
99
+ });
100
+ }).toErrorDev(
101
+ 'The current testing environment is not configured to support act(...)',
102
+ {withoutStack: true},
103
+ );
104
+ expect(Scheduler).toHaveYielded([1]);
105
+ expect(root).toMatchRenderedOutput('1');
106
+
107
+ // Flag is true. Don't warn.
108
+ withActEnvironment(true, () => {
109
+ act(() => {
110
+ setState(2);
111
+ });
112
+ expect(Scheduler).toHaveYielded([2]);
113
+ expect(root).toMatchRenderedOutput('2');
114
+ });
115
+
116
+ // Flag is false. Warn.
117
+ withActEnvironment(false, () => {
118
+ expect(() => {
119
+ act(() => {
120
+ setState(1);
121
+ });
122
+ }).toErrorDev(
123
+ 'The current testing environment is not configured to support act(...)',
124
+ {withoutStack: true},
125
+ );
126
+ expect(Scheduler).toHaveYielded([1]);
127
+ expect(root).toMatchRenderedOutput('1');
128
+ });
129
+ });
130
+});