Files
notifications-admin/tests/javascripts/support/helpers.js

241 lines
6.2 KiB
JavaScript
Raw Normal View History

const triggerEvent = (el, evtType, options) => {
const eventInit = {
bubbles: true,
cancelable: true
};
let setPositionData = () => {
const browserUI = {
leftFrameBorder: 0,
topHeight: 100
};
const cursorOffset = {
x: 5,
y: 5
};
const elBoundingBox = el.getBoundingClientRect();
if (!eventInit.clientX) { eventInit.clientX = elBoundingBox.left + cursorOffset.x; }
if (!eventInit.clientY) { eventInit.clientY = elBoundingBox.top + cursorOffset.y; }
if (!eventInit.pageX) { eventInit.pageX = elBoundingBox.left + cursorOffset.x; }
if (!eventInit.pageY) { eventInit.pageY = elBoundingBox.top + cursorOffset.y; }
if (!eventInit.screenX) { eventInit.screenX = eventInit.clientX + browserUI.leftFrameBorder; }
if (!eventInit.screenY) { eventInit.screenY = eventInit.clientY + browserUI.topHeight; }
if (!eventInit.offsetX) { eventInit.offsetX = cursorOffset.x; }
if (!eventInit.offsetY) { eventInit.offsetY = cursorOffset.y; }
};
let Instance;
// mixin any specified event properties with the defaults
if (options && ('eventInit' in options)) {
Object.assign(eventInit, options.eventInit);
}
// use event interface if specified
if (options && ('interface' in options)) {
Instance = options.interface;
} else {
// otherwise, derive from the event type
switch (evtType) {
case 'click':
// click events are part of the MouseEvent interface
Instance = window.MouseEvent;
break;
case 'mousedown':
Instance = window.MouseEvent;
break;
case 'mouseup':
Instance = window.MouseEvent;
break;
case 'keydown':
Instance = window.KeyboardEvent;
break;
case 'keyup':
Instance = window.KeyboardEvent;
break;
default:
Instance = Event;
}
}
if (evtType === 'click') {
// hack for click events to simulate details of pointer interaction
setPositionData();
}
const evt = new Instance(evtType, eventInit);
el.dispatchEvent(evt);
};
function clickElementWithMouse (el) {
triggerEvent(el, 'mousedown');
triggerEvent(el, 'mouseup');
triggerEvent(el, 'click');
};
function moveSelectionToRadio (el, options) {
// movement within a radio group with arrow keys fires no keyboard events
// click event fired from option radio being activated
triggerEvent(el, 'click', {
eventInit: { pageX: 0 }
});
};
function activateRadioWithSpace (el) {
// simulate events for space key press to confirm selection
// event for space key press
triggerEvent(el, 'keydown', {
eventInit: { which: 32 }
});
// click event fired from option radio being activated
triggerEvent(el, 'click', {
eventInit: { pageX: 0 }
});
};
class ElementQuery {
constructor (el) {
this.el = el;
}
get nodeName () {
return this.el.nodeName.toLowerCase();
}
get firstTextNodeValue () {
const textNodes = Array.from(this.el.childNodes).filter(el => el.nodeType === 3);
return textNodes.length ? textNodes[0].nodeValue : undefined;
};
// returns the elements attributes as an object
hasAttributesSetTo (mappings) {
if (!this.el.hasAttributes()) { return false; }
const keys = Object.keys(mappings);
let matches = 0;
keys.forEach(key => {
if (this.el.hasAttribute(key) && (this.el.attributes[key].value === mappings[key])) {
matches++;
}
});
return matches === keys.length;
}
hasClass (classToken) {
return Array.from(this.el.classList).includes(classToken);
}
is (state) {
const test = `_is${state.charAt(0).toUpperCase()}${state.slice(1)}`;
if (ElementQuery.prototype.hasOwnProperty(test)) {
return this[test]();
}
}
// looks for a sibling before the el that matches the supplied test function
// the test function gets sent each sibling, wrapped in an Element instance
getPreviousSibling (test) {
let node = this.el.previousElementSibling;
let el;
while(node) {
el = element(node);
if (test(el)) {
return node;
}
node = node.previousElementSibling;
}
return null;
}
_isHidden () {
const display = window.getComputedStyle(this.el).getPropertyValue('display');
return display === 'none';
}
};
class WindowMock {
constructor (jest) {
this._defaults = {
height: window.innerHeight,
width: window.innerWidth
};
this._spies = {
document: {}
};
this._jest = jest;
}
setHeightTo (height) {
// mock DOM calls for window height
window.innerHeight = height;
// remove calls to document.documentElement.clientHeight when jQuery is gone. It's called to support older browsers like IE8
this._spies.document.clientHeight = this._jest.spyOn(document.documentElement, 'clientHeight', 'get').mockImplementation(() => height);
}
setWidthTo (width) {
// mock DOM calls for window width
window.innerWidth = width;
// remove calls to document.documentElement.clientWidth when jQuery is gone. It's called to support older browsers like IE8
this._spies.document.clientWidth = this._jest.spyOn(document.documentElement, 'clientWidth', 'get').mockImplementation(() => height);
}
resizeTo (dimensions) {
this.setHeightTo(dimensions.height);
this.setWidthTo(dimensions.width);
triggerEvent(window, 'resize');
}
scrollBy (scrollPosition) {
document.documentElement.scrollTop = scrollPosition;
triggerEvent(window, 'scroll');
}
reset () {
window.innerHeight = this._defaults.height;
window.innerWidth = this._defaults.width;
document.documentElement.scrollTop = 0;
// reset all spies
Object.keys(this._spies).forEach(key => {
const objectSpies = this._spies[key];
Object.keys(objectSpies).forEach(key => objectSpies[key].mockClear());
});
}
}
// function to ask certain questions of a DOM Element
const element = function (el) {
return new ElementQuery(el);
};
exports.triggerEvent = triggerEvent;
exports.clickElementWithMouse = clickElementWithMouse;
exports.moveSelectionToRadio = moveSelectionToRadio;
exports.activateRadioWithSpace = activateRadioWithSpace;
exports.element = element;
exports.WindowMock = WindowMock;