main
js 195 lines 5.32 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and 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 * @flow
8 */
9
10 import {getVersionedRenderImplementation} from './utils';
11
12 describe('useEditableValue', () => {
13 let act;
14 let React;
15 let useEditableValue;
16
17 beforeEach(() => {
18 const utils = require('./utils');
19 act = utils.act;
20
21 React = require('react');
22
23 useEditableValue = require('../devtools/views/hooks').useEditableValue;
24 });
25
26 const {render} = getVersionedRenderImplementation();
27
28 it('should not cause a loop with values like NaN', () => {
29 let state;
30
31 function Example({value = NaN}) {
32 const tuple = useEditableValue(value);
33 state = tuple[0];
34 return null;
35 }
36
37 act(() => render(<Example />));
38
39 expect(state.editableValue).toEqual('NaN');
40 expect(state.externalValue).toEqual(NaN);
41 expect(state.parsedValue).toEqual(NaN);
42 expect(state.hasPendingChanges).toBe(false);
43 expect(state.isValid).toBe(true);
44 });
45
46 it('should override editable state when external props are updated', () => {
47 let state;
48
49 function Example({value}) {
50 const tuple = useEditableValue(value);
51 state = tuple[0];
52 return null;
53 }
54
55 act(() => render(<Example value={1} />));
56
57 expect(state.editableValue).toEqual('1');
58 expect(state.externalValue).toEqual(1);
59 expect(state.parsedValue).toEqual(1);
60 expect(state.hasPendingChanges).toBe(false);
61 expect(state.isValid).toBe(true);
62
63 // If there are NO pending changes,
64 // an update to the external prop value should override the local/pending value.
65 act(() => render(<Example value={2} />));
66
67 expect(state.editableValue).toEqual('2');
68 expect(state.externalValue).toEqual(2);
69 expect(state.parsedValue).toEqual(2);
70 expect(state.hasPendingChanges).toBe(false);
71 expect(state.isValid).toBe(true);
72 });
73
74 it('should not override editable state when external props are updated if there are pending changes', () => {
75 let dispatch, state;
76
77 function Example({value}) {
78 const tuple = useEditableValue(value);
79 state = tuple[0];
80 dispatch = tuple[1];
81 return null;
82 }
83
84 act(() => render(<Example value={1} />));
85
86 expect(state.editableValue).toEqual('1');
87 expect(state.externalValue).toEqual(1);
88 expect(state.parsedValue).toEqual(1);
89 expect(state.hasPendingChanges).toBe(false);
90 expect(state.isValid).toBe(true);
91
92 // Update (local) editable state.
93 act(() =>
94 dispatch({
95 type: 'UPDATE',
96 editableValue: '2',
97 externalValue: 1,
98 }),
99 );
100 expect(state.editableValue).toEqual('2');
101 expect(state.externalValue).toEqual(1);
102 expect(state.parsedValue).toEqual(2);
103 expect(state.hasPendingChanges).toBe(true);
104 expect(state.isValid).toBe(true);
105
106 // If there ARE pending changes,
107 // an update to the external prop value should NOT override the local/pending value.
108 act(() => render(<Example value={3} />));
109
110 expect(state.editableValue).toEqual('2');
111 expect(state.externalValue).toEqual(3);
112 expect(state.parsedValue).toEqual(2);
113 expect(state.hasPendingChanges).toBe(true);
114 expect(state.isValid).toBe(true);
115 });
116
117 it('should parse edits to ensure valid JSON', () => {
118 let dispatch, state;
119
120 function Example({value}) {
121 const tuple = useEditableValue(value);
122 state = tuple[0];
123 dispatch = tuple[1];
124 return null;
125 }
126
127 act(() => render(<Example value={1} />));
128
129 expect(state.editableValue).toEqual('1');
130 expect(state.externalValue).toEqual(1);
131 expect(state.parsedValue).toEqual(1);
132 expect(state.hasPendingChanges).toBe(false);
133 expect(state.isValid).toBe(true);
134
135 // Update (local) editable state.
136 act(() =>
137 dispatch({
138 type: 'UPDATE',
139 editableValue: '"a',
140 externalValue: 1,
141 }),
142 );
143 expect(state.editableValue).toEqual('"a');
144 expect(state.externalValue).toEqual(1);
145 expect(state.parsedValue).toEqual(1);
146 expect(state.hasPendingChanges).toBe(true);
147 expect(state.isValid).toBe(false);
148 });
149
150 it('should reset to external value upon request', () => {
151 let dispatch, state;
152
153 function Example({value}) {
154 const tuple = useEditableValue(value);
155 state = tuple[0];
156 dispatch = tuple[1];
157 return null;
158 }
159
160 act(() => render(<Example value={1} />));
161
162 expect(state.editableValue).toEqual('1');
163 expect(state.externalValue).toEqual(1);
164 expect(state.parsedValue).toEqual(1);
165 expect(state.hasPendingChanges).toBe(false);
166 expect(state.isValid).toBe(true);
167
168 // Update (local) editable state.
169 act(() =>
170 dispatch({
171 type: 'UPDATE',
172 editableValue: '2',
173 externalValue: 1,
174 }),
175 );
176 expect(state.editableValue).toEqual('2');
177 expect(state.externalValue).toEqual(1);
178 expect(state.parsedValue).toEqual(2);
179 expect(state.hasPendingChanges).toBe(true);
180 expect(state.isValid).toBe(true);
181
182 // Reset editable state
183 act(() =>
184 dispatch({
185 type: 'RESET',
186 externalValue: 1,
187 }),
188 );
189 expect(state.editableValue).toEqual('1');
190 expect(state.externalValue).toEqual(1);
191 expect(state.parsedValue).toEqual(1);
192 expect(state.hasPendingChanges).toBe(false);
193 expect(state.isValid).toBe(true);
194 });
195 });