diff --git a/README.md b/README.md index 208b9b14..a96c8a12 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ const chromeless = new Chromeless({ - [`end()`](docs/api.md#api-end) **Chrome methods** -- [`goto(url: string)`](docs/api.md#api-goto) +- [`goto(url: string, timeout?: number)`](docs/api.md#api-goto) - [`setUserAgent(useragent: string)`](docs/api.md#api-setUserAgent) - [`click(selector: string)`](docs/api.md#api-click) - [`wait(timeout: number)`](docs/api.md#api-wait-timeout) diff --git a/docs/api.md b/docs/api.md index 35ca0af0..bd23d660 100644 --- a/docs/api.md +++ b/docs/api.md @@ -19,7 +19,7 @@ Chromeless provides TypeScript typings. - [`end()`](#api-end) ### Chrome methods -- [`goto(url: string)`](#api-goto) +- [`goto(url: string, timeout?: number)`](#api-goto) - [`setUserAgent(useragent: string)`](#api-setuseragent) - [`click(selector: string)`](#api-click) - [`wait(timeout: number)`](#api-wait-timeout) @@ -73,12 +73,13 @@ await chromeless.end() -### goto(url: string): Chromeless +### goto(url: string, timeout?: number): Chromeless Navigate to a URL. __Arguments__ - `url` - URL to navigate to +- `timeout` -How long to wait for page to load (default is value of waitTimeout option) __Example__ @@ -173,7 +174,7 @@ __Arguments__ __Example__ ```js -await chromeless.wait(() => { +await chromeless.wait(() => { return new Promise((resolve, reject) => { // do something async, setTimeout... resolve(); diff --git a/package-lock.json b/package-lock.json index d0db1e1d..8be9b7a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,7 +2,6 @@ "name": "chromeless", "version": "1.4.0", "lockfileVersion": 1, - "requires": true, "dependencies": { "@ava/babel-plugin-throws-helper": { "version": "2.0.0", @@ -5958,6 +5957,16 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "JSONStream": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", + "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -11082,6 +11091,14 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -11109,14 +11126,6 @@ } } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - }, "stringify-object": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.2.2.tgz", diff --git a/src/api.ts b/src/api.ts index 840a977e..24c3cbc6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -73,8 +73,8 @@ export default class Chromeless implements Promise { return this.lastReturnPromise.catch(onrejected) as Promise } - goto(url: string): Chromeless { - this.queue.enqueue({ type: 'goto', url }) + goto(url: string, timeout?: number): Chromeless { + this.queue.enqueue({ type: 'goto', url, timeout }) return this } diff --git a/src/chrome/local-runtime.ts b/src/chrome/local-runtime.ts index af1f7392..016caa5b 100644 --- a/src/chrome/local-runtime.ts +++ b/src/chrome/local-runtime.ts @@ -40,6 +40,8 @@ import { writeToFile, isS3Configured, uploadToS3, + eventToPromise, + waitForPromise, } from '../util' export default class LocalRuntime { @@ -55,7 +57,7 @@ export default class LocalRuntime { async run(command: Command): Promise { switch (command.type) { case 'goto': - return this.goto(command.url) + return this.goto(command.url, command.timeout) case 'setViewport': return setViewport(this.client, command.options) case 'wait': { @@ -126,13 +128,18 @@ export default class LocalRuntime { } } - private async goto(url: string): Promise { + private async goto( + url: string, + waitTimeout: number = this.chromelessOptions.waitTimeout, + ): Promise { const { Network, Page } = this.client await Promise.all([Network.enable(), Page.enable()]) if (!this.userAgentValue) this.userAgentValue = `Chromeless ${version}` await Network.setUserAgentOverride({ userAgent: this.userAgentValue }) + const e2p = eventToPromise() + Page.loadEventFired(e2p.onEvent) await Page.navigate({ url }) - await Page.loadEventFired() + await waitForPromise(e2p.fired(), waitTimeout, 'page load event') this.log(`Navigated to ${url}`) } diff --git a/src/types.ts b/src/types.ts index 5f5c7f97..ef09ec64 100644 --- a/src/types.ts +++ b/src/types.ts @@ -64,6 +64,7 @@ export type Command = | { type: 'goto' url: string + timeout?: number } | { type: 'clearCache' diff --git a/src/util.ts b/src/util.ts index f46f9eb0..03d89947 100644 --- a/src/util.ts +++ b/src/util.ts @@ -106,6 +106,42 @@ export async function wait(timeout: number): Promise { return new Promise((resolve, reject) => setTimeout(resolve, timeout)) } +export async function waitForPromise( + promise: Promise, + waitTimeout: number, + label?: string, +): Promise { + return new Promise((resolve, reject) => { + let fullfilled = false + setTimeout(() => { + fullfilled = true + reject( + new Error( + `wait(${label || 'Promise'}) timed out after ${waitTimeout}ms`, + ), + ) + }, waitTimeout) + return promise + .then(res => (fullfilled ? void 0 : resolve(res))) + .catch(err => (fullfilled ? void 0 : reject(err))) + }) +} + +export function eventToPromise() { + let resolve + const promise = new Promise(res => { + resolve = res + }) + return { + onEvent(...args) { + resolve(args.length > 1 ? args : args[0]) + }, + fired() { + return promise + }, + } +} + export async function nodeExists( client: Client, selector: string,