diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8c22099cc8..baf58ccc43 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2725,7 +2725,7 @@ /types/pug/ @TonyPythoneer @19majkel94 /types/pulltorefreshjs/ @DanielRosenwasser /types/pump/ @tlaziuk -/types/puppeteer/ @marvinhagemeister +/types/puppeteer/ @marvinhagemeister @cdeutsch /types/pure-render-decorator/ @seansfkelley /types/purl/ @danfma /types/pusher-js/ @tkqubo diff --git a/types/puppeteer/index.d.ts b/types/puppeteer/index.d.ts index 708ad90d95..12af2a6eeb 100644 --- a/types/puppeteer/index.d.ts +++ b/types/puppeteer/index.d.ts @@ -1,14 +1,17 @@ // Type definitions for puppeteer 0.12 // Project: https://github.com/GoogleChrome/puppeteer#readme // Definitions by: Marvin Hagemeister +// Christopher Deutsch // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.3 /// export interface Keyboard { - down(key: string, options?: { text: string }): Promise; + down(key: string, options?: { text?: string }): Promise; + press(key: string, options?: { text?: string, delay?: number }): Promise; sendCharacter(char: string): Promise; + type(text: string, options?: { delay?: number }): Promise; up(key: string): Promise; } @@ -41,6 +44,14 @@ export interface Dialog { type: "alert" | "beforeunload" | "confirm" | "prompt"; } +export interface ConsoleMessage { + args: JSHandle[]; + text: string; + type: 'log' | 'debug' | 'info' | 'error' | 'warning' | 'dir' | 'dirxml' | 'table' | + 'trace' | 'clear' | 'startGroup' | 'startGroupCollapsed' | 'endGroup' | 'assert' | + 'profile' | 'profileEnd' | 'count' | 'timeEnd'; +} + export type PageEvents = | "console" | "dialog" @@ -99,7 +110,7 @@ export interface EmulateOptions { userAgent?: string; } -export type EvaluateFn = (elem?: ElementHandle) => Promise; +export type EvaluateFn = string | ((...args: any[]) => any); export interface NavigationOptions { timeout?: number; @@ -149,28 +160,78 @@ export interface ScreenshotOptions { /** The quality of the image, between 0-100. Not applicable to png images. */ quality?: number; fullPage?: boolean; - clip?: { - x: number; - y: number; - width: number; - height: number; - }; + clip?: BoundingBox; omitBackground?: boolean; } +export interface TagOptions { + url?: string; + path?: string; + content?: string; +} + export interface PageFnOptions { polling?: "raf" | "mutation" | number; timeout?: number; } -export interface ElementHandle { +export interface BoundingBox { + x: number; + y: number; + width: number; + height: number; +} + +export interface ElementHandle extends JSHandle { + boundingBox(): BoundingBox; click(options?: ClickOptions): Promise; - dispose(): Promise; + focus(): Promise; hover(): Promise; + press(key: string, options?: { text?: string, delay?: number }): Promise; + screenshot(options?: ScreenshotOptions): Promise; tap(): Promise; + toString(): string; + type(selector: string, text: string, options?: { delay: number }): Promise; uploadFile(...filePaths: string[]): Promise; } +export interface ExecutionContext { + evaluate( + fn: EvaluateFn, + ...args: any[] + ): Promise; + evaluateHandle( + fn: EvaluateFn, + ...args: any[] + ): Promise; + queryObjects(prototypeHandle: JSHandle): JSHandle; +} + +export interface JSHandle { + asElement(): ElementHandle; + dispose(): Promise; + executionContext(): ExecutionContext; + getProperties(): Promise>; + getProperty(propertyName: string): Promise; + jsonValue(): Promise; +} + +export interface Metrics { + Timestamp: number; + Documents: number; + Frames: number; + JSEventListeners: number; + Nodes: number; + LayoutCount: number; + RecalcStyleCount: number; + LayoutDuration: number; + RecalcStyleDuration: number; + ScriptDuration: number; + TaskDuration: number; + JSHeapUsedSize: number; + JSHeapTotalSize: number; +} + export type Headers = Record; export type HttpMethod = | "GET" @@ -216,7 +277,7 @@ export interface Request { export interface Response { buffer(): Promise; headers: Headers; - json(): Promise; + json(): Promise; ok: boolean; request(): Request; status: number; @@ -224,28 +285,32 @@ export interface Response { url: string; } -export type Serializable = boolean | number | string | object; - export interface FrameBase { $(selector: string): Promise; $$(selector: string): Promise; + $$eval( + selector: string, + fn: (...args: any[]) => void + ): Promise; $eval( selector: string, - fn: (...args: Array) => void - ): Promise; - addScriptTag(url: string): Promise; + fn: (...args: any[]) => void + ): Promise; + addScriptTag(options: TagOptions): Promise; + addStyleTag(options: TagOptions): Promise; injectFile(filePath: string): Promise; - evaluate( - fn: T | EvaluateFn, - ...args: Array - ): Promise; + evaluate( + fn: EvaluateFn, + ...args: any[] + ): Promise; title(): Promise; url(): string; waitFor( // fn can be an abritary function // tslint:disable-next-line ban-types selectorOrFunctionOrTimeout: string | number | Function, - options?: object + options?: any, + ...args: any[] ): Promise; waitForFunction( // fn can be an abritary function @@ -254,7 +319,6 @@ export interface FrameBase { options?: PageFnOptions, ...args: any[] ): Promise; - waitForNavigation(options?: NavigationOptions): Promise; waitForSelector( selector: string, options?: { visible: boolean; timeout: number } @@ -263,19 +327,21 @@ export interface FrameBase { export interface Frame extends FrameBase { childFrames(): Frame[]; + executionContext(): ExecutionContext; isDetached(): boolean; name(): string; parentFrame(): Frame | undefined; } export interface EventObj { - console: string; + console: ConsoleMessage; dialog: Dialog; error: Error; frameattached: Frame; framedetached: Frame; framenavigated: Frame; load: undefined; + metrics: { title: string, metrics: any }; pageerror: string; request: Request; requestfailed: Request; @@ -284,7 +350,6 @@ export interface EventObj { } export interface Page extends FrameBase { - on(event: "console", handler: (...args: any[]) => void): void; on( event: K, handler: (e: EventObj[K], ...args: any[]) => void @@ -304,17 +369,22 @@ export interface Page extends FrameBase { }> ): Promise; emulate(options: Partial): Promise; - emulateMedia(mediaType: string | null): Promise; + emulateMedia(mediaType: 'screen' | 'print' | null): Promise; + evaluateHandle( + fn: EvaluateFn, + ...args: any[] + ): Promise; evaluateOnNewDocument( - fn: EvaluateFn, - ...args: object[] + fn: EvaluateFn, + ...args: any[] ): Promise; // Argument `fn` can be an arbitrary function - exposeFunction(name: string, fn: any): Promise; + exposeFunction(name: string, fn: (...args: any[]) => any): Promise; focus(selector: string): Promise; frames(): Frame[]; + getMetrics(): Metrics; goBack(options?: Partial): Promise; goForward(options?: Partial): Promise; goto(url: string, options?: Partial): Promise; @@ -323,10 +393,10 @@ export interface Page extends FrameBase { mainFrame(): Frame; mouse: Mouse; pdf(options?: Partial): Promise; - plainText(): Promise; - press(key: string, options?: { text: string; delay: number }): Promise; + queryObjects(prototypeHandle: JSHandle): Promise; reload(options?: NavigationOptions): Promise; screenshot(options?: ScreenshotOptions): Promise; + select(selector: string, ...values: string[]): Promise; setContent(html: string): Promise; setCookie(...cookies: Cookie[]): Promise; setExtraHTTPHeaders(headers: Headers): Promise; @@ -337,8 +407,9 @@ export interface Page extends FrameBase { tap(selector: string): Promise; touchscreen: Touchscreen; tracing: Tracing; - type(text: string, options?: { delay: number }): Promise; + type(selector: string, text: string, options?: { delay: number }): Promise; viewport(): Viewport; + waitForNavigation(options?: NavigationOptions): Promise; } export interface Browser { @@ -383,6 +454,10 @@ export interface LaunchOptions { dumpio?: boolean; /** Path to a User Data Directory. */ userDataDir?: string; + /** Specify environment variables that will be visible to Chromium. Defaults to process.env. */ + env?: any; + /** Whether to auto-open DevTools panel for each tab. If this option is true, the headless option will be set false. */ + devtools?: boolean; } export interface ConnectOptions { diff --git a/types/puppeteer/puppeteer-tests.ts b/types/puppeteer/puppeteer-tests.ts index b7ca789cde..403c3ac7ac 100644 --- a/types/puppeteer/puppeteer-tests.ts +++ b/types/puppeteer/puppeteer-tests.ts @@ -41,7 +41,7 @@ import * as puppeteer from "puppeteer"; // The following examples are taken from the docs itself puppeteer.launch().then(async browser => { const page = await browser.newPage(); - page.on("console", (...args) => { + page.on("console", (...args: any[]) => { for (let i = 0; i < args.length; ++i) console.log(`${i}: ${args[i]}`); }); page.evaluate(() => console.log(5, "hello", { foo: "bar" })); @@ -109,8 +109,8 @@ puppeteer.launch().then(async browser => { else interceptedRequest.continue(); }); - page.type("Hello"); // Types instantly - page.type("World", { delay: 100 }); // Types slower, like a user + page.keyboard.type("Hello"); // Types instantly + page.keyboard.type("World", { delay: 100 }); // Types slower, like a user const watchDog = page.waitForFunction("window.innerWidth < 100"); page.setViewport({ width: 50, height: 50 }); @@ -128,16 +128,16 @@ puppeteer.launch().then(async browser => { await page.goto(currentURL); } - page.type("Hello World!"); - page.press("ArrowLeft"); + page.keyboard.type("Hello World!"); + page.keyboard.press("ArrowLeft"); page.keyboard.down("Shift"); // tslint:disable-next-line prefer-for-of for (let i = 0; i < " World".length; i++) { - page.press("ArrowLeft"); + page.keyboard.press("ArrowLeft"); } page.keyboard.up("Shift"); - page.press("Backspace"); + page.keyboard.press("Backspace"); page.keyboard.sendCharacter("嗨"); await page.tracing.start({ path: "trace.json" }); @@ -168,3 +168,89 @@ puppeteer.launch().then(async browser => { browser.close(); })(); + +// Test v0.12 features +(async () => { + const browser = await puppeteer.launch({ + devtools: true, + env: { + JEST_TEST: true + } + }); + const page = await browser.newPage(); + const button = await page.$("#myButton"); + const div = await page.$("#myDiv"); + const input = await page.$("#myInput"); + + await page.addStyleTag({ + url: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" + }); + + console.log(page.url()); + + page.type("#myInput", "Hello World!"); + + page.on("console", (event: puppeteer.ConsoleMessage, ...args: any[]) => { + console.log(event.text, event.type); + for (let i = 0; i < args.length; ++i) console.log(`${i}: ${args[i]}`); + }); + + await button.focus(); + await button.press("Enter"); + await button.screenshot({ + type: "jpeg", + omitBackground: true, + clip: { + x: 0, + y: 0, + width: 200, + height: 100 + } + }); + console.log(button.toString()); + div.type("input", "Hello World", { delay: 10 }); + + const buttonText = await (await button.getProperty('textContent')).jsonValue(); + + await page.deleteCookie(...await page.cookies()); + + const metrics = page.getMetrics(); + console.log(metrics.Documents, metrics.Frames, metrics.JSEventListeners); + + const navResponse = await page.waitForNavigation({ + timeout: 1000 + }); + console.log(navResponse.ok, navResponse.status, navResponse.url, navResponse.headers); + + // evaluate example + const bodyHandle = await page.$('body'); + const html = await page.evaluate(body => body.innerHTML, bodyHandle); + await bodyHandle.dispose(); + + // getProperties example + const handle = await page.evaluateHandle(() => ({window, document})); + const properties = await handle.getProperties(); + const windowHandle = properties.get('window'); + const documentHandle = properties.get('document'); + await handle.dispose(); + + // queryObjects example + // Create a Map object + await page.evaluate(() => (window as any).map = new Map()); + // Get a handle to the Map object prototype + const mapPrototype = await page.evaluateHandle(() => Map.prototype); + // Query all map instances into an array + const mapInstances = await page.queryObjects(mapPrototype); + // Count amount of map objects in heap + const count = await page.evaluate(maps => maps.length, mapInstances); + await mapInstances.dispose(); + await mapPrototype.dispose(); + + // evaluateHandle example + const aHandle = await page.evaluateHandle(() => document.body); + const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle); + console.log(await resultHandle.jsonValue()); + await resultHandle.dispose(); + + browser.close(); +})();