/**
* Copyright 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
describe('ReactNativeOperationHistoryDevtool', () => {
var React;
var ReactDebugTool;
var ReactDOM;
var ReactDOMComponentTree;
var ReactDOMFeatureFlags;
var ReactNativeOperationHistoryDevtool;
beforeEach(() => {
jest.resetModuleRegistry();
React = require('React');
ReactDebugTool = require('ReactDebugTool');
ReactDOM = require('ReactDOM');
ReactDOMComponentTree = require('ReactDOMComponentTree');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactNativeOperationHistoryDevtool = require('ReactNativeOperationHistoryDevtool');
ReactDebugTool.addDevtool(ReactNativeOperationHistoryDevtool);
});
afterEach(() => {
ReactDebugTool.removeDevtool(ReactNativeOperationHistoryDevtool);
});
function assertHistoryMatches(expectedHistory) {
var actualHistory = ReactNativeOperationHistoryDevtool.getHistory();
expect(actualHistory).toEqual(expectedHistory);
}
describe('mount', () => {
it('gets recorded for native roots', () => {
var node = document.createElement('div');
ReactDOM.render(
, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: ReactDOMFeatureFlags.useCreateElement ?
'DIV' :
'',
}]);
});
it('gets recorded for composite roots', () => {
function Foo() {
return ;
}
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: ReactDOMFeatureFlags.useCreateElement ?
'DIV' :
'',
}]);
});
it('gets recorded for composite roots that return null', () => {
function Foo() {
return null;
}
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: ReactDOMFeatureFlags.useCreateElement ?
'#comment' :
'',
}]);
});
});
describe('update styles', () => {
it('gets recorded during mount', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
if (ReactDOMFeatureFlags.useCreateElement) {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update styles',
payload: {
color: 'red',
backgroundColor: 'yellow',
},
}, {
instanceID: inst._debugID,
type: 'mount',
payload: 'DIV',
}]);
} else {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: '',
}]);
}
});
it('gets recorded during an update', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update styles',
payload: { color: 'red' },
}, {
instanceID: inst._debugID,
type: 'update styles',
payload: { color: 'blue', backgroundColor: 'yellow' },
}, {
instanceID: inst._debugID,
type: 'update styles',
payload: { color: '', backgroundColor: 'green' },
}, {
instanceID: inst._debugID,
type: 'update styles',
payload: { backgroundColor: '' },
}]);
});
it('gets ignored if the styles are shallowly equal', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update styles',
payload: {
color: 'red',
backgroundColor: 'yellow',
},
}]);
});
});
describe('update attribute', () => {
describe('simple attribute', () => {
it('gets recorded during mount', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
if (ReactDOMFeatureFlags.useCreateElement) {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 42 },
}, {
instanceID: inst._debugID,
type: 'mount',
payload: 'DIV',
}]);
} else {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: '',
}]);
}
});
it('gets recorded during an update', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'mad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 42 },
}, {
instanceID: inst._debugID,
type: 'remove attribute',
payload: 'className',
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 43 },
}]);
});
});
describe('attribute that gets removed with certain values', () => {
it('gets recorded as a removal during an update', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { disabled: true },
}, {
instanceID: inst._debugID,
type: 'remove attribute',
payload: 'disabled',
}]);
});
});
describe('custom attribute', () => {
it('gets recorded during mount', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
if (ReactDOMFeatureFlags.useCreateElement) {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-x': 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-y': 42 },
}, {
instanceID: inst._debugID,
type: 'mount',
payload: 'DIV',
}]);
} else {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: '',
}]);
}
});
it('gets recorded during an update', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-x': 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-x': 'mad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-y': 42 },
}, {
instanceID: inst._debugID,
type: 'remove attribute',
payload: 'data-x',
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { 'data-y': 43 },
}]);
});
});
describe('attribute on a web component', () => {
it('gets recorded during mount', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
if (ReactDOMFeatureFlags.useCreateElement) {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 42 },
}, {
instanceID: inst._debugID,
type: 'mount',
payload: 'MY-COMPONENT',
}]);
} else {
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
payload: '',
}]);
}
});
it('gets recorded during an update', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
ReactDOM.render(, node);
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'rad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { className: 'mad' },
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 42 },
}, {
instanceID: inst._debugID,
type: 'remove attribute',
payload: 'className',
}, {
instanceID: inst._debugID,
type: 'update attribute',
payload: { tabIndex: 43 },
}]);
});
});
});
describe('replace text', () => {
describe('text content', () => {
it('gets recorded during an update from text content', () => {
var node = document.createElement('div');
ReactDOM.render(Hi.
, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(Bye.
, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'replace text',
payload: 'Bye.',
}]);
});
it('gets recorded during an update from html', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(Bye.
, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'replace text',
payload: 'Bye.',
}]);
});
it('gets recorded during an update from children', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(Bye.
, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'remove child',
payload: {fromIndex: 0},
}, {
instanceID: inst._debugID,
type: 'remove child',
payload: {fromIndex: 1},
}, {
instanceID: inst._debugID,
type: 'replace text',
payload: 'Bye.',
}]);
});
it('gets ignored if new text is equal', () => {
var node = document.createElement('div');
ReactDOM.render(Hi.
, node);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(Hi.
, node);
assertHistoryMatches([]);
});
});
describe('text node', () => {
it('gets recorded during an update', () => {
var node = document.createElement('div');
ReactDOM.render({'Hi.'}{42}
, node);
var inst1 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[0]);
var inst2 = ReactDOMComponentTree.getInstanceFromNode(node.firstChild.childNodes[3]);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render({'Bye.'}{43}
, node);
assertHistoryMatches([{
instanceID: inst1._debugID,
type: 'replace text',
payload: 'Bye.',
}, {
instanceID: inst2._debugID,
type: 'replace text',
payload: '43',
}]);
});
it('gets ignored if new text is equal', () => {
var node = document.createElement('div');
ReactDOM.render({'Hi.'}{42}
, node);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render({'Hi.'}{42}
, node);
assertHistoryMatches([]);
});
});
});
describe('replace with', () => {
it('gets recorded when composite renders to a different type', () => {
var element;
function Foo() {
return element;
}
var node = document.createElement('div');
element = ;
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
element = ;
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'replace with',
payload: 'SPAN',
}]);
});
it('gets ignored if the type has not changed', () => {
var element;
function Foo() {
return element;
}
var node = document.createElement('div');
element = ;
ReactDOM.render(, node);
ReactNativeOperationHistoryDevtool.clearHistory();
element = ;
ReactDOM.render(, node);
assertHistoryMatches([]);
});
});
describe('replace children', () => {
it('gets recorded during an update from text content', () => {
var node = document.createElement('div');
ReactDOM.render(Hi.
, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(
,
node
);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'replace children',
payload: 'Bye.',
}]);
});
it('gets recorded during an update from html', () => {
var node = document.createElement('div');
ReactDOM.render(
,
node
);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(
,
node
);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'replace children',
payload: 'Bye.',
}]);
});
it('gets recorded during an update from children', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(
,
node
);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'remove child',
payload: {fromIndex: 0},
}, {
instanceID: inst._debugID,
type: 'remove child',
payload: {fromIndex: 1},
}, {
instanceID: inst._debugID,
type: 'replace children',
payload: 'Hi.',
}]);
});
it('gets ignored if new html is equal', () => {
var node = document.createElement('div');
ReactDOM.render(
,
node
);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(
,
node
);
assertHistoryMatches([]);
});
});
describe('insert child', () => {
it('gets reported when a child is inserted', () => {
var node = document.createElement('div');
ReactDOM.render(
, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'insert child',
payload: {toIndex: 1, content: 'P'},
}]);
});
});
describe('move child', () => {
it('gets reported when a child is inserted', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'move child',
payload: {fromIndex: 0, toIndex: 1},
}]);
});
});
describe('remove child', () => {
it('gets reported when a child is removed', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
ReactNativeOperationHistoryDevtool.clearHistory();
ReactDOM.render(
, node);
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'remove child',
payload: {fromIndex: 1},
}]);
});
});
});