| 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); |
| } |
| } |