Skip to content

Commit

Permalink
chore: migrate src/LifecycleWatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
jackfranklin committed Apr 23, 2020
1 parent 8509f46 commit 7af3d54
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 78 deletions.
2 changes: 1 addition & 1 deletion src/DOMWorld.js
Expand Up @@ -205,7 +205,7 @@ class DOMWorld {

/**
* @param {string} html
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
*/
async setContent(html, options = {}) {
const {
Expand Down
10 changes: 5 additions & 5 deletions src/FrameManager.js
Expand Up @@ -89,7 +89,7 @@ class FrameManager extends EventEmitter {
/**
* @param {!Puppeteer.Frame} frame
* @param {string} url
* @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async navigateFrame(frame, url, options = {}) {
Expand Down Expand Up @@ -137,7 +137,7 @@ class FrameManager extends EventEmitter {

/**
* @param {!Puppeteer.Frame} frame
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async waitForFrameNavigation(frame, options = {}) {
Expand Down Expand Up @@ -410,15 +410,15 @@ class Frame {

/**
* @param {string} url
* @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async goto(url, options) {
return await this._frameManager.navigateFrame(this, url, options);
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async waitForNavigation(options) {
Expand Down Expand Up @@ -503,7 +503,7 @@ class Frame {

/**
* @param {string} html
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
*/
async setContent(html, options = {}) {
return this._secondaryWorld.setContent(html, options);
Expand Down
122 changes: 57 additions & 65 deletions src/LifecycleWatcher.js → src/LifecycleWatcher.ts
Expand Up @@ -14,18 +14,49 @@
* limitations under the License.
*/

const {helper, assert} = require('./helper');
const {Events} = require('./Events');
const {TimeoutError} = require('./Errors');

class LifecycleWatcher {
/**
* @param {!Puppeteer.FrameManager} frameManager
* @param {!Puppeteer.Frame} frame
* @param {string|!Array<string>} waitUntil
* @param {number} timeout
*/
constructor(frameManager, frame, waitUntil, timeout) {
import {helper, assert, PuppeteerEventListener} from './helper';
import {Events} from './Events';
import {TimeoutError} from './Errors';

type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
type ProtocolLifeCycleEvent = 'load' | 'DOMContentLoaded' | 'networkIdle' | 'networkAlmostIdle';

const puppeteerToProtocolLifecycle = new Map<PuppeteerLifeCycleEvent, ProtocolLifeCycleEvent>([
['load', 'load'],
['domcontentloaded', 'DOMContentLoaded'],
['networkidle0', 'networkIdle'],
['networkidle2', 'networkAlmostIdle'],
]);


export class LifecycleWatcher {
_expectedLifecycle: ProtocolLifeCycleEvent[];
_frameManager: Puppeteer.FrameManager;
_frame: Puppeteer.Frame;
_timeout: number;
_navigationRequest?: Puppeteer.Request;
_eventListeners: PuppeteerEventListener[];
_initialLoaderId: string;


_sameDocumentNavigationPromise: Promise<Error | null>;
_sameDocumentNavigationCompleteCallback: (x?: Error) => void;

_lifecyclePromise: Promise<void>;
_lifecycleCallback: () => void;

_newDocumentNavigationPromise: Promise<Error | null>;
_newDocumentNavigationCompleteCallback: (x?: Error) => void;

_terminationPromise: Promise<Error | null>;
_terminationCallback: (x?: Error) => void;

_timeoutPromise: Promise<TimeoutError | null>;

_maximumTimer?: NodeJS.Timeout;
_hasSameDocumentNavigation?: boolean;

constructor(frameManager: Puppeteer.FrameManager, frame: Puppeteer.Frame, waitUntil: PuppeteerLifeCycleEvent|PuppeteerLifeCycleEvent[], timeout: number) {
if (Array.isArray(waitUntil))
waitUntil = waitUntil.slice();
else if (typeof waitUntil === 'string')
Expand All @@ -50,7 +81,7 @@ class LifecycleWatcher {
helper.addEventListener(this._frameManager.networkManager(), Events.NetworkManager.Request, this._onRequest.bind(this)),
];

this._sameDocumentNavigationPromise = new Promise(fulfill => {
this._sameDocumentNavigationPromise = new Promise<Error | null>(fulfill => {
this._sameDocumentNavigationCompleteCallback = fulfill;
});

Expand All @@ -69,90 +100,60 @@ class LifecycleWatcher {
this._checkLifecycleComplete();
}

/**
* @param {!Puppeteer.Request} request
*/
_onRequest(request) {
_onRequest(request: Puppeteer.Request): void {
if (request.frame() !== this._frame || !request.isNavigationRequest())
return;
this._navigationRequest = request;
}

/**
* @param {!Puppeteer.Frame} frame
*/
_onFrameDetached(frame) {
_onFrameDetached(frame: Puppeteer.Frame): void {
if (this._frame === frame) {
this._terminationCallback.call(null, new Error('Navigating frame was detached'));
return;
}
this._checkLifecycleComplete();
}

/**
* @return {?Puppeteer.Response}
*/
navigationResponse() {
navigationResponse(): Puppeteer.Response | null {
return this._navigationRequest ? this._navigationRequest.response() : null;
}

/**
* @param {!Error} error
*/
_terminate(error) {
_terminate(error: Error): void {
this._terminationCallback.call(null, error);
}

/**
* @return {!Promise<?Error>}
*/
sameDocumentNavigationPromise() {
sameDocumentNavigationPromise(): Promise<Error | null> {
return this._sameDocumentNavigationPromise;
}

/**
* @return {!Promise<?Error>}
*/
newDocumentNavigationPromise() {
newDocumentNavigationPromise(): Promise<Error | null> {
return this._newDocumentNavigationPromise;
}

/**
* @return {!Promise}
*/
lifecyclePromise() {
lifecyclePromise(): Promise<void> {
return this._lifecyclePromise;
}

/**
* @return {!Promise<?Error>}
*/
timeoutOrTerminationPromise() {
timeoutOrTerminationPromise(): Promise<Error | TimeoutError | null> {
return Promise.race([this._timeoutPromise, this._terminationPromise]);
}

/**
* @return {!Promise<?Error>}
*/
_createTimeoutPromise() {
_createTimeoutPromise(): Promise<TimeoutError | null> {
if (!this._timeout)
return new Promise(() => {});
const errorMessage = 'Navigation timeout of ' + this._timeout + ' ms exceeded';
return new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout))
.then(() => new TimeoutError(errorMessage));
}

/**
* @param {!Puppeteer.Frame} frame
*/
_navigatedWithinDocument(frame) {
_navigatedWithinDocument(frame: Puppeteer.Frame): void {
if (frame !== this._frame)
return;
this._hasSameDocumentNavigation = true;
this._checkLifecycleComplete();
}

_checkLifecycleComplete() {
_checkLifecycleComplete(): void {
// We expect navigation to commit.
if (!checkLifecycle(this._frame, this._expectedLifecycle))
return;
Expand All @@ -169,7 +170,7 @@ class LifecycleWatcher {
* @param {!Array<string>} expectedLifecycle
* @return {boolean}
*/
function checkLifecycle(frame, expectedLifecycle) {
function checkLifecycle(frame: Puppeteer.Frame, expectedLifecycle: ProtocolLifeCycleEvent[]): boolean {
for (const event of expectedLifecycle) {
if (!frame._lifecycleEvents.has(event))
return false;
Expand All @@ -182,17 +183,8 @@ class LifecycleWatcher {
}
}

dispose() {
dispose(): void {
helper.removeEventListeners(this._eventListeners);
clearTimeout(this._maximumTimer);
}
}

const puppeteerToProtocolLifecycle = new Map([
['load', 'load'],
['domcontentloaded', 'DOMContentLoaded'],
['networkidle0', 'networkIdle'],
['networkidle2', 'networkAlmostIdle'],
]);

module.exports = {LifecycleWatcher};
14 changes: 7 additions & 7 deletions src/Page.js
Expand Up @@ -675,23 +675,23 @@ class Page extends EventEmitter {

/**
* @param {string} html
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
*/
async setContent(html, options) {
await this._frameManager.mainFrame().setContent(html, options);
}

/**
* @param {string} url
* @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{referer?: string, timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async goto(url, options) {
return await this._frameManager.mainFrame().goto(url, options);
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async reload(options) {
Expand All @@ -705,7 +705,7 @@ class Page extends EventEmitter {
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async waitForNavigation(options = {}) {
Expand Down Expand Up @@ -755,23 +755,23 @@ class Page extends EventEmitter {
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async goBack(options) {
return this._go(-1, options);
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async goForward(options) {
return this._go(+1, options);
}

/**
* @param {!{timeout?: number, waitUntil?: string|!Array<string>}=} options
* @param {!{timeout?: number, waitUntil?: !Puppeteer.PuppeteerLifeCycleEvent|!Array<!Puppeteer.PuppeteerLifeCycleEvent>}=} options
* @return {!Promise<?Puppeteer.Response>}
*/
async _go(delta, options) {
Expand Down
6 changes: 6 additions & 0 deletions src/externs.d.ts
Expand Up @@ -18,6 +18,12 @@ declare global {
export class Response extends RealResponse { }
export class Request extends RealRequest { }


/* TODO(jacktfranklin@): once DOMWorld, Page, and FrameManager are in TS
* we can remove this and instead use the type defined in LifeCycleWatcher
*/
export type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';

export interface ConnectionTransport {
send(string);
close();
Expand Down

0 comments on commit 7af3d54

Please sign in to comment.