Merge pull request #4987 from spicyj/perf
Add unit test to ensure DOM mutations are instrumented
This commit is contained in:
commit
c512603a8c
|
@ -16,6 +16,7 @@ var EventConstants = require('EventConstants');
|
|||
var EventPluginHub = require('EventPluginHub');
|
||||
var EventPluginRegistry = require('EventPluginRegistry');
|
||||
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ViewportMetrics = require('ViewportMetrics');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
|
@ -373,4 +374,9 @@ var ReactBrowserEventEmitter = assign({}, ReactEventEmitterMixin, {
|
|||
|
||||
});
|
||||
|
||||
ReactPerf.measureMethods(ReactBrowserEventEmitter, 'ReactBrowserEventEmitter', {
|
||||
putListener: 'putListener',
|
||||
deleteListener: 'deleteListener',
|
||||
});
|
||||
|
||||
module.exports = ReactBrowserEventEmitter;
|
||||
|
|
|
@ -92,7 +92,6 @@ var ReactDOMIDOperations = {
|
|||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
|
||||
updatePropertyByID: 'updatePropertyByID',
|
||||
dangerouslyReplaceNodeWithMarkupByID: 'dangerouslyReplaceNodeWithMarkupByID',
|
||||
dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates',
|
||||
});
|
||||
|
|
|
@ -440,6 +440,9 @@ TopLevelWrapper.prototype.render = function() {
|
|||
* Inside of `container`, the first element rendered is the "reactRoot".
|
||||
*/
|
||||
var ReactMount = {
|
||||
|
||||
TopLevelWrapper: TopLevelWrapper,
|
||||
|
||||
/** Exposed for debugging purposes **/
|
||||
_instancesByReactRootID: instancesByReactRootID,
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
var Danger = require('Danger');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
var setInnerHTML = require('setInnerHTML');
|
||||
var setTextContent = require('setTextContent');
|
||||
|
@ -151,4 +152,8 @@ var DOMChildrenOperations = {
|
|||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
|
||||
updateTextContent: 'updateTextContent',
|
||||
});
|
||||
|
||||
module.exports = DOMChildrenOperations;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
var CSSProperty = require('CSSProperty');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
var camelizeStyleName = require('camelizeStyleName');
|
||||
var dangerousStyleValue = require('dangerousStyleValue');
|
||||
|
@ -185,4 +186,8 @@ var CSSPropertyOperations = {
|
|||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(CSSPropertyOperations, 'CSSPropertyOperations', {
|
||||
setValueForStyles: 'setValueForStyles',
|
||||
});
|
||||
|
||||
module.exports = CSSPropertyOperations;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
'use strict';
|
||||
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser');
|
||||
var warning = require('warning');
|
||||
|
@ -247,4 +248,10 @@ var DOMPropertyOperations = {
|
|||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
|
||||
setValueForProperty: 'setValueForProperty',
|
||||
setValueForAttribute: 'setValueForAttribute',
|
||||
deleteValueForProperty: 'deleteValueForProperty',
|
||||
});
|
||||
|
||||
module.exports = DOMPropertyOperations;
|
||||
|
|
|
@ -165,7 +165,11 @@ var ReactDefaultPerf = {
|
|||
].totalTime = performanceNow() - start;
|
||||
return rv;
|
||||
} else if (fnName === '_mountImageIntoNode' ||
|
||||
moduleName === 'ReactDOMIDOperations') {
|
||||
moduleName === 'ReactBrowserEventEmitter' ||
|
||||
moduleName === 'ReactDOMIDOperations' ||
|
||||
moduleName === 'CSSPropertyOperations' ||
|
||||
moduleName === 'DOMChildrenOperations' ||
|
||||
moduleName === 'DOMPropertyOperations') {
|
||||
start = performanceNow();
|
||||
rv = func.apply(this, args);
|
||||
totalTime = performanceNow() - start;
|
||||
|
@ -198,8 +202,12 @@ var ReactDefaultPerf = {
|
|||
});
|
||||
} else {
|
||||
// basic format
|
||||
var id = args[0];
|
||||
if (typeof id === 'object') {
|
||||
id = ReactMount.getID(args[0]);
|
||||
}
|
||||
ReactDefaultPerf._recordWrite(
|
||||
args[0],
|
||||
id,
|
||||
fnName,
|
||||
totalTime,
|
||||
Array.prototype.slice.call(args, 1)
|
||||
|
@ -211,7 +219,7 @@ var ReactDefaultPerf = {
|
|||
fnName === 'updateComponent' || // TODO: receiveComponent()?
|
||||
fnName === '_renderValidatedComponent')) {
|
||||
|
||||
if (typeof this._currentElement.type === 'string') {
|
||||
if (this._currentElement.type === ReactMount.TopLevelWrapper) {
|
||||
return func.apply(this, args);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ var DOM_OPERATION_TYPES = {
|
|||
REMOVE_NODE: 'remove',
|
||||
SET_MARKUP: 'set innerHTML',
|
||||
TEXT_CONTENT: 'set textContent',
|
||||
'updatePropertyByID': 'update attribute',
|
||||
'setValueForProperty': 'update attribute',
|
||||
'setValueForAttribute': 'update attribute',
|
||||
'deleteValueForProperty': 'remove attribute',
|
||||
'dangerouslyReplaceNodeWithMarkupByID': 'replace',
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* Copyright 2013-2015, 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 ReactDefaultPerf;
|
||||
var ReactTestUtils;
|
||||
|
||||
var App;
|
||||
var Box;
|
||||
var Div;
|
||||
|
||||
beforeEach(function() {
|
||||
var now = 0;
|
||||
require('mock-modules').setMock('performanceNow', function() {
|
||||
return now++;
|
||||
});
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDefaultPerf = require('ReactDefaultPerf');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
App = React.createClass({
|
||||
render: function() {
|
||||
return <div><Box /><Box /></div>;
|
||||
},
|
||||
});
|
||||
|
||||
Box = React.createClass({
|
||||
render: function() {
|
||||
return <div><input /></div>;
|
||||
},
|
||||
});
|
||||
|
||||
// ReactPerf only measures composites, so we put everything in one.
|
||||
Div = React.createClass({
|
||||
render: function() {
|
||||
return <div {...this.props} />;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
function measure(fn) {
|
||||
ReactDefaultPerf.start();
|
||||
fn();
|
||||
ReactDefaultPerf.stop();
|
||||
return ReactDefaultPerf.getLastMeasurements();
|
||||
}
|
||||
|
||||
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.getMeasurementsSummaryMap(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 */
|
||||
});
|
||||
|
||||
function expectNoWaste(fn) {
|
||||
var measurements = measure(fn);
|
||||
var summary = ReactDefaultPerf.getMeasurementsSummaryMap(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 listener update as waste', function() {
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
|
||||
expectNoWaste(() => {
|
||||
ReactDOM.render(<Div onClick={function() {}}>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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue