/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
/* eslint-disable max-len */
'use strict';

const escapeHTML = require('./escapeHTML');

const reactTestInstance = Symbol.for('react.test.json');

function printChildren(children, print, indent, colors, opts) {
  return children.map(child => printInstance(child, print, indent, colors, opts)).join(opts.edgeSpacing);
}

function printProps(props, print, indent, colors, opts) {
  return Object.keys(props).sort().map(name => {
    const prop = props[name];
    let printed = print(prop);

    if (typeof prop !== 'string') {
      if (printed.indexOf('\n') !== -1) {
        printed = '{' + opts.edgeSpacing + indent(indent(printed) + opts.edgeSpacing + '}');
      } else {
        printed = '{' + printed + '}';
      }
    }

    return opts.spacing + indent(colors.prop.open + name + colors.prop.close + '=') + colors.value.open + printed + colors.value.close;
  }).join('');
}

function printInstance(instance, print, indent, colors, opts) {
  if (typeof instance == 'number') {
    return print(instance);
  } else if (typeof instance === 'string') {
    return colors.content.open + escapeHTML(instance) + colors.content.close;
  }

  let result = colors.tag.open + '<' + instance.type + colors.tag.close;
  let closeInNewLine = false;

  if (instance.props) {
    result += printProps(instance.props, print, indent, colors, opts);
    closeInNewLine = !!Object.keys(instance.props).length && !opts.min;
  }

  if (instance.children) {
    const children = printChildren(instance.children, print, indent, colors, opts);
    result += colors.tag.open + (closeInNewLine ? '\n' : '') + '>' + colors.tag.close + opts.edgeSpacing + indent(children) + opts.edgeSpacing + colors.tag.open + '</' + instance.type + '>' + colors.tag.close;
  } else {
    result += colors.tag.open + (closeInNewLine ? '\n' : ' ') + '/>' + colors.tag.close;
  }

  return result;
}

module.exports = {
  print(val, print, indent, opts, colors) {
    return printInstance(val, print, indent, colors, opts);
  },
  test(object) {
    return object && object.$$typeof === reactTestInstance;
  },
};

@@ -295,16 +295,28 @@
 ectory.%0A
+ *%0A * @flow%0A
  */%0A/* e
@@ -458,16 +458,271 @@
 son');%0A%0A
+type ReactTestObject = %7B%7C%0A  $$typeof: Symbol,%0A  type: string,%0A  props?: Object,%0A  children?: null %7C Array%3CReactTestChild%3E,%0A%7C%7D;%0A// Child can be %60number%60 in Stack renderer but not in Fiber renderer.%0Atype ReactTestChild = ReactTestObject %7C string %7C number;%0A%0A
 function
@@ -736,32 +736,55 @@
 hildren(children
+: Array%3CReactTestChild%3E
 , print, indent,
@@ -930,24 +930,32 @@
 tProps(props
+: Object
 , print, ind
@@ -1511,16 +1511,32 @@
 instance
+: ReactTestChild
 , print,
@@ -1753,16 +1753,46 @@
 e;%0A  %7D%0A%0A
+  let closeInNewLine = false;%0A
   let re
@@ -1860,133 +1860,195 @@
 se;%0A
-  let closeInNewLine = false;%0A%0A  if (instance.props) %7B%0A    result += printProps(instance.props, print, indent, colors, opts);
+%0A  if (instance.props) %7B%0A    // If assignments are in opposite order, Flow 0.39.0 finds incorrect error:%0A    // element of Object.keys. Expected object instead of possibly undefined value
 %0A   
@@ -2116,16 +2116,87 @@
 ts.min;%0A
+    result += printProps(instance.props, print, indent, colors, opts);%0A
   %7D%0A%0A  i
@@ -2672,37 +2672,117 @@
 (val
-, print, indent, opts, colors
+: ReactTestObject, print: (val: any) =%3E string, indent: (str: string) =%3E string, opts: Object, colors: Object
 ) %7B%0A
@@ -2855,24 +2855,32 @@
  test(object
+: Object
 ) %7B%0A    retu

Add Flow type annotations to ReactTestComponent plugin (#2948)