text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  /* eslint-disable react-internal/invariant-args */
  
  'use strict';
  
  // Mock of the Native Hooks
  
  import invariant from 'shared/invariant';
  
  // Map of viewTag -> {children: [childTag], parent: ?parentTag}
  const roots = [];
  const views = new Map();
  
  function autoCreateRoot(tag) {
    // Seriously, this is how we distinguish roots in RN.
    if (!views.has(tag) && tag % 10 === 1) {
      roots.push(tag);
      views.set(tag, {
        children: [],
        parent: null,
        props: {},
        viewName: '<native root>',
      });
    }
  }
  
  function insertSubviewAtIndex(parent, child, index) {
    const parentInfo = views.get(parent);
    const childInfo = views.get(child);
    invariant(
      childInfo.parent === null,
      'Inserting view %s %s which already has parent',
      child,
      JSON.stringify(childInfo.props),
    );
    invariant(
      0 <= index && index <= parentInfo.children.length,
      'Invalid index %s for children %s',
      index,
      parentInfo.children,
    );
    parentInfo.children.splice(index, 0, child);
    childInfo.parent = parent;
  }
  
  function removeChild(parent, child) {
    const parentInfo = views.get(parent);
    const childInfo = views.get(child);
    const index = parentInfo.children.indexOf(child);
    invariant(index >= 0, 'Missing view %s during removal', child);
    parentInfo.children.splice(index, 1);
    childInfo.parent = null;
  }
  
  const RCTUIManager = {
    __dumpHierarchyForJestTestsOnly: function() {
      function dumpSubtree(tag, indent) {
        const info = views.get(tag);
        let out = '';
        out +=
          ' '.repeat(indent) + info.viewName + ' ' + JSON.stringify(info.props);
        // eslint-disable-next-line no-for-of-loops/no-for-of-loops
        for (const child of info.children) {
          out += '\n' + dumpSubtree(child, indent + 2);
        }
        return out;
      }
      return roots.map(tag => dumpSubtree(tag, 0)).join('\n');
    },
    clearJSResponder: jest.fn(),
    createView: jest.fn(function createView(reactTag, viewName, rootTag, props) {
      invariant(
        !views.has(reactTag),
        'Created two native views with tag %s',
        reactTag,
      );
      views.set(reactTag, {
        children: [],
        parent: null,
        props: props,
        viewName: viewName,
      });
    }),
    dispatchViewManagerCommand: jest.fn(),
    setJSResponder: jest.fn(),
    setChildren: jest.fn(function setChildren(parentTag, reactTags) {
      autoCreateRoot(parentTag);
      // Native doesn't actually check this but it seems like a good idea
      invariant(
        views.get(parentTag).children.length === 0,
        'Calling .setChildren on nonempty view %s',
        parentTag,
      );
      // This logic ported from iOS (RCTUIManager.m)
      reactTags.forEach((tag, i) => {
        insertSubviewAtIndex(parentTag, tag, i);
      });
    }),
    manageChildren: jest.fn(function manageChildren(
      parentTag,
      moveFromIndices = [],
      moveToIndices = [],
      addChildReactTags = [],
      addAtIndices = [],
      removeAtIndices = [],
    ) {
      autoCreateRoot(parentTag);
      // This logic ported from iOS (RCTUIManager.m)
      invariant(
        moveFromIndices.length === moveToIndices.length,
        'Mismatched move indices %s and %s',
        moveFromIndices,
        moveToIndices,
      );
      invariant(
        addChildReactTags.length === addAtIndices.length,
        'Mismatched add indices %s and %s',
        addChildReactTags,
        addAtIndices,
      );
      const parentInfo = views.get(parentTag);
      const permanentlyRemovedChildren = removeAtIndices.map(
        index => parentInfo.children[index],
      );
      const temporarilyRemovedChildren = moveFromIndices.map(
        index => parentInfo.children[index],
      );
      permanentlyRemovedChildren.forEach(tag => removeChild(parentTag, tag));
      temporarilyRemovedChildren.forEach(tag => removeChild(parentTag, tag));
      permanentlyRemovedChildren.forEach(tag => {
        views.delete(tag);
      });
      // List of [index, tag]
      const indicesToInsert = [];
      temporarilyRemovedChildren.forEach((tag, i) => {
        indicesToInsert.push([moveToIndices[i], temporarilyRemovedChildren[i]]);
      });
      addChildReactTags.forEach((tag, i) => {
        indicesToInsert.push([addAtIndices[i], addChildReactTags[i]]);
      });
      indicesToInsert.sort((a, b) => a[0] - b[0]);
      // eslint-disable-next-line no-for-of-loops/no-for-of-loops
      for (const [i, tag] of indicesToInsert) {
        insertSubviewAtIndex(parentTag, tag, i);
      }
    }),
    updateView: jest.fn(),
    removeSubviewsFromContainerWithID: jest.fn(function(parentTag) {
      views.get(parentTag).children.forEach(tag => removeChild(parentTag, tag));
    }),
    replaceExistingNonRootView: jest.fn(),
    measure: jest.fn(function measure(tag, callback) {
      invariant(
        typeof tag === 'number',
        'Expected tag to be a number, was passed %s',
        tag,
      );
      callback(10, 10, 100, 100, 0, 0);
    }),
    measureInWindow: jest.fn(function measureInWindow(tag, callback) {
      invariant(
        typeof tag === 'number',
        'Expected tag to be a number, was passed %s',
        tag,
      );
      callback(10, 10, 100, 100);
    }),
    measureLayout: jest.fn(function measureLayout(
      tag,
      relativeTag,
      fail,
      success,
    ) {
      invariant(
        typeof tag === 'number',
        'Expected tag to be a number, was passed %s',
        tag,
      );
      invariant(
        typeof relativeTag === 'number',
        'Expected relativeTag to be a number, was passed %s',
        relativeTag,
      );
      success(1, 1, 100, 100);
    }),
    __takeSnapshot: jest.fn(),
  };
  
  module.exports = RCTUIManager;
  