UNPKG

@sap_oss/wdio-qmate-service

Version:

[![REUSE status](https://api.reuse.software/badge/github.com/SAP/wdio-qmate-service)](https://api.reuse.software/info/github.com/SAP/wdio-qmate-service)[![Node.js CI](https://github.com/SAP/wdio-qmate-service/actions/workflows/node.js.yml/badge.svg)](http

532 lines 26.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserInteraction = void 0; const verboseLogger_1 = require("../../helper/verboseLogger"); const errorHandler_1 = __importDefault(require("../../helper/errorHandler")); const elementHighlight_1 = __importDefault(require("../../helper/elementHighlight")); const elementResolving_1 = require("../../helper/elementResolving"); const inputValidation_1 = require("../../helper/inputValidation"); const constants_1 = require("../constants"); const userInteraction_constants_1 = require("../common/constants/userInteraction.constants"); /** * @class userInteraction * @memberof nonUi5 */ class UserInteraction { vlf = new verboseLogger_1.VerboseLoggerFactory("nonUi5", "userInteraction"); ErrorHandler = new errorHandler_1.default(); // =================================== CLICK =================================== /** * @function click * @memberOf nonUi5.userInteraction * @description Clicks on the passed element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [timeout=30000] - The timeout to wait (ms). * @example const elem = await nonUi5.element.getById("button01"); * await nonUi5.userInteraction.click(elem); */ async click(elementOrSelector, timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT) { const vl = this.vlf.initLog(this.click); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("click"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log("Expecting element to be displayed and enabled"); await Promise.all([ expect(element).toBeDisplayed({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is displayed.` }), expect(element).toBeEnabled({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is enabled.` }) ]); vl.log("Clicking the element"); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); await element.click(); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function clickAndRetry * @memberOf nonUi5.userInteraction * @description Clicks on the passed element, retries in case it fails. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [timeout=30000] - The timeout to wait (ms). * @param {Number} [retries=3] - The number of retries, can be set in config for all functions under params stepsRetries. * @param {Number} [interval=5000] - The delay between the retries (ms). Can be set in config for all functions under params.stepRetriesIntervals. * @example const elem = await nonUi5.element.getById("button01"); * await nonUi5.userInteraction.clickAndRetry(elem); */ async clickAndRetry(elementOrSelector, timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT, retries = 3, interval = 5000) { const vl = this.vlf.initLog(this.click); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log("Clicking the element"); return await util.function.retry(this.click, [element, timeout], retries, interval, this); } catch (error) { return this.ErrorHandler.logException(error); } } /** * @function doubleClick * @memberOf nonUi5.userInteraction * @description Double Clicks on the passed element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [timeout=30000] - The timeout to wait (ms). * @example const elem = await nonUi5.element.getById("button01"); * await nonUi5.userInteraction.doubleClick(elem); */ async doubleClick(elementOrSelector, timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT) { const vl = this.vlf.initLog(this.doubleClick); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("doubleClick"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log("Expecting element to be displayed and enabled"); await Promise.all([ expect(element).toBeDisplayed({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is displayed.` }), expect(element).toBeEnabled({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is enabled.` }) ]); vl.log("Clicking the element"); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); await element.doubleClick(); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function rightClick * @memberOf nonUi5.userInteraction * @description Right Clicks on the passed element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [timeout=30000] - The timeout to wait (ms). * @example const elem = await nonUi5.element.getById("button01"); * await nonUi5.userInteraction.rightClick(elem); */ async rightClick(elementOrSelector, timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT) { const vl = this.vlf.initLog(this.rightClick); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("rightClick"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log("Expecting element to be displayed and enabled"); await Promise.all([ expect(element).toBeDisplayed({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is displayed.` }), expect(element).toBeEnabled({ wait: timeout, interval: constants_1.GLOBAL_DEFAULT_WAIT_INTERVAL, message: `Timeout '${+timeout / 1000}s' by waiting for element is enabled.` }) ]); vl.log("Clicking the element"); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); await element.click({ button: "right" }); } catch (error) { this.ErrorHandler.logException(error); } } // =================================== CHECK =================================== /** * @function check * @memberOf nonUi5.userInteraction * @description Checks the given checkbox. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @example await nonUi5.userInteraction.check(selector); */ async check(elementOrSelector) { const vl = this.vlf.initLog(this.check); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); const isSelected = await this._isItemSelected(element); if (!isSelected) { await this.click(element); } else { vl.log("Checkbox already selected."); } } catch (error) { this.ErrorHandler.logException(error); } } /** * @function uncheck * @memberOf nonUi5.userInteraction * @description Unchecks the given checkbox. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @example await nonUi5.userInteraction.uncheck(selector); */ async uncheck(elementOrSelector) { const vl = this.vlf.initLog(this.uncheck); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); const isSelected = await this._isItemSelected(element); if (isSelected) { await this.click(element); } else { vl.log("Checkbox already unchecked."); } } catch (error) { this.ErrorHandler.logException(error); } } // =================================== FILL =================================== /** * @function fill * @memberOf nonUi5.userInteraction * @description Fills the given value into the passed input. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {String | Number} value - The value to enter. * @example const elem = await nonUi5.element.getById("input01"); * await nonUi5.userInteraction.fill(elem, "Service 01"); */ async fill(elementOrSelector, value) { const vl = this.vlf.initLog(this.fill); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("fill"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); (0, inputValidation_1.validateValue)(value); vl.log(`Setting the value of element to ${value}`); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); await element.setValue(value); } catch (error) { this.ErrorHandler.logException(new Error(), error.message); } } /** * @function fillAndRetry * @memberOf nonUi5.userInteraction * @description Fills the given value into the passed input, retries in case of a failure. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {String | Number} value - The value to enter. * @param {Number} [retries=3] - The number of retries, can be set in config for all functions under params stepsRetries. * @param {Number} [interval=5000] - The delay between the retries (ms). Can be set in config for all functions under params.stepRetriesIntervals. * @example const elem = await nonUi5.element.getById("input01"); * await nonUi5.userInteraction.fillAndRetry(elem, "Service 01"); */ async fillAndRetry(elementOrSelector, value, retries = 3, interval = 5000) { const vl = this.vlf.initLog(this.fillAndRetry); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); (0, inputValidation_1.validateValue)(value); vl.log(`Setting the value of element to ${value}`); return util.function.retry(this.fill, [element, value], retries, interval, this); } catch (error) { this.ErrorHandler.logException(error); } } // =================================== CLEAR =================================== /** * @function clear * @memberOf nonUi5.userInteraction * @description Clears the passed input element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @example const elem = await nonUi5.element.getById("input01"); * await nonUi5.userInteraction.clear(elem); */ async clear(elementOrSelector) { const vl = this.vlf.initLog(this.clear); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("clear"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log(`Clearing the value of element`); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); return element.clearValue(); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function clearAndRetry * @memberOf nonUi5.userInteraction * @description Clears the passed input element, retries in case of a failure. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [retries=3] - The number of retries, can be set in config for all functions under params stepsRetries. * @param {Number} [interval=5000] - The delay between the retries (ms). Can be set in config for all functions under params.stepRetriesIntervals. * @example const elem = await nonUi5.element.getById("input01", 10000); * await nonUi5.userInteraction.clearAndRetry(elem); */ async clearAndRetry(elementOrSelector, retries = 3, interval = 5000) { const vl = this.vlf.initLog(this.clearAndRetry); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log(`Clearing the value of element`); return await util.function.retry(this.clear, [element], retries, interval, this); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function clearAndFill * @memberOf nonUi5.userInteraction * @description Clears and fills the passed input element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {String | Number} value - The value to enter in. * @example const elem = await nonUi5.element.getById("input01"); * await nonUi5.userInteraction.clearAndFill(elem, "Service 01"); */ async clearAndFill(elementOrSelector, value) { const vl = this.vlf.initLog(this.clearAndFill); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("clearAndFill"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); (0, inputValidation_1.validateValue)(value); await this.clear(element); vl.log(`Setting the value of element to ${value}`); if (highlightConfig.enable) await nonUi5.element.highlight(element, highlightConfig.duration, highlightConfig.color); await element.setValue(value); } catch (error) { this.ErrorHandler.logException(new Error(), error.message); } } /** * @function clearAndFillAndRetry * @memberOf nonUi5.userInteraction * @description Clears and fills the passed input, retries in case it fails. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {String | Number} value - The value to enter in. * @param {Number} [retries=3] - The number of retries, can be set in config for all functions under params stepsRetries. * @param {Number} [interval=5000] - The delay between the retries (ms). Can be set in config for all functions under params.stepRetriesIntervals. * @param {Boolean} [verify=true] - Specifies if the filled value should be verified. * @example const elem = await nonUi5.element.getById("input01"); * await nonUi5.userInteraction.clearAndFillAndRetry(elem, "Service 01"); */ async clearAndFillAndRetry(elementOrSelector, value, retries = 3, interval = 5000, verify = true) { const vl = this.vlf.initLog(this.clearAndFillAndRetry); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); (0, inputValidation_1.validateValue)(value); return await util.function.retry(async (elem, value) => { await this.clearAndFill(elem, value); if (verify) { const elemValue = await elem.getValue(); if (elemValue != value) throw new Error("Verification of value failed."); } }, [element, value], retries, interval, this); } catch (error) { this.ErrorHandler.logException(error); } } // =================================== OTHERS =================================== /** * @function mouseOverElement * @memberOf nonUi5.userInteraction * @description Moves the cursor/focus to the passed element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Number} [xOffset] - X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element. * @param {Number} [yOffset] - Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element. * @example const elem = await nonUi5.element.getById("dropdown42"); * await nonUi5.userInteraction.mouseOverElement(elem); */ async mouseOverElement(elementOrSelector, xOffset, yOffset) { const vl = this.vlf.initLog(this.mouseOverElement); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); vl.log("Moving mouse to element"); await element.moveTo({ xOffset, yOffset }); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function scrollToElement * @memberOf nonUi5.userInteraction * @description Scrolls an element into view. * @param {Element} elem - The target element to scroll to. * @param {String | Object} [alignment="center"] - The alignment option for scrolling. * Can be one of: "start", "center", "end", "nearest", or an object with properties: * - block: Vertical alignment ("start", "center", "end", "nearest"). * - inline: Horizontal alignment ("start", "center", "end", "nearest"). * * @example * // Scroll to element with center alignment. * const elem = await nonUi5.element.getById("footer01"); * await nonUi5.userInteraction.scrollToElement(elem, "center"); * * @example * // Scroll to element with custom alignment. * const elem = await nonUi5.element.getById("footer01"); * const alignment = { * block: "start", * inline: "center" * }; * await nonUi5.userInteraction.scrollToElement(elem, alignment); */ async scrollToElement(elementOrSelector, alignment = "center", timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT) { const vl = this.vlf.initLog(this.scrollToElement); let options = {}; try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector, timeout); if (typeof alignment === "string") { options = { block: alignment, inline: alignment }; } else if (typeof alignment === "object") { options = alignment; } vl.log("Scrolling to element"); await element.scrollIntoView(options); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function selectAll * @memberOf nonUi5.userInteraction * @description Performs "select all" (ctrl + a) at the element with the given selector. * @param {Object} [selector] - The selector describing the element. * @param {Number} [timeout=30000] - The timeout to wait (ms). * @example await nonUi5.userInteraction.selectAll(selector); */ async selectAll(selector, timeout = parseFloat(process.env.QMATE_CUSTOM_TIMEOUT) || constants_1.GLOBAL_DEFAULT_WAIT_TIMEOUT) { const vl = this.vlf.initLog(this.selectAll); if (selector !== undefined) await this.click(selector, timeout); else { util.console.info("Selector properties are undefined. Action will be performed on current element."); } await common.userInteraction.pressKey([userInteraction_constants_1.KeyCodes.CONTROL, "a"]); } /** * @function dragAndDrop * @memberOf nonUi5.userInteraction * @description Drags and drops the given element to the given target element. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @param {Object} targetElem - The target element to drop the element. * @example const elem = await nonUi5.element.getById("drag01"); * @example const targetElem = await nonUi5.element.getById("drop02"); * await nonUi5.userInteraction.dragAndDrop(elem, targetElem); */ async dragAndDrop(elementOrSelector, targetElem) { const vl = this.vlf.initLog(this.dragAndDrop); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); const sourceSize = await element.getSize(); const targetSize = await targetElem.getSize(); const sourceLocation = await element.getLocation(); const targetLocation = await targetElem.getLocation(); // Get centers of elements to move from center to center (e.g. to avoid errors in rounded elements) const sourceCenterLocation = { x: +Number(sourceSize.width / 2).toFixed(0) + +Number(sourceLocation.x).toFixed(0) + 1, y: +Number(sourceSize.height / 2).toFixed(0) + +Number(sourceLocation.y).toFixed(0) + 1 }; const targetCenterLocation = { x: +Number(targetSize.width / 2).toFixed(0) + +Number(targetLocation.x).toFixed(0) + 1, y: +Number(targetSize.height / 2).toFixed(0) + +Number(targetLocation.y).toFixed(0) + 1 }; await browser .action("pointer") .move({ duration: 0, x: sourceCenterLocation.x, y: sourceCenterLocation.y }) .down({ button: 0 }) //left button .move({ duration: 0, x: targetCenterLocation.x, y: targetCenterLocation.y }) .down({ button: 0 }) //left button .perform(); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function moveCursorAndClick * @memberOf nonUi5.userInteraction * @description Moves the cursor to the target element and clicks on it. Can be used for charts. * @param {Element | string} elementOrSelector - The element or CSS selector describing the element. * @example const elem = await nonUi5.element.getById("chartPartToClick"); * await nonUi5.userInteraction.moveCursorAndClick(elem); */ async moveCursorAndClick(elementOrSelector) { const vl = this.vlf.initLog(this.moveCursorAndClick); const highlightConfig = await elementHighlight_1.default.getElementHighlightData("moveCursorAndClick"); try { const element = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); await element.moveTo(); await element.click(); } catch (error) { this.ErrorHandler.logException(error); } } /** * @function clickElementInSvg * @memberOf nonUi5.userInteraction * @description Clicks on an inner element within a SVG element. * @param {Object | string} elementOrSelector - The SVG element or CSS selector describing the element. * @param {String} innerSelector - The CSS selector describing the inner element to be clicked. * @example const svgElem = await nonUi5.element.getByCss("svg"); * const innerSelector = "circle:nth-child(6)"; * await nonUi5.userInteraction.clickElementInSvg(svgElem, innerSelector); */ async clickElementInSvg(elementOrSelector, innerSelector) { const vl = this.vlf.initLog(this.clickElementInSvg); try { const svgElem = await (0, elementResolving_1.resolveCssSelectorOrElement)(elementOrSelector); const innerElem = await $(innerSelector); const svgPos = await svgElem.getLocation(); const innerPos = await innerElem.getLocation(); const svgSize = await svgElem.getSize(); const innerSize = await innerElem.getSize(); const diffX = innerPos.x - svgPos.x; const diffY = innerPos.y - svgPos.y; const centerOffsetX = -(svgSize.width / 2) + diffX + innerSize.width / 2; const centerOffsetY = -(svgSize.height / 2) + diffY + innerSize.height / 2; // @ts-ignore await svgElem.click({ x: parseInt(centerOffsetX), y: parseInt(centerOffsetY) }); } catch (error) { this.ErrorHandler.logException(error); } } // =================================== HELPER =================================== async _isItemSelected(element) { const ariaSelected = await element.getAttribute("aria-selected"); if (ariaSelected !== null && ariaSelected !== undefined) { return ariaSelected.toLowerCase() === "true"; } return await nonUi5.element.isSelected(element); } } exports.UserInteraction = UserInteraction; exports.default = new UserInteraction(); //# sourceMappingURL=userInteraction.js.map