@browserstack/testcafe
Version:
Automated browser testing for the modern web development stack.
1,163 lines (1,141 loc) • 155 kB
JavaScript
// NOTE: We should have the capability to initialize scripts with different contexts.
// This is required for iframes without the src attribute because Hammerhead does not
// inject scripts into such iframes. So, we wrap all scripts in initialization functions.
(function () {
function initTestCafeCore(window, isIFrameWithoutSrc) {
var document = window.document;
(function (hammerhead) {
var hammerhead__default = 'default' in hammerhead ? hammerhead['default'] : hammerhead;
var browserUtils = hammerhead__default.utils.browser;
var MODIFIERS = {
alt: 18,
ctrl: 17,
meta: 91,
shift: 16
};
var SHIFT_MAP = {
'~': '`',
'!': '1',
'@': '2',
'#': '3',
'$': '4',
'%': '5',
'^': '6',
'&': '7',
'*': '8',
'(': '9',
')': '0',
'_': '-',
'+': '=',
'{': '[',
'}': ']',
':': ';',
'"': '\'',
'|': '\\',
'<': ',',
'>': '.',
'?': '/',
'±': '§'
};
var SPECIAL_KEYS = {
backspace: 8,
capslock: 20,
delete: 46,
down: 40,
end: 35,
enter: 13,
esc: 27,
home: 36,
ins: 45,
left: 37,
pagedown: 34,
pageup: 33,
right: 39,
space: 32,
tab: 9,
up: 38
};
var KEY_PROPERTY = {
left: browserUtils.isIE ? 'Left' : 'ArrowLeft',
down: browserUtils.isIE ? 'Down' : 'ArrowDown',
right: browserUtils.isIE ? 'Right' : 'ArrowRight',
up: browserUtils.isIE ? 'Up' : 'ArrowUp',
backspace: 'Backspace',
capslock: 'CapsLock',
delete: 'Delete',
end: 'End',
enter: 'Enter',
esc: 'Escape',
home: 'Home',
ins: 'Insert',
pagedown: 'PageDown',
pageup: 'PageUp',
space: browserUtils.isIE ? 'Spacebar' : ' ',
tab: 'Tab',
alt: 'Alt',
ctrl: 'Control',
meta: 'Meta',
shift: 'Shift'
};
function reverseMap(map) {
var reversed = {};
for (var key in map) {
if (map.hasOwnProperty(key))
reversed[map[key]] = key;
}
return reversed;
}
var KEY_MAPS = {
modifiers: MODIFIERS,
shiftMap: SHIFT_MAP,
specialKeys: SPECIAL_KEYS,
keyProperty: KEY_PROPERTY,
modifiersMap: {
option: 'alt'
},
symbolCharCodeToKeyCode: {
96: 192,
91: 219,
93: 221,
92: 220,
59: 186,
39: 222,
44: 188,
45: browserUtils.isFirefox ? 173 : 189,
46: 190,
47: 191 // /
},
symbolKeysCharCodes: {
109: 45,
173: 45,
186: 59,
187: 61,
188: 44,
189: 45,
190: 46,
191: 47,
192: 96,
219: 91,
220: 92,
221: 93,
222: 39,
110: 46,
96: 48,
97: 49,
98: 50,
99: 51,
100: 52,
101: 53,
102: 54,
103: 55,
104: 56,
105: 57,
107: 43,
106: 42,
111: 47
},
reversedModifiers: reverseMap(MODIFIERS),
reversedShiftMap: reverseMap(SHIFT_MAP),
reversedSpecialKeys: reverseMap(SPECIAL_KEYS),
reversedKeyProperty: reverseMap(KEY_PROPERTY)
};
// NOTE: node description by node type
var NODE_TYPE_DESCRIPTIONS = {
1: 'element',
2: 'attribute',
3: 'text',
4: 'cdata section',
5: 'entity reference',
6: 'entity node',
7: 'processing instruction',
8: 'comment',
9: 'document',
10: 'document type',
11: 'document fragment',
12: 'notation'
};
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var Promise = hammerhead__default.Promise;
var nativeMethods = hammerhead__default.nativeMethods;
function delay (ms) {
return new Promise(function (resolve) { return nativeMethods.setTimeout.call(window, resolve, ms); });
}
var nativeIndexOf = Array.prototype.indexOf;
var nativeForEach = Array.prototype.forEach;
var nativeSome = Array.prototype.some;
var nativeMap = Array.prototype.map;
var nativeFilter = Array.prototype.filter;
var nativeReverse = Array.prototype.reverse;
var nativeReduce = Array.prototype.reduce;
var nativeSplice = Array.prototype.splice;
function toArray(arg) {
var arr = [];
var length = arg.length;
for (var i = 0; i < length; i++)
arr.push(arg[i]);
return arr;
}
function reverse(arr) {
return nativeReverse.call(arr);
}
function isArray(arg) {
return hammerhead__default.nativeMethods.objectToString.call(arg) === '[object Array]';
}
function find(arr, callback) {
var length = arr.length;
for (var i = 0; i < length; i++) {
if (callback(arr[i], i, arr))
return arr[i];
}
return null;
}
function indexOf(arr, arg) {
return nativeIndexOf.call(arr, arg);
}
function forEach(arr, callback) {
nativeForEach.call(arr, callback);
}
function some(arr, callback) {
return nativeSome.call(arr, callback);
}
function map(arr, callback) {
return nativeMap.call(arr, callback);
}
function filter(arr, callback) {
return nativeFilter.call(arr, callback);
}
function reduce(arr, callback, initialValue) {
return nativeReduce.call(arr, callback, initialValue);
}
function remove(arr, item) {
var index = indexOf(arr, item);
if (index > -1)
nativeSplice.call(arr, index, 1);
}
function equals(arr1, arr2) {
if (arr1.length !== arr2.length)
return false;
for (var i = 0, l = arr1.length; i < l; i++) {
if (arr1[i] !== arr2[i])
return false;
}
return true;
}
function getCommonElement(arr1, arr2) {
for (var i = 0; i < arr1.length; i++) {
for (var t = 0; t < arr2.length; t++) {
if (arr1[i] === arr2[t])
return arr1[i];
}
}
return null;
}
var arrayUtils = /*#__PURE__*/Object.freeze({
__proto__: null,
toArray: toArray,
reverse: reverse,
isArray: isArray,
find: find,
indexOf: indexOf,
forEach: forEach,
some: some,
map: map,
filter: filter,
reduce: reduce,
remove: remove,
equals: equals,
getCommonElement: getCommonElement
});
function inherit(Child, Parent) {
var Func = function () {
};
Func.prototype = Parent.prototype;
hammerhead__default.utils.extend(Child.prototype, new Func());
Child.prototype.constructor = Child;
Child.base = Parent.prototype;
}
var EventEmitter = function () {
this.eventsListeners = [];
};
EventEmitter.prototype.emit = function (evt) {
var listeners = this.eventsListeners[evt];
if (listeners) {
for (var i = 0; i < listeners.length; i++) {
try {
if (listeners[i])
listeners[i].apply(this, Array.prototype.slice.apply(arguments, [1]));
}
catch (e) {
// Hack for IE: after document.write calling IFrameSandbox event handlers
// rises 'Can't execute code from a freed script' exception because document has been
// recreated
if (e.message && e.message.indexOf('freed script') > -1)
listeners[i] = null;
else
throw e;
}
}
}
};
EventEmitter.prototype.off = function (evt, listener) {
var listeners = this.eventsListeners[evt];
if (listeners)
this.eventsListeners[evt] = filter(listeners, function (item) { return item !== listener; });
};
EventEmitter.prototype.on = function (evt, listener) {
if (!this.eventsListeners[evt])
this.eventsListeners[evt] = [];
this.eventsListeners[evt].push(listener);
};
EventEmitter.prototype.once = function (evt, listener) {
var _this = this;
this.on(evt, function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
_this.off(evt, listener);
return listener.apply(void 0, args);
});
};
var serviceUtils = /*#__PURE__*/Object.freeze({
__proto__: null,
inherit: inherit,
EventEmitter: EventEmitter
});
var Promise$1 = hammerhead__default.Promise;
var nativeMethods$1 = hammerhead__default.nativeMethods;
var REQUESTS_COLLECTION_DELAY_DEFAULT = 50;
var REQUESTS_FINISHED_EVENT = 'requests-finished';
var GLOBAL_REQUEST_BARRIER_FIELD = 'testcafe|request-barrier';
var RequestBarrier = /** @class */ (function (_super) {
__extends(RequestBarrier, _super);
function RequestBarrier(delays) {
if (delays === void 0) { delays = {}; }
var _this = _super.call(this) || this;
_this.BARRIER_TIMEOUT = 3000;
_this.delays = {
requestsCollection: delays.requestsCollection === void 0 ?
REQUESTS_COLLECTION_DELAY_DEFAULT : delays.requestsCollection,
additionalRequestsCollection: delays.additionalRequestsCollection === void 0 ?
REQUESTS_COLLECTION_DELAY_DEFAULT : delays.additionalRequestsCollection,
pageInitialRequestsCollection: delays.pageInitialRequestsCollection === void 0 ?
REQUESTS_COLLECTION_DELAY_DEFAULT : delays.pageInitialRequestsCollection
};
_this.collectingReqs = true;
_this.requests = [];
_this.watchdog = null;
_this._unbindHandlers = null;
_this._init();
return _this;
}
RequestBarrier.prototype._init = function () {
var _this = this;
var onXhrSend = function (e) { return _this._onXhrSend(e.xhr); };
var onXhrCompleted = function (e) { return _this._onRequestCompleted(e.xhr); };
var onXhrError = function (e) { return _this._onRequestError(e.xhr, e.err); };
var onFetchSend = function (e) { return _this._onFetchSend(e); };
hammerhead__default.on(hammerhead__default.EVENTS.beforeXhrSend, onXhrSend);
hammerhead__default.on(hammerhead__default.EVENTS.xhrCompleted, onXhrCompleted);
hammerhead__default.on(hammerhead__default.EVENTS.xhrError, onXhrError);
hammerhead__default.on(hammerhead__default.EVENTS.fetchSent, onFetchSend);
this._unbindHandlers = function () {
hammerhead__default.off(hammerhead__default.EVENTS.beforeXhrSend, onXhrSend);
hammerhead__default.off(hammerhead__default.EVENTS.xhrCompleted, onXhrCompleted);
hammerhead__default.off(hammerhead__default.EVENTS.xhrError, onXhrError);
hammerhead__default.off(hammerhead__default.EVENTS.fetchSent, onFetchSend);
};
};
RequestBarrier.prototype._onXhrSend = function (request) {
if (this.collectingReqs)
this.requests.push(request);
};
RequestBarrier.prototype._onRequestCompleted = function (request) {
var _this = this;
// NOTE: let the last real XHR handler finish its job and try to obtain
// any additional requests if they were initiated by this handler
delay(this.delays.additionalRequestsCollection)
.then(function () { return _this._onRequestFinished(request); });
};
RequestBarrier.prototype._onRequestError = function (request) {
this._onRequestFinished(request);
};
RequestBarrier.prototype._onRequestFinished = function (request) {
if (indexOf(this.requests, request) > -1) {
remove(this.requests, request);
if (!this.collectingReqs && !this.requests.length)
this.emit(REQUESTS_FINISHED_EVENT);
}
};
RequestBarrier.prototype._onFetchSend = function (request) {
var _this = this;
if (this.collectingReqs) {
this.requests.push(request);
request
.then(function () { return _this._onRequestCompleted(request); })
.catch(function () { return _this._onRequestError(request); });
}
};
RequestBarrier.prototype.wait = function (isPageLoad) {
var _this = this;
return new Promise$1(function (resolve) {
delay(isPageLoad ? _this.delays.pageInitialRequestsCollection : _this.delays.requestsCollection)
.then(function () {
_this.collectingReqs = false;
var onRequestsFinished = function () {
if (_this.watchdog)
nativeMethods$1.clearTimeout.call(window, _this.watchdog);
_this._unbindHandlers();
resolve();
};
if (_this.requests.length) {
_this.watchdog = nativeMethods$1.setTimeout.call(window, onRequestsFinished, _this.BARRIER_TIMEOUT);
_this.on(REQUESTS_FINISHED_EVENT, onRequestsFinished);
}
else
onRequestsFinished();
});
});
};
return RequestBarrier;
}(EventEmitter));
RequestBarrier.GLOBAL_REQUEST_BARRIER_FIELD = GLOBAL_REQUEST_BARRIER_FIELD;
window[RequestBarrier.GLOBAL_REQUEST_BARRIER_FIELD] = RequestBarrier;
var browserUtils$1 = hammerhead__default.utils.browser;
var nativeMethods$2 = hammerhead__default.nativeMethods;
// NOTE: We have to retrieve styleUtils.get from hammerhead
// to avoid circular dependencies between domUtils and styleUtils
var getElementStyleProperty = hammerhead__default.utils.style.get;
var getActiveElement = hammerhead__default.utils.dom.getActiveElement;
var findDocument = hammerhead__default.utils.dom.findDocument;
var isElementInDocument = hammerhead__default.utils.dom.isElementInDocument;
var isElementInIframe = hammerhead__default.utils.dom.isElementInIframe;
var getIframeByElement = hammerhead__default.utils.dom.getIframeByElement;
var isCrossDomainWindows = hammerhead__default.utils.dom.isCrossDomainWindows;
var getSelectParent = hammerhead__default.utils.dom.getSelectParent;
var getChildVisibleIndex = hammerhead__default.utils.dom.getChildVisibleIndex;
var getSelectVisibleChildren = hammerhead__default.utils.dom.getSelectVisibleChildren;
var isElementNode = hammerhead__default.utils.dom.isElementNode;
var isTextNode = hammerhead__default.utils.dom.isTextNode;
var isRenderedNode = hammerhead__default.utils.dom.isRenderedNode;
var isIframeElement = hammerhead__default.utils.dom.isIframeElement;
var isInputElement = hammerhead__default.utils.dom.isInputElement;
var isButtonElement = hammerhead__default.utils.dom.isButtonElement;
var isFileInput = hammerhead__default.utils.dom.isFileInput;
var isTextAreaElement = hammerhead__default.utils.dom.isTextAreaElement;
var isAnchorElement = hammerhead__default.utils.dom.isAnchorElement;
var isImgElement = hammerhead__default.utils.dom.isImgElement;
var isFormElement = hammerhead__default.utils.dom.isFormElement;
var isLabelElement = hammerhead__default.utils.dom.isLabelElement;
var isSelectElement = hammerhead__default.utils.dom.isSelectElement;
var isRadioButtonElement = hammerhead__default.utils.dom.isRadioButtonElement;
var isColorInputElement = hammerhead__default.utils.dom.isColorInputElement;
var isCheckboxElement = hammerhead__default.utils.dom.isCheckboxElement;
var isOptionElement = hammerhead__default.utils.dom.isOptionElement;
var isSVGElement = hammerhead__default.utils.dom.isSVGElement;
var isMapElement = hammerhead__default.utils.dom.isMapElement;
var isBodyElement = hammerhead__default.utils.dom.isBodyElement;
var isHtmlElement = hammerhead__default.utils.dom.isHtmlElement;
var isDocument = hammerhead__default.utils.dom.isDocument;
var isWindow = hammerhead__default.utils.dom.isWindow;
var isTextEditableInput = hammerhead__default.utils.dom.isTextEditableInput;
var isTextEditableElement = hammerhead__default.utils.dom.isTextEditableElement;
var isTextEditableElementAndEditingAllowed = hammerhead__default.utils.dom.isTextEditableElementAndEditingAllowed;
var isContentEditableElement = hammerhead__default.utils.dom.isContentEditableElement;
var isDomElement = hammerhead__default.utils.dom.isDomElement;
var isShadowUIElement = hammerhead__default.utils.dom.isShadowUIElement;
var isElementFocusable = hammerhead__default.utils.dom.isElementFocusable;
var isHammerheadAttr = hammerhead__default.utils.dom.isHammerheadAttr;
var isElementReadOnly = hammerhead__default.utils.dom.isElementReadOnly;
var getScrollbarSize = hammerhead__default.utils.dom.getScrollbarSize;
var getMapContainer = hammerhead__default.utils.dom.getMapContainer;
var getTagName = hammerhead__default.utils.dom.getTagName;
var closest = hammerhead__default.utils.dom.closest;
var getParents = hammerhead__default.utils.dom.getParents;
var findParent = hammerhead__default.utils.dom.findParent;
var getTopSameDomainWindow = hammerhead__default.utils.dom.getTopSameDomainWindow;
function getElementsWithTabIndex(elements) {
return filter(elements, function (el) { return el.tabIndex > 0; });
}
function getElementsWithoutTabIndex(elements) {
return filter(elements, function (el) { return el.tabIndex <= 0; });
}
function sortElementsByFocusingIndex(elements) {
if (!elements || !elements.length)
return [];
var elementsWithTabIndex = getElementsWithTabIndex(elements);
//iframes
var iframes = filter(elements, function (el) { return isIframeElement(el); });
if (!elementsWithTabIndex.length) {
if (iframes.length)
elements = insertIframesContentElements(elements, iframes);
return elements;
}
elementsWithTabIndex = elementsWithTabIndex.sort(sortBy('tabIndex'));
var elementsWithoutTabIndex = getElementsWithoutTabIndex(elements);
if (iframes.length)
return insertIframesContentElements(elementsWithTabIndex, iframes).concat(insertIframesContentElements(elementsWithoutTabIndex, iframes));
return elementsWithTabIndex.concat(elementsWithoutTabIndex);
}
function insertIframesContentElements(elements, iframes) {
var sortedIframes = sortElementsByTabIndex(iframes);
var results = [];
var iframesElements = [];
var iframeFocusedElements = [];
var i = 0;
for (i = 0; i < sortedIframes.length; i++) {
//NOTE: We can get elements of the same domain iframe only
try {
iframeFocusedElements = getFocusableElements(nativeMethods$2.contentDocumentGetter.call(sortedIframes[i]));
}
catch (e) {
iframeFocusedElements = [];
}
iframesElements.push(sortElementsByFocusingIndex(iframeFocusedElements));
}
for (i = 0; i < elements.length; i++) {
results.push(elements[i]);
if (isIframeElement(elements[i])) {
if (browserUtils$1.isIE) {
results.pop();
var iFrameElements = iframesElements[indexOf(iframes, elements[i])];
var elementsWithTabIndex = getElementsWithTabIndex(iFrameElements);
var elementsWithoutTabIndexArray = getElementsWithoutTabIndex(iFrameElements);
elementsWithTabIndex = elementsWithTabIndex.sort(sortBy('tabIndex'));
results = results.concat(elementsWithTabIndex);
results.push(elements[i]);
results = results.concat(elementsWithoutTabIndexArray);
}
else {
if (browserUtils$1.isWebKit && iframesElements[indexOf(iframes, elements[i])].length)
results.pop();
results = results.concat(iframesElements[indexOf(iframes, elements[i])]);
}
}
}
return results;
}
function sortElementsByTabIndex(elements) {
var elementsWithTabIndex = getElementsWithTabIndex(elements);
if (!elementsWithTabIndex.length)
return elements;
return elementsWithTabIndex.sort(sortBy('tabIndex')).concat(getElementsWithoutTabIndex(elements));
}
function sortBy(property) {
return function (a, b) {
if (a[property] < b[property])
return -1;
if (a[property] > b[property])
return 1;
return 0;
};
}
function getFocusableElements(doc, sort) {
if (sort === void 0) { sort = false; }
// NOTE: We don't take into account the case of embedded contentEditable
// elements and specify the contentEditable attribute for focusable elements
var allElements = doc.querySelectorAll('*');
var invisibleElements = getInvisibleElements(allElements);
var inputElementsRegExp = /^(input|button|select|textarea)$/;
var focusableElements = [];
var element = null;
var tagName = null;
var tabIndex = null;
var needPush = false;
for (var i = 0; i < allElements.length; i++) {
element = allElements[i];
tagName = getTagName(element);
tabIndex = getTabIndexAttributeIntValue(element);
needPush = false;
if (element.disabled)
continue;
if (getElementStyleProperty(element, 'display') === 'none' || getElementStyleProperty(element, 'visibility') === 'hidden')
continue;
if ((browserUtils$1.isIE || browserUtils$1.isAndroid) && isOptionElement(element))
continue;
if (tabIndex !== null && tabIndex < 0)
continue;
if (inputElementsRegExp.test(tagName))
needPush = true;
else if (browserUtils$1.isIE && isIframeElement(element))
focusableElements.push(element);
else if (isAnchorElement(element) && element.hasAttribute('href'))
needPush = element.getAttribute('href') !== '' || !browserUtils$1.isIE || tabIndex !== null;
var contentEditableAttr = element.getAttribute('contenteditable');
if (contentEditableAttr === '' || contentEditableAttr === 'true')
needPush = true;
if (tabIndex !== null)
needPush = true;
if (needPush)
focusableElements.push(element);
}
//NOTE: remove children of invisible elements
var result = filter(focusableElements, function (el) { return !containsElement(invisibleElements, el); });
if (sort)
result = sortElementsByFocusingIndex(result);
return result;
}
function getInvisibleElements(elements) {
var invisibleElements = [];
for (var i = 0; i < elements.length; i++) {
if (getElementStyleProperty(elements[i], 'display') === 'none')
invisibleElements.push(elements[i]);
}
return invisibleElements;
}
function getTabIndexAttributeIntValue(el) {
var tabIndex = el.getAttribute('tabIndex');
if (tabIndex !== null) {
tabIndex = parseInt(tabIndex, 10);
tabIndex = isNaN(tabIndex) ? null : tabIndex;
}
return tabIndex;
}
function containsElement(elements, element) {
if (elements.contains)
return elements.contains(element);
return some(elements, function (parent) { return parent.contains(element); });
}
function getTextareaIndentInLine(textarea, position) {
var textareaValue = getTextAreaValue(textarea);
if (!textareaValue)
return 0;
var topPart = textareaValue.substring(0, position);
var linePosition = topPart.lastIndexOf('\n') === -1 ? 0 : topPart.lastIndexOf('\n') + 1;
return position - linePosition;
}
function getTextareaLineNumberByPosition(textarea, position) {
var textareaValue = getTextAreaValue(textarea);
var lines = textareaValue.split('\n');
var topPartLength = 0;
var line = 0;
for (var i = 0; topPartLength <= position; i++) {
if (position <= topPartLength + lines[i].length) {
line = i;
break;
}
topPartLength += lines[i].length + 1;
}
return line;
}
function getTextareaPositionByLineAndOffset(textarea, line, offset) {
var textareaValue = getTextAreaValue(textarea);
var lines = textareaValue.split('\n');
var lineIndex = 0;
for (var i = 0; i < line; i++)
lineIndex += lines[i].length + 1;
return lineIndex + offset;
}
// NOTE: the form is also submitted on enter key press if there is only one input of certain
// types (referred to as types that block implicit submission in the HTML5 standard) on the
// form and this input is focused (http://www.w3.org/TR/html5/forms.html#implicit-submission)
function blocksImplicitSubmission(el) {
var inputTypeRegExp = null;
if (browserUtils$1.isSafari)
inputTypeRegExp = /^(text|password|color|date|time|datetime|datetime-local|email|month|number|search|tel|url|week|image)$/i;
else if (browserUtils$1.isFirefox)
inputTypeRegExp = /^(text|password|date|time|datetime|datetime-local|email|month|number|search|tel|url|week|image)$/i;
else if (browserUtils$1.isIE)
inputTypeRegExp = /^(text|password|color|date|time|datetime|datetime-local|email|file|month|number|search|tel|url|week|image)$/i;
else
inputTypeRegExp = /^(text|password|datetime|email|number|search|tel|url|image)$/i;
return inputTypeRegExp.test(el.type);
}
function isEditableElement(el, checkEditingAllowed) {
return checkEditingAllowed ?
isTextEditableElementAndEditingAllowed(el) || isContentEditableElement(el) :
isTextEditableElement(el) || isContentEditableElement(el);
}
function isElementContainsNode(parentElement, childNode) {
if (isTheSameNode(childNode, parentElement))
return true;
var childNodes = nativeMethods$2.nodeChildNodesGetter.call(parentElement);
var length = getChildNodesLength(childNodes);
for (var i = 0; i < length; i++) {
var el = childNodes[i];
if (!isShadowUIElement(el) && isElementContainsNode(el, childNode))
return true;
}
return false;
}
function isOptionGroupElement(element) {
return hammerhead__default.utils.dom.instanceToString(element) === '[object HTMLOptGroupElement]';
}
function getElementIndexInParent(parent, child) {
var children = parent.querySelectorAll(getTagName(child));
return indexOf(children, child);
}
function isTheSameNode(node1, node2) {
//NOTE: Mozilla has not isSameNode method
if (node1 && node2 && node1.isSameNode)
return node1.isSameNode(node2);
return node1 === node2;
}
function getElementDescription(el) {
var attributes = {
id: 'id',
name: 'name',
'class': 'className'
};
var res = [];
res.push('<');
res.push(getTagName(el));
for (var attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
var val = el[attributes[attr]];
if (val)
res.push(' ' + attr + '="' + val + '"');
}
}
res.push('>');
return res.join('');
}
function getFocusableParent(el) {
var parents = getParents(el);
for (var i = 0; i < parents.length; i++) {
if (isElementFocusable(parents[i]))
return parents[i];
}
return null;
}
function remove$1(el) {
if (el && el.parentElement)
el.parentElement.removeChild(el);
}
function isIFrameWindowInDOM(win) {
//NOTE: In MS Edge, if an iframe is removed from DOM, the browser throws an exception when accessing window.top
//and window.frameElement. Fortunately, setTimeout is set to undefined in this case.
if (!win.setTimeout)
return false;
var frameElement = null;
try {
//NOTE: This may raise a cross-domain policy error in some browsers.
frameElement = win.frameElement;
}
catch (e) {
return !!win.top;
}
// NOTE: in Firefox and WebKit, frameElement is null for cross-domain iframes even if they are in the DOM.
// But these browsers don't execute scripts in removed iframes, so we suppose that the iframe is in the DOM.
if ((browserUtils$1.isFirefox || browserUtils$1.isWebKit) && win.top !== win && !frameElement)
return true;
return !!(frameElement && nativeMethods$2.contentDocumentGetter.call(frameElement));
}
function isTopWindow(win) {
try {
//NOTE: MS Edge throws an exception when trying to access window.top from an iframe removed from DOM
return win.top === win;
}
catch (e) {
return false;
}
}
function findIframeByWindow(iframeWindow, iframeDestinationWindow) {
var iframes = (iframeDestinationWindow || window).document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
if (nativeMethods$2.contentWindowGetter.call(iframes[i]) === iframeWindow)
return iframes[i];
}
return null;
}
function isEditableFormElement(element) {
return isTextEditableElement(element) || isSelectElement(element);
}
function getCommonAncestor(element1, element2) {
if (isTheSameNode(element1, element2))
return element1;
var el1Parents = [element1].concat(getParents(element1));
var commonAncestor = element2;
while (commonAncestor) {
if (indexOf(el1Parents, commonAncestor) > -1)
return commonAncestor;
commonAncestor = nativeMethods$2.nodeParentNodeGetter.call(commonAncestor);
}
return commonAncestor;
}
function getChildrenLength(children) {
return nativeMethods$2.htmlCollectionLengthGetter.call(children);
}
function getChildNodesLength(childNodes) {
return nativeMethods$2.nodeListLengthGetter.call(childNodes);
}
function getInputValue(input) {
return nativeMethods$2.inputValueGetter.call(input);
}
function getTextAreaValue(textArea) {
return nativeMethods$2.textAreaValueGetter.call(textArea);
}
function setInputValue(input, value) {
return nativeMethods$2.inputValueSetter.call(input, value);
}
function setTextAreaValue(textArea, value) {
return nativeMethods$2.textAreaValueSetter.call(textArea, value);
}
function getElementValue(element) {
if (isInputElement(element))
return getInputValue(element);
else if (isTextAreaElement(element))
return getTextAreaValue(element);
/*eslint-disable no-restricted-properties*/
return element.value;
/*eslint-enable no-restricted-properties*/
}
function setElementValue(element, value) {
if (isInputElement(element))
return setInputValue(element, value);
else if (isTextAreaElement(element))
return setTextAreaValue(element, value);
/*eslint-disable no-restricted-properties*/
element.value = value;
/*eslint-enable no-restricted-properties*/
return value;
}
function isShadowElement(element) {
return element && element.getRootNode && findDocument(element) !== element.getRootNode();
}
function contains(element, target) {
if (!element || !target)
return false;
if (element.contains)
return element.contains(target);
return !!findParent(target, true, function (node) { return node === element; });
}
var domUtils = /*#__PURE__*/Object.freeze({
__proto__: null,
getActiveElement: getActiveElement,
findDocument: findDocument,
isElementInDocument: isElementInDocument,
isElementInIframe: isElementInIframe,
getIframeByElement: getIframeByElement,
isCrossDomainWindows: isCrossDomainWindows,
getSelectParent: getSelectParent,
getChildVisibleIndex: getChildVisibleIndex,
getSelectVisibleChildren: getSelectVisibleChildren,
isElementNode: isElementNode,
isTextNode: isTextNode,
isRenderedNode: isRenderedNode,
isIframeElement: isIframeElement,
isInputElement: isInputElement,
isButtonElement: isButtonElement,
isFileInput: isFileInput,
isTextAreaElement: isTextAreaElement,
isAnchorElement: isAnchorElement,
isImgElement: isImgElement,
isFormElement: isFormElement,
isLabelElement: isLabelElement,
isSelectElement: isSelectElement,
isRadioButtonElement: isRadioButtonElement,
isColorInputElement: isColorInputElement,
isCheckboxElement: isCheckboxElement,
isOptionElement: isOptionElement,
isSVGElement: isSVGElement,
isMapElement: isMapElement,
isBodyElement: isBodyElement,
isHtmlElement: isHtmlElement,
isDocument: isDocument,
isWindow: isWindow,
isTextEditableInput: isTextEditableInput,
isTextEditableElement: isTextEditableElement,
isTextEditableElementAndEditingAllowed: isTextEditableElementAndEditingAllowed,
isContentEditableElement: isContentEditableElement,
isDomElement: isDomElement,
isShadowUIElement: isShadowUIElement,
isElementFocusable: isElementFocusable,
isHammerheadAttr: isHammerheadAttr,
isElementReadOnly: isElementReadOnly,
getScrollbarSize: getScrollbarSize,
getMapContainer: getMapContainer,
getTagName: getTagName,
closest: closest,
getParents: getParents,
findParent: findParent,
getTopSameDomainWindow: getTopSameDomainWindow,
getFocusableElements: getFocusableElements,
containsElement: containsElement,
getTextareaIndentInLine: getTextareaIndentInLine,
getTextareaLineNumberByPosition: getTextareaLineNumberByPosition,
getTextareaPositionByLineAndOffset: getTextareaPositionByLineAndOffset,
blocksImplicitSubmission: blocksImplicitSubmission,
isEditableElement: isEditableElement,
isElementContainsNode: isElementContainsNode,
isOptionGroupElement: isOptionGroupElement,
getElementIndexInParent: getElementIndexInParent,
isTheSameNode: isTheSameNode,
getElementDescription: getElementDescription,
getFocusableParent: getFocusableParent,
remove: remove$1,
isIFrameWindowInDOM: isIFrameWindowInDOM,
isTopWindow: isTopWindow,
findIframeByWindow: findIframeByWindow,
isEditableFormElement: isEditableFormElement,
getCommonAncestor: getCommonAncestor,
getChildrenLength: getChildrenLength,
getChildNodesLength: getChildNodesLength,
getInputValue: getInputValue,
getTextAreaValue: getTextAreaValue,
setInputValue: setInputValue,
setTextAreaValue: setTextAreaValue,
getElementValue: getElementValue,
setElementValue: setElementValue,
isShadowElement: isShadowElement,
contains: contains
});
var Promise$2 = hammerhead__default.Promise;
var nativeMethods$3 = hammerhead__default.nativeMethods;
var listeners = hammerhead__default.eventSandbox.listeners;
var browserUtils$2 = hammerhead__default.utils.browser;
// Imported form the hammerhead
var BUTTON = hammerhead__default.utils.event.BUTTON;
var BUTTONS_PARAMETER = hammerhead__default.utils.event.BUTTONS_PARAMETER;
var DOM_EVENTS = hammerhead__default.utils.event.DOM_EVENTS;
var WHICH_PARAMETER = hammerhead__default.utils.event.WHICH_PARAMETER;
var preventDefault = hammerhead__default.utils.event.preventDefault;
function bind(el, event, handler, useCapture) {
if (browserUtils$2.isIE11 && isWindow(el))
nativeMethods$3.windowAddEventListener.call(el, event, handler, useCapture);
else
nativeMethods$3.addEventListener.call(el, event, handler, useCapture);
}
function unbind(el, event, handler, useCapture) {
if (browserUtils$2.isIE11 && isWindow(el))
nativeMethods$3.windowRemoveEventListener.call(el, event, handler, useCapture);
else
nativeMethods$3.removeEventListener.call(el, event, handler, useCapture);
}
// Document ready
var waitForDomContentLoaded = function () {
// NOTE: We can't use a regular Promise here, because window.load event can happen in the same event loop pass
// The default Promise will call resolve handlers in the next pass, and load event will be lost.
var resolveHandlers = [];
function createPromiseResolver(resolveHandler) {
return new Promise$2(function (resolve) { return resolveHandlers.push(function () { return resolve(resolveHandler()); }); });
}
var isReady = false;
function ready() {
if (isReady)
return;
if (!document.body) {
nativeMethods$3.setTimeout.call(window, ready, 1);
return;
}
isReady = true;
resolveHandlers.forEach(function (handler) { return handler(); });
}
function onContentLoaded() {
if (!isIFrameWindowInDOM(window) && !isTopWindow(window))
return;
unbind(document, 'DOMContentLoaded', onContentLoaded);
ready();
}
if (document.readyState === 'complete')
nativeMethods$3.setTimeout.call(window, onContentLoaded, 1);
else
bind(document, 'DOMContentLoaded', onContentLoaded);
return { then: function (handler) { return createPromiseResolver(handler); } };
};
var waitForWindowLoad = function () { return new Promise$2(function (resolve) { return bind(window, 'load', resolve); }); };
function documentReady(pageLoadTimeout) {
if (pageLoadTimeout === void 0) { pageLoadTimeout = 0; }
return waitForDomContentLoaded()
.then(function () {
if (!listeners.getEventListeners(window, 'load').length)
return null;
return Promise$2.race([waitForWindowLoad(), delay(pageLoadTimeout)]);
});
}
var eventUtils = /*#__PURE__*/Object.freeze({
__proto__: null,
BUTTON: BUTTON,
BUTTONS_PARAMETER: BUTTONS_PARAMETER,
DOM_EVENTS: DOM_EVENTS,
WHICH_PARAMETER: WHICH_PARAMETER,
preventDefault: preventDefault,
bind: bind,
unbind: unbind,
documentReady: documentReady
});
var MESSAGE = {
ready: 'ready',
readyForBrowserManipulation: 'ready-for-browser-manipulation',
waitForFileDownload: 'wait-for-file-download'
};
var Promise$3 = hammerhead__default.Promise;
var browserUtils$3 = hammerhead__default.utils.browser;
var nativeMethods$4 = hammerhead__default.nativeMethods;
var transport = hammerhead__default.transport;
var DEFAULT_BARRIER_TIMEOUT = 400;
var SHORT_WAIT_FOR_UNLOAD_TIMEOUT = 30;
var FILE_DOWNLOAD_CHECK_DELAY = 500;
var MAX_UNLOADING_TIMEOUT = 15 * 1000;
var waitingForUnload = false;
var waitingForUnloadTimeoutId = null;
var waitingPromiseResolvers = [];
var unloading = false;
var pageNavigationTriggeredListener = null;
var pageNavigationTriggered = false;
function onBeforeUnload() {
if (!browserUtils$3.isIE) {
unloading = true;
return;
}
prolongUnloadWaiting(SHORT_WAIT_FOR_UNLOAD_TIMEOUT);
delay(0)
.then(function () {
// NOTE: except file downloading
if (document.readyState === 'loading') {
var activeElement = nativeMethods$4.documentActiveElementGetter.call(document);
if (!activeElement || !isAnchorElement(activeElement) || !activeElement.hasAttribute('download'))
unloading = true;
}
});
}
function prolongUnloadWaiting(timeout) {
if (waitingForUnloadTimeoutId)
nativeMethods$4.clearTimeout.call(window, waitingForUnloadTimeoutId);
waitingForUnload = true;
waitingForUnloadTimeoutId = nativeMethods$4.setTimeout.call(window, function () {
waitingForUnloadTimeoutId = null;
waitingForUnload = false;
waitingPromiseResolvers.forEach(function (resolve) { return resolve(); });
waitingPromiseResolvers = [];
}, timeout);
}
function waitForFileDownload() {
return new Promise$3(function (resolve) {
nativeMethods$4.setTimeout.call(window, function () {
transport
.queuedAsyncServiceMsg({ cmd: MESSAGE.waitForFileDownload })
.then(function (fileDownloadingHandled) {
// NOTE: we use a flag to confirm file download because if unload
// is raised the browser can respond with an empty string
if (fileDownloadingHandled)
resolve();
});
}, FILE_DOWNLOAD_CHECK_DELAY);
});
}
// API
function init() {
hammerhead__default.on(hammerhead__default.EVENTS.beforeUnload, onBeforeUnload);
bind(window, 'unload', function () {
unloading = true;
});
}
function watchForPageNavigationTriggers() {
pageNavigationTriggeredListener = function () {
pageNavigationTriggered = true;
};
hammerhead__default.on(hammerhead__default.EVENTS.pageNavigationTriggered, pageNavigationTriggeredListener);
}
function wait(timeout) {
var waitForUnloadingPromise = new Promise$3(function (resolve) {
if (timeout === void 0)
timeout = !pageNavigationTriggeredListener || pageNavigationTriggered ? DEFAULT_BARRIER_TIMEOUT : 0;
if (pageNavigationTriggeredListener) {
hammerhead__default.off(hammerhead__default.EVENTS.pageNavigationTriggered, pageNavigationTriggeredListener);
pageNavigationTriggeredListener = null;
}
delay(timeout)
.then(function () {
if (unloading) {
waitForFileDownload()
.then(function () {
unloading = false;
resolve();
});
return;
}
if (!waitingForUnload)
resolve();
else
waitingPromiseResolvers.push(resolve);
});
});
// NOTE: sometimes the page isn't actually unloaded after the beforeunload event
// fires (see issues #664, #437). To avoid test hanging, we resolve the unload
// barrier waiting promise in MAX_UNLOADING_TIMEOUT. We can improve this logic when
// the https://github.com/DevExpress/testcafe-hammerhead/issues/667 issue is fixed.
var watchdog = delay(MAX_UNLOADING_TIMEOUT)
.then(function () {
unloading = false;
});
return Promise$3.race([waitForUnloadingPromise, watchdog]);
}
var pageUnloadBarrier = /*#__PURE__*/Object.freeze({
__proto__: null,
init: init,
watchForPageNavigationTriggers: watchForPageNavigationTriggers,
wait: wait
});
var listeners$1 = hammerhead.eventSandbox.listeners;
var ScrollController = /** @class */ (function () {
function ScrollController() {
this.initialized = false;
this.stopPropagationFlag = false;
this.events = new EventEmitter();
}
ScrollController.prototype._internalListener = function (event, dispatched, preventEvent, cancelHandlers, stopPropagation) {
this.events.emit('scroll', event);
if (this.stopPropagationFlag) {
cancelHandlers();
stopPropagation();
}
};
ScrollController.prototype.init = function () {
var _this = this;
if (this.initialized)
return;
this.initialized = true;
listeners$1.initElementListening(window, ['scroll']);
listeners$1.addFirstInternalHandler(window, ['scroll'], function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return _this._internalListener.apply(_this, args);
});
};
ScrollController.prototype.waitForScroll = function (scrollElement) {
var _this = this;
var promiseResolver = null;
var promise = new hammerhead.Promise(function (resolve) {
promiseResolver = resolve;
});
promise.cancel = function () { return _this.events.off('scroll', promiseResolver); };
if (this.initialized)
this.handleScrollEvents(scrollElement, promiseResolver);
else
promiseResolver();
return promise;
};
ScrollController.prototype.handleScrollEvents = function (el, handler) {
var _this = this;
this.events.once('scroll', handler);
if (isShadowElement(el)) {
listeners$1.initElementListening(el, ['scroll']);
listeners$1.addFirstInternalHandler(el, ['scroll'], function () {
var args = [];
for (var _i = 0; _i < arguments.len