Delete the old ReactPerf

This commit is contained in:
Dan Abramov committed 2016-05-03 14:21:28 +01:00
1 parent 222f5087fe
commit 5f8f1f6c16
19 files changed
+15 -1095

No files matched your search

-361
View File
@@ -1,361 +0,0 @@
/**
* Copyright 2013-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.
*
* @providesModule ReactDefaultPerf
*/
'use strict';
var DOMProperty = require('DOMProperty');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');
var performanceNow = require('performanceNow');
var warning = require('warning');
function roundFloat(val) {
return Math.floor(val * 100) / 100;
}
function addValue(obj, key, val) {
obj[key] = (obj[key] || 0) + val;
}
// Composite/text components don't have any built-in ID: we have to make our own
var compositeIDMap;
var compositeIDCounter = 17000;
function getIDOfComposite(inst) {
if (!compositeIDMap) {
compositeIDMap = new WeakMap();
}
if (compositeIDMap.has(inst)) {
return compositeIDMap.get(inst);
} else {
var id = compositeIDCounter++;
compositeIDMap.set(inst, id);
return id;
}
}
function getID(inst) {
if (inst.hasOwnProperty('_rootNodeID')) {
return inst._rootNodeID;
} else {
return getIDOfComposite(inst);
}
}
function stripComplexValues(key, value) {
if (typeof value !== 'object' || Array.isArray(value) || value == null) {
return value;
}
var prototype = Object.getPrototypeOf(value);
if (!prototype || prototype === Object.prototype) {
return value;
}
return '<not serializable>';
}
// This implementation of ReactPerf is going away some time mid 15.x.
// While we plan to keep most of the API, the actual format of measurements
// will change dramatically. To signal this, we wrap them into an opaque-ish
// object to discourage reaching into it until the API stabilizes.
function wrapLegacyMeasurements(measurements) {
return { __unstable_this_format_will_change: measurements };
}
function unwrapLegacyMeasurements(measurements) {
return measurements && measurements.__unstable_this_format_will_change || measurements;
}
var warnedAboutPrintDOM = false;
var warnedAboutGetMeasurementsSummaryMap = false;
var ReactDefaultPerf = {
_allMeasurements: [], // last item in the list is the current one
_mountStack: [0],
_compositeStack: [],
_injected: false,
start: function() {
if (!ReactDefaultPerf._injected) {
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
}
ReactDefaultPerf._allMeasurements.length = 0;
ReactPerf.enableMeasure = true;
},
stop: function() {
ReactPerf.enableMeasure = false;
},
getLastMeasurements: function() {
return wrapLegacyMeasurements(ReactDefaultPerf._allMeasurements);
},
printExclusive: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Component class name': item.componentName,
'Total inclusive time (ms)': roundFloat(item.inclusive),
'Exclusive mount time (ms)': roundFloat(item.exclusive),
'Exclusive render time (ms)': roundFloat(item.render),
'Mount time per instance (ms)': roundFloat(item.exclusive / item.count),
'Render time per instance (ms)': roundFloat(item.render / item.count),
'Instances': item.count,
};
}));
// TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct
// number.
},
printInclusive: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Inclusive time (ms)': roundFloat(item.time),
'Instances': item.count,
};
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
getMeasurementsSummaryMap: function(measurements) {
warning(
warnedAboutGetMeasurementsSummaryMap,
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.'
);
warnedAboutGetMeasurementsSummaryMap = true;
return ReactDefaultPerf.getWasted(measurements);
},
getWasted: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
measurements,
true
);
return summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Wasted time (ms)': item.time,
'Instances': item.count,
};
});
},
printWasted: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
console.table(ReactDefaultPerf.getWasted(measurements));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
printDOM: function(measurements) {
warning(
warnedAboutPrintDOM,
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.'
);
warnedAboutPrintDOM = true;
return ReactDefaultPerf.printOperations(measurements);
},
printOperations: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
console.table(summary.map(function(item) {
var result = {};
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
result.type = item.type;
result.args = JSON.stringify(item.args, stripComplexValues);
return result;
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
_recordWrite: function(id, fnName, totalTime, args) {
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
var entry =
ReactDefaultPerf
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
var writes = entry.writes;
writes[id] = writes[id] || [];
writes[id].push({
type: fnName,
time: totalTime,
args: args,
});
},
measure: function(moduleName, fnName, func) {
return function(...args) {
var totalTime;
var rv;
var start;
var entry = ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
];
if (fnName === '_renderNewRootComponent' ||
fnName === 'flushBatchedUpdates') {
// A "measurement" is a set of metrics recorded for each flush. We want
// to group the metrics for a given flush together so we can look at the
// components that rendered and the DOM operations that actually
// happened to determine the amount of "wasted work" performed.
ReactDefaultPerf._allMeasurements.push(entry = {
exclusive: {},
inclusive: {},
render: {},
counts: {},
writes: {},
displayNames: {},
hierarchy: {},
totalTime: 0,
created: {},
});
start = performanceNow();
rv = func.apply(this, args);
entry.totalTime = performanceNow() - start;
return rv;
} else if (fnName === '_mountImageIntoNode' ||
moduleName === 'ReactDOMIDOperations' ||
moduleName === 'CSSPropertyOperations' ||
moduleName === 'DOMChildrenOperations' ||
moduleName === 'DOMPropertyOperations' ||
moduleName === 'ReactComponentBrowserEnvironment') {
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
if (fnName === '_mountImageIntoNode') {
ReactDefaultPerf._recordWrite('', fnName, totalTime, args[0]);
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
// special format
args[1].forEach(function(update) {
var writeArgs = {};
if (update.fromIndex !== null) {
writeArgs.fromIndex = update.fromIndex;
}
if (update.toIndex !== null) {
writeArgs.toIndex = update.toIndex;
}
if (update.content !== null) {
writeArgs.content = update.content;
}
ReactDefaultPerf._recordWrite(
args[0]._rootNodeID,
update.type,
totalTime,
writeArgs
);
});
} else {
// basic format
var id = args[0];
if (moduleName === 'EventPluginHub') {
id = id._rootNodeID;
} else if (fnName === 'replaceNodeWithMarkup') {
// Old node is already unmounted; can't get its instance
id = ReactDOMComponentTree.getInstanceFromNode(args[1].node)._rootNodeID;
} else if (fnName === 'replaceDelimitedText') {
id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
} else if (typeof id === 'object') {
id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
}
ReactDefaultPerf._recordWrite(
id,
fnName,
totalTime,
Array.prototype.slice.call(args, 1)
);
}
return rv;
} else if (moduleName === 'ReactCompositeComponent' && (
fnName === 'mountComponent' ||
fnName === 'updateComponent' || // TODO: receiveComponent()?
fnName === '_renderValidatedComponent')) {
if (this._currentElement.type === ReactMount.TopLevelWrapper) {
return func.apply(this, args);
}
var rootNodeID = getIDOfComposite(this);
var isRender = fnName === '_renderValidatedComponent';
var isMount = fnName === 'mountComponent';
var mountStack = ReactDefaultPerf._mountStack;
if (isRender) {
addValue(entry.counts, rootNodeID, 1);
} else if (isMount) {
entry.created[rootNodeID] = true;
mountStack.push(0);
}
ReactDefaultPerf._compositeStack.push(rootNodeID);
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
ReactDefaultPerf._compositeStack.pop();
if (isRender) {
addValue(entry.render, rootNodeID, totalTime);
} else if (isMount) {
var subMountTime = mountStack.pop();
mountStack[mountStack.length - 1] += totalTime;
addValue(entry.exclusive, rootNodeID, totalTime - subMountTime);
addValue(entry.inclusive, rootNodeID, totalTime);
} else {
addValue(entry.inclusive, rootNodeID, totalTime);
}
entry.displayNames[rootNodeID] = {
current: this.getName(),
owner: this._currentElement._owner ?
this._currentElement._owner.getName() :
'<root>',
};
return rv;
} else if (
(moduleName === 'ReactDOMComponent' ||
moduleName === 'ReactDOMTextComponent') &&
(fnName === 'mountComponent' ||
fnName === 'receiveComponent')) {
rv = func.apply(this, args);
entry.hierarchy[getID(this)] =
ReactDefaultPerf._compositeStack.slice();
return rv;
} else {
return func.apply(this, args);
}
};
},
};
module.exports = ReactDefaultPerf;
-214
View File
@@ -1,214 +0,0 @@
/**
* Copyright 2013-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.
*
* @providesModule ReactDefaultPerfAnalysis
*/
'use strict';
// Don't try to save users less than 1.2ms (a number I made up)
var DONT_CARE_THRESHOLD = 1.2;
var DOM_OPERATION_TYPES = {
'_mountImageIntoNode': 'set innerHTML',
INSERT_MARKUP: 'set innerHTML',
MOVE_EXISTING: 'move',
REMOVE_NODE: 'remove',
SET_MARKUP: 'set innerHTML',
TEXT_CONTENT: 'set textContent',
'setValueForProperty': 'update attribute',
'setValueForAttribute': 'update attribute',
'deleteValueForProperty': 'remove attribute',
'setValueForStyles': 'update styles',
'replaceNodeWithMarkup': 'replace',
'replaceDelimitedText': 'replace',
};
function getTotalTime(measurements) {
// TODO: return number of DOM ops? could be misleading.
// TODO: measure dropped frames after reconcile?
// TODO: log total time of each reconcile and the top-level component
// class that triggered it.
var totalTime = 0;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
totalTime += measurement.totalTime;
}
return totalTime;
}
function getDOMSummary(measurements) {
var items = [];
measurements.forEach(function(measurement) {
Object.keys(measurement.writes).forEach(function(id) {
measurement.writes[id].forEach(function(write) {
items.push({
id: id,
type: DOM_OPERATION_TYPES[write.type] || write.type,
args: write.args,
});
});
});
});
return items;
}
function getExclusiveSummary(measurements) {
var candidates = {};
var displayName;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
);
for (var id in allIDs) {
displayName = measurement.displayNames[id].current;
candidates[displayName] = candidates[displayName] || {
componentName: displayName,
inclusive: 0,
exclusive: 0,
render: 0,
count: 0,
};
if (measurement.render[id]) {
candidates[displayName].render += measurement.render[id];
}
if (measurement.exclusive[id]) {
candidates[displayName].exclusive += measurement.exclusive[id];
}
if (measurement.inclusive[id]) {
candidates[displayName].inclusive += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[displayName].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (displayName in candidates) {
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
arr.push(candidates[displayName]);
}
}
arr.sort(function(a, b) {
return b.exclusive - a.exclusive;
});
return arr;
}
function getInclusiveSummary(measurements, onlyClean) {
var candidates = {};
var inclusiveKey;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
);
var cleanComponents;
if (onlyClean) {
cleanComponents = getUnchangedComponents(measurement);
}
for (var id in allIDs) {
if (onlyClean && !cleanComponents[id]) {
continue;
}
var displayName = measurement.displayNames[id];
// Inclusive time is not useful for many components without knowing where
// they are instantiated. So we aggregate inclusive time with both the
// owner and current displayName as the key.
inclusiveKey = displayName.owner + ' > ' + displayName.current;
candidates[inclusiveKey] = candidates[inclusiveKey] || {
componentName: inclusiveKey,
time: 0,
count: 0,
};
if (measurement.inclusive[id]) {
candidates[inclusiveKey].time += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[inclusiveKey].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (inclusiveKey in candidates) {
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
arr.push(candidates[inclusiveKey]);
}
}
arr.sort(function(a, b) {
return b.time - a.time;
});
return arr;
}
function getUnchangedComponents(measurement) {
// For a given reconcile, look at which components did not actually
// render anything to the DOM and return a mapping of their ID to
// the amount of time it took to render the entire subtree.
var cleanComponents = {};
var writes = measurement.writes;
var hierarchy = measurement.hierarchy;
var dirtyComposites = {};
Object.keys(writes).forEach(function(id) {
writes[id].forEach(function(write) {
// Root mounting (innerHTML set) is recorded with an ID of ''
if (id !== '' && hierarchy.hasOwnProperty(id)) {
hierarchy[id].forEach((c) => dirtyComposites[c] = true);
}
});
});
var allIDs = Object.assign({}, measurement.exclusive, measurement.inclusive);
for (var id in allIDs) {
var isDirty = false;
// See if any of the DOM operations applied to this component's subtree.
if (dirtyComposites[id]) {
isDirty = true;
}
// check if component newly created
if (measurement.created[id]) {
isDirty = true;
}
if (!isDirty && measurement.counts[id] > 0) {
cleanComponents[id] = true;
}
}
return cleanComponents;
}
var ReactDefaultPerfAnalysis = {
getExclusiveSummary: getExclusiveSummary,
getInclusiveSummary: getInclusiveSummary,
getDOMSummary: getDOMSummary,
getTotalTime: getTotalTime,
};
module.exports = ReactDefaultPerfAnalysis;
-100
View File
@@ -1,100 +0,0 @@
/**
* Copyright 2013-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.
*
* @providesModule ReactPerf
*/
'use strict';
/**
* ReactPerf is a general AOP system designed to measure performance. This
* module only has the hooks: see ReactDefaultPerf for the analysis tool.
* See also https://facebook.github.io/react/docs/perf.html
*/
var ReactPerf = {
/**
* Boolean to enable/disable measurement. Set to false by default to prevent
* accidental logging and perf loss.
*/
enableMeasure: false,
/**
* Holds onto the measure function in use. By default, don't measure
* anything, but we'll override this if we inject a measure function.
*/
storedMeasure: _noMeasure,
/**
* @param {object} object
* @param {string} objectName
* @param {object<string>} methodNames
*/
measureMethods: function(object, objectName, methodNames) {
if (__DEV__) {
for (var key in methodNames) {
if (!methodNames.hasOwnProperty(key)) {
continue;
}
object[key] = ReactPerf.measure(
objectName,
methodNames[key],
object[key]
);
}
}
},
/**
* Use this to wrap methods you want to measure. Zero overhead in production.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
measure: function(objName, fnName, func) {
if (__DEV__) {
var measuredFunc = null;
var wrapper = function() {
if (ReactPerf.enableMeasure) {
if (!measuredFunc) {
measuredFunc = ReactPerf.storedMeasure(objName, fnName, func);
}
return measuredFunc.apply(this, arguments);
}
return func.apply(this, arguments);
};
wrapper.displayName = objName + '_' + fnName;
return wrapper;
}
return func;
},
injection: {
/**
* @param {function} measure
*/
injectMeasure: function(measure) {
ReactPerf.storedMeasure = measure;
},
},
};
/**
* Simply passes through the measured function, without measuring it.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
function _noMeasure(objName, fnName, func) {
return func;
}
module.exports = ReactPerf;
-299
View File
@@ -1,299 +0,0 @@
/**
* Copyright 2013-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('ReactDefaultPerf', function() {
var React;
var ReactDOM;
var ReactDOMFeatureFlags;
var ReactDefaultPerf;
var ReactTestUtils;
var ReactDefaultPerfAnalysis;
var App;
var Box;
var Div;
beforeEach(function() {
var now = 0;
jest.setMock('fbjs/lib/performanceNow', function() {
return now++;
});
if (typeof console.table !== 'function') {
console.table = () => {};
console.table.isFake = true;
}
React = require('React');
ReactDOM = require('ReactDOM');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactDefaultPerf = require('ReactDefaultPerf');
ReactTestUtils = require('ReactTestUtils');
ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
App = React.createClass({
render: function() {
return <div><Box /><Box flip={this.props.flipSecond} /></div>;
},
});
Box = React.createClass({
render: function() {
return <div key={!!this.props.flip}><input /></div>;
},
});
// ReactPerf only measures composites, so we put everything in one.
Div = React.createClass({
render: function() {
return <div {...this.props} />;
},
});
});
afterEach(function() {
if (console.table.isFake) {
delete console.table;
}
});
function measure(fn) {
ReactDefaultPerf.start();
fn();
ReactDefaultPerf.stop();
return ReactDefaultPerf.getLastMeasurements().__unstable_this_format_will_change;
}
it('should count no-op update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<App />, container);
var measurements = measure(() => {
ReactDOM.render(<App />, container);
});
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(2);
/*eslint-disable dot-notation */
expect(summary[0]['Owner > component']).toBe('<root> > App');
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
expect(summary[0]['Instances']).toBe(1);
expect(summary[1]['Owner > component']).toBe('App > Box');
expect(summary[1]['Wasted time (ms)']).not.toBe(0);
expect(summary[1]['Instances']).toBe(2);
/*eslint-enable dot-notation */
});
it('should count no-op update in child as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<App />, container);
// Here, we add a Box -- two of the <Box /> updates are wasted time (but the
// addition of the third is not)
var measurements = measure(() => {
ReactDOM.render(<App flipSecond={true} />, container);
});
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(1);
/*eslint-disable dot-notation */
expect(summary[0]['Owner > component']).toBe('App > Box');
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
expect(summary[0]['Instances']).toBe(1);
/*eslint-enable dot-notation */
});
function expectNoWaste(fn) {
var measurements = measure(fn);
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary).toEqual([]);
}
it('should not count initial render as waste', function() {
expectNoWaste(() => {
ReactTestUtils.renderIntoDocument(<App />);
});
});
it('should not count unmount as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>hello</Div>, container);
expectNoWaste(() => {
ReactDOM.unmountComponentAtNode(container);
});
});
it('should not count content update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>hello</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>hello world</Div>, container);
});
});
it('should not count child addition as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><span /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><span /><span /></Div>, container);
});
});
it('should not count child removal as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><span /><span /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><span /></Div>, container);
});
});
it('should not count property update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div className="yellow">hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div className="blue">hey</Div>, container);
});
});
it('should not count style update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div style={{color: 'yellow'}}>hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div style={{color: 'blue'}}>hey</Div>, container);
});
});
it('should not count property removal as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div className="yellow">hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
});
it('should not count raw HTML update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(
<Div dangerouslySetInnerHTML={{__html: 'me'}} />,
container
);
expectNoWaste(() => {
ReactDOM.render(
<Div dangerouslySetInnerHTML={{__html: 'you'}} />,
container
);
});
});
it('should not count child reordering as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><div key="A" /><div key="B" /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><div key="B" /><div key="A" /></Div>, container);
});
});
it('should not count text update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>{'hello'}{'world'}</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>{'hello'}{'friend'}</Div>, container);
});
});
it('putListener should not be instrumented', function() {
var container = document.createElement('div');
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
var measurements = measure(() => {
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
});
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
expect(summary).toEqual([]);
});
it('deleteListener should not be instrumented', function() {
var container = document.createElement('div');
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
var measurements = measure(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
expect(summary).toEqual([]);
});
it('should not fail on input change events', function() {
var container = document.createElement('div');
var onChange = () => {};
var input = ReactDOM.render(
<input checked={true} onChange={onChange} />,
container
);
expectNoWaste(() => {
ReactTestUtils.Simulate.change(input);
});
});
it('should print a table after calling printOperations', function() {
var container = document.createElement('div');
var measurements = measure(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
spyOn(console, 'table');
ReactDefaultPerf.printOperations(measurements);
expect(console.table.calls.length).toBe(1);
expect(console.table.argsForCall[0][0]).toEqual([{
'data-reactid': '',
type: 'set innerHTML',
args: ReactDOMFeatureFlags.useCreateElement ?
'{"node":"<not serializable>","children":[],"html":null,"text":null}' :
'"<div data-reactroot=\\"\\" data-reactid=\\"1\\">hey</div>"',
}]);
});
it('warns once when using getMeasurementsSummaryMap', function() {
var measurements = measure(() => {});
spyOn(console, 'error');
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.'
);
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
expect(console.error.calls.length).toBe(1);
});
it('warns once when using printDOM', function() {
var measurements = measure(() => {});
spyOn(console, 'error');
ReactDefaultPerf.printDOM(measurements);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.'
);
ReactDefaultPerf.printDOM(measurements);
expect(console.error.calls.length).toBe(1);
});
});