mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
922 lines
23 KiB
JavaScript
922 lines
23 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @emails react-core
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const React = require('react');
|
|
const ReactDOM = require('react-dom');
|
|
|
|
const stripEmptyValues = function(obj) {
|
|
const ret = {};
|
|
for (const name in obj) {
|
|
if (!obj.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
if (obj[name] !== null && obj[name] !== undefined) {
|
|
ret[name] = obj[name];
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
let idCounter = 123;
|
|
|
|
/**
|
|
* Contains internal static internal state in order to test that updates to
|
|
* existing children won't reinitialize components, when moving children -
|
|
* reusing existing DOM/memory resources.
|
|
*/
|
|
class StatusDisplay extends React.Component {
|
|
state = {internalState: idCounter++};
|
|
|
|
getStatus() {
|
|
return this.props.status;
|
|
}
|
|
|
|
getInternalState() {
|
|
return this.state.internalState;
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.props.onFlush();
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
this.props.onFlush();
|
|
}
|
|
|
|
render() {
|
|
return <div>{this.props.contentKey}</div>;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Displays friends statuses.
|
|
*/
|
|
class FriendsStatusDisplay extends React.Component {
|
|
/**
|
|
* Gets the order directly from each rendered child's `index` field.
|
|
* Refs are not maintained in the rendered order, and neither is
|
|
* `this._renderedChildren` (surprisingly).
|
|
*/
|
|
getOriginalKeys() {
|
|
const originalKeys = [];
|
|
for (const key in this.props.usernameToStatus) {
|
|
if (this.props.usernameToStatus[key]) {
|
|
originalKeys.push(key);
|
|
}
|
|
}
|
|
return originalKeys;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the rendered children in a nice format for comparing to the input
|
|
* `this.props.usernameToStatus`.
|
|
*/
|
|
getStatusDisplays() {
|
|
const res = {};
|
|
const originalKeys = this.getOriginalKeys();
|
|
for (let i = 0; i < originalKeys.length; i++) {
|
|
const key = originalKeys[i];
|
|
res[key] = this.refs[key];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Verifies that by the time a child is flushed, the refs that appeared
|
|
* earlier have already been resolved.
|
|
* TODO: This assumption will likely break with incremental reconciler
|
|
* but our internal layer API depends on this assumption. We need to change
|
|
* it to be more declarative before making ref resolution indeterministic.
|
|
*/
|
|
verifyPreviousRefsResolved(flushedKey) {
|
|
const originalKeys = this.getOriginalKeys();
|
|
for (let i = 0; i < originalKeys.length; i++) {
|
|
const key = originalKeys[i];
|
|
if (key === flushedKey) {
|
|
// We are only interested in children up to the current key.
|
|
return;
|
|
}
|
|
expect(this.refs[key]).toBeTruthy();
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const children = [];
|
|
for (const key in this.props.usernameToStatus) {
|
|
const status = this.props.usernameToStatus[key];
|
|
children.push(
|
|
!status ? null : (
|
|
<StatusDisplay
|
|
key={key}
|
|
ref={key}
|
|
contentKey={key}
|
|
onFlush={this.verifyPreviousRefsResolved.bind(this, key)}
|
|
status={status}
|
|
/>
|
|
),
|
|
);
|
|
}
|
|
const childrenToRender = this.props.prepareChildren(children);
|
|
return <div>{childrenToRender}</div>;
|
|
}
|
|
}
|
|
|
|
function getInternalStateByUserName(statusDisplays) {
|
|
return Object.keys(statusDisplays).reduce((acc, key) => {
|
|
acc[key] = statusDisplays[key].getInternalState();
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
/**
|
|
* Verifies that the rendered `StatusDisplay` instances match the `props` that
|
|
* were responsible for allocating them. Checks the content of the user's status
|
|
* message as well as the order of them.
|
|
*/
|
|
function verifyStatuses(statusDisplays, props) {
|
|
const nonEmptyStatusDisplays = stripEmptyValues(statusDisplays);
|
|
const nonEmptyStatusProps = stripEmptyValues(props.usernameToStatus);
|
|
let username;
|
|
expect(Object.keys(nonEmptyStatusDisplays).length).toEqual(
|
|
Object.keys(nonEmptyStatusProps).length,
|
|
);
|
|
for (username in nonEmptyStatusDisplays) {
|
|
if (!nonEmptyStatusDisplays.hasOwnProperty(username)) {
|
|
continue;
|
|
}
|
|
expect(nonEmptyStatusDisplays[username].getStatus()).toEqual(
|
|
nonEmptyStatusProps[username],
|
|
);
|
|
}
|
|
|
|
// now go the other way to make sure we got them all.
|
|
for (username in nonEmptyStatusProps) {
|
|
if (!nonEmptyStatusProps.hasOwnProperty(username)) {
|
|
continue;
|
|
}
|
|
expect(nonEmptyStatusDisplays[username].getStatus()).toEqual(
|
|
nonEmptyStatusProps[username],
|
|
);
|
|
}
|
|
|
|
expect(Object.keys(nonEmptyStatusDisplays)).toEqual(
|
|
Object.keys(nonEmptyStatusProps),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* For all statusDisplays that existed in the previous iteration of the
|
|
* sequence, verify that the state has been preserved. `StatusDisplay` contains
|
|
* a unique number that allows us to track internal state across ordering
|
|
* movements.
|
|
*/
|
|
function verifyStatesPreserved(lastInternalStates, statusDisplays) {
|
|
let key;
|
|
for (key in statusDisplays) {
|
|
if (!statusDisplays.hasOwnProperty(key)) {
|
|
continue;
|
|
}
|
|
if (lastInternalStates[key]) {
|
|
expect(lastInternalStates[key]).toEqual(
|
|
statusDisplays[key].getInternalState(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verifies that the internal representation of a set of `renderedChildren`
|
|
* accurately reflects what is in the DOM.
|
|
*/
|
|
function verifyDomOrderingAccurate(outerContainer, statusDisplays) {
|
|
const containerNode = outerContainer.firstChild;
|
|
const statusDisplayNodes = containerNode.childNodes;
|
|
const orderedDomKeys = [];
|
|
for (let i = 0; i < statusDisplayNodes.length; i++) {
|
|
const contentKey = statusDisplayNodes[i].textContent;
|
|
orderedDomKeys.push(contentKey);
|
|
}
|
|
|
|
const orderedLogicalKeys = [];
|
|
let username;
|
|
for (username in statusDisplays) {
|
|
if (!statusDisplays.hasOwnProperty(username)) {
|
|
continue;
|
|
}
|
|
const statusDisplay = statusDisplays[username];
|
|
orderedLogicalKeys.push(statusDisplay.props.contentKey);
|
|
}
|
|
expect(orderedDomKeys).toEqual(orderedLogicalKeys);
|
|
}
|
|
|
|
function testPropsSequenceWithPreparedChildren(sequence, prepareChildren) {
|
|
const container = document.createElement('div');
|
|
const parentInstance = ReactDOM.render(
|
|
<FriendsStatusDisplay {...sequence[0]} prepareChildren={prepareChildren} />,
|
|
container,
|
|
);
|
|
let statusDisplays = parentInstance.getStatusDisplays();
|
|
let lastInternalStates = getInternalStateByUserName(statusDisplays);
|
|
verifyStatuses(statusDisplays, sequence[0]);
|
|
|
|
for (let i = 1; i < sequence.length; i++) {
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...sequence[i]}
|
|
prepareChildren={prepareChildren}
|
|
/>,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
verifyStatuses(statusDisplays, sequence[i]);
|
|
verifyStatesPreserved(lastInternalStates, statusDisplays);
|
|
verifyDomOrderingAccurate(container, statusDisplays);
|
|
|
|
lastInternalStates = getInternalStateByUserName(statusDisplays);
|
|
}
|
|
}
|
|
|
|
function prepareChildrenArray(childrenArray) {
|
|
return childrenArray;
|
|
}
|
|
|
|
function prepareChildrenLegacyIterable(childrenArray) {
|
|
return {
|
|
'@@iterator': function*() {
|
|
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
for (const child of childrenArray) {
|
|
yield child;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function prepareChildrenModernIterable(childrenArray) {
|
|
return {
|
|
[Symbol.iterator]: function*() {
|
|
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
for (const child of childrenArray) {
|
|
yield child;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function testPropsSequence(sequence) {
|
|
testPropsSequenceWithPreparedChildren(sequence, prepareChildrenArray);
|
|
testPropsSequenceWithPreparedChildren(
|
|
sequence,
|
|
prepareChildrenLegacyIterable,
|
|
);
|
|
testPropsSequenceWithPreparedChildren(
|
|
sequence,
|
|
prepareChildrenModernIterable,
|
|
);
|
|
}
|
|
|
|
describe('ReactMultiChildReconcile', () => {
|
|
beforeEach(() => {
|
|
jest.resetModules();
|
|
});
|
|
|
|
it('should reset internal state if removed then readded in an array', () => {
|
|
// Test basics.
|
|
const props = {
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
},
|
|
};
|
|
|
|
const container = document.createElement('div');
|
|
const parentInstance = ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenArray}
|
|
/>,
|
|
container,
|
|
);
|
|
let statusDisplays = parentInstance.getStatusDisplays();
|
|
const startingInternalState = statusDisplays.jcw.getInternalState();
|
|
|
|
// Now remove the child.
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay prepareChildren={prepareChildrenArray} />,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeFalsy();
|
|
|
|
// Now reset the props that cause there to be a child
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenArray}
|
|
/>,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeTruthy();
|
|
expect(statusDisplays.jcw.getInternalState()).not.toBe(
|
|
startingInternalState,
|
|
);
|
|
});
|
|
|
|
it('should reset internal state if removed then readded in a legacy iterable', () => {
|
|
// Test basics.
|
|
const props = {
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
},
|
|
};
|
|
|
|
const container = document.createElement('div');
|
|
const parentInstance = ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenLegacyIterable}
|
|
/>,
|
|
container,
|
|
);
|
|
let statusDisplays = parentInstance.getStatusDisplays();
|
|
const startingInternalState = statusDisplays.jcw.getInternalState();
|
|
|
|
// Now remove the child.
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay prepareChildren={prepareChildrenLegacyIterable} />,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeFalsy();
|
|
|
|
// Now reset the props that cause there to be a child
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenLegacyIterable}
|
|
/>,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeTruthy();
|
|
expect(statusDisplays.jcw.getInternalState()).not.toBe(
|
|
startingInternalState,
|
|
);
|
|
});
|
|
|
|
it('should reset internal state if removed then readded in a modern iterable', () => {
|
|
// Test basics.
|
|
const props = {
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
},
|
|
};
|
|
|
|
const container = document.createElement('div');
|
|
const parentInstance = ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenModernIterable}
|
|
/>,
|
|
container,
|
|
);
|
|
let statusDisplays = parentInstance.getStatusDisplays();
|
|
const startingInternalState = statusDisplays.jcw.getInternalState();
|
|
|
|
// Now remove the child.
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay prepareChildren={prepareChildrenModernIterable} />,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeFalsy();
|
|
|
|
// Now reset the props that cause there to be a child
|
|
ReactDOM.render(
|
|
<FriendsStatusDisplay
|
|
{...props}
|
|
prepareChildren={prepareChildrenModernIterable}
|
|
/>,
|
|
container,
|
|
);
|
|
statusDisplays = parentInstance.getStatusDisplays();
|
|
expect(statusDisplays.jcw).toBeTruthy();
|
|
expect(statusDisplays.jcw.getInternalState()).not.toBe(
|
|
startingInternalState,
|
|
);
|
|
});
|
|
|
|
it('should create unique identity', () => {
|
|
// Test basics.
|
|
const usernameToStatus = {
|
|
jcw: 'jcwStatus',
|
|
awalke: 'awalkeStatus',
|
|
bob: 'bobStatus',
|
|
};
|
|
|
|
testPropsSequence([{usernameToStatus: usernameToStatus}]);
|
|
});
|
|
|
|
it('should preserve order if children order has not changed', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should transition from zero to one children correctly', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{usernameToStatus: {}},
|
|
{
|
|
usernameToStatus: {
|
|
first: 'firstStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should transition from one to zero children correctly', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
first: 'firstStatus',
|
|
},
|
|
},
|
|
{usernameToStatus: {}},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should transition from one child to null children', () => {
|
|
testPropsSequence([
|
|
{
|
|
usernameToStatus: {
|
|
first: 'firstStatus',
|
|
},
|
|
},
|
|
{},
|
|
]);
|
|
});
|
|
|
|
it('should transition from null children to one child', () => {
|
|
testPropsSequence([
|
|
{},
|
|
{
|
|
usernameToStatus: {
|
|
first: 'firstStatus',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should transition from zero children to null children', () => {
|
|
testPropsSequence([
|
|
{
|
|
usernameToStatus: {},
|
|
},
|
|
{},
|
|
]);
|
|
});
|
|
|
|
it('should transition from null children to zero children', () => {
|
|
testPropsSequence([
|
|
{},
|
|
{
|
|
usernameToStatus: {},
|
|
},
|
|
]);
|
|
});
|
|
|
|
/**
|
|
* `FriendsStatusDisplay` renders nulls as empty children (it's a convention
|
|
* of `FriendsStatusDisplay`, nothing related to React or these test cases.
|
|
*/
|
|
it('should remove nulled out children at the beginning', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: null,
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should remove nulled out children at the end', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
jordanjcw: null,
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should reverse the order of two children', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userTwo: 'userTwoStatus',
|
|
userOne: 'userOneStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should reverse the order of more than two children', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userThree: 'userThreeStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userOne: 'userOneStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should cycle order correctly', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
// Full circle!
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should cycle order correctly in the other direction', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
userOne: 'userOneStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
// Full circle!
|
|
userOne: 'userOneStatus',
|
|
userTwo: 'userTwoStatus',
|
|
userThree: 'userThreeStatus',
|
|
userFour: 'userFourStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should remove nulled out children and ignore new null children', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
jcw: null,
|
|
another: null,
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should remove nulled out children and reorder remaining', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
john: 'johnStatus', // john will go away
|
|
joe: 'joeStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jordanjcw: 'jordanjcwStatus',
|
|
joe: 'joeStatus',
|
|
jcw: 'jcwStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should append children to the end', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
jordanjcwnew: 'jordanjcwnewStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should append multiple children to the end', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
jordanjcwnew: 'jordanjcwnewStatus',
|
|
jordanjcwnew2: 'jordanjcwnewStatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should prepend children to the beginning', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
newUsername: 'newUsernameStatus',
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should prepend multiple children to the beginning', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
newNewUsername: 'newNewUsernameStatus',
|
|
newUsername: 'newUsernameStatus',
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should not prepend an empty child to the beginning', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
emptyUsername: null,
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should not append an empty child to the end', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
emptyUsername: null,
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should not insert empty children in the middle', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
skipOverMe: null,
|
|
skipOverMeToo: null,
|
|
definitelySkipOverMe: null,
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should insert one new child in the middle', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
insertThis: 'insertThisStatus',
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should insert multiple new truthy children in the middle', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
insertThis: 'insertThisStatus',
|
|
insertThisToo: 'insertThisTooStatus',
|
|
definitelyInsertThisToo: 'definitelyInsertThisTooStatus',
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
|
|
it('should insert non-empty children in middle where nulls were', () => {
|
|
const PROPS_SEQUENCE = [
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwStatus',
|
|
insertThis: null,
|
|
insertThisToo: null,
|
|
definitelyInsertThisToo: null,
|
|
jordanjcw: 'jordanjcwStatus',
|
|
},
|
|
},
|
|
{
|
|
usernameToStatus: {
|
|
jcw: 'jcwstatus2',
|
|
insertThis: 'insertThisStatus',
|
|
insertThisToo: 'insertThisTooStatus',
|
|
definitelyInsertThisToo: 'definitelyInsertThisTooStatus',
|
|
jordanjcw: 'jordanjcwstatus2',
|
|
},
|
|
},
|
|
];
|
|
testPropsSequence(PROPS_SEQUENCE);
|
|
});
|
|
});
|