puppeteer 1.0 (#23014)

This commit is contained in:
jwbay
2018-01-23 13:53:59 -05:00
committed by Andy
parent 22afda5072
commit f08ddbb734
6 changed files with 1679 additions and 28 deletions

View File

@@ -1,13 +1,15 @@
// Type definitions for puppeteer 0.13
// Type definitions for puppeteer 1.0
// Project: https://github.com/GoogleChrome/puppeteer#readme
// Definitions by: Marvin Hagemeister <https://github.com/marvinhagemeister>
// Christopher Deutsch <https://github.com/cdeutsch>
// jwbay <https://github.com/jwbay>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
/// <reference types="node" />
import { EventEmitter } from "events";
import { ChildProcess } from "child_process";
/** Keyboard provides an api for managing a virtual keyboard. */
export interface Keyboard {
@@ -91,10 +93,16 @@ export interface Touchscreen {
* You can use `tracing.start` and `tracing.stop` to create a trace file which can be opened in Chrome DevTools or timeline viewer.
*/
export interface Tracing {
start(options: { path: string; screenshots?: boolean }): Promise<void>;
start(options: TracingStartOptions): Promise<void>;
stop(): Promise<void>;
}
export interface TracingStartOptions {
path: string;
screenshots?: boolean;
categories?: string[];
}
/** Dialog objects are dispatched by page via the 'dialog' event. */
export interface Dialog {
/**
@@ -113,16 +121,16 @@ export interface Dialog {
message(): string;
/** The dialog type. Dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`. */
type: "alert" | "beforeunload" | "confirm" | "prompt";
type(): "alert" | "beforeunload" | "confirm" | "prompt";
}
/** ConsoleMessage objects are dispatched by page via the 'console' event. */
export interface ConsoleMessage {
/** The message arguments. */
args: JSHandle[];
args(): JSHandle[];
/** The message text. */
text: string;
type: 'log' | 'debug' | 'info' | 'error' | 'warning' | 'dir' | 'dirxml' | 'table' |
text(): string;
type(): 'log' | 'debug' | 'info' | 'error' | 'warning' | 'dir' | 'dirxml' | 'table' |
'trace' | 'clear' | 'startGroup' | 'startGroupCollapsed' | 'endGroup' | 'assert' |
'profile' | 'profileEnd' | 'count' | 'timeEnd';
}
@@ -302,6 +310,24 @@ export interface PDFOptions {
* @default false
*/
displayHeaderFooter?: boolean;
/**
* HTML template for the print header. Should be valid HTML markup with following classes used to inject printing values into them:
* - `date` formatted print date
* - `title` document title
* - `url` document location
* - `pageNumber` current page number
* - `totalPages` total pages in the document
*/
headerTemplate?: string;
/**
* HTML template for the print footer. Should be valid HTML markup with following classes used to inject printing values into them:
* - `date` formatted print date
* - `title` document title
* - `url` document location
* - `pageNumber` current page number
* - `totalPages` total pages in the document
*/
footerTemplate?: string;
/**
* Print background graphics.
* @default false
@@ -418,6 +444,10 @@ export interface ElementHandle extends JSHandle {
* @since 0.13.0
*/
$$(selector: string): Promise<ElementHandle[]>;
/**
* @param selector XPath expression to evaluate.
*/
$x(expression: string): Promise<ElementHandle[]>;
/**
* This method returns the value resolve to the bounding box of the element (relative to the main frame), or null if the element is not visible.
*/
@@ -590,20 +620,25 @@ export interface Request {
*/
continue(overrides?: Overrides): Promise<void>;
/**
* @returns The `Frame` object that initiated the request, or `null` if navigating to error pages
*/
frame(): Promise<Frame | null>;
/**
* An object with HTTP headers associated with the request.
* All header names are lower-case.
*/
headers: Headers;
headers(): Headers;
/** Returns the request's method (GET, POST, etc.) */
method: HttpMethod;
method(): HttpMethod;
/** Contains the request's post body, if any. */
postData: string | undefined;
postData(): string | undefined;
/** Contains the request's resource type as it was perceived by the rendering engine. */
resourceType: ResourceType;
resourceType(): ResourceType;
/**
* Fulfills request with given response.
@@ -617,7 +652,7 @@ export interface Request {
response(): Response | null;
/** Contains the URL of the request. */
url: string;
url(): string;
}
/** Options for `Request.respond` method */
export interface RespondOptions {
@@ -639,22 +674,22 @@ export interface Response {
/** Promise which resolves to a buffer with response body. */
buffer(): Promise<Buffer>;
/** An object with HTTP headers associated with the response. All header names are lower-case. */
headers: Headers;
headers(): Headers;
/**
* Promise which resolves to a JSON representation of response body.
* @throws This method will throw if the response body is not parsable via `JSON.parse`.
*/
json(): Promise<any>;
/** Contains a boolean stating whether the response was successful (status in the range 200-299) or not. */
ok: boolean;
ok(): boolean;
/** A matching Request object. */
request(): Request;
/** Contains the status code of the response (e.g., 200 for a success). */
status: number;
status(): number;
/** Promise which resolves to a text representation of response body. */
text(): Promise<string>;
/** Contains the URL of the response. */
url: string;
url(): string;
}
export interface FrameBase {
@@ -667,6 +702,10 @@ export interface FrameBase {
* The method runs document.querySelectorAll within the page. If no elements match the selector, the return value resolve to [].
*/
$$(selector: string): Promise<ElementHandle[]>;
/**
* @param expression XPath expression to evaluate.
*/
$x(expression: string): Promise<ElementHandle[]>;
/**
* This method runs document.querySelector within the page and passes it as the first argument to `fn`.
@@ -699,6 +738,9 @@ export interface FrameBase {
/** Adds a `<link rel="stylesheet">` tag into the page with the desired url or a `<style type="text/css">` tag with the content. */
addStyleTag(options: StyleTagOptions): Promise<void>;
/** Gets the full HTML contents of the page, including the doctype. */
content(): Promise<string>;
/**
* Evaluates a function in the browser context.
* If the function, passed to the frame.evaluate, returns a Promise, then frame.evaluate would wait for the promise to resolve and return its value.
@@ -711,6 +753,12 @@ export interface FrameBase {
...args: any[]
): Promise<any>;
/**
* Sets the page content.
* @param html HTML markup to assign to the page.
*/
setContent(html: string): Promise<void>;
/** Returns page's title. */
title(): Promise<string>;
@@ -723,7 +771,7 @@ export interface FrameBase {
selectorOrFunctionOrTimeout: string | number | Function,
options?: any,
...args: any[]
): Promise<void>;
): Promise<any>;
waitForFunction(
// fn can be an abritary function
@@ -731,11 +779,12 @@ export interface FrameBase {
fn: string | Function,
options?: PageFnOptions,
...args: any[]
): Promise<void>;
): Promise<any>;
waitForSelector(
selector: string,
options?: { visible?: boolean; hidden?: boolean; timeout?: number }
): Promise<void>;
): Promise<ElementHandle>;
}
export interface Frame extends FrameBase {
@@ -837,9 +886,6 @@ export interface Page extends EventEmitter, FrameBase {
/** Closes the current page. */
close(): Promise<void>;
/** Gets the full HTML contents of the page, including the doctype. */
content(): Promise<string>;
/**
* Gets the cookies.
* If no URLs are specified, this method returns cookies for the current page URL.
@@ -847,6 +893,8 @@ export interface Page extends EventEmitter, FrameBase {
*/
cookies(...urls: string[]): Promise<Cookie[]>;
coverage: Coverage;
/**
* Deletes the specified cookies.
*/
@@ -970,17 +1018,22 @@ export interface Page extends EventEmitter, FrameBase {
*/
select(selector: string, ...values: string[]): Promise<string[]>;
/**
* Sets the page content.
* @param html HTML markup to assign to the page.
*/
setContent(html: string): Promise<void>;
/**
* Sets the cookies on the page.
* @param cookies The cookies to set.
*/
setCookie(...cookies: SetCookie[]): Promise<void>;
/**
* This setting will change the default maximum navigation time of 30 seconds for the following methods:
* - `page.goto`
* - `page.goBack`
* - `page.goForward`
* - `page.reload`
* - `page.waitForNavigation`
*/
setDefaultNavigationTimeout(timeout: number): void;
/**
* The extra HTTP headers will be sent with every request the page initiates.
* @param headers An object containing additional http headers to be sent with every request. All header values must be strings.
@@ -1024,6 +1077,9 @@ export interface Page extends EventEmitter, FrameBase {
*/
tap(selector: string): Promise<void>;
/** @returns The target this page was created from */
target(): Target;
/** Returns the page's title. */
title(): Promise<string>;
@@ -1099,9 +1155,18 @@ export interface Browser extends EventEmitter {
/** Promise which resolves to an array of all open pages. */
pages(): Promise<Page[]>;
/** Spawned browser process. Returns `null` if the browser instance was created with `puppeteer.connect` method */
process(): ChildProcess;
/** Promise which resolves to an array of all active targets. */
targets(): Promise<Target[]>;
/**
* Promise which resolves to the browser's original user agent.
* **NOTE** Pages can override browser user agent with `page.setUserAgent`.
*/
userAgent(): Promise<string>;
/** For headless Chromium, this is similar to HeadlessChrome/61.0.3153.0. For non-headless, this is similar to Chrome/61.0.3153.0. */
version(): Promise<string>;
@@ -1124,6 +1189,9 @@ export interface BrowserEventObj {
}
export interface Target {
/** Creates a Chrome Devtools Protocol session attached to the target. */
createCDPSession(): Promise<CDPSession>;
/** Returns the target `Page` or a `null` if the type of the page is not "page". */
page(): Promise<Page>;
@@ -1137,6 +1205,8 @@ export interface Target {
export interface LaunchOptions {
/** Whether to ignore HTTPS errors during navigation. Defaults to false. */
ignoreHTTPSErrors?: boolean;
/** Do not use `puppeteer.defaultArgs()` for launching Chromium. Defaults to false. */
ignoreDefaultArgs?: boolean;
/** Whether to run Chromium in headless mode. Defaults to true. */
headless?: boolean;
/**
@@ -1189,8 +1259,41 @@ export interface ConnectOptions {
ignoreHTTPSErrors?: boolean;
}
export interface CDPSession extends EventEmitter {
/**
* Detaches session from target. Once detached, session won't emit any events and can't be used
* to send messages.
*/
detach(): Promise<void>;
/**
* @param method Protocol method name
*/
send(method: string, params?: object): Promise<any>;
}
export interface Coverage {
startCSSCoverage(options?: StartCoverageOptions): Promise<void>;
startJSCoverage(options?: StartCoverageOptions): Promise<void>;
stopCSSCoverage(): Promise<CoverageEntry[]>;
stopJSCoverage(): Promise<CoverageEntry[]>;
}
export interface StartCoverageOptions {
/** Whether to reset coverage on every navigation. Defaults to `true`. */
resetOnNavigation?: boolean;
}
export interface CoverageEntry {
url: string;
text: string;
ranges: Array<{start: number, end: number}>;
}
/** Attaches Puppeteer to an existing Chromium instance */
export function connect(options?: ConnectOptions): Promise<Browser>;
/** The default flags that Chromium will be launched with */
export function defaultArgs(): string[];
/** Path where Puppeteer expects to find bundled Chromium */
export function executablePath(): string;
/** The method launches a browser instance with given arguments. The browser will be closed when the parent node.js process is closed. */

View File

@@ -102,8 +102,8 @@ puppeteer.launch().then(async browser => {
await page.setRequestInterception(true);
page.on("request", interceptedRequest => {
if (
interceptedRequest.url.endsWith(".png") ||
interceptedRequest.url.endsWith(".jpg")
interceptedRequest.url().endsWith(".png") ||
interceptedRequest.url().endsWith(".jpg")
)
interceptedRequest.abort();
else interceptedRequest.continue();
@@ -281,3 +281,36 @@ puppeteer.launch().then(async browser => {
browser.close();
})();
// Test 1.0 features
(async () => {
const browser = await puppeteer.launch({
ignoreDefaultArgs: true,
});
const page = await browser.newPage();
const args: string[] = puppeteer.defaultArgs();
await page.pdf({
headerTemplate: 'header',
footerTemplate: 'footer',
});
await page.coverage.startCSSCoverage();
await page.coverage.startJSCoverage();
let cov = await page.coverage.stopCSSCoverage();
cov = await page.coverage.stopJSCoverage();
const text: string = cov[0].text;
const url: string = cov[0].url;
const firstRange: number = cov[0].ranges[0].end - cov[0].ranges[0].start;
let [handle]: puppeteer.ElementHandle[] = await page.$x('expression');
([handle] = await page.mainFrame().$x('expression'));
([handle] = await handle.$x('expression'));
const target = page.target();
const session = await target.createCDPSession();
await session.send('methodname', { option: 42 });
await session.detach();
await page.tracing.start({ path: "trace.json", categories: ["one", "two"] });
});

1197
types/puppeteer/v0/index.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,283 @@
import * as puppeteer from "puppeteer";
// Examples taken from README
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
await page.screenshot({ path: "example.png" });
browser.close();
})();
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://news.ycombinator.com", { waitUntil: "networkidle0" });
await page.pdf({ path: "hn.pdf", format: "A4" });
browser.close();
})();
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log("Dimensions:", dimensions);
browser.close();
})();
// The following examples are taken from the docs itself
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
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" }));
const result = await page.evaluate(() => {
return Promise.resolve(8 * 7);
});
console.log(await page.evaluate("1 + 2"));
const bodyHandle = await page.$("body");
// Typings for this are really difficult since they depend on internal state
// of the page class.
const html = await page.evaluate(
(body: HTMLElement) => body.innerHTML,
bodyHandle
);
});
import * as crypto from "crypto";
import * as fs from "fs";
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
page.on("console", console.log);
await page.exposeFunction("md5", (text: string) =>
crypto
.createHash("md5")
.update(text)
.digest("hex")
);
await page.evaluate(async () => {
// use window.md5 to compute hashes
const myString = "PUPPETEER";
const myHash = await (window as any).md5(myString);
console.log(`md5 of ${myString} is ${myHash}`);
});
browser.close();
page.on("console", console.log);
await page.exposeFunction("readfile", async (filePath: string) => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf8", (err, text) => {
if (err) reject(err);
else resolve(text);
});
});
});
await page.evaluate(async () => {
// use window.readfile to read contents of a file
const content = await (window as any).readfile("/etc/hosts");
console.log(content);
});
await page.emulateMedia("screen");
await page.pdf({ path: "page.pdf" });
await page.setRequestInterception(true);
page.on("request", interceptedRequest => {
if (
interceptedRequest.url.endsWith(".png") ||
interceptedRequest.url.endsWith(".jpg")
)
interceptedRequest.abort();
else interceptedRequest.continue();
});
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 });
await watchDog;
let currentURL: string;
page
.waitForSelector("img", { visible: true })
.then(() => console.log("First URL with image: " + currentURL));
for (currentURL of [
"https://example.com",
"https://google.com",
"https://bbc.com"
]) {
await page.goto(currentURL);
}
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.keyboard.press("ArrowLeft");
}
page.keyboard.up("Shift");
page.keyboard.press("Backspace");
page.keyboard.sendCharacter("嗨");
await page.tracing.start({ path: "trace.json" });
await page.goto("https://www.google.com");
await page.tracing.stop();
page.on("dialog", async dialog => {
console.log(dialog.message());
await dialog.dismiss();
browser.close();
});
const inputElement = (await page.$("input[type=submit]"))!;
await inputElement.click();
});
// Example with launch options
(async () => {
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
],
handleSIGINT: true,
handleSIGHUP: true,
handleSIGTERM: true,
});
const page = await browser.newPage();
await page.goto("https://example.com");
await page.screenshot({ path: "example.png" });
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"))!;
if (!button)
throw new Error('Unable to select myButton');
if (!input)
throw new Error('Unable to select 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());
input.type("Hello World", { delay: 10 });
const buttonText = await (await button.getProperty('textContent')).jsonValue();
await page.deleteCookie(...await page.cookies());
const metrics = await page.metrics();
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();
})();
// test $eval and $$eval
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
let elementText = await page.$eval('#someElement', (element) => {
return element.innerHTML;
});
elementText = await page.$$eval('.someClassName', (elements) => {
console.log(elements.length);
console.log(elements.item(0).outerHTML);
return elements[3].innerHTML;
});
browser.close();
})();

View File

@@ -0,0 +1,34 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom",
"es2017"
],
"target": "es2017",
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../../",
"typeRoots": [
"../../"
],
"paths": {
"puppeteer": [
"puppeteer/v0"
],
"puppeteer/*": [
"puppeteer/v0/*"
]
},
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"puppeteer-tests.ts"
]
}

View File

@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }