UNPKG

@browserstack/testcafe

Version:

Automated browser testing for the modern web development stack.

1,163 lines (1,141 loc) 155 kB
// 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