@serenity-js/webdriverio
Version:
Adapter that integrates @serenity-js/web with the latest stable version of WebdriverIO, enabling Serenity/JS reporting and using the Screenplay Pattern to write web and mobile test scenarios
258 lines • 10.4 kB
JavaScript
;
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.WebdriverIOPage = void 0;
require("webdriverio");
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/index.js"));
const index_js_1 = require("./locators/index.js");
const WebdriverIOCookie_js_1 = require("./WebdriverIOCookie.js");
const WebdriverIOPageElement_js_1 = require("./WebdriverIOPageElement.js");
/**
* WebdriverIO-specific implementation of [`Page`](https://serenity-js.org/api/web/class/Page/).
*
* @group Models
*/
class WebdriverIOPage extends web_1.Page {
browser;
errorHandler;
lastScriptExecutionSummary;
/* eslint-disable unicorn/consistent-function-scoping */
dehydrator = new web_1.ArgumentDehydrator((item) => item instanceof web_1.PageElement, (item) => item.nativeElement());
/* eslint-enable */
constructor(session, browser, modalDialogHandler, errorHandler, pageId) {
super(session, new index_js_1.WebdriverIORootLocator(browser), modalDialogHandler, pageId);
this.browser = browser;
this.errorHandler = errorHandler;
}
createPageElement(nativeElement) {
return new WebdriverIOPageElement_js_1.WebdriverIOPageElement(new index_js_1.WebdriverIOExistingElementLocator(this.rootLocator, new web_1.ByCss(String(nativeElement.selector)), this.errorHandler, nativeElement));
}
locate(selector) {
return new WebdriverIOPageElement_js_1.WebdriverIOPageElement(new index_js_1.WebdriverIOLocator(this.rootLocator, selector, this.errorHandler));
}
locateAll(selector) {
return core_1.List.of(new web_1.PageElementsLocator(new index_js_1.WebdriverIOLocator(this.rootLocator, selector, this.errorHandler)));
}
async navigateTo(destination) {
await this.inContextOfThisPage(() => this.browser.url(destination));
await this.resetState();
}
async navigateBack() {
await this.inContextOfThisPage(() => this.browser.back());
await this.resetState();
}
async navigateForward() {
await this.inContextOfThisPage(() => this.browser.forward());
await this.resetState();
}
async reload() {
await this.inContextOfThisPage(() => this.browser.refresh());
await this.resetState();
}
async sendKeys(keys) {
const keySequence = keys.map(key => {
if (!web_1.Key.isKey(key)) {
return key;
}
return key.utf16codePoint;
});
await this.inContextOfThisPage(() => this.browser.keys(keySequence));
}
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 await this.browser.execute(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 this.browser.executeAsync(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 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(async () => {
try {
return await this.browser.takeScreenshot();
}
catch (error) {
if (error.name === 'ProtocolError' && error.message.includes('Target closed')) {
throw new web_1.BrowserWindowClosedError(`Couldn't take screenshot since the browser window is already closed`, error);
}
throw error;
}
});
}
async cookie(name) {
return new WebdriverIOCookie_js_1.WebdriverIOCookie(this.browser, name);
}
async setCookie(cookieData) {
return await this.inContextOfThisPage(() => {
return this.browser.setCookies({
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,
// see https://w3c.github.io/webdriver-bidi/#type-network-Cookie
sameSite: cookieData?.sameSite?.toLowerCase(),
});
});
}
async deleteAllCookies() {
return await this.inContextOfThisPage(() => {
return this.browser.deleteCookies();
});
}
async title() {
return await this.inContextOfThisPage(() => this.browser.execute(() => document.title));
}
async name() {
return await this.inContextOfThisPage(() => {
return this.browser.execute(() => window.name);
});
}
async url() {
return await this.inContextOfThisPage(async () => {
return new node_url_1.URL(await this.browser.execute(() => window.location.href));
});
}
async viewportSize() {
return await this.inContextOfThisPage(async () => {
const calculatedViewportSize = await this.browser.execute(`
return {
width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
}
`);
// Chrome headless hard-codes window.innerWidth and window.innerHeight to 0
if (calculatedViewportSize.width > 0 && calculatedViewportSize.height > 0) {
return calculatedViewportSize;
}
return this.browser.getWindowSize();
});
}
async setViewportSize(size) {
return await this.inContextOfThisPage(async () => {
await this.browser.setViewport(size);
});
}
async close() {
await this.resetState();
await this.inContextOfThisPage(() => this.browser.closeWindow());
}
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 resetState() {
this.lastScriptExecutionSummary = undefined;
await this.rootLocator.switchToMainFrame();
await this.modalDialogHandler.reset();
}
async discard() {
await this.modalDialogHandler.discard();
}
async inContextOfThisPage(action) {
let originalCurrentPage;
try {
originalCurrentPage = await this.session.currentPage();
await this.session.changeCurrentPageTo(this);
return await action();
}
catch (error) {
return await this.errorHandler.executeIfHandled(error, action);
}
finally {
await this.session.changeCurrentPageTo(originalCurrentPage);
}
}
}
exports.WebdriverIOPage = WebdriverIOPage;
/**
* @package
*/
class LastScriptExecutionSummary {
result;
constructor(result) {
this.result = result;
}
}
//# sourceMappingURL=WebdriverIOPage.js.map