mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-06 11:23:48 -05:00
Add ScreenMock to helpers
Mocks DOM API calls for position and dimension of elements and provides an API to allow access to them.
This commit is contained in:
@@ -1,3 +1,20 @@
|
||||
function getDescriptorForProperty (prop, obj) {
|
||||
const descriptors = Object.getOwnPropertyDescriptors(obj);
|
||||
const prototype = Object.getPrototypeOf(obj);
|
||||
|
||||
if ((descriptors !== {}) && (prop in descriptors)) {
|
||||
return descriptors[prop];
|
||||
}
|
||||
|
||||
// if not in this object's descriptors, check the prototype chain
|
||||
if (prototype !== null) {
|
||||
return getDescriptorForProperty(prop, prototype);
|
||||
}
|
||||
|
||||
// no descriptor for this prop and no prototypes left in the chain
|
||||
return null;
|
||||
};
|
||||
|
||||
const triggerEvent = (el, evtType, options) => {
|
||||
const eventInit = {
|
||||
bubbles: true,
|
||||
@@ -235,6 +252,7 @@ class WindowMock {
|
||||
document.documentElement.scrollTop = scrollPosition;
|
||||
window.scrollY = scrollPosition;
|
||||
window.pageYOffset = scrollPosition;
|
||||
|
||||
triggerEvent(window, 'scroll');
|
||||
|
||||
}
|
||||
@@ -297,6 +315,162 @@ class SelectionMock extends DOMInterfaceMock {
|
||||
}
|
||||
|
||||
}
|
||||
class ScreenRenderItem {
|
||||
constructor (jest, node) {
|
||||
|
||||
this._jest = jest;
|
||||
this._node = node;
|
||||
this._storeProps();
|
||||
this._mockAPICalls();
|
||||
|
||||
}
|
||||
|
||||
setData (itemData) {
|
||||
|
||||
// check all the item data is present
|
||||
const itemProps = Object.keys(itemData);
|
||||
const missingKeys = ScreenRenderItem.REQUIRED_PROPS.filter(prop => !itemProps.includes(prop));
|
||||
|
||||
this._data = {};
|
||||
|
||||
if (missingKeys.length) {
|
||||
throw Error(`${itemData.name ? itemData.name : itemProps.join(', ')} is missing these properties: ${missingKeys.join(', ')}`);
|
||||
}
|
||||
|
||||
// default left if not set
|
||||
if (!('offsetLeft' in itemData)) { itemData.offsetLeft = 0; }
|
||||
|
||||
// copy onto internal store
|
||||
Object.assign(this._data, itemData);
|
||||
|
||||
}
|
||||
|
||||
_getBoundingClientRect () {
|
||||
const {offsetHeight, offsetWidth, offsetTop, offsetLeft} = this._data;
|
||||
const x = offsetLeft - window.scrollX;
|
||||
const y = offsetTop - window.scrollY;
|
||||
|
||||
return {
|
||||
'x': x,
|
||||
'y': y,
|
||||
'top': (offsetHeight < 0) ? y + offsetHeight : y,
|
||||
'left': (offsetWidth < 0) ? x + offsetWidth : x,
|
||||
'bottom': (offsetTop + offsetHeight) - window.scrollY,
|
||||
'right': (offsetLeft + offsetWidth) - window.scrollX,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
reset () {
|
||||
|
||||
// reset DOMRect mock
|
||||
this._node.getBoundingClientRect.mockClear();
|
||||
|
||||
ScreenRenderItem.OFFSET_PROPS.forEach(prop => {
|
||||
|
||||
if (prop in this._propStore) {
|
||||
// replace property implementation
|
||||
Object.defineProperty(this._node, prop, this._propStore[prop]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
_storeProps () {
|
||||
|
||||
this._propStore = {};
|
||||
|
||||
ScreenRenderItem.OFFSET_PROPS.forEach(prop => {
|
||||
const descriptor = getDescriptorForProperty(prop, this._node);
|
||||
|
||||
if (descriptor !== null) {
|
||||
this._propStore[prop] = descriptor;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// mock any calls to the node's DOM API for position/dimension
|
||||
_mockAPICalls () {
|
||||
|
||||
// proxy boundingClientRect property calls to item data
|
||||
this._jest.spyOn(this._node, 'getBoundingClientRect').mockImplementation(() => this._getBoundingClientRect());
|
||||
|
||||
// handle calls to offset properties
|
||||
ScreenRenderItem.OFFSET_PROPS.forEach(prop => {
|
||||
|
||||
this._jest.spyOn(this._node, prop, 'get').mockImplementation(() => this._data[prop]);
|
||||
|
||||
// proxy DOM API sets for offsetValues (not possible to mock directly)
|
||||
Object.defineProperty(this._node, prop, {
|
||||
configurable: true,
|
||||
set: jest.fn(value => {
|
||||
this._data[prop] = value;
|
||||
return true;
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
ScreenRenderItem.OFFSET_PROPS = ['offsetHeight', 'offsetWidth', 'offsetTop', 'offsetLeft'];
|
||||
ScreenRenderItem.REQUIRED_PROPS = ['name', 'offsetHeight', 'offsetHeight', 'offsetWidth', 'offsetTop'];
|
||||
|
||||
class ScreenMock {
|
||||
constructor (jest) {
|
||||
|
||||
this._jest = jest
|
||||
this._items = {};
|
||||
|
||||
}
|
||||
|
||||
mockPositionAndDimension (itemName, node, itemData) {
|
||||
|
||||
if (itemName in this._items) { throw new Error(`${itemName} already has its position and dimension mocked`); }
|
||||
|
||||
const data = Object.assign({ 'name': itemName }, itemData);
|
||||
const item = new ScreenRenderItem(this._jest, node);
|
||||
|
||||
item.setData(data);
|
||||
|
||||
this._items[itemName] = item;
|
||||
|
||||
}
|
||||
|
||||
setWindow (windowData) {
|
||||
|
||||
this.window = new WindowMock(this._jest);
|
||||
|
||||
// check all the window data is present
|
||||
const missingKeys = Object.keys(windowData).filter(key => !ScreenMock.REQUIRED_WINDOW_PROPS.includes(key));
|
||||
|
||||
if (missingKeys.length) {
|
||||
throw Error(`Window definition is missing these properties: ${missingKeys.join(', ')}`);
|
||||
}
|
||||
|
||||
this.window.setHeightTo(windowData.height);
|
||||
this.window.setWidthTo(windowData.width);
|
||||
this.window.scrollTo(windowData.scrollTop);
|
||||
|
||||
}
|
||||
|
||||
scrollTo (scrollTop) {
|
||||
|
||||
this.window.scrollTo(scrollTop);
|
||||
|
||||
}
|
||||
|
||||
reset () {
|
||||
|
||||
Object.keys(this._items).forEach(itemName => this._items[itemName].reset());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
ScreenMock.REQUIRED_WINDOW_PROPS = ['height', 'width', 'scrollTop'];
|
||||
|
||||
// function to ask certain questions of a DOM Element
|
||||
const element = function (el) {
|
||||
@@ -311,3 +485,4 @@ exports.RangeMock = RangeMock;
|
||||
exports.SelectionMock = SelectionMock;
|
||||
exports.element = element;
|
||||
exports.WindowMock = WindowMock;
|
||||
exports.ScreenMock = ScreenMock;
|
||||
|
||||
Reference in New Issue
Block a user