function findAllInRenderedTreeInternal(inst, test) {
    if (!inst || !inst.vtype) {
        return [];
    }
    var ret = test(inst._hostNode) ? [inst._hostNode] : [];
    if (inst.vtype === 1) {
        var renderedChildren = inst.vchildren;
        for (var i = 0, el; el = renderedChildren[i++];) {
            ret = ret.concat(findAllInRenderedTreeInternal(el, test));
        }
    } else if (inst.vtype > 1) {
        ret = ret.concat(findAllInRenderedTreeInternal(inst.__rendered, test));
    }
    return ret;
}

var ReactTestUtils = {
    renderIntoDocument: function (element) {
        var div = document.createElement("div");
        return ReactDOM.render(element, div);
    },

    isElement: function (element) {
        return React.isValidElement(element);
    },

    isElementOfType: function (inst, convenienceConstructor) {
        return React.isValidElement(inst) && inst.type === convenienceConstructor;
    },

    isDOMComponent: function (inst) {
        return !!(inst && inst.nodeType === 1 && inst.tagName);
    },

    isDOMComponentElement: function (inst) {
        //是否为元素节点
        return !!(inst && React.isValidElement(inst) && !!inst.tagName);
    },

    isCompositeComponent: function (inst) {
        //是否为组件实例
        if (ReactTestUtils.isDOMComponent(inst)) {
            return false;
        }
        return inst != null && typeof inst.render === "function" && typeof inst.setState === "function";
    },

    isCompositeComponentWithType: function (inst, type) {
        if (!ReactTestUtils.isCompositeComponent(inst)) {
            return false;
        }
       
        return inst.constructor === type;
    },

    isCompositeComponentElement: function (inst) {
        if (!React.isValidElement(inst)) {
            return false;
        }

        var prototype = inst.constructor.prototype;
        return typeof prototype.render === "function" && typeof prototype.setState === "function";
    },

    isCompositeComponentElementWithType: function (inst, type) {
        var constructor = inst.constructor;

        return !!(ReactTestUtils.isCompositeComponentElement(inst) && constructor === type);
    },

    getRenderedChildOfCompositeComponent: function (inst) {
        if (!ReactTestUtils.isCompositeComponent(inst)) {
            return null;
        }
        return inst.__rendered;
    },

    findAllInRenderedTree: function (inst, fn) {
        if (!inst) {
            return [];
        }
        return findAllInRenderedTreeInternal(inst, fn);
    },

    /**
     * 找出所有匹配指定className的节点
     */
    scryRenderedDOMComponentsWithClass: function (root, classNames) {
        return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
            if (ReactTestUtils.isDOMComponent(inst)) {
                var className = inst.className;
                if (typeof className !== "string") {
                    // SVG, probably.
                    className = inst.getAttribute("class") || "";
                }
                var classList = className.split(/\s+/);

                if (!Array.isArray(classNames)) {
                    classNames = classNames.split(/\s+/);
                }
                return classNames.every(function (name) {
                    return classList.indexOf(name) !== -1;
                });
            }
            return false;
        });
    },

    /**
     *与scryRenderedDOMComponentsWithClass用法相同，但只返回一个节点，如有零个或多个匹配的节点就报错
     */
    findRenderedDOMComponentWithClass: function (root, className) {
        var all = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, className);
        if (all.length !== 1) {
            throw new Error("Did not find exactly one match (found: " + all.length + ") " + "for class:" + className);
        }
        return all[0];
    },

    /**
     * Finds all instance of components in the rendered tree that are DOM
     * components with the tag name matching `tagName`.
     * @return {array} an array of all the matches.
     */
    scryRenderedDOMComponentsWithTag: function (root, tagName) {
        return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
            return ReactTestUtils.isDOMComponent(inst) && inst.tagName.toUpperCase() === tagName.toUpperCase();
        });
    },

    /**
     * Like scryRenderedDOMComponentsWithTag but expects there to be one result,
     * and returns that one result, or throws exception if there is any other
     * number of matches besides one.
     * @return {!ReactDOMComponent} The one match.
     */
    findRenderedDOMComponentWithTag: function (root, tagName) {
        var all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
        if (all.length !== 1) {
            throw new Error("Did not find exactly one match (found: " + all.length + ") " + "for tag:" + tagName);
        }
        return all[0];
    },

    /**
     * Finds all instances of components with type equal to `componentType`.
     * @return {array} an array of all the matches.
     */
    scryRenderedComponentsWithType: function (root, componentType) {
        return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
            return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
        });
    },

    /**
     * Same as `scryRenderedComponentsWithType` but expects there to be one result
     * and returns that one result, or throws exception if there is any other
     * number of matches besides one.
     * @return {!ReactComponent} The one match.
     */
    findRenderedComponentWithType: function (root, componentType) {
        var all = ReactTestUtils.scryRenderedComponentsWithType(root, componentType);
        if (all.length !== 1) {
            throw new Error("Did not find exactly one match (found: " + all.length + ") " + "for componentType:" + componentType);
        }
        return all[0];
    },

    /**
     * Pass a mocked component module to this method to augment it with
     * useful methods that allow it to be used as a dummy React component.
     * Instead of rendering as usual, the component will become a simple
     * <div> containing any provided children.
     *
     * @param {object} module the mock function object exported from a
     *                        module that defines the component to be mocked
     * @param {?string} mockTagName optional dummy root tag name to return
     *                              from render method (overrides
     *                              module.mockTagName if provided)
     * @return {object} the ReactTestUtils object (for chaining)
     */
    mockComponent: function (module, mockTagName) {
        mockTagName = mockTagName || module.mockTagName || "div";

        module.prototype.render.mockImplementation(function () {
            return React.createElement(mockTagName, null, this.props.children);
        });

        return this;
    },

    /**
     * Simulates a top level event being dispatched from a raw event that occurred
     * on an `Element` node.
     * @param {Object} topLevelType A type from `EventConstants.topLevelTypes`
     * @param {!Element} node The dom to simulate an event occurring on.
     * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
     */
    simulateNativeEventOnNode: function (topLevelType, node, fakeNativeEvent) {
        fakeNativeEvent.target = node;
        fakeNativeEvent.simulated = true;
        ReactBrowserEventEmitter.ReactEventListener.dispatchEvent(topLevelType, fakeNativeEvent);
    },

    /**
     * Simulates a top level event being dispatched from a raw event that occurred
     * on the `ReactDOMComponent` `comp`.
     * @param {Object} topLevelType A type from `EventConstants.topLevelTypes`.
     * @param {!ReactDOMComponent} comp
     * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
     */
    simulateNativeEventOnDOMComponent: function (topLevelType, comp, fakeNativeEvent) {
        ReactTestUtils.simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
    },

    nativeTouchData: function (x, y) {
        return {
            touches: [{ pageX: x, pageY: y }]
        };
    },

    createRenderer: createRendererWithWarning,

    Simulate: null,
    SimulateNative: {}
};

/**
   * Exports:
   *
   * - `ReactTestUtils.Simulate.click(Element/ReactDOMComponent)`
   * - `ReactTestUtils.Simulate.mouseMove(Element/ReactDOMComponent)`
   * - `ReactTestUtils.Simulate.change(Element/ReactDOMComponent)`
   * - ... (All keys from event plugin `eventTypes` objects)
   */
function makeSimulator(eventType) {
    return function (domComponentOrNode, eventData) {
        var node;
        React.isValidElement(domComponentOrNode) ? process.env.NODE_ENV !== "production" ? invariant(false, "TestUtils.Simulate expects a component instance and not a ReactElement.TestUtils.Simulate will not work if you are using shallow rendering.") : _prodInvariant("14") : void 0;
        if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
            node = findDOMNode(domComponentOrNode);
        } else if (domComponentOrNode.tagName) {
            node = domComponentOrNode;
        }

        var dispatchConfig = EventPluginRegistry.eventNameDispatchConfigs[eventType];

        var fakeNativeEvent = new Event();
        fakeNativeEvent.target = node;
        fakeNativeEvent.type = eventType.toLowerCase();

        // We don't use SyntheticEvent.getPooled in order to not have to worry about
        // properly destroying any properties assigned from `eventData` upon release
        var event = new SyntheticEvent(dispatchConfig, ReactDOMComponentTree.getInstanceFromNode(node), fakeNativeEvent, node);
        // Since we aren't using pooling, always persist the event. This will make
        // sure it's marked and won't warn when setting additional properties.
        event.persist();
        _assign(event, eventData);

        if (dispatchConfig.phasedRegistrationNames) {
            EventPropagators.accumulateTwoPhaseDispatches(event);
        } else {
            EventPropagators.accumulateDirectDispatches(event);
        }

        ReactUpdates.batchedUpdates(function () {
            EventPluginHub.enqueueEvents(event);
            EventPluginHub.processEventQueue(true);
        });
    };
}

function buildSimulators() {
    ReactTestUtils.Simulate = {};

    var eventType;
    for (eventType in EventPluginRegistry.eventNameDispatchConfigs) {
        /**
       * @param {!Element|ReactDOMComponent} domComponentOrNode
       * @param {?object} eventData Fake event data to use in SyntheticEvent.
       */
        ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
    }
}

// Rebuild ReactTestUtils.Simulate whenever event plugins are injected
var oldInjectEventPluginOrder = EventPluginHub.injection.injectEventPluginOrder;
EventPluginHub.injection.injectEventPluginOrder = function () {
    oldInjectEventPluginOrder.apply(this, arguments);
    buildSimulators();
};
var oldInjectEventPlugins = EventPluginHub.injection.injectEventPluginsByName;
EventPluginHub.injection.injectEventPluginsByName = function () {
    oldInjectEventPlugins.apply(this, arguments);
    buildSimulators();
};

buildSimulators();

/**
   * Exports:
   *
   * - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
   * - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
   * - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
   * - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
   * - ... (All keys from `EventConstants.topLevelTypes`)
   *
   * Note: Top level event types are a subset of the entire set of handler types
   * (which include a broader set of "synthetic" events). For example, onDragDone
   * is a synthetic event. Except when testing an event plugin or React's event
   * handling code specifically, you probably want to use ReactTestUtils.Simulate
   * to dispatch synthetic events.
   */

function makeNativeSimulator(eventType) {
    return function (domComponentOrNode, nativeEventData) {
        var fakeNativeEvent = new Event(eventType);
        _assign(fakeNativeEvent, nativeEventData);
        if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
            ReactTestUtils.simulateNativeEventOnDOMComponent(eventType, domComponentOrNode, fakeNativeEvent);
        } else if (domComponentOrNode.tagName) {
            // Will allow on actual dom nodes.
            ReactTestUtils.simulateNativeEventOnNode(eventType, domComponentOrNode, fakeNativeEvent);
        }
    };
}

Object.keys(topLevelTypes).forEach(function (eventType) {
    // Event type is stored as 'topClick' - we transform that to 'click'
    var convenienceName = eventType.indexOf("top") === 0 ? eventType.charAt(3).toLowerCase() + eventType.substr(4) : eventType;
    /**
     * @param {!Element|ReactDOMComponent} domComponentOrNode
     * @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
     */
    ReactTestUtils.SimulateNative[convenienceName] = makeNativeSimulator(eventType);
});

module.exports = ReactTestUtils;