@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
132 lines • 6.36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebdriverIOBrowsingSession = void 0;
require("webdriverio");
const core_1 = require("@serenity-js/core");
const index_js_1 = require("@serenity-js/core/lib/model/index.js");
const web_1 = require("@serenity-js/web");
const index_js_2 = require("../models/index.js");
const WebdriverIOErrorHandler_js_1 = require("./WebdriverIOErrorHandler.js");
const WebdriverIOModalDialogHandler_js_1 = require("./WebdriverIOModalDialogHandler.js");
const WebdriverProtocolErrorCode_js_1 = require("./WebdriverProtocolErrorCode.js");
/**
* WebdriverIO-specific implementation of [`BrowsingSession`](https://serenity-js.org/api/web/class/BrowsingSession/).
*
* @group Models
*/
class WebdriverIOBrowsingSession extends web_1.BrowsingSession {
browser;
constructor(browser) {
super();
this.browser = browser;
if (!browser.$ || !browser.$$) {
throw new core_1.LogicError(`WebdriverIO browser object is not initialised yet, so can't be assigned to an actor. Are you trying to instantiate an actor outside of a test or a test hook?`);
}
}
async allPages() {
// scan all the active window handles and add any newly opened windows if needed
const windowHandles = await this.browser.getWindowHandles();
// remove pages that are no longer open
const closedPageIds = this.registeredPageIds()
.filter(id => !windowHandles.includes(id.value));
this.deregister(...closedPageIds);
// add any new pages that might have been opened (e.g. popup windows)
const registeredWindowHandles = new Set(this.registeredPageIds().map(id => id.value));
const newlyOpenedWindowHandles = windowHandles.filter(windowHandle => !registeredWindowHandles.has(windowHandle));
for (const newlyOpenedWindowHandle of newlyOpenedWindowHandles) {
const modalDialogHandler = new WebdriverIOModalDialogHandler_js_1.WebdriverIOModalDialogHandler(this.browser);
const errorHandler = new WebdriverIOErrorHandler_js_1.WebdriverIOErrorHandler();
errorHandler.setHandlerFor(WebdriverProtocolErrorCode_js_1.WebdriverProtocolErrorCode.UnexpectedAlertOpenError, error => modalDialogHandler.dismiss());
this.register(new index_js_2.WebdriverIOPage(this, this.browser, modalDialogHandler, errorHandler, new index_js_1.CorrelationId(newlyOpenedWindowHandle)));
}
return super.allPages();
}
/**
* @param page
*/
async changeCurrentPageTo(page) {
const currentPage = await this.currentPage();
// are we already on this page?
if (currentPage.id.equals(page.id)) {
return void 0;
}
// does the new page exist, or has it been closed in the meantime by user action, script, or similar?
if (!await page.isPresent()) {
return void 0;
}
// the page seems to be legit, switch to it
await this.browser.switchToWindow(page.id.value);
// and update the cached reference
await super.changeCurrentPageTo(page);
}
async activeWindowHandle() {
try {
return await this.browser.getWindowHandle();
}
catch (error) {
// If the window is closed by user action Webdriver will still hold the reference to the closed window.
if (['NoSuchWindowError', 'no such window'].includes(error.name)) {
const allHandles = await this.browser.getWindowHandles();
if (allHandles.length > 0) {
const handle = allHandles.at(-1);
await this.browser.switchToWindow(handle);
return handle;
}
}
throw error;
}
}
async currentPage() {
const actualCurrentPageHandle = await this.activeWindowHandle();
const actualCurrentPageId = index_js_1.CorrelationId.fromJSON(actualCurrentPageHandle);
if (this.currentBrowserPage && this.currentBrowserPage.id.equals(actualCurrentPageId)) {
return this.currentBrowserPage;
}
// Looks like the actual current page is not what we thought the current page was.
// Is it one of the pages we are aware of?
const allPages = await this.allPages();
const found = allPages.find(page => page.id.equals(actualCurrentPageId));
if (found) {
this.currentBrowserPage = found;
return this.currentBrowserPage;
}
// OK, so that's a handle that we haven't seen before, let's register it and set as current page.
this.currentBrowserPage = await this.registerCurrentPage();
return this.currentBrowserPage;
}
async registerCurrentPage() {
const pageId = await this.assignPageId();
const modalDialogHandler = new WebdriverIOModalDialogHandler_js_1.WebdriverIOModalDialogHandler(this.browser);
const errorHandler = new WebdriverIOErrorHandler_js_1.WebdriverIOErrorHandler();
errorHandler.setHandlerFor(WebdriverProtocolErrorCode_js_1.WebdriverProtocolErrorCode.UnexpectedAlertOpenError, error => modalDialogHandler.dismiss());
const page = new index_js_2.WebdriverIOPage(this, this.browser, modalDialogHandler, errorHandler, pageId);
this.register(page);
return page;
}
async assignPageId() {
if (this.browser.isMobile) {
const context = await this.browser.getContext();
if (typeof context === 'string') {
return new index_js_1.CorrelationId(context);
}
if (context.id) {
return new index_js_1.CorrelationId(context.id);
}
}
if (this.browser['getWindowHandle']) {
const handle = await this.browser.getWindowHandle();
return new index_js_1.CorrelationId(handle);
}
return index_js_1.CorrelationId.create();
}
browserCapabilities() {
return Promise.resolve(this.browser.capabilities);
}
async discard() {
for (const page of await this.allPages()) {
await page.discard();
}
}
}
exports.WebdriverIOBrowsingSession = WebdriverIOBrowsingSession;
//# sourceMappingURL=WebdriverIOBrowsingSession.js.map