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