UNPKG

@serenity-js/protractor

Version:

Adapter that integrates @serenity-js/web with Protractor, enabling Serenity/JS reporting and using the Screenplay Pattern to write end-to-end test scenarios

321 lines (320 loc) 13.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ProtractorPage = void 0; const node_url_1 = require("node:url"); const core_1 = require("@serenity-js/core"); const web_1 = require("@serenity-js/web"); const scripts = __importStar(require("@serenity-js/web/lib/scripts")); const promised_1 = require("../promised"); const locators_1 = require("./locators"); const ProtractorSelectors_1 = require("./locators/ProtractorSelectors"); const ProtractorCookie_1 = require("./ProtractorCookie"); const ProtractorPageElement_1 = require("./ProtractorPageElement"); /** * Protractor-specific implementation of [`Page`](https://serenity-js.org/api/web/class/Page/). * * @group Models */ class ProtractorPage extends web_1.Page { browser; errorHandler; lastScriptExecutionSummary; /* eslint-disable unicorn/consistent-function-scoping */ dehydrator = new web_1.ArgumentDehydrator((item) => item instanceof web_1.PageElement, async (item) => { const nativeElement = await item.nativeElement(); return nativeElement.getWebElement(); }); /* eslint-enable */ constructor(session, browser, modalDialogHandler, errorHandler, pageId) { super(session, new locators_1.ProtractorRootLocator(browser), modalDialogHandler, pageId); this.browser = browser; this.errorHandler = errorHandler; } createPageElement(nativeElement) { return new ProtractorPageElement_1.ProtractorPageElement(new locators_1.ProtractorExistingElementLocator(this.rootLocator, ProtractorSelectors_1.ProtractorSelectors.selectorFrom(nativeElement.locator()), this.errorHandler, nativeElement)); } locate(selector) { return new ProtractorPageElement_1.ProtractorPageElement(new locators_1.ProtractorLocator(this.rootLocator, selector, this.errorHandler)); } locateAll(selector) { return core_1.List.of(new web_1.PageElementsLocator(new locators_1.ProtractorLocator(this.rootLocator, selector, this.errorHandler))); } /** * If set to `false`, Protractor will not wait for Angular 1.x `$http` and `$timeout` * tasks to complete before interacting with the browser. * * This can be useful when: * - you need to switch to a non-Angular app during your tests, e.g. to sign in using an SSO gateway * - your app continuously polls an API with `$timeout` * * If you're not testing an Angular app, it's better to disable Angular synchronisation completely * in protractor configuration: * * ```js * // protractor.conf.js * exports.config = { * onPrepare: function () { * return browser.waitForAngularEnabled(false) * }, * * // ... other config * } * ``` * * @param enable */ async enableAngularSynchronisation(enable) { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.waitForAngularEnabled(enable)); }); } async navigateTo(destination) { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.get(destination)); }); } async navigateBack() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.navigate().back()); }); } async navigateForward() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.navigate().forward()); }); } async reload() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.navigate().refresh()); }); } async sendKeys(keys) { function isModifier(maybeKey) { return web_1.Key.isKey(maybeKey) && maybeKey.isModifier; } function asCodePoint(maybeKey) { if (!web_1.Key.isKey(maybeKey)) { return maybeKey; } return maybeKey.utf16codePoint; } return await this.inContextOfThisPage(() => { // keyDown for any modifier keys and sendKeys otherwise const keyDownActions = keys.reduce((actions, key) => { return isModifier(key) ? actions.keyDown(asCodePoint(key)) : actions.sendKeys(asCodePoint(key)); }, this.browser.actions()); // keyUp for any modifier keys, ignore for regular keys const keyUpActions = keys.reduce((actions, key) => { return isModifier(key) ? actions.keyUp(asCodePoint(key)) : actions; }, keyDownActions); return (0, promised_1.promised)(keyUpActions.perform()); }); } async executeScript(script, ...args) { const serialisedScript = typeof script === 'function' ? String(script) : String(`function script() { ${script} }`); const executableScript = new Function(` var parameters = (${scripts.rehydrate}).apply(null, arguments); return (${serialisedScript}).apply(null, parameters); `); const result = await this.inContextOfThisPage(async () => { const dehydratedArguments = await this.dehydrator.dehydrate(args); return (0, promised_1.promised)(this.browser.executeScript(executableScript, ...dehydratedArguments)); }); this.lastScriptExecutionSummary = new LastScriptExecutionSummary(result); return result; } async executeAsyncScript(script, ...args) { const serialisedScript = typeof script === 'function' ? String(script) : String(`function script() { ${script} }`); const executableScript = new Function(` var args = Array.prototype.slice.call(arguments, 0, -1); var callback = arguments[arguments.length - 1]; var parameters = (${scripts.rehydrate}).apply(null, args); (${serialisedScript}).apply(null, parameters.concat(callback)); `); const result = await this.inContextOfThisPage(async () => { const dehydratedArguments = await this.dehydrator.dehydrate(args); return (0, promised_1.promised)(this.browser.executeAsyncScript(executableScript, ...dehydratedArguments)); }); this.lastScriptExecutionSummary = new LastScriptExecutionSummary(result); return result; } lastScriptExecutionResult() { if (!this.lastScriptExecutionSummary) { throw new core_1.LogicError(`Make sure to execute a script before checking on the result`); } // Selenium 3 returns `null` when the script it executed returns `undefined` // so we're mapping the result back. return this.lastScriptExecutionSummary.result === null ? undefined : this.lastScriptExecutionSummary.result; } async takeScreenshot() { return await this.inContextOfThisPage(() => { try { return (0, promised_1.promised)(this.browser.takeScreenshot()); } catch (error) { if (error.name && error.name === 'NoSuchSessionError') { throw new web_1.BrowserWindowClosedError('Browser window is not available to take a screenshot', error); } throw error; } }); } async cookie(name) { return new ProtractorCookie_1.ProtractorCookie(this.browser, name); } async setCookie(cookieData) { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.manage().addCookie({ name: cookieData.name, value: cookieData.value, path: cookieData.path, domain: cookieData.domain, secure: cookieData.secure, httpOnly: cookieData.httpOnly, expiry: cookieData.expiry ? cookieData.expiry.toSeconds() : undefined, })); }); } async deleteAllCookies() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.manage().deleteAllCookies()); }); } async title() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.executeScript('return document.title')); }); } async name() { return await this.inContextOfThisPage(() => { return (0, promised_1.promised)(this.browser.executeScript('return window.name')); }); } async url() { return await this.inContextOfThisPage(async () => { return new node_url_1.URL(await (0, promised_1.promised)(this.browser.executeScript('return window.location.href'))); }); } async viewportSize() { return await this.inContextOfThisPage(async () => { const calculatedViewportSize = await (0, promised_1.promised)(this.browser.executeScript(`return { width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) };`)); if (calculatedViewportSize.width > 0 && calculatedViewportSize.height > 0) { return calculatedViewportSize; } // Chrome headless hard-codes window.innerWidth and window.innerHeight to 0 return await (0, promised_1.promised)(this.browser.manage().window().getSize()); }); } async setViewportSize(size) { return await this.inContextOfThisPage(async () => { const desiredWindowSize = await (0, promised_1.promised)(this.browser.executeScript(` var currentViewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0) var currentViewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) return { width: Math.max(window.outerWidth - currentViewportWidth + ${size.width}, ${size.width}), height: Math.max(window.outerHeight - currentViewportHeight + ${size.height}, ${size.height}), }; `)); return (0, promised_1.promised)(this.browser.manage().window().setSize(desiredWindowSize.width, desiredWindowSize.height)); }); } async close() { try { await this.inContextOfThisPage(async () => { await (0, promised_1.promised)(this.browser.close()); }); } catch (error) { if (error.name !== 'NoSuchWindowError') { throw error; } } } async closeOthers() { await this.session.closePagesOtherThan(this); } async isPresent() { const allPages = await this.session.allPages(); for (const page of allPages) { if (page === this) { return true; } } return false; } async inContextOfThisPage(action) { let originalPage; try { originalPage = await this.session.currentPage(); await this.session.changeCurrentPageTo(this); return await action(); } catch (error) { return await this.errorHandler.executeIfHandled(error, action); } finally { if (originalPage) { await this.session.changeCurrentPageTo(originalPage); } } } } exports.ProtractorPage = ProtractorPage; /** * @package */ class LastScriptExecutionSummary { result; constructor(result) { this.result = result; } } //# sourceMappingURL=ProtractorPage.js.map