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. 
    
  8. import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
    
  9. 
    
  10. import isArray from 'shared/isArray';
    
  11. 
    
  12. function captureAssertion(fn) {
    
  13.   // Trick to use a Jest matcher inside another Jest matcher. `fn` contains an
    
  14.   // assertion; if it throws, we capture the error and return it, so the stack
    
  15.   // trace presented to the user points to the original assertion in the
    
  16.   // test file.
    
  17.   try {
    
  18.     fn();
    
  19.   } catch (error) {
    
  20.     return {
    
  21.       pass: false,
    
  22.       message: () => error.message,
    
  23.     };
    
  24.   }
    
  25.   return {pass: true};
    
  26. }
    
  27. 
    
  28. function assertYieldsWereCleared(root) {
    
  29.   const Scheduler = root._Scheduler;
    
  30.   const actualYields = Scheduler.unstable_clearLog();
    
  31.   if (actualYields.length !== 0) {
    
  32.     const error = Error(
    
  33.       'Log of yielded values is not empty. ' +
    
  34.         'Call expect(ReactTestRenderer).unstable_toHaveYielded(...) first.',
    
  35.     );
    
  36.     Error.captureStackTrace(error, assertYieldsWereCleared);
    
  37.     throw error;
    
  38.   }
    
  39. }
    
  40. 
    
  41. export function unstable_toMatchRenderedOutput(root, expectedJSX) {
    
  42.   assertYieldsWereCleared(root);
    
  43.   const actualJSON = root.toJSON();
    
  44. 
    
  45.   let actualJSX;
    
  46.   if (actualJSON === null || typeof actualJSON === 'string') {
    
  47.     actualJSX = actualJSON;
    
  48.   } else if (isArray(actualJSON)) {
    
  49.     if (actualJSON.length === 0) {
    
  50.       actualJSX = null;
    
  51.     } else if (actualJSON.length === 1) {
    
  52.       actualJSX = jsonChildToJSXChild(actualJSON[0]);
    
  53.     } else {
    
  54.       const actualJSXChildren = jsonChildrenToJSXChildren(actualJSON);
    
  55.       if (actualJSXChildren === null || typeof actualJSXChildren === 'string') {
    
  56.         actualJSX = actualJSXChildren;
    
  57.       } else {
    
  58.         actualJSX = {
    
  59.           $$typeof: REACT_ELEMENT_TYPE,
    
  60.           type: REACT_FRAGMENT_TYPE,
    
  61.           key: null,
    
  62.           ref: null,
    
  63.           props: {
    
  64.             children: actualJSXChildren,
    
  65.           },
    
  66.           _owner: null,
    
  67.           _store: __DEV__ ? {} : undefined,
    
  68.         };
    
  69.       }
    
  70.     }
    
  71.   } else {
    
  72.     actualJSX = jsonChildToJSXChild(actualJSON);
    
  73.   }
    
  74. 
    
  75.   return captureAssertion(() => {
    
  76.     expect(actualJSX).toEqual(expectedJSX);
    
  77.   });
    
  78. }
    
  79. 
    
  80. function jsonChildToJSXChild(jsonChild) {
    
  81.   if (jsonChild === null || typeof jsonChild === 'string') {
    
  82.     return jsonChild;
    
  83.   } else {
    
  84.     const jsxChildren = jsonChildrenToJSXChildren(jsonChild.children);
    
  85.     return {
    
  86.       $$typeof: REACT_ELEMENT_TYPE,
    
  87.       type: jsonChild.type,
    
  88.       key: null,
    
  89.       ref: null,
    
  90.       props:
    
  91.         jsxChildren === null
    
  92.           ? jsonChild.props
    
  93.           : {...jsonChild.props, children: jsxChildren},
    
  94.       _owner: null,
    
  95.       _store: __DEV__ ? {} : undefined,
    
  96.     };
    
  97.   }
    
  98. }
    
  99. 
    
  100. function jsonChildrenToJSXChildren(jsonChildren) {
    
  101.   if (jsonChildren !== null) {
    
  102.     if (jsonChildren.length === 1) {
    
  103.       return jsonChildToJSXChild(jsonChildren[0]);
    
  104.     } else if (jsonChildren.length > 1) {
    
  105.       const jsxChildren = [];
    
  106.       let allJSXChildrenAreStrings = true;
    
  107.       let jsxChildrenString = '';
    
  108.       for (let i = 0; i < jsonChildren.length; i++) {
    
  109.         const jsxChild = jsonChildToJSXChild(jsonChildren[i]);
    
  110.         jsxChildren.push(jsxChild);
    
  111.         if (allJSXChildrenAreStrings) {
    
  112.           if (typeof jsxChild === 'string') {
    
  113.             jsxChildrenString += jsxChild;
    
  114.           } else if (jsxChild !== null) {
    
  115.             allJSXChildrenAreStrings = false;
    
  116.           }
    
  117.         }
    
  118.       }
    
  119.       return allJSXChildrenAreStrings ? jsxChildrenString : jsxChildren;
    
  120.     }
    
  121.   }
    
  122.   return null;
    
  123. }