Skip to content

Commit

Permalink
[enzyme-adapter-react-16] [new] Add support for wrapping Profiler
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored and ljharb committed Mar 17, 2019
1 parent 970779f commit a90a845
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ function toTree(vnode) {
case FiberTags.ContextProvider:
case FiberTags.ContextConsumer:
return childrenToTree(node.child);
case FiberTags.Profiler:
case FiberTags.ForwardRef: {
return {
nodeType: 'function',
Expand Down
4 changes: 4 additions & 0 deletions packages/enzyme-adapter-react-16/src/detectFiberTags.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = function detectFiberTags() {
const supportsContext = typeof React.createContext !== 'undefined';
const supportsForwardRef = typeof React.forwardRef !== 'undefined';
const supportsMemo = typeof React.memo !== 'undefined';
const supportsProfiler = typeof React.unstable_Profiler !== 'undefined';

function Fn() {
return null;
Expand Down Expand Up @@ -66,5 +67,8 @@ module.exports = function detectFiberTags() {
ForwardRef: supportsForwardRef
? getFiber(React.createElement(FwdRef)).tag
: -1,
Profiler: supportsProfiler
? getFiber(React.createElement(React.unstable_Profiler, { id: 'mock', onRender: () => {} })).tag
: -1,
};
};
117 changes: 117 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
Fragment,
forwardRef,
memo,
Profiler,
PureComponent,
useEffect,
useState,
Expand Down Expand Up @@ -844,6 +845,122 @@ describeWithDOM('mount', () => {
});
});

describeIf(is('>= 16.4'), 'Profiler', () => {
function SomeComponent() {
return (
<Profiler id="SomeComponent" onRender={() => {}}>
<main>
<div className="child" />
</main>
</Profiler>
);
}

wrap()
.withConsoleThrows()
.it('mounts without complaint', () => {
expect(() => mount(<SomeComponent />)).not.to.throw();
});

it('renders', () => {
const wrapper = mount(<SomeComponent />);
expect(wrapper.debug()).to.equal(`<SomeComponent>
<Profiler id="SomeComponent" onRender={[Function: onRender]}>
<main>
<div className="child" />
</main>
</Profiler>
</SomeComponent>`);
});

it('finds elements through Profiler elements', () => {
const wrapper = mount(<SomeComponent />);

expect(wrapper.find('.child')).to.have.lengthOf(1);
});

it('finds Profiler element', () => {
const Parent = () => <span><SomeComponent foo="hello" /></span>;

const wrapper = mount(<Parent foo="hello" />);
const results = wrapper.find(SomeComponent);

expect(results).to.have.lengthOf(1);
expect(results.type()).to.equal(SomeComponent);
expect(results.props()).to.eql({ foo: 'hello' });
});

it('can find Profiler by id', () => {
const wrapper = mount(<SomeComponent />);
expect(wrapper.find('[id="SomeComponent"]').exists()).to.equal(true);
});

it('can find Profiler by display name', () => {
const wrapper = mount(<SomeComponent />);
const profiler = wrapper.find('Profiler');
expect(profiler).to.have.lengthOf(1);
expect(profiler.type()).to.equal(Profiler);
});

it('recognizes render phases', () => {
const handleRender = sinon.spy();
function AnotherComponent() {
return (
<Profiler id="AnotherComponent" onRender={handleRender}>
<div />
</Profiler>
);
}

const wrapper = mount(<AnotherComponent />);
expect(handleRender).to.have.property('callCount', 1);
expect(handleRender.args[0][1]).to.equal('mount');

wrapper.setProps({ unusedProp: true });
expect(handleRender).to.have.property('callCount', 2);
expect(handleRender.args[1][1]).to.equal('update');
});

it('measures timings', () => {
/**
* test environment has no access to the performance API at which point
* the profiling API has to fallback to Date.now() which isn't precise enough
* which results in 0 duration for these simple examples most of the time.
* With performance API it should test for greaterThan(0) instead of least(0)
*/
const handleRender = sinon.spy();
function AnotherComponent() {
return (
<Profiler id="AnotherComponent" onRender={handleRender}>
<div />
</Profiler>
);
}

const wrapper = mount(<AnotherComponent />);
expect(handleRender).to.have.property('callCount', 1);
const [firstArgs] = handleRender.args;
if (typeof performance === 'undefined') {
expect(firstArgs[2]).to.be.least(0);
expect(firstArgs[3]).to.be.least(0);
} else {
expect(firstArgs[2]).to.be.greaterThan(0);
expect(firstArgs[3]).to.be.greaterThan(0);
}

wrapper.setProps({ unusedProp: true });
expect(handleRender).to.have.property('callCount', 2);
const [, secondArgs] = handleRender.args;
if (typeof performance === 'undefined') {
expect(secondArgs[2]).to.be.least(0);
expect(secondArgs[3]).to.be.least(0);
} else {
expect(secondArgs[2]).to.be.greaterThan(0);
expect(secondArgs[3]).to.be.greaterThan(0);
}
});
});

describeIf(is('>= 16.8'), 'hooks', () => {
it('works with `useEffect`', (done) => {
function ComponentUsingEffectHook() {
Expand Down
117 changes: 117 additions & 0 deletions packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
PureComponent,
useEffect,
useState,
Profiler,
} from './_helpers/react-compat';
import {
describeIf,
Expand Down Expand Up @@ -1007,6 +1008,122 @@ describe('shallow', () => {
});
});

describeIf(is('>= 16.4'), 'Profiler', () => {
function SomeComponent() {
return (
<Profiler id="SomeComponent" onRender={() => {}}>
<main>
<div className="child" />
</main>
</Profiler>
);
}

wrap()
.withConsoleThrows()
.it('mounts without complaint', () => {
expect(() => shallow(<SomeComponent />)).not.to.throw();
});

it('renders', () => {
const wrapper = shallow(<SomeComponent />);
expect(wrapper.debug()).to.equal(`<Profiler id="SomeComponent" onRender={[Function: onRender]}>
<main>
<div className="child" />
</main>
</Profiler>`);
});

it('finds elements through Profiler elements', () => {
const wrapper = shallow(<SomeComponent />);

expect(wrapper.find('.child')).to.have.lengthOf(1);
});

it('finds Profiler element', () => {
const Parent = () => <span><SomeComponent foo="hello" /></span>;

const wrapper = shallow(<Parent foo="hello" />);
const results = wrapper.find(SomeComponent);

expect(results).to.have.lengthOf(1);
expect(results.type()).to.equal(SomeComponent);
expect(results.props()).to.eql({ foo: 'hello' });
});

it('can find Profiler by id', () => {
const wrapper = shallow(<SomeComponent />);
expect(wrapper.find('[id="SomeComponent"]').exists()).to.equal(true);
});

it('can find Profiler by display name', () => {
const wrapper = shallow(<SomeComponent />);
const profiler = wrapper.find('Profiler');
expect(profiler).to.have.lengthOf(1);
expect(profiler.type()).to.equal(Profiler);
});

// TODO: enable when Profiler is no longer unstable
it.skip('recognizes render phases', () => {
const handleRender = sinon.spy();
function AnotherComponent() {
return (
<Profiler id="AnotherComponent" onRender={handleRender}>
<div />
</Profiler>
);
}

const wrapper = shallow(<AnotherComponent />);
expect(handleRender).to.have.property('callCount', 1);
expect(handleRender.args[0][1]).to.equal('mount');

wrapper.setProps({ unusedProp: true });
expect(handleRender).to.have.property('callCount', 2);
expect(handleRender.args[1][1]).to.equal('update');
});

// TODO: enable when Profiler is no longer unstable
it.skip('measures timings', () => {
/**
* test environment has no access to the performance API at which point
* the profiling API has to fallback to Date.now() which isn't precise enough
* which results in 0 duration for these simple examples most of the time.
* With performance API it should test for greaterThan(0) instead of least(0)
*/
const handleRender = sinon.spy();
function AnotherComponent() {
return (
<Profiler id="AnotherComponent" onRender={handleRender}>
<div />
</Profiler>
);
}

const wrapper = shallow(<AnotherComponent />);
expect(handleRender).to.have.property('callCount', 1);
const [firstArgs] = handleRender.args;
if (typeof performance === 'undefined') {
expect(firstArgs[2]).to.be.least(0);
expect(firstArgs[3]).to.be.least(0);
} else {
expect(firstArgs[2]).to.be.greaterThan(0);
expect(firstArgs[3]).to.be.greaterThan(0);
}

wrapper.setProps({ unusedProp: true });
expect(handleRender).to.have.property('callCount', 2);
const [, secondArgs] = handleRender.args;
if (typeof performance === 'undefined') {
expect(secondArgs[2]).to.be.least(0);
expect(secondArgs[3]).to.be.least(0);
} else {
expect(secondArgs[2]).to.be.greaterThan(0);
expect(secondArgs[3]).to.be.greaterThan(0);
}
});
});

describeIf(is('>= 16.8.5'), 'hooks', () => {
// TODO: enable when the shallow renderer fixes its bug
it.skip('works with `useEffect`', (done) => {
Expand Down

0 comments on commit a90a845

Please sign in to comment.