UNPKG

nightwatch

Version:

Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.

920 lines (711 loc) 24.6 kB
const {WebElement, Origin, By} = require('selenium-webdriver'); const {Locator} = require('../../element'); const {isString} = require('../../utils'); module.exports = class MethodMappings { get driver() { return this.transport.driver; } get settings() { return this.transport.settings; } getWebElement(webElementOrString) { if (webElementOrString && (webElementOrString.webElement instanceof WebElement)) { webElementOrString = webElementOrString.webElement; } if (webElementOrString instanceof WebElement) { return webElementOrString; } if (isString(webElementOrString)) { return new WebElement(this.driver, webElementOrString); } throw new Error(`Unknown element: ${webElementOrString}`); } async runScriptForElement(scriptFn, webElementOrId) { const element = this.getWebElement(webElementOrId); const parentElementId = await element.getId(); const {elementKey} = this.transport; return this.driver.executeScript(scriptFn, {[elementKey]: parentElementId}); } async getElementByJs(scriptFn, webElementOrId) { try { const result = await this.runScriptForElement(scriptFn, webElementOrId); if (!result) { return { value: null, status: 0 }; } const {elementKey} = this.transport; const elementId = await result.getId(); const returnValue = { value: { [elementKey]: elementId }, status: 0, elementId }; Object.assign(returnValue.value, { get getId() { return function() { return elementId; }; } }); return returnValue; } catch (error) { if (error.name === 'NoSuchElementError') { return { status: 0, value: null }; } throw error; } } constructor(transport) { this.transport = transport; } executeFn(fn, name, args) { if (!Array.isArray(args)) { args = [args]; } return this.driver[name](fn, ...args); } get methods() { return { /////////////////////////////////////////////////////////// // Session related /////////////////////////////////////////////////////////// async sessionAction(action) { switch (action) { case 'DELETE': { await this.driver.quit(); return { state: 'success', value: null }; } default: return this.driver.getSession(); } }, getSessions() { return '/sessions'; }, getStatus() { return '/status'; }, session: { /////////////////////////////////////////////////////////// // Timeouts /////////////////////////////////////////////////////////// async setTimeoutType(type, value) { await this.driver.manage().setTimeouts({ [type]: value }); return null; }, async setTimeoutsAsyncScript(value) { await this.driver.manage().setTimeouts({script: value}); return null; }, async setTimeoutsImplicitWait(value) { await this.driver.manage().setTimeouts({implicit: value}); return null; }, getTimeouts() { return this.driver.manage().getTimeouts(); }, /////////////////////////////////////////////////////////// // Session log /////////////////////////////////////////////////////////// async getSessionLogTypes() { const value = await this.driver.manage().logs().getAvailableLogTypes(); return { value }; }, async getLogContents(type) { const value = await this.driver.manage().logs().get(type); return { value: value.map(item => { return { level: { value: item.level.value, name: item.level.name }, type: item.type, timestamp: item.timestamp, message: item.message }; }) }; }, /////////////////////////////////////////////////////////// // Navigation /////////////////////////////////////////////////////////// async navigateTo(url) { await this.driver.navigate().to(url); return null; }, async getCurrentUrl() { const url = await this.driver.getCurrentUrl(); return url; }, async navigateBack() { await this.driver.navigate().back(); return null; }, async navigateForward() { await this.driver.navigate().forward(); return null; }, async pageRefresh() { await this.driver.navigate().refresh(); return null; }, async getPageTitle() { const title = await this.driver.getTitle(); return title; }, /////////////////////////////////////////////////////////// // Windows /////////////////////////////////////////////////////////// async switchToWindow(handleOrName) { await this.driver.switchTo().window(handleOrName); return null; }, async closeWindow() { return this.driver.close(); }, /** * @returns {string} */ async getWindowHandle() { const value = await this.driver.getWindowHandle(); return value; }, async getAllWindowHandles() { const value = await this.driver.getAllWindowHandles(); return { value }; }, async getWindowPosition() { const {x, y} = await this.driver.manage().window().getRect(); return { value: { x, y } }; }, async maximizeWindow(windowHandle) { await this.driver.manage().window().maximize(); return null; }, async minimizeWindow() { await this.driver.manage().window().minimize(); return null; }, async fullscreenWindow() { await this.driver.manage().window().fullscreen(); return null; }, async openNewWindow(type = 'tab') { await this.driver.switchTo().newWindow(type); return null; }, async setWindowPosition(windowHandle, x, y) { await this.driver.manage().window().setRect({ x, y }); return { value: null }; }, async getWindowSize() { const value = await this.driver.manage().window().getRect(); return { value }; }, //Kept windowHandle as params for backward compatibility. async setWindowSize(windowHandle, width, height) { await this.driver.manage().window().setRect({ width, height }); return { value: null }; }, async getWindowRect() { const value = await this.driver.manage().window().getRect(); return { value }; }, async setWindowRect(data) { await this.driver.manage().window().setRect(data); return { value: null }; }, /////////////////////////////////////////////////////////// // Frames /////////////////////////////////////////////////////////// async switchToFrame(frameId) { if (frameId === undefined) { frameId = null; } await this.driver.switchTo().frame(frameId); return { value: null }; }, async switchToParentFrame() { await this.driver.switchTo().parentFrame(); return { value: null }; }, /////////////////////////////////////////////////////////// // Elements /////////////////////////////////////////////////////////// async locateSingleElement(element) { const locator = Locator.create(element); const webElement = await this.driver.findElement(locator); const elementId = await webElement.getId(); const {elementKey} = this.transport; return { value: {[elementKey]: elementId} }; }, async locateMultipleElements(element) { const locator = Locator.create(element); const resultValue = await this.driver.findElements(locator); if (Array.isArray(resultValue) && resultValue.length === 0) { return { status: -1, value: [], error: 'no such element', message: `Unable to locate element: ${locator.value} using ${locator.using}` }; } const {elementKey} = this.transport; const value = await Promise.all(resultValue.map(async webElement => { const elementId = await webElement.getId(); return {[elementKey]: elementId}; })); return value; }, elementIdEquals(id, otherId) { return `/element/${id}/equals/${otherId}`; }, async locateSingleElementByElementId({id, using, value}) { const locator = Locator.create({using, value}); const element = await this.getWebElement(id); const webElement = await element.findElement(locator); const elementId = await webElement.getId(); const {elementKey} = this.transport; return { value: {[elementKey]: elementId}, status: 0, elementId }; }, async locateMultipleElementsByElementId({id, using, value}) { const locator = Locator.create({using, value}); const element = await this.getWebElement(id); const resultValue = await element.findElements(locator); const {elementKey} = this.transport; const resultProcessed = await Promise.all(resultValue.map(async webElement => { const elementId = await webElement.getId(); return {[elementKey]: elementId}; })); return resultProcessed; }, async getActiveElement() { const webElement = await this.driver.switchTo().activeElement(); const elementId = await webElement.getId(); return elementId; }, getElementAttribute(id, attributeName) { return `/element/${id}/attribute/${attributeName}`; }, async getElementAccessibleName(webElementOrId) { const element = this.getWebElement(webElementOrId); const elementAccessibleName = await element.getAccessibleName(); return elementAccessibleName; }, async getElementAriaRole(webElementOrId) { const element = this.getWebElement(webElementOrId); const elementAriaRole = await element.getAriaRole(); return elementAriaRole; }, async takeElementScreenshot(webElementOrId, scroll) { const element = this.getWebElement(webElementOrId); const screenshotData = await element.takeScreenshot(scroll); return screenshotData; }, async getElementCSSValue(webElementOrId, cssPropertyName) { const element = this.getWebElement(webElementOrId); const elementCssValue = await element.getCssValue(cssPropertyName); return elementCssValue; }, async getElementProperty(webElementOrId, propertyName) { const element = this.getWebElement(webElementOrId); const elementValue = await element.getProperty(propertyName); return { value: elementValue }; }, async setElementAttribute(webElement, attrName, value) { const element = this.getWebElement(webElement); // eslint-disable-next-line const fn = function(e,a,v){try {if(e&&typeof e.setAttribute=='function'){e.setAttribute(a,v)} return true} catch(err){return {error:err.message,message:err.name+': '+err.message}}}; const elementId = await element.getId(); const result = await this.driver.executeScript('var passedArgs = Array.prototype.slice.call(arguments,0); ' + 'return (' + fn.toString() + ').apply(window, passedArgs);', {[this.transport.elementKey]: elementId}, attrName, value); return result; }, async getElementTagName(id) { const element = this.getWebElement(id); const elementTagName = await element.getTagName(); return elementTagName; }, async getElementRect(id) { const element = this.getWebElement(id); try { const value = await element.getRect(); return { value }; } catch (error) { error.message = `Unable to get element rect because of: ${error.message}`; return { error, status: -1, value: null }; } }, async getElementText(id) { const element = this.getWebElement(id); const elementText = await element.getText(); return elementText; }, // the value param is compulsory async getElementValue(webElementOrId, value) { const element = this.getWebElement(webElementOrId); const elementValue = await element.getAttribute(value); return elementValue; }, isElementLocationInView(id) { return `/element/${id}/location_in_view`; }, async isElementDisplayed(webElementOrId) { const element = this.getWebElement(webElementOrId); const result = await element.isDisplayed(); return result; }, async isElementEnabled(webElementOrId) { const element = this.getWebElement(webElementOrId); const value = await element.isEnabled(); return value; }, async isElementSelected(webElementOrId) { const element = this.getWebElement(webElementOrId); const value = await element.isSelected(); return value; }, async clearElementValue(webElementOrId) { const element = this.getWebElement(webElementOrId); await element.clear(); return null; }, setElementValueRedacted(...args) { // FIXME: redact password in verbose HTTP logs, it's only redacted in the command logs return this.methods.session.setElementValue.call(this, ...args); }, async setElementValue(webElementOrId, value) { if (Array.isArray(value)) { value = value.join(''); } else { value = String(value); } const element = this.getWebElement(webElementOrId); // clear Element value await element.clear(); await element.sendKeys(value); return null; }, async sendKeysToElement(webElementOrId, value) { if (Array.isArray(value)) { value = value.join(''); } else { value = String(value); } const element = this.getWebElement(webElementOrId); await element.sendKeys(value); return null; }, async uploadFile(webElementOrId, filepath) { const remote = require('selenium-webdriver/remote'); this.driver.setFileDetector(new remote.FileDetector()); const element = this.getWebElement(webElementOrId); await element.sendKeys(filepath); return { value: null }; }, async clickElement(webElementOrId) { const element = this.getWebElement(webElementOrId); await element.click(); return null; }, async elementSubmit(webElementOrId) { const element = this.getWebElement(webElementOrId); await element.submit(); return null; }, sendKeys(keys) { return { method: 'POST', path: '/keys', data: { value: keys } }; }, async getFirstElementChild(webElementOrId) { return await this.getElementByJs(function(element) { return element && element.firstElementChild; }, webElementOrId); }, async getLastElementChild(webElementOrId) { return await this.getElementByJs(function(element) { return element && element.lastElementChild; }, webElementOrId); }, async getNextSibling(webElementOrId) { return await this.getElementByJs(function(element) { return element && element.nextElementSibling; }, webElementOrId); }, async getPreviousSibling(webElementOrId) { return await this.getElementByJs(function(element) { return element && element.previousElementSibling; }, webElementOrId); }, async getShadowRoot(webElementOrId) { return await this.getElementByJs(function(element) { return element && element.shadowRoot; }, webElementOrId); }, async elementHasDescendants(webElementOrId) { const count = await this.runScriptForElement(function(element) { return element ? element.childElementCount : null; }, webElementOrId); if (count === null) { throw new Error('No such element: ' + webElementOrId); } return count > 0; }, /////////////////////////////////////////////////////////// // Document Handling /////////////////////////////////////////////////////////// async getPageSource() { const value = await this.driver.getPageSource(); return value; }, async executeScript(script, args) { const value = await this.executeFn(script, 'executeScript', args); return { value }; }, async executeScriptAsync(fn, args) { const value = await this.executeFn(fn, 'executeAsyncScript', args); return { value }; }, /////////////////////////////////////////////////////////// // Cookies /////////////////////////////////////////////////////////// async addCookie(cookie) { await this.driver.manage().addCookie(cookie); return null; }, async deleteCookie(cookieName) { await this.driver.manage().deleteCookie(cookieName); return null; }, async deleteAllCookies() { await this.driver.manage().deleteAllCookies(); return null; }, getCookies() { return this.driver.manage().getCookies(); }, async getCookie(name) { const result = await this.driver.manage().getCookie(name); return result; }, /////////////////////////////////////////////////////////// // User Actions /////////////////////////////////////////////////////////// async doubleClick(webElement) { if (webElement) { webElement = this.getWebElement(webElement); } await this.driver.actions({async: true}).doubleClick(webElement).perform(); return null; }, /** * @deprecated */ mouseButtonClick(buttonIndex) { return { method: 'POST', path: '/click', data: { button: buttonIndex } }; }, mouseButtonUp(buttonIndex) { return { method: 'POST', path: '/buttonup', data: { button: buttonIndex } }; }, mouseButtonDown(buttonIndex) { return { method: 'POST', path: '/buttondown', data: { button: buttonIndex } }; }, /** * @param {string|WebElement|null} origin * @param {number} xOffset * @param {number} yOffset * @param {object} [options] * @param {number} [duration] */ async moveTo(origin, x, y, duration, options = {}) { switch (origin) { case Origin.POINTER: case Origin.VIEWPORT: break; default: origin = this.getWebElement(origin); } const moveOptions = {origin, x, y}; if (typeof duration != 'undefined') { moveOptions.duration = duration; } await this.driver.actions(options).move(moveOptions).perform(); return null; }, async dragElement(source, destination) { source = this.getWebElement(source); //destination could be webElementId or {x,y} offset if (typeof destination === 'string') { destination = this.getWebElement(destination); } await this.driver.actions({async: true}).dragAndDrop(source, destination).perform(); return null; }, async contextClick(webElementOrId) { const element = this.getWebElement(webElementOrId); await this.driver.actions({async: true}).contextClick(element).perform(); return null; }, async pressAndHold(webElementOrId) { const element = this.getWebElement(webElementOrId); await this.driver.actions({async: true}).move({origin: element}).press().perform(); return null; }, async release(webElementOrId) { await this.driver.actions({async: true}).release().perform(); return null; }, /////////////////////////////////////////////////////////// // User Prompts /////////////////////////////////////////////////////////// async acceptAlert() { await this.driver.switchTo().alert().accept(); return null; }, async dismissAlert() { await this.driver.switchTo().alert().dismiss(); return null; }, getAlertText() { return this.driver.switchTo().alert().getText(); }, async setAlertText(keys) { await this.driver.switchTo().alert().sendKeys(keys); return null; }, /////////////////////////////////////////////////////////// // Screen /////////////////////////////////////////////////////////// async getScreenshot(logBase64Data) { const data = await this.driver.takeScreenshot(); return { value: data, status: 0, suppressBase64Data: !logBase64Data }; }, getScreenOrientation() { return '/orientation'; }, setScreenOrientation(orientation) { return { method: 'POST', path: '/orientation', data: { orientation } }; }, getAvailableContexts() { return '/contexts'; }, getCurrentContext() { return '/context'; }, setCurrentContext(context) { return { method: 'POST', path: '/context', data: { name: context } }; }, /////////////////////////////////////////////////////////////////////////// // Selenium Webdriver /////////////////////////////////////////////////////////////////////////// async wait(conditionFn, timeMs, message, retryInterval) { await this.driver.wait(conditionFn(), timeMs, message, retryInterval); return { value: null }; } } }; } };