chore: migrate src/Target to TypeScript (#5771)

This commit is contained in:
Jack Franklin 2020-04-29 15:18:09 +01:00 committed by GitHub
parent 8a5008e30b
commit af2e45820f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 83 deletions

View File

@ -23,12 +23,6 @@ import {Connection} from './Connection';
type BrowserCloseCallback = () => Promise<void> | void; type BrowserCloseCallback = () => Promise<void> | void;
/* TODO(jacktfranklin): once Target is migrated to TS
* we can import + use its type here. But right now Target
* is implemented in JS so we can't import the type and have to use
* Puppeteer.Target
*/
export class Browser extends EventEmitter { export class Browser extends EventEmitter {
static async create(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback): Promise<Browser> { static async create(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback): Promise<Browser> {
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback); const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
@ -44,7 +38,7 @@ export class Browser extends EventEmitter {
_defaultContext: BrowserContext; _defaultContext: BrowserContext;
_contexts: Map<string, BrowserContext>; _contexts: Map<string, BrowserContext>;
// TODO: once Target is in TypeScript we can type this properly. // TODO: once Target is in TypeScript we can type this properly.
_targets: Map<string, Puppeteer.Target>; _targets: Map<string, Target>;
constructor(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback) { constructor(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Puppeteer.Viewport, process?: Puppeteer.ChildProcess, closeCallback?: BrowserCloseCallback) {
super(); super();
@ -154,11 +148,11 @@ export class Browser extends EventEmitter {
return page; return page;
} }
targets(): Puppeteer.Target[] { targets(): Target[] {
return Array.from(this._targets.values()).filter(target => target._isInitialized); return Array.from(this._targets.values()).filter(target => target._isInitialized);
} }
target(): Puppeteer.Target { target(): Target {
return this.targets().find(target => target.type() === 'browser'); return this.targets().find(target => target.type() === 'browser');
} }
@ -167,7 +161,7 @@ export class Browser extends EventEmitter {
* @param {{timeout?: number}=} options * @param {{timeout?: number}=} options
* @return {!Promise<!Target>} * @return {!Promise<!Target>}
*/ */
async waitForTarget(predicate: (x: Puppeteer.Target) => boolean, options: { timeout?: number} = {}): Promise<Puppeteer.Target> { async waitForTarget(predicate: (x: Target) => boolean, options: { timeout?: number} = {}): Promise<Target> {
const { const {
timeout = 30000 timeout = 30000
} = options; } = options;
@ -175,19 +169,19 @@ export class Browser extends EventEmitter {
if (existingTarget) if (existingTarget)
return existingTarget; return existingTarget;
let resolve; let resolve;
const targetPromise = new Promise<Puppeteer.Target>(x => resolve = x); const targetPromise = new Promise<Target>(x => resolve = x);
this.on(Events.Browser.TargetCreated, check); this.on(Events.Browser.TargetCreated, check);
this.on(Events.Browser.TargetChanged, check); this.on(Events.Browser.TargetChanged, check);
try { try {
if (!timeout) if (!timeout)
return await targetPromise; return await targetPromise;
return await helper.waitWithTimeout<Puppeteer.Target>(targetPromise, 'target', timeout); return await helper.waitWithTimeout<Target>(targetPromise, 'target', timeout);
} finally { } finally {
this.removeListener(Events.Browser.TargetCreated, check); this.removeListener(Events.Browser.TargetCreated, check);
this.removeListener(Events.Browser.TargetChanged, check); this.removeListener(Events.Browser.TargetChanged, check);
} }
function check(target: Puppeteer.Target): void { function check(target: Target): void {
if (predicate(target)) if (predicate(target))
resolve(target); resolve(target);
} }
@ -242,11 +236,11 @@ export class BrowserContext extends EventEmitter {
this._id = contextId; this._id = contextId;
} }
targets(): Puppeteer.Target[] { targets(): Target[] {
return this._browser.targets().filter(target => target.browserContext() === this); return this._browser.targets().filter(target => target.browserContext() === this);
} }
waitForTarget(predicate: (x: Puppeteer.Target) => boolean, options: { timeout?: number}): Promise<Puppeteer.Target> { waitForTarget(predicate: (x: Target) => boolean, options: { timeout?: number}): Promise<Target> {
return this._browser.waitForTarget(target => target.browserContext() === this && predicate(target), options); return this._browser.waitForTarget(target => target.browserContext() === this && predicate(target), options);
} }

View File

@ -36,6 +36,9 @@ const {Worker: PuppeteerWorker} = require('./Worker');
const {Browser, BrowserContext} = require('./Browser'); const {Browser, BrowserContext} = require('./Browser');
// Import used as typedef // Import used as typedef
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const {Target} = require('./Target');
// Import used as typedef
// eslint-disable-next-line no-unused-vars
const {createJSHandle, JSHandle, ElementHandle} = require('./JSHandle'); const {createJSHandle, JSHandle, ElementHandle} = require('./JSHandle');
const {Accessibility} = require('./Accessibility'); const {Accessibility} = require('./Accessibility');
const {TimeoutSettings} = require('./TimeoutSettings'); const {TimeoutSettings} = require('./TimeoutSettings');
@ -49,7 +52,7 @@ const writeFileAsync = helper.promisify(fs.writeFile);
class Page extends EventEmitter { class Page extends EventEmitter {
/** /**
* @param {!CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.Target} target * @param {!Target} target
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.Viewport} defaultViewport
* @param {!TaskQueue} screenshotTaskQueue * @param {!TaskQueue} screenshotTaskQueue
@ -65,7 +68,7 @@ class Page extends EventEmitter {
/** /**
* @param {!CDPSession} client * @param {!CDPSession} client
* @param {!Puppeteer.Target} target * @param {!Target} target
* @param {boolean} ignoreHTTPSErrors * @param {boolean} ignoreHTTPSErrors
* @param {!TaskQueue} screenshotTaskQueue * @param {!TaskQueue} screenshotTaskQueue
*/ */
@ -202,7 +205,7 @@ class Page extends EventEmitter {
} }
/** /**
* @return {!Puppeteer.Target} * @return {!Target}
*/ */
target() { target() {
return this._target; return this._target;

View File

@ -14,30 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
const {Events} = require('./Events'); import {Events} from './Events';
const {Page} = require('./Page'); import {Page} from './Page';
const {Worker: PuppeteerWorker} = require('./Worker'); import {Worker as PuppeteerWorker} from './Worker';
// CDPSession is used only as a typedef import {CDPSession} from './Connection';
// eslint-disable-next-line no-unused-vars import {TaskQueue} from './TaskQueue';
const {CDPSession} = require('./Connection'); import {Browser, BrowserContext} from './Browser';
// This import is used as a TypeDef, but ESLint's rule doesn't
// understand that unfortunately.
// eslint-disable-next-line no-unused-vars
const {TaskQueue} = require('./TaskQueue');
// Import used as typedef
// eslint-disable-next-line no-unused-vars
const {Browser, BrowserContext} = require('./Browser');
class Target { export class Target {
/** _targetInfo: Protocol.Target.TargetInfo;
* @param {!Protocol.Target.TargetInfo} targetInfo _browserContext: BrowserContext;
* @param {!BrowserContext} browserContext _targetId: string;
* @param {!function():!Promise<!CDPSession>} sessionFactory _sessionFactory: () => Promise<CDPSession>;
* @param {boolean} ignoreHTTPSErrors _ignoreHTTPSErrors: boolean;
* @param {?Puppeteer.Viewport} defaultViewport _defaultViewport?: Puppeteer.Viewport;
* @param {!TaskQueue} screenshotTaskQueue _screenshotTaskQueue: TaskQueue;
*/ _pagePromise?: Promise<Puppeteer.Page>;
constructor(targetInfo, browserContext, sessionFactory, ignoreHTTPSErrors, defaultViewport, screenshotTaskQueue) { _workerPromise?: Promise<PuppeteerWorker>;
_initializedPromise: Promise<boolean>;
_initializedCallback: (x: boolean) => void;
_isClosedPromise: Promise<boolean>;
_closedCallback: () => void;
_isInitialized: boolean;
constructor(targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext, sessionFactory: () => Promise<CDPSession>, ignoreHTTPSErrors: boolean, defaultViewport: Puppeteer.Viewport | null, screenshotTaskQueue: TaskQueue) {
this._targetInfo = targetInfo; this._targetInfo = targetInfo;
this._browserContext = browserContext; this._browserContext = browserContext;
this._targetId = targetInfo.targetId; this._targetId = targetInfo.targetId;
@ -49,7 +49,7 @@ class Target {
this._pagePromise = null; this._pagePromise = null;
/** @type {?Promise<!PuppeteerWorker>} */ /** @type {?Promise<!PuppeteerWorker>} */
this._workerPromise = null; this._workerPromise = null;
this._initializedPromise = new Promise(fulfill => this._initializedCallback = fulfill).then(async success => { this._initializedPromise = new Promise<boolean>(fulfill => this._initializedCallback = fulfill).then(async success => {
if (!success) if (!success)
return false; return false;
const opener = this.opener(); const opener = this.opener();
@ -62,23 +62,17 @@ class Target {
openerPage.emit(Events.Page.Popup, popupPage); openerPage.emit(Events.Page.Popup, popupPage);
return true; return true;
}); });
this._isClosedPromise = new Promise(fulfill => this._closedCallback = fulfill); this._isClosedPromise = new Promise<boolean>(fulfill => this._closedCallback = fulfill);
this._isInitialized = this._targetInfo.type !== 'page' || this._targetInfo.url !== ''; this._isInitialized = this._targetInfo.type !== 'page' || this._targetInfo.url !== '';
if (this._isInitialized) if (this._isInitialized)
this._initializedCallback(true); this._initializedCallback(true);
} }
/** createCDPSession(): Promise<CDPSession> {
* @return {!Promise<!CDPSession>}
*/
createCDPSession() {
return this._sessionFactory(); return this._sessionFactory();
} }
/** async page(): Promise<Puppeteer.Page | null> {
* @return {!Promise<?Page>}
*/
async page() {
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) { if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
this._pagePromise = this._sessionFactory() this._pagePromise = this._sessionFactory()
.then(client => Page.create(client, this, this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue)); .then(client => Page.create(client, this, this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue));
@ -86,10 +80,7 @@ class Target {
return this._pagePromise; return this._pagePromise;
} }
/** async worker(): Promise<PuppeteerWorker | null> {
* @return {!Promise<?PuppeteerWorker>}
*/
async worker() {
if (this._targetInfo.type !== 'service_worker' && this._targetInfo.type !== 'shared_worker') if (this._targetInfo.type !== 'service_worker' && this._targetInfo.type !== 'shared_worker')
return null; return null;
if (!this._workerPromise) { if (!this._workerPromise) {
@ -100,51 +91,33 @@ class Target {
return this._workerPromise; return this._workerPromise;
} }
/** url(): string {
* @return {string}
*/
url() {
return this._targetInfo.url; return this._targetInfo.url;
} }
/** type(): 'page'|'background_page'|'service_worker'|'shared_worker'|'other'|'browser'{
* @return {"page"|"background_page"|"service_worker"|"shared_worker"|"other"|"browser"}
*/
type() {
const type = this._targetInfo.type; const type = this._targetInfo.type;
if (type === 'page' || type === 'background_page' || type === 'service_worker' || type === 'shared_worker' || type === 'browser') if (type === 'page' || type === 'background_page' || type === 'service_worker' || type === 'shared_worker' || type === 'browser')
return type; return type;
return 'other'; return 'other';
} }
/** browser(): Browser {
* @return {!Browser}
*/
browser() {
return this._browserContext.browser(); return this._browserContext.browser();
} }
/** browserContext(): BrowserContext {
* @return {!BrowserContext}
*/
browserContext() {
return this._browserContext; return this._browserContext;
} }
/** opener(): Target | null {
* @return {?Puppeteer.Target}
*/
opener() {
const {openerId} = this._targetInfo; const {openerId} = this._targetInfo;
if (!openerId) if (!openerId)
return null; return null;
return this.browser()._targets.get(openerId); return this.browser()._targets.get(openerId);
} }
/** _targetInfoChanged(targetInfo: Protocol.Target.TargetInfo): void {
* @param {!Protocol.Target.TargetInfo} targetInfo
*/
_targetInfoChanged(targetInfo) {
this._targetInfo = targetInfo; this._targetInfo = targetInfo;
if (!this._isInitialized && (this._targetInfo.type !== 'page' || this._targetInfo.url !== '')) { if (!this._isInitialized && (this._targetInfo.type !== 'page' || this._targetInfo.url !== '')) {
@ -154,5 +127,3 @@ class Target {
} }
} }
} }
module.exports = {Target};

2
src/externs.d.ts vendored
View File

@ -1,10 +1,8 @@
import {Target as RealTarget} from './Target.js';
import {Page as RealPage} from './Page.js'; import {Page as RealPage} from './Page.js';
import { NetworkManager as RealNetworkManager, Request as RealRequest, Response as RealResponse } from './NetworkManager.js'; import { NetworkManager as RealNetworkManager, Request as RealRequest, Response as RealResponse } from './NetworkManager.js';
import * as child_process from 'child_process'; import * as child_process from 'child_process';
declare global { declare global {
module Puppeteer { module Puppeteer {
export class Target extends RealTarget {}
export class NetworkManager extends RealNetworkManager {} export class NetworkManager extends RealNetworkManager {}
export class Page extends RealPage { } export class Page extends RealPage { }
export class Response extends RealResponse { } export class Response extends RealResponse { }