react-lazily-render
Version:
Lazily render react components
322 lines (296 loc) • 12.2 kB
JavaScript
;
var _react = _interopRequireDefault(require("react"));
var _enzyme = require("enzyme");
var _ = _interopRequireDefault(require("."));
var _getViewportBounds = require("./utils/getViewportBounds");
var _getElementBounds = require("./utils/getElementBounds");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
var mockScrollParent = window;
jest.mock('scrollparent', function () {
return function (element) {
return mockScrollParent;
};
});
jest.mock('./utils/eventListenerOptions', function () {
return {
passive: true
};
});
jest.mock('./utils/getViewportBounds', function () {
var bounds = undefined;
var getViewportBounds = function getViewportBounds() {
return bounds;
};
getViewportBounds.__setViewportBounds = function (val) {
return bounds = val;
};
return getViewportBounds;
});
jest.mock('./utils/getElementBounds', function () {
var bounds = undefined;
var getElementBounds = function getElementBounds() {
return bounds;
};
getElementBounds.__setElementBounds = function (val) {
return bounds = val;
};
return getElementBounds;
});
function setupElementInView() {
(0, _getViewportBounds.__setViewportBounds)({
top: 0,
right: 1000,
bottom: 1000,
left: 0
});
(0, _getElementBounds.__setElementBounds)({
top: 100,
right: 200,
bottom: 200,
left: 100
});
}
function setupElementOutOfView() {
(0, _getViewportBounds.__setViewportBounds)({
top: 0,
right: 1000,
bottom: 1000,
left: 0
});
(0, _getElementBounds.__setElementBounds)({
top: 1100,
right: 1200,
bottom: 1200,
left: 1100
});
}
var compatMode;
function mockCompatMode(mode) {
compatMode = Object.getOwnPropertyDescriptor(Document.prototype, 'compatMode');
Object.defineProperty(document, 'compatMode', {
value: mode,
writable: false,
configurable: true
});
}
function unmockCompatMode() {
Object.defineProperty(document, 'compatMode', compatMode);
}
function wrapper(node) {
jest.useFakeTimers();
var wrapper = (0, _enzyme.mount)(node);
jest.runAllTimers();
wrapper.update();
return wrapper;
} // return the div wrapper
function getWrapper(element) {
return element.children().first();
}
describe('LazyRender', function () {
var placeholder = '...';
var content = 'Hello World!';
var children = 'foo bar';
describe('.render()', function () {
it('should render the class name on the wrapper', function () {
var wrapper = (0, _enzyme.mount)(_react["default"].createElement(_["default"], {
className: "my-cool-class"
}, function () {
return _react["default"].createElement("span", null, "HelloWorld!");
}));
expect(wrapper.prop('className')).toEqual('my-cool-class');
});
describe("when component=\"span\"", function () {
it("should render the wrapper as <span>", function () {
var wrapper = (0, _enzyme.shallow)(_react["default"].createElement(_["default"], {
component: "span"
}, function () {
return _react["default"].createElement("span", null, "HelloWorld!");
}));
expect(wrapper.type()).toEqual('span');
});
});
describe("when component=\"div\"", function () {
it("should render the wrapper as <div>", function () {
var wrapper = (0, _enzyme.shallow)(_react["default"].createElement(_["default"], {
component: "div"
}, function () {
return _react["default"].createElement("span", null, "HelloWorld!");
}));
expect(wrapper.type()).toEqual('div');
});
});
it('should call the render fn with true when the component is visible in the viewport', function () {
setupElementInView();
var render = jest.fn();
var element = wrapper(_react["default"].createElement(_["default"], null, render));
expect(render).toBeCalledWith(true);
});
it('should call the render fn with false when the component is not visible in the viewport', function () {
setupElementOutOfView();
var render = jest.fn();
var element = wrapper(_react["default"].createElement(_["default"], null, render));
expect(render).toBeCalledWith(false);
});
describe('has not been scrolled into view:', function () {
beforeEach(function () {
return setupElementOutOfView();
});
it('should render the "placeholder" when there is a "placeholder"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
placeholder: placeholder,
content: content
}, function () {
return children;
}));
expect(getWrapper(element).contains(placeholder)).toBeTruthy();
});
it('should render the "render fn" when there is no "placeholder"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
content: content
}, function () {
return children;
}));
expect(getWrapper(element).contains(children)).toBeTruthy();
});
it('should render "null" when there is no "placeholder" and no "render fn"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
content: content
}));
expect(getWrapper(element).children().exists()).toBeFalsy();
});
});
describe('has been scrolled into view:', function () {
beforeEach(function () {
return setupElementInView();
});
it('should render the "content" when there is "content"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
placeholder: placeholder,
content: content
}, function () {
return children;
}));
expect(getWrapper(element).contains(content)).toBeTruthy();
});
it('should render the "render fn" when there is no "content"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
placeholder: placeholder
}, function () {
return children;
}));
expect(getWrapper(element).contains(children)).toBeTruthy();
});
it('should render "null" when there is no "content" and no "render fn"', function () {
var element = wrapper(_react["default"].createElement(_["default"], {
placeholder: placeholder
}));
expect(getWrapper(element).children().exists()).toBeFalsy();
});
});
});
describe('Event handling', function () {
var windowAddEventSpy;
var bodyAddEventSpy;
var windowRemoveEventSpy;
var bodyRemoveEventSpy;
beforeEach(function () {
windowAddEventSpy = jest.spyOn(window, 'addEventListener');
bodyAddEventSpy = jest.spyOn(document.body, 'addEventListener');
windowRemoveEventSpy = jest.spyOn(window, 'removeEventListener');
bodyRemoveEventSpy = jest.spyOn(document.body, 'removeEventListener');
});
it('should use passive event listeners when available', function () {
var element = wrapper(_react["default"].createElement(_["default"], null, function () {
return children;
}));
expect(windowAddEventSpy).toBeCalledWith('scroll', expect.anything(), {
passive: true
});
element.unmount();
expect(windowRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), {
passive: true
});
});
it('should use passive event listeners when available, container specified', function () {
var bodyAddEventSpy = jest.spyOn(document.body, 'addEventListener');
var bodyRemoveEventSpy = jest.spyOn(document.body, 'removeEventListener');
var element = wrapper(_react["default"].createElement(_["default"], {
scrollContainer: document.body
}, function () {
return children;
}));
expect(bodyAddEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
bodyAddEventSpy.mockClear();
bodyRemoveEventSpy.mockClear();
element.unmount();
expect(bodyRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
});
it('should cleanup and setup containers accurately when container changes', function () {
// Aim: consumer wants to switch to a nested scroll container.
var innerScrollContainer = document.createElement('div', {
className: 'inner-scroll-container'
});
document.body.appendChild(innerScrollContainer);
var bodyAddEventSpy = jest.spyOn(document.body, 'addEventListener');
var bodyRemoveEventSpy = jest.spyOn(document.body, 'removeEventListener');
var containerAddEventSpy = jest.spyOn(innerScrollContainer, 'addEventListener');
var containerRemoveEventSpy = jest.spyOn(innerScrollContainer, 'removeEventListener');
var element = wrapper(_react["default"].createElement(_["default"], {
scrollContainer: document.body
}, function () {
return children;
})); // Initial events should be attached to the first scroll container specified.
expect(bodyAddEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
bodyAddEventSpy.mockClear(); // Switch to the next container.
element.setProps({
scrollContainer: innerScrollContainer
});
expect(element.prop('scrollContainer')).toBe(innerScrollContainer); // Next, a cleanup should be done of the first container, and setup of the new container.
expect(bodyRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
expect(containerAddEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
containerAddEventSpy.mockClear();
bodyRemoveEventSpy.mockClear(); // Lastly, the component teardown should reference the second container.
element.unmount();
expect(containerRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
containerRemoveEventSpy.mockClear();
});
it('should listen to the window when scrollparent returns body in CSS1Compat mode', function () {
mockCompatMode('CSS1Compat');
mockScrollParent = document.body;
var element = wrapper(_react["default"].createElement(_["default"], null, function () {
return children;
}));
expect(windowAddEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
expect(bodyAddEventSpy).not.toBeCalledWith('scroll', expect.anything(), expect.anything());
windowAddEventSpy.mockClear();
bodyAddEventSpy.mockClear();
windowRemoveEventSpy.mockClear();
bodyRemoveEventSpy.mockClear();
element.unmount();
expect(windowRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
expect(bodyRemoveEventSpy).not.toBeCalledWith('scroll', expect.anything(), expect.anything());
unmockCompatMode();
});
it('should listen to the body when scrollparent returns body in BackCompat mode', function () {
mockCompatMode('BackCompat');
mockScrollParent = document.body;
var windowAddEventSpy = jest.spyOn(window, 'addEventListener');
var bodyAddEventSpy = jest.spyOn(document.body, 'addEventListener');
var windowRemoveEventSpy = jest.spyOn(window, 'removeEventListener');
var bodyRemoveEventSpy = jest.spyOn(document.body, 'removeEventListener');
var element = wrapper(_react["default"].createElement(_["default"], null, function () {
return children;
}));
expect(windowAddEventSpy).not.toBeCalledWith('scroll', expect.anything(), expect.anything());
expect(bodyAddEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
windowAddEventSpy.mockClear();
bodyAddEventSpy.mockClear();
windowRemoveEventSpy.mockClear();
bodyRemoveEventSpy.mockClear();
element.unmount();
expect(windowRemoveEventSpy).not.toBeCalledWith('scroll', expect.anything(), expect.anything());
expect(bodyRemoveEventSpy).toBeCalledWith('scroll', expect.anything(), expect.anything());
unmockCompatMode();
});
});
});