@serenity-js/web
Version:
Serenity/JS Screenplay Pattern library offering a flexible, web driver-agnostic approach for interacting with web-based user interfaces and components, suitable for various testing contexts
256 lines • 9.61 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Page = void 0;
const core_1 = require("@serenity-js/core");
const tiny_types_1 = require("tiny-types");
const abilities_1 = require("../abilities");
/**
* Serenity/JS Screenplay Pattern-style model that enables interactions with a Web page
* rendered in a Web browser tab.
*
* ## Referring to the current page
*
* ```ts
* import { Ensure, endsWith } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Navigate, Page } from '@serenity-js/web'
*
* await actorCalled('Serena').attemptsTo(
* Navigate.to('https://serenity-js.org'),
* Ensure.that(Page.current().title(), endsWith('Serenity/JS')),
* )
* ```
*
* ## Switching to another open page
*
* ```ts
* import { Ensure, equals, includes, startsWith } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Navigate, Page, Switch, Text } from '@serenity-js/web'
*
* const Navigation = {
* linkTo = (name: Answerable<string>) =>
* PageElements.located(By.css('nav a'))
* .where(Text, includes(name))
* .first()
* }
*
* await actorCalled('Serena').attemptsTo(
* Navigate.to('https://serenity-js.org'),
* Click.on(Navigation.linkTo('GitHub')),
*
* Switch.to(Page.whichUrl(startsWith('https://github.com')))
*
* Ensure.that(
* Page.current().url().href,
* equals('https://github.com/serenity-js/serenity-js')
* ),
* )
* ```
*
* ## Retrieving information about another open page
*
* You can retrieve information about another open page without having to explicitly switch to it:
*
* ```ts
* import { Ensure, equals, includes, startsWith } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Navigate, Page, Text } from '@serenity-js/web'
*
* const Navigation = {
* linkTo = (name: Answerable<string>) =>
* PageElements.located(By.css('nav a'))
* .where(Text, includes(name))
* .first()
* }
*
* await actorCalled('Serena').attemptsTo(
* Navigate.to('https://serenity-js.org'),
* Click.on(Navigation.linkTo('GitHub')),
* Ensure.that(
* Page.whichUrl(startsWith('https://github.com')).url().href,
* equals('https://github.com/serenity-js/serenity-js')
* ),
* )
* ```
*
* ## Performing activities in the context of another page
*
* ```ts
* import { Ensure, equals, includes, startsWith } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Navigate, Page, Text } from '@serenity-js/web'
*
* const Navigation = {
* linkTo = (name: Answerable<string>) =>
* PageElements.located(By.css('nav a'))
* .where(Text, includes(name))
* .first()
* }
*
* await actorCalled('Serena').attemptsTo(
*
* // Serenity/JS GitHub repository opens in a new browser tab
* Navigate.to('https://serenity-js.org'),
* Click.on(Navigation.linkTo('GitHub')),
*
* // Switch to the newly opened page and perform an assertion
* Switch.to(Page.whichUrl(startsWith('https://github.com')))
* .and(
* Ensure.that(
* Page.current().url().href,
* equals('https://github.com/serenity-js/serenity-js')
* )
* ),
* // Automatically switch back to the original page
*
* Ensure.that(Page.current().url().href, equals('https://serenity-js.org'),
* )
* ```
*
* ## Learn more
*
* - [`BrowseTheWeb`](https://serenity-js.org/api/web/class/BrowseTheWeb/)
* - [`PageElement`](https://serenity-js.org/api/web/class/PageElement/)
* - [`Optional`](https://serenity-js.org/api/core/interface/Optional/)
* - [`Switchable`](https://serenity-js.org/api/web/interface/Switchable/)
*
* @group Models
*/
class Page {
session;
rootLocator;
modalDialogHandler;
id;
/**
* Creates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) representing the currently active [`Page`](https://serenity-js.org/api/web/class/Page/).
*/
static current() {
return core_1.Question.about('current page', actor => {
return abilities_1.BrowseTheWeb.as(actor).currentPage();
});
}
/**
* Creates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) that resolves to a [`Page`](https://serenity-js.org/api/web/class/Page/) which [`Page.name`](https://serenity-js.org/api/web/class/Page/#name)
* meets the [expectation](https://serenity-js.org/api/core/class/Expectation/).
*
* #### Switching to a page with the desired name
*
* ```ts
* import { includes } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Switch } from '@serenity-js/web'
*
* actorCalled('Bernie').attemptsTo(
* Switch.to(Page.whichName(includes(`photo-gallery`))),
* )
* ```
*
* @param expectation
*/
static whichName(expectation) {
return core_1.Question.about(`page which name does ${expectation}`, async (actor) => {
const pages = await abilities_1.BrowseTheWeb.as(actor).allPages();
return Page.findMatchingPage(`name does ${expectation}`, pages, page => actor.answer(expectation.isMetFor(page.name())));
});
}
/**
* Creates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) that resolves to a [`Page`](https://serenity-js.org/api/web/class/Page/) which [`Page.title`](https://serenity-js.org/api/web/class/Page/#title)
* meets the [expectation](https://serenity-js.org/api/core/class/Expectation/).
*
* #### Switching to a page with the desired title
*
* ```ts
* import { includes } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Switch } from '@serenity-js/web'
*
* actorCalled('Bernie').attemptsTo(
* Switch.to(Page.whichTitle(includes(`Summer collection`))),
* )
* ```
*
* @param expectation
*/
static whichTitle(expectation) {
return core_1.Question.about(`page which title does ${expectation}`, async (actor) => {
const pages = await abilities_1.BrowseTheWeb.as(actor).allPages();
return Page.findMatchingPage(`title does ${expectation}`, pages, page => actor.answer(expectation.isMetFor(page.title())));
});
}
/**
* Creates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) that resolves to a [`Page`](https://serenity-js.org/api/web/class/Page/) which [`Page.url`](https://serenity-js.org/api/web/class/Page/#url)
* meets the [expectation](https://serenity-js.org/api/core/class/Expectation/).
*
* #### Switching to a page with the desired URL
*
* ```ts
* import { endsWith } from '@serenity-js/assertions'
* import { actorCalled } from '@serenity-js/core'
* import { Switch } from '@serenity-js/web'
*
* actorCalled('Bernie').attemptsTo(
* Switch.to(Page.whichUrl(endsWith(`/gallery.html`))),
* )
* ```
*
* @param expectation
*/
static whichUrl(expectation) {
return core_1.Question.about(`page which URL does ${expectation}`, async (actor) => {
const pages = await abilities_1.BrowseTheWeb.as(actor).allPages();
return Page.findMatchingPage(`url does ${expectation}`, pages, page => actor.answer(expectation.isMetFor(page.url().then(url => url.toString()))));
});
}
static async findMatchingPage(expectationDescription, pages, matcher) {
for (const page of pages) {
const outcome = await matcher(page);
if (outcome instanceof core_1.ExpectationMet) {
return page;
}
}
throw new core_1.LogicError(`Couldn't find a page which ${expectationDescription}`);
}
constructor(session, rootLocator, modalDialogHandler, id) {
this.session = session;
this.rootLocator = rootLocator;
this.modalDialogHandler = modalDialogHandler;
this.id = id;
(0, tiny_types_1.ensure)('session', session, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('rootLocator', rootLocator, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('modalDialogHandler', modalDialogHandler, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('id', id, (0, tiny_types_1.isDefined)());
}
/**
* Switches the current browsing context to the given page
* and returns an object that allows the caller to switch back
* to the previous context when needed.
*
* ## Learn more
* - [`Switch`](https://serenity-js.org/api/web/class/Switch/)
* - [`Switchable`](https://serenity-js.org/api/web/interface/Switchable/)
*/
async switchTo() {
const originalPage = await this.session.currentPage();
await this.session.changeCurrentPageTo(this);
return {
switchBack: async () => {
await this.session.changeCurrentPageTo(originalPage);
}
};
}
/**
* Returns the [`ModalDialogHandler`](https://serenity-js.org/api/web/class/ModalDialogHandler/) for the current [`Page`](https://serenity-js.org/api/web/class/Page/).
*/
modalDialog() {
return this.modalDialogHandler;
}
/**
* Returns a description of this Page and its ID.
*/
toString() {
return `page (id=${this.id.value})`;
}
}
exports.Page = Page;
//# sourceMappingURL=Page.js.map
;