(function() { "use strict"; var idCounter = 0; function getInViewCenterPoint(rect) { var left = Math.max(0, rect.left); var right = Math.min(window.innerWidth, rect.right); var top = Math.max(0, rect.top); var bottom = Math.min(window.innerHeight, rect.bottom); var x = 0.5 * (left + right); var y = 0.5 * (top + bottom); return [x, y]; } function getPointerInteractablePaintTree(element) { if (!window.document.contains(element)) { return []; } var rectangles = element.getClientRects(); if (rectangles.length === 0) { return []; } var centerPoint = getInViewCenterPoint(rectangles[0]); if ("elementsFromPoint" in document) { return document.elementsFromPoint(centerPoint[0], centerPoint[1]); } else if ("msElementsFromPoint" in document) { var rv = document.msElementsFromPoint(centerPoint[0], centerPoint[1]); return Array.prototype.slice.call(rv ? rv : []); } else { throw new Error("document.elementsFromPoint unsupported"); } } function inView(element) { var pointerInteractablePaintTree = getPointerInteractablePaintTree(element); return pointerInteractablePaintTree.indexOf(element) !== -1; } /** * @namespace */ window.test_driver = { /** * Trigger user interaction in order to grant additional privileges to * a provided function. * * https://html.spec.whatwg.org/#triggered-by-user-activation * * @param {String} intent - a description of the action which much be * triggered by user interaction * @param {Function} action - code requiring escalated privileges * * @returns {Promise} fulfilled following user interaction and * execution of the provided `action` function; * rejected if interaction fails or the provided * function throws an error */ bless: function(intent, action) { var button = document.createElement("button"); button.innerHTML = "This test requires user interaction.
" + "Please click here to allow " + intent + "."; button.id = "wpt-test-driver-bless-" + (idCounter += 1); const elem = document.body || document.documentElement; elem.appendChild(button); return new Promise(function(resolve, reject) { button.addEventListener("click", resolve); test_driver.click(button).catch(reject); }).then(function() { button.remove(); if (typeof action === "function") { return action(); } }); }, /** * Triggers a user-initiated click * * This matches the behaviour of the {@link * https://w3c.github.io/webdriver/webdriver-spec.html#element-click|WebDriver * Element Click command}. * * @param {Element} element - element to be clicked * @returns {Promise} fulfilled after click occurs, or rejected in * the cases the WebDriver command errors */ click: function(element) { if (window.top !== window) { return Promise.reject(new Error("can only click in top-level window")); } if (!window.document.contains(element)) { return Promise.reject(new Error("element in different document or shadow tree")); } if (!inView(element)) { element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"}); } var pointerInteractablePaintTree = getPointerInteractablePaintTree(element); if (pointerInteractablePaintTree.length === 0 || !element.contains(pointerInteractablePaintTree[0])) { return Promise.reject(new Error("element click intercepted error")); } var rect = element.getClientRects()[0]; var centerPoint = getInViewCenterPoint(rect); return window.test_driver_internal.click(element, {x: centerPoint[0], y: centerPoint[1]}); }, /** * Send keys to an element * * This matches the behaviour of the {@link * https://w3c.github.io/webdriver/webdriver-spec.html#element-send-keys|WebDriver * Send Keys command}. * * @param {Element} element - element to send keys to * @param {String} keys - keys to send to the element * @returns {Promise} fulfilled after keys are sent, or rejected in * the cases the WebDriver command errors */ send_keys: function(element, keys) { if (window.top !== window) { return Promise.reject(new Error("can only send keys in top-level window")); } if (!window.document.contains(element)) { return Promise.reject(new Error("element in different document or shadow tree")); } if (!inView(element)) { element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"}); } var pointerInteractablePaintTree = getPointerInteractablePaintTree(element); if (pointerInteractablePaintTree.length === 0 || !element.contains(pointerInteractablePaintTree[0])) { return Promise.reject(new Error("element send_keys intercepted error")); } return window.test_driver_internal.send_keys(element, keys); }, /** * Freeze the current page * * The freeze function transitions the page from the HIDDEN state to * the FROZEN state as described in {@link * https://github.com/WICG/page-lifecycle/blob/master/README.md|Lifecycle API * for Web Pages} * * @returns {Promise} fulfilled after the freeze request is sent, or rejected * in case the WebDriver command errors */ freeze: function() { return window.test_driver_internal.freeze(); } }; window.test_driver_internal = { /** * Triggers a user-initiated click * * @param {Element} element - element to be clicked * @param {{x: number, y: number} coords - viewport coordinates to click at * @returns {Promise} fulfilled after click occurs or rejected if click fails */ click: function(element, coords) { return Promise.reject(new Error("unimplemented")); }, /** * Triggers a user-initiated click * * @param {Element} element - element to be clicked * @param {String} keys - keys to send to the element * @returns {Promise} fulfilled after keys are sent or rejected if click fails */ send_keys: function(element, keys) { return Promise.reject(new Error("unimplemented")); }, /** * Freeze the current page * * @returns {Promise} fulfilled after freeze request is sent, otherwise * it gets rejected */ freeze: function() { return Promise.reject(new Error("unimplemented")); } }; })();