blob: a13da7d98877c18f3d12b3be30d7bed17deb58db [file] [log] [blame]
import { ElementHandle } from 'puppeteer';
import { AsyncList, PageObjectElement, PageObjectElementList } from './page_object_element';
/**
* A base class for writing page objects[1] that work both on in-browser and Puppeteer tests.
*
* A page object written as a subclass of PageObject will have two layers of wrapping:
*
* 1. The PageObject wraps a PageObjectElement.
* 2. The PageObjectElement wraps either a DOM node (HTMLElement) or a Puppeteer handle
* (ElementHandle).
*
* The PageObjectElement wraps the root node of the component under test, and provides an
* abstraction layer on top of the underlying DOM node or Puppeteer handle. A page object interacts
* with the component under test exclusively via the PageObjectElement's API. This guarantees
* compatibility with both in-browser and Puppeteer tests.
*
* PageObject and PageObjectElement are inspired by PageLoader[2], a Dart framework for creating
* page objects compatible with both in-browser and WebDriver tests.
*
* For best practices, please see PageLoader Best Practices[3].
*
* [1] https://martinfowler.com/bliki/PageObject.html
* [2] https://github.com/google/pageloader
* [3] https://github.com/google/pageloader/blob/master/best_practices.md
*/
export abstract class PageObject {
protected element: PageObjectElement;
constructor(
element:
HTMLElement |
ElementHandle<HTMLElement> |
Promise<ElementHandle<HTMLElement> | null> |
PageObjectElement) {
if (element instanceof PageObjectElement) {
this.element = element;
} else {
this.element = new PageObjectElement(element);
}
}
/** Returns true if the underlying PageObjectElement is empty. */
async isEmpty(): Promise<boolean> {
return await this.element.isEmpty();
}
/**
* Returns the result of calling PageObjectElement#bySelector() on the underlying
* PageObjectElement.
*/
protected bySelector(selector: string): PageObjectElement {
return this.element.bySelector(selector);
}
/**
* Returns the result of calling PageObjectElement#bySelectorAll() on the underlying
* PageObjectElement.
*/
protected bySelectorAll(selector: string): PageObjectElementList {
return this.element.bySelectorAll(selector);
}
/** Instantiates a PageObject with the first element that matches the given selector. */
protected poBySelector<T extends PageObject>(
selector: string, ctor: { new(...args: any): T }): T {
return new ctor(this.bySelector(selector));
}
/** Instantiates one PageObject for each element that match the given selector. */
protected poBySelectorAll<T extends PageObject>(
selector: string, ctor: { new(...args: any): T }): PageObjectList<T> {
return new PageObjectList(this.bySelectorAll(selector)
.map(async (poe: PageObjectElement) => new ctor(poe)));
}
}
/** Convenience wrapper around a promise of a list of page objects. */
export class PageObjectList<T extends PageObject> extends AsyncList<T> {
constructor(itemsPromise: Promise<T[]>) {
super(itemsPromise);
}
}