RubyLouvre/anu

View on GitHub
packages/core/__tests__/ReactChildren-test.js

Summary

Maintainability
F
3 wks
Test Coverage

"use strict";

describe("ReactChildren", () => {
    let React;
    let ReactTestUtils;

    beforeEach(() => {
        jest.resetModules();
        React = require("react");
        ReactTestUtils = require("test-utils");
    });
    it("should support identity for simple", () => {
        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            expect(index).toBe(0);
            return kid;
        });

        var simpleKid = <span key="simple" />;

        // First pass children into a component to fully simulate what happens when
        // using structures that arrive from transforms.

        var instance = <div>{simpleKid}</div>;
        React.Children.forEach(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        callback.calls.reset();
        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        expect(mappedChildren[0]).toEqual(<span key=".$simple" />);
    });

    it("should treat single arrayless child as being in array", () => {
        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            expect(index).toBe(0);
            return kid;
        });

        var simpleKid = <span />;
        var instance = <div>{simpleKid}</div>;
        React.Children.forEach(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        callback.calls.reset();
        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        expect(mappedChildren[0]).toEqual(<span key=".0" />);
    });

    it("should treat single child in array as expected", () => {
        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        var simpleKid = <span key="simple" />;
        var instance = <div>{[simpleKid]}</div>;
        React.Children.forEach(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        callback.calls.reset();
        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        expect(callback).toHaveBeenCalledWith(simpleKid, 0);
        expect(mappedChildren[0]).toEqual(<span key=".$simple" />);
    });

    it("should be called for each child", () => {
        var zero = <div key="keyZero" />;
        var one = null;
        var two = <div key="keyTwo" />;
        var three = null;
        var four = <div key="keyFour" />;
        var context = {};

        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        var instance = (
            <div>
                {zero}
                {one}
                {two}
                {three}
                {four}
            </div>
        );

        function assertCalls() {
            expect(callback).toHaveBeenCalledWith(zero, 0);
            expect(callback).toHaveBeenCalledWith(one, 1);
            expect(callback).toHaveBeenCalledWith(two, 2);
            expect(callback).toHaveBeenCalledWith(three, 3);
            expect(callback).toHaveBeenCalledWith(four, 4);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual([<div key=".$keyZero" />,
        <div key=".$keyTwo" />, <div key=".$keyFour" />]);
    });
    it("React.Children.forEach不处理null void 0", () => {
        var i = 0;
        React.Children.forEach(null, function () {
            i++;
        });
        React.Children.forEach(void 0, function () {
            i++;
        });
        expect(i).toBe(0);
    });
    it("should traverse children of different kinds", () => {
        var div = <div key="divNode" />;
        var span = <span key="spanNode" />;
        var a = <a key="aNode" />;

        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });
        var instance = (
            <div>
                {div}
                {[[span]]}
                {[a]}
                {"string"}
                {1234}
                {true}
                {false}
                {null}
                {undefined}
            </div>
        );

        function assertCalls() {
            expect(callback.calls.count()).toBe(9);
            expect(callback).toHaveBeenCalledWith(div, 0);
            expect(callback).toHaveBeenCalledWith(span, 1);
            expect(callback).toHaveBeenCalledWith(a, 2);
            expect(callback).toHaveBeenCalledWith("string", 3);
            expect(callback).toHaveBeenCalledWith(1234, 4);
            expect(callback).toHaveBeenCalledWith(null, 5);
            expect(callback).toHaveBeenCalledWith(null, 6);
            expect(callback).toHaveBeenCalledWith(null, 7);
            expect(callback).toHaveBeenCalledWith(null, 8);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual([<div key=".$divNode" />,
        <span key=".1:0:$spanNode" />,
        <a key=".2:$aNode" />, "string", 1234]);
    });

    it("should be called for each child in nested structure", () => {
        var zero = <div key="keyZero" />;
        var one = null;
        var two = <div key="keyTwo" />;
        var three = null;
        var four = <div key="keyFour" />;
        var five = <div key="keyFive" />;

        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        var instance = <div>{[[zero, one, two], [three, four], five]}</div>;

        function assertCalls() {
            expect(callback.calls.count()).toBe(6);
            expect(callback).toHaveBeenCalledWith(zero, 0);
            expect(callback).toHaveBeenCalledWith(one, 1);
            expect(callback).toHaveBeenCalledWith(two, 2);
            expect(callback).toHaveBeenCalledWith(three, 3);
            expect(callback).toHaveBeenCalledWith(four, 4);
            expect(callback).toHaveBeenCalledWith(five, 5);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual([<div key=".0:$keyZero" />, <div key=".0:$keyTwo" />, <div key=".1:$keyFour" />, <div key=".$keyFive" />]);
    });

    it("should retain key across two mappings", () => {
        var zeroForceKey = <div key="keyZero" />;
        var oneForceKey = <div key="keyOne" />;
        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });
        var forcedKeys = (
            <div>
                {zeroForceKey}
                {oneForceKey}
            </div>
        );

        function assertCalls() {
            expect(callback).toHaveBeenCalledWith(zeroForceKey, 0);
            expect(callback).toHaveBeenCalledWith(oneForceKey, 1);
            callback.calls.reset();
        }

        React.Children.forEach(forcedKeys.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(forcedKeys.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual([<div key=".$keyZero" />, <div key=".$keyOne" />]);
    });

    it("should be called for each child in an iterable without keys", () => {
        //spyOn(console, "error");
        var threeDivIterable = {
            "@@iterator": function () {
                var i = 0;
                return {
                    next: function () {
                        if (i++ < 3) {
                            return { value: <div />, done: false };
                        } else {
                            return { value: undefined, done: true };
                        }
                    }
                };
            }
        };

        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        var instance = <div>{threeDivIterable}</div>;

        function assertCalls() {
            expect(callback.calls.count()).toBe(3);
            expect(callback).toHaveBeenCalledWith(<div />, 0);
            expect(callback).toHaveBeenCalledWith(<div />, 1);
            expect(callback).toHaveBeenCalledWith(<div />, 2);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();


        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        var compareChildren = [<div key=".0" />, <div key=".1" />, <div key=".2" />];
        expect(mappedChildren).toEqual(compareChildren);
    });

    it("should be called for each child in an iterable with keys", () => {
        var threeDivIterable = {
            "@@iterator": function () {
                var i = 0;
                return {
                    next: function () {
                        if (i++ < 3) {
                            return { value: <div key={"#" + i} />, done: false };
                        } else {
                            return { value: undefined, done: true };
                        }
                    }
                };
            }
        };

        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        var instance = <div>{threeDivIterable}</div>;

        function assertCalls() {
            expect(callback.calls.count()).toBe(3);
            expect(callback).toHaveBeenCalledWith(<div key="#1" />, 0);
            expect(callback).toHaveBeenCalledWith(<div key="#2" />, 1);
            expect(callback).toHaveBeenCalledWith(<div key="#3" />, 2);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual([<div key=".$#1" />, <div key=".$#2" />, <div key=".$#3" />]);
    });

    it("should not enumerate enumerable numbers (#4776)", () => {
        /*eslint-disable no-extend-native */
        Number.prototype["@@iterator"] = function () {
            throw new Error("number iterator called");
        };
        /*eslint-enable no-extend-native */

        try {
            var instance = (
                <div>
                    {5}
                    {12}
                    {13}
                </div>
            );

            var context = {};
            var callback = jasmine.createSpy().and.callFake(function (kid, index) {
                expect(this).toBe(context);
                return kid;
            });

            var assertCalls = function () {
                expect(callback.calls.count()).toBe(3);
                expect(callback).toHaveBeenCalledWith(5, 0);
                expect(callback).toHaveBeenCalledWith(12, 1);
                expect(callback).toHaveBeenCalledWith(13, 2);
                callback.calls.reset();
            };

            React.Children.forEach(instance.props.children, callback, context);
            assertCalls();

            var mappedChildren = React.Children.map(instance.props.children, callback, context);
            assertCalls();
            expect(mappedChildren).toEqual([5, 12, 13]);
        } finally {
            delete Number.prototype["@@iterator"];
        }
    });

    it("should allow extension of native prototypes", () => {
        /*eslint-disable no-extend-native */
        String.prototype.key = "react";
        Number.prototype.key = "rocks";
        /*eslint-enable no-extend-native */

        var instance = (
            <div>
                {"a"}
                {13}
            </div>
        );

        var context = {};
        var callback = jasmine.createSpy().and.callFake(function (kid, index) {
            expect(this).toBe(context);
            return kid;
        });

        function assertCalls() {
            expect(callback.calls.count()).toBe(2, 0);
            expect(callback).toHaveBeenCalledWith("a", 0);
            expect(callback).toHaveBeenCalledWith(13, 1);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        var mappedChildren = React.Children.map(instance.props.children, callback, context);
        assertCalls();
        expect(mappedChildren).toEqual(["a", 13]);

        delete String.prototype.key;
        delete Number.prototype.key;
    });

    it("should pass key to returned component", () => {
        var mapFn = function (kid, index) {
            return <div>{kid}</div>;
        };

        var simpleKid = <span key="simple" />;

        var instance = <div>{simpleKid}</div>;
        var mappedChildren = React.Children.map(instance.props.children, mapFn);

        expect(React.Children.count(mappedChildren)).toBe(1);
        expect(mappedChildren[0]).not.toBe(simpleKid);
        expect(mappedChildren[0].props.children).toBe(simpleKid);
        expect(mappedChildren[0].key).toBe(".$simple");
    });

    it("should invoke callback with the right context", () => {
        var lastContext;
        var callback = function (kid, index) {
            lastContext = this;
            return this;
        };

        // TODO: Use an object to test, after non-object fragments has fully landed.
        var scopeTester = "scope tester";

        var simpleKid = <span key="simple" />;
        var instance = <div>{simpleKid}</div>;
        React.Children.forEach(instance.props.children, callback, scopeTester);
        expect(lastContext).toBe(scopeTester);

        var mappedChildren = React.Children.map(instance.props.children, callback, scopeTester);

        expect(React.Children.count(mappedChildren)).toBe(1);
        expect(mappedChildren[0]).toBe(scopeTester);
    });

    it("should be called for each child", () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;
        const context = {};

        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        const instance = (
            <div>
                {zero}
                {one}
                {two}
                {three}
                {four}
            </div>
        );

        function assertCalls() {
            expect(callback).toHaveBeenCalledWith(zero, 0);
            expect(callback).toHaveBeenCalledWith(one, 1);
            expect(callback).toHaveBeenCalledWith(two, 2);
            expect(callback).toHaveBeenCalledWith(three, 3);
            expect(callback).toHaveBeenCalledWith(four, 4);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".$keyZero" />,
            <div key=".$keyTwo" />,
            <div key=".$keyFour" />,
        ]);
    });
    it('should traverse children of different kinds', () => {
        const div = <div key="divNode" />;
        const span = <span key="spanNode" />;
        const a = <a key="aNode" />;

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        const instance = (
            <div>
                {div}
                {[[span]]}
                {[a]}
                {'string'}
                {1234}
                {true}
                {false}
                {null}
                {undefined}
            </div>
        );

        function assertCalls() {
            expect(callback.calls.count()).toBe(9);
            expect(callback).toHaveBeenCalledWith(div, 0);
            expect(callback).toHaveBeenCalledWith(span, 1);
            expect(callback).toHaveBeenCalledWith(a, 2);
            expect(callback).toHaveBeenCalledWith('string', 3);
            expect(callback).toHaveBeenCalledWith(1234, 4);
            expect(callback).toHaveBeenCalledWith(null, 5);
            expect(callback).toHaveBeenCalledWith(null, 6);
            expect(callback).toHaveBeenCalledWith(null, 7);
            expect(callback).toHaveBeenCalledWith(null, 8);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".$divNode" />,
            <span key=".1:0:$spanNode" />,
            <a key=".2:$aNode" />,
            'string',
            1234,
        ]);
    });

    it('should be called for each child in nested structure', () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;
        const five = <div key="keyFive" />;

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            return kid;
        });

        const instance = <div>{[[zero, one, two], [three, four], five]}</div>;

        function assertCalls() {
            expect(callback.calls.count()).toBe(6);
            expect(callback).toHaveBeenCalledWith(zero, 0);
            expect(callback).toHaveBeenCalledWith(one, 1);
            expect(callback).toHaveBeenCalledWith(two, 2);
            expect(callback).toHaveBeenCalledWith(three, 3);
            expect(callback).toHaveBeenCalledWith(four, 4);
            expect(callback).toHaveBeenCalledWith(five, 5);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".0:$keyZero" />,
            <div key=".0:$keyTwo" />,
            <div key=".1:$keyFour" />,
            <div key=".$keyFive" />,
        ]);
    });

    it('should retain key across two mappings', () => {
        const zeroForceKey = <div key="keyZero" />;
        const oneForceKey = <div key="keyOne" />;
        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        const forcedKeys = (
            <div>
                {zeroForceKey}
                {oneForceKey}
            </div>
        );

        function assertCalls() {
            expect(callback).toHaveBeenCalledWith(zeroForceKey, 0);
            expect(callback).toHaveBeenCalledWith(oneForceKey, 1);
            callback.calls.reset();
        }

        React.Children.forEach(forcedKeys.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            forcedKeys.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".$keyZero" />,
            <div key=".$keyOne" />,
        ]);
    });

    it('should be called for each child in an iterable without keys', () => {
        const threeDivIterable = {
            '@@iterator': function () {
                let i = 0;
                return {
                    next: function () {
                        if (i++ < 3) {
                            return { value: <div />, done: false };
                        } else {
                            return { value: undefined, done: true };
                        }
                    },
                };
            },
        };

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        let instance;
        expect(() => (instance = <div>{threeDivIterable}</div>)).toWarnDev(
            'Warning: Each child in an array or iterator should have a unique "key" prop.',
        );

        function assertCalls() {
            expect(callback.calls.count()).toBe(3);
            expect(callback).toHaveBeenCalledWith(<div />, 0);
            expect(callback).toHaveBeenCalledWith(<div />, 1);
            expect(callback).toHaveBeenCalledWith(<div />, 2);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".0" />,
            <div key=".1" />,
            <div key=".2" />,
        ]);
    });


    it('should be called for each child in an iterable without keys', () => {
        const threeDivIterable = {
            '@@iterator': function () {
                let i = 0;
                return {
                    next: function () {
                        if (i++ < 3) {
                            return { value: <div />, done: false };
                        } else {
                            return { value: undefined, done: true };
                        }
                    },
                };
            },
        };

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        let instance;
        expect(() => (instance = <div>{threeDivIterable}</div>)).toWarnDev(
            'Warning: Each child in an array or iterator should have a unique "key" prop.',
        );

        function assertCalls() {
            expect(callback.calls.count()).toBe(3);
            expect(callback).toHaveBeenCalledWith(<div />, 0);
            expect(callback).toHaveBeenCalledWith(<div />, 1);
            expect(callback).toHaveBeenCalledWith(<div />, 2);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".0" />,
            <div key=".1" />,
            <div key=".2" />,
        ]);
    });

    it('should be called for each child in an iterable with keys', () => {
        const threeDivIterable = {
            '@@iterator': function () {
                let i = 0;
                return {
                    next: function () {
                        if (i++ < 3) {
                            return { value: <div key={'#' + i} />, done: false };
                        } else {
                            return { value: undefined, done: true };
                        }
                    },
                };
            },
        };

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        const instance = <div>{threeDivIterable}</div>;

        function assertCalls() {
            expect(callback.calls.count()).toBe(3);
            expect(callback).toHaveBeenCalledWith(<div key="#1" />, 0);
            expect(callback).toHaveBeenCalledWith(<div key="#2" />, 1);
            expect(callback).toHaveBeenCalledWith(<div key="#3" />, 2);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual([
            <div key=".$#1" />,
            <div key=".$#2" />,
            <div key=".$#3" />,
        ]);
    });

    it('should not enumerate enumerable numbers (#4776)', () => {
        /*eslint-disable no-extend-native */
        Number.prototype['@@iterator'] = function () {
            throw new Error('number iterator called');
        };
        /*eslint-enable no-extend-native */

        try {
            const instance = (
                <div>
                    {5}
                    {12}
                    {13}
                </div>
            );

            const context = {};
            const callback = jasmine.createSpy().and.callFake(function (kid) {
                expect(this).toBe(context);
                return kid;
            });

            const assertCalls = function () {
                expect(callback.calls.count()).toBe(3);
                expect(callback).toHaveBeenCalledWith(5, 0);
                expect(callback).toHaveBeenCalledWith(12, 1);
                expect(callback).toHaveBeenCalledWith(13, 2);
                callback.calls.reset();
            };

            React.Children.forEach(instance.props.children, callback, context);
            assertCalls();

            const mappedChildren = React.Children.map(
                instance.props.children,
                callback,
                context,
            );
            assertCalls();
            expect(mappedChildren).toEqual([5, 12, 13]);
        } finally {
            delete Number.prototype['@@iterator'];
        }
    });

    it('should allow extension of native prototypes', () => {
        /*eslint-disable no-extend-native */
        String.prototype.key = 'react';
        Number.prototype.key = 'rocks';
        /*eslint-enable no-extend-native */

        const instance = (
            <div>
                {'a'}
                {13}
            </div>
        );

        const context = {};
        const callback = jasmine.createSpy().and.callFake(function (kid) {
            expect(this).toBe(context);
            return kid;
        });

        function assertCalls() {
            expect(callback.calls.count()).toBe(2, 0);
            expect(callback).toHaveBeenCalledWith('a', 0);
            expect(callback).toHaveBeenCalledWith(13, 1);
            callback.calls.reset();
        }

        React.Children.forEach(instance.props.children, callback, context);
        assertCalls();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            context,
        );
        assertCalls();
        expect(mappedChildren).toEqual(['a', 13]);

        delete String.prototype.key;
        delete Number.prototype.key;
    });

    it('should pass key to returned component', () => {
        const mapFn = function (kid, index) {
            return <div>{kid}</div>;
        };

        const simpleKid = <span key="simple" />;

        const instance = <div>{simpleKid}</div>;
        const mappedChildren = React.Children.map(instance.props.children, mapFn);

        expect(React.Children.count(mappedChildren)).toBe(1);
        expect(mappedChildren[0]).not.toBe(simpleKid);
        expect(mappedChildren[0].props.children).toBe(simpleKid);
        expect(mappedChildren[0].key).toBe('.$simple');
    });

    it('should invoke callback with the right context', () => {
        let lastContext;
        const callback = function (kid, index) {
            lastContext = this;
            return this;
        };

        // TODO: Use an object to test, after non-object fragments has fully landed.
        const scopeTester = 'scope tester';

        const simpleKid = <span key="simple" />;
        const instance = <div>{simpleKid}</div>;
        React.Children.forEach(instance.props.children, callback, scopeTester);
        expect(lastContext).toBe(scopeTester);

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
            scopeTester,
        );

        expect(React.Children.count(mappedChildren)).toBe(1);
        expect(mappedChildren[0]).toBe(scopeTester);
    });

    it('should be called for each child', () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;

        const mapped = [
            <div key="giraffe" />, // Key should be joined to obj key
            null, // Key should be added even if we don't supply it!
            <div />, // Key should be added even if not supplied!
            <span />, // Map from null to something.
            <div key="keyFour" />,
        ];
        const callback = jasmine.createSpy().and.callFake(function (kid, index) {
            return mapped[index];
        });

        const instance = (
            <div>
                {zero}
                {one}
                {two}
                {three}
                {four}
            </div>
        );

        React.Children.forEach(instance.props.children, callback);
        expect(callback).toHaveBeenCalledWith(zero, 0);
        expect(callback).toHaveBeenCalledWith(one, 1);
        expect(callback).toHaveBeenCalledWith(two, 2);
        expect(callback).toHaveBeenCalledWith(three, 3);
        expect(callback).toHaveBeenCalledWith(four, 4);
        callback.calls.reset();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
        );
        expect(callback.calls.count()).toBe(5);
        expect(React.Children.count(mappedChildren)).toBe(4);
        // Keys default to indices.
        expect([
            mappedChildren[0].key,
            mappedChildren[1].key,
            mappedChildren[2].key,
            mappedChildren[3].key,
        ]).toEqual(['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour']);

        expect(callback).toHaveBeenCalledWith(zero, 0);
        expect(callback).toHaveBeenCalledWith(one, 1);
        expect(callback).toHaveBeenCalledWith(two, 2);
        expect(callback).toHaveBeenCalledWith(three, 3);
        expect(callback).toHaveBeenCalledWith(four, 4);

        expect(mappedChildren[0]).toEqual(<div key="giraffe/.$keyZero" />);
        expect(mappedChildren[1]).toEqual(<div key=".$keyTwo" />);
        expect(mappedChildren[2]).toEqual(<span key=".3" />);
        expect(mappedChildren[3]).toEqual(<div key=".$keyFour" />);
    });

    it('should be called for each child in nested structure', () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;
        const five = <div key="keyFive" />;

        const zeroMapped = <div key="giraffe" />; // Key should be overridden
        const twoMapped = <div />; // Key should be added even if not supplied!
        const fourMapped = <div key="keyFour" />;
        const fiveMapped = <div />;

        const callback = jasmine.createSpy().and.callFake(function (kid) {
            switch (kid) {
                case zero:
                    return zeroMapped;
                case two:
                    return twoMapped;
                case four:
                    return fourMapped;
                case five:
                    return fiveMapped;
                default:
                    return kid;
            }
        });

        const frag = [[zero, one, two], [three, four], five];
        const instance = <div>{[frag]}</div>;

        React.Children.forEach(instance.props.children, callback);
        expect(callback.calls.count()).toBe(6);
        expect(callback).toHaveBeenCalledWith(zero, 0);
        expect(callback).toHaveBeenCalledWith(one, 1);
        expect(callback).toHaveBeenCalledWith(two, 2);
        expect(callback).toHaveBeenCalledWith(three, 3);
        expect(callback).toHaveBeenCalledWith(four, 4);
        expect(callback).toHaveBeenCalledWith(five, 5);
        callback.calls.reset();

        const mappedChildren = React.Children.map(
            instance.props.children,
            callback,
        );
        expect(callback.calls.count()).toBe(6);
        expect(callback).toHaveBeenCalledWith(zero, 0);
        expect(callback).toHaveBeenCalledWith(one, 1);
        expect(callback).toHaveBeenCalledWith(two, 2);
        expect(callback).toHaveBeenCalledWith(three, 3);
        expect(callback).toHaveBeenCalledWith(four, 4);
        expect(callback).toHaveBeenCalledWith(five, 5);

        expect(React.Children.count(mappedChildren)).toBe(4);
        // Keys default to indices.
        expect([
            mappedChildren[0].key,
            mappedChildren[1].key,
            mappedChildren[2].key,
            mappedChildren[3].key,
        ]).toEqual([
            'giraffe/.0:0:$keyZero',
            '.0:0:$keyTwo',
            '.0:1:$keyFour',
            '.0:$keyFive',
        ]);

        expect(mappedChildren[0]).toEqual(<div key="giraffe/.0:0:$keyZero" />);
        expect(mappedChildren[1]).toEqual(<div key=".0:0:$keyTwo" />);
        expect(mappedChildren[2]).toEqual(<div key=".0:1:$keyFour" />);
        expect(mappedChildren[3]).toEqual(<div key=".0:$keyFive" />);
    });

    it('should retain key across two mappings', () => {
        const zeroForceKey = <div key="keyZero" />;
        const oneForceKey = <div key="keyOne" />;

        // Key should be joined to object key
        const zeroForceKeyMapped = <div key="giraffe" />;
        // Key should be added even if we don't supply it!
        const oneForceKeyMapped = <div />;

        const mapFn = function (kid, index) {
            return index === 0 ? zeroForceKeyMapped : oneForceKeyMapped;
        };

        const forcedKeys = (
            <div>
                {zeroForceKey}
                {oneForceKey}
            </div>
        );

        const expectedForcedKeys = ['giraffe/.$keyZero', '.$keyOne'];
        const mappedChildrenForcedKeys = React.Children.map(
            forcedKeys.props.children,
            mapFn,
        );
        const mappedForcedKeys = mappedChildrenForcedKeys.map(c => c.key);
        expect(mappedForcedKeys).toEqual(expectedForcedKeys);

        const expectedRemappedForcedKeys = [
            'giraffe/.$giraffe/.$keyZero',
            '.$.$keyOne',
        ];
        const remappedChildrenForcedKeys = React.Children.map(
            mappedChildrenForcedKeys,
            mapFn,
        );
        expect(remappedChildrenForcedKeys.map(c => c.key)).toEqual(
            expectedRemappedForcedKeys,
        );
    });

    it('should not throw if key provided is a dupe with array key', () => {
        const zero = <div />;
        const one = <div key="0" />;

        const mapFn = function () {
            return null;
        };

        const instance = (
            <div>
                {zero}
                {one}
            </div>
        );

        expect(function () {
            React.Children.map(instance.props.children, mapFn);
        }).not.toThrow();
    });

    it('should use the same key for a cloned element', () => {
        const instance = (
            <div>
                <div />
            </div>
        );

        const mapped = React.Children.map(
            instance.props.children,
            element => element,
        );

        const mappedWithClone = React.Children.map(
            instance.props.children,
            element => React.cloneElement(element),
        );

        expect(mapped[0].key).toBe(mappedWithClone[0].key);
    });

    it('should use the same key for a cloned element with key', () => {
        const instance = (
            <div>
                <div key="unique" />
            </div>
        );

        const mapped = React.Children.map(
            instance.props.children,
            element => element,
        );

        const mappedWithClone = React.Children.map(
            instance.props.children,
            element => React.cloneElement(element, { key: 'unique' }),
        );

        expect(mapped[0].key).toBe(mappedWithClone[0].key);
    });

    it('should return 0 for null children', () => {
        const numberOfChildren = React.Children.count(null);
        expect(numberOfChildren).toBe(0);
    });

    it('should return 0 for undefined children', () => {
        const numberOfChildren = React.Children.count(undefined);
        expect(numberOfChildren).toBe(0);
    });

    it('should return 1 for single child', () => {
        const simpleKid = <span key="simple" />;
        const instance = <div>{simpleKid}</div>;
        const numberOfChildren = React.Children.count(instance.props.children);
        expect(numberOfChildren).toBe(1);
    });

    it('should count the number of children in flat structure', () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;

        const instance = (
            <div>
                {zero}
                {one}
                {two}
                {three}
                {four}
            </div>
        );
        const numberOfChildren = React.Children.count(instance.props.children);
        expect(numberOfChildren).toBe(5);
    });

    it('should count the number of children in nested structure', () => {
        const zero = <div key="keyZero" />;
        const one = null;
        const two = <div key="keyTwo" />;
        const three = null;
        const four = <div key="keyFour" />;
        const five = <div key="keyFive" />;

        const instance = (
            <div>{[[[zero, one, two], [three, four], five], null]}</div>
        );
        const numberOfChildren = React.Children.count(instance.props.children);
        expect(numberOfChildren).toBe(7);
    });

    it('should flatten children to an array', () => {
        expect(React.Children.toArray(undefined)).toEqual([]);
        expect(React.Children.toArray(null)).toEqual([]);

        expect(React.Children.toArray(<div />).length).toBe(1);
        expect(React.Children.toArray([<div />]).length).toBe(1);
        expect(React.Children.toArray(<div />)[0].key).toBe(
            React.Children.toArray([<div />])[0].key,
        );

        const flattened = React.Children.toArray([
            [<div key="apple" />, <div key="banana" />, <div key="camel" />],
            [<div key="banana" />, <div key="camel" />, <div key="deli" />],
        ]);
        expect(flattened.length).toBe(6);
        expect(flattened[1].key).toContain('banana');
        expect(flattened[3].key).toContain('banana');
        expect(flattened[1].key).not.toBe(flattened[3].key);

        const reversed = React.Children.toArray([
            [<div key="camel" />, <div key="banana" />, <div key="apple" />],
            [<div key="deli" />, <div key="camel" />, <div key="banana" />],
        ]);
        expect(flattened[0].key).toBe(reversed[2].key);
        expect(flattened[1].key).toBe(reversed[1].key);
        expect(flattened[2].key).toBe(reversed[0].key);
        expect(flattened[3].key).toBe(reversed[5].key);
        expect(flattened[4].key).toBe(reversed[4].key);
        expect(flattened[5].key).toBe(reversed[3].key);

        // null/undefined/bool are all omitted
        expect(React.Children.toArray([1, 'two', null, undefined, true])).toEqual([
            1,
            'two',
        ]);
    });

    it('should escape keys', () => {
        const zero = <div key="1" />;
        const one = <div key="1=::=2" />;
        const instance = (
            <div>
                {zero}
                {one}
            </div>
        );
        const mappedChildren = React.Children.map(
            instance.props.children,
            kid => kid,
        );
        expect(mappedChildren).toEqual([
            <div key=".$1" />,
            <div key=".$1=0=2=2=02" />,
        ]);
    });

    it("should throw on object", () => {
        // 'Objects are not valid as a React child (found: object with keys ' +
        // '{a, b}). If you meant to render a collection of children, use an ' +
        // 'array instead.',
        expect(function () {
            React.Children.forEach({ a: 1, b: 2 }, function () { }, null);
        }).toThrow("children: type is invalid.");
    });

    it("should throw on regex", () => {
        // Really, we care about dates (#4840) but those have nondeterministic
        // serialization (timezones) so let's test a regex instead:
        expect(function () {
            React.Children.forEach(/abc/, function () { }, null);
        }).toThrow("children: type is invalid.");
    });

    it("复杂的孩子转换", function () {
        function getString(nodes) {
            var str = [];
            for (var i = 0, node; (node = nodes[i++]);) {
                if (node.nodeType === 8 && node.nodeValue.indexOf("react-text") !== 0) {
                    continue;
                }
                str.push(node.nodeName.toLowerCase());
            }
            return str.join(" ");
        }
        var index = 0;
        var map = [
            <div>
                1111<p>ddd</p>
                <span>333</span>
                <Link />
            </div>,
            <div>
                <em>新的</em>
                <span>111</span>222<span>333</span>
                <b>444</b>
                <Link />
            </div>,
            <div>
                <span>33</span>
            </div>
        ];
        function Link() {
            return index == 1 ? <strong>ddd</strong> : <i>ddd</i>;
        }
        class App extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    aaa: "aaa"
                };
            }
            change(a) {
                this.setState({
                    aaa: a
                });
            }
            componentDidMount() {
            }
            componentWillUpdate() {
            }
            render() {
                return map[index++];
            }
        }
        var div = document.createElement("div")
        var s = ReactDOM.render(<App />, div);

        expect(getString(div.firstChild.childNodes)).toBe("#text p span strong");
        s.change(100);
        expect(getString(div.firstChild.childNodes)).toBe("em span #text span b i");
        s.change(100);
        expect(getString(div.firstChild.childNodes)).toBe("span");
    });

    it("对一个容器节点反复渲染组件或元素 ", () => {
        class Comp extends React.Component {
            render() {
                return <span>span in a component</span>;
            }
        }
        var div = document.createElement("div")
        function test(content) {
            React.render(content, div);
        }

        test(<Comp />);
        test(<div>just a div</div>);
        test(<Comp />);

        expect(div.firstChild.innerHTML).toEqual("span in a component");
    });
});