packages/core/__tests__/ReactChildren-test.js
"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");
});
});