以下是单元测试解决方案:
index.jsx
:
import React, { Component } from 'react';
class SomeComponent extends Component {
constructor(props) {
super(props);
this.state = { sticky: false };
}
handleWindowScroll() {
const header = document.querySelector('.productNavLinksContainer');
const dataHeader = document.querySelector('.productNavDataContainer');
if (header && dataHeader) {
const headerPos = header.getBoundingClientRect();
const dataHeaderPos = dataHeader.getBoundingClientRect();
const sticky = header.offsetTop;
if (dataHeaderPos.top > headerPos.height) {
this.setState({ sticky: false });
} else if (window.pageYOffset > sticky) {
this.setState({ sticky: true });
}
} else {
return;
}
}
render() {
return null;
}
}
export default SomeComponent;
index.test.jsx
:
import SomeComponent from '.';
import { shallow } from 'enzyme';
import React from 'react';
describe('60784579', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should not sticky', () => {
const mDataHeaderPos = { top: 100 };
const mHeaderPos = { height: 50 };
const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
switch (selector) {
case '.productNavLinksContainer':
return mHeader;
case '.productNavDataContainer':
return mDataHeader;
}
});
const wrapper = shallow(<SomeComponent></SomeComponent>);
const instance = wrapper.instance();
instance.handleWindowScroll();
expect(document.querySelector).toBeCalledTimes(2);
expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(instance.state).toEqual({ sticky: false });
});
it('should sticky if pageYOffset greater than offsetTop', () => {
const mDataHeaderPos = { top: 40 };
const mHeaderPos = { height: 50 };
const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
switch (selector) {
case '.productNavLinksContainer':
return mHeader;
case '.productNavDataContainer':
return mDataHeader;
}
});
Object.defineProperty(window, 'pageYOffset', { value: 200 });
const wrapper = shallow(<SomeComponent></SomeComponent>);
const instance = wrapper.instance();
instance.handleWindowScroll();
expect(document.querySelector).toBeCalledTimes(2);
expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(instance.state).toEqual({ sticky: true });
});
it('should do nothing if header or dataHeader does not exist', () => {
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
switch (selector) {
case '.productNavLinksContainer':
return undefined;
case '.productNavDataContainer':
return undefined;
}
});
const wrapper = shallow(<SomeComponent></SomeComponent>);
const instance = wrapper.instance();
const actual = instance.handleWindowScroll();
expect(actual).toBeUndefined();
expect(document.querySelector).toBeCalledTimes(2);
expect(instance.state).toEqual({ sticky: false });
});
it('should do nothing if pageYOffset less than offsetTop', () => {
const mDataHeaderPos = { top: 40 };
const mHeaderPos = { height: 50 };
const mHeader = { offsetTop: 100, getBoundingClientRect: jest.fn().mockReturnValueOnce(mHeaderPos) };
const mDataHeader = { getBoundingClientRect: jest.fn().mockReturnValueOnce(mDataHeaderPos) };
jest.spyOn(document, 'querySelector').mockImplementation((selector) => {
switch (selector) {
case '.productNavLinksContainer':
return mHeader;
case '.productNavDataContainer':
return mDataHeader;
}
});
Object.defineProperty(window, 'pageYOffset', { value: 80 });
const wrapper = shallow(<SomeComponent></SomeComponent>);
const instance = wrapper.instance();
const actual = instance.handleWindowScroll();
expect(actual).toBeUndefined();
expect(document.querySelector).toBeCalledTimes(2);
expect(mHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(mDataHeader.getBoundingClientRect).toBeCalledTimes(1);
expect(instance.state).toEqual({ sticky: false });
});
});
100% 覆盖率的单元测试结果:
PASS stackoverflow/60784579/index.test.jsx (7.912s)
60784579
✓ should not sticky (9ms)
✓ should sticky if pageYOffset greater than offsetTop (2ms)
✓ should do nothing if header or dataHeader does not exist (2ms)
✓ should do nothing if pageYOffset less than offsetTop (1ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.jsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 9.223s
源代码:https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60784579