Skip to content

Commit

Permalink
✨ Add timeout option (#189)
Browse files Browse the repository at this point in the history
* timeout support added

* 💡 Remove lint comments from tests
---------

Co-authored-by: Yvonnick Frin <frin.yvonnick@gmail.com>
  • Loading branch information
adasq and frinyvonnick authored Aug 1, 2023
1 parent 7486250 commit 20110bc
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 16 deletions.
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,23 @@ import nodeHtmlToImage from 'node-html-to-image'

List of all available options:

| option | description | type | required |
|-------------------------|-------------------------------------------------------------------------------------------------|----------------------------|-------------|
| output | The ouput path for generated image | string | optional |
| html | The html used to generate image content | string | required |
| type | The type of the generated image | jpeg or png (default: png) | optional |
| quality | The quality of the generated image (only applicable to jpg) | number (default: 80) | optional |
| content | If provided html property is considered an handlebars template and use content value to fill it | object or Array | optional |
| waitUntil | Define when to consider markup succeded. [Learn more](https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#pagesetcontenthtml-options). | string or Array<string> (default: networkidle0) | optional |
| puppeteer | The puppeteer property let you use a different puppeteer library (like puppeteer-core or puppeteer-extra). | object (default: puppeteer) | optional |
| puppeteerArgs | The puppeteerArgs property let you pass down custom configuration to puppeteer. [Learn more](https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#puppeteerlaunchoptions). | object | optional |
| beforeScreenshot | An async function that will execute just before screenshot is taken. Gives access to puppeteer page element. | Function | optional |
| transparent | The transparent property lets you generate images with transparent background (for png type). | boolean | optional |
| encoding | The encoding property of the image. Options are `binary` (default) or `base64`. | string | optional |
| selector | The selector property lets you target a specific element to perform the screenshot on. (default `body`) | string | optional |
| handlebarsHelpers | The handlebarsHelpers property lets add custom logic to the templates using Handlebars sub-expressions. [Learn more](https://handlebarsjs.com/guide/builtin-helpers.html#sub-expressions). | object | optional |
| option | description | type | required |
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|-------------|
| output | The ouput path for generated image | string | optional |
| html | The html used to generate image content | string | required |
| type | The type of the generated image | jpeg or png (default: png) | optional |
| quality | The quality of the generated image (only applicable to jpg) | number (default: 80) | optional |
| content | If provided html property is considered an handlebars template and use content value to fill it | object or Array | optional |
| waitUntil | Define when to consider markup succeded. [Learn more](https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#pagesetcontenthtml-options). | string or Array<string> (default: networkidle0) | optional |
| puppeteer | The puppeteer property let you use a different puppeteer library (like puppeteer-core or puppeteer-extra). | object (default: puppeteer) | optional |
| puppeteerArgs | The puppeteerArgs property let you pass down custom configuration to puppeteer. [Learn more](https://github.com/puppeteer/puppeteer/blob/8370ec88ae94fa59d9e9dc0c154e48527d48c9fe/docs/api.md#puppeteerlaunchoptions). | object | optional |
| beforeScreenshot | An async function that will execute just before screenshot is taken. Gives access to puppeteer page element. | Function | optional |
| transparent | The transparent property lets you generate images with transparent background (for png type). | boolean | optional |
| encoding | The encoding property of the image. Options are `binary` (default) or `base64`. | string | optional |
| selector | The selector property lets you target a specific element to perform the screenshot on. (default `body`) | string | optional |
| handlebarsHelpers | The handlebarsHelpers property lets add custom logic to the templates using Handlebars sub-expressions. [Learn more](https://handlebarsjs.com/guide/builtin-helpers.html#sub-expressions). | object | optional |
| timeout | Timeout for a [puppeteer-cluster](https://github.com/thomasdondorf/puppeteer-cluster#clusterlaunchoptions) (in `ms`). Defaults to `30000` (30 seconds). | number | optional |


### Setting output image resolution

Expand Down
12 changes: 12 additions & 0 deletions src/main.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ describe("node-html-to-image", () => {
);
});

it("should throw timeout error", async () => {
await expect(async () => {
await nodeHtmlToImage({
timeout: 500,
html: "<html></html>"
});
}).rejects.toThrow();
expect(mockConsoleErr).toHaveBeenCalledWith(
new Error("Timeout hit: 500")
);
});

it("should generate an jpeg image", async () => {
await nodeHtmlToImage({
output: "./generated/image.jpg",
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ export async function nodeHtmlToImage(options: Options) {
type,
quality,
puppeteerArgs = {},
timeout = 30000,
puppeteer = undefined,
} = options;

const cluster: Cluster<ScreenshotParams> = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 2,
timeout,
puppeteerOptions: { ...puppeteerArgs, headless: true },
puppeteer: puppeteer,
});
Expand Down
13 changes: 12 additions & 1 deletion src/main.unit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

describe("node-html-to-image | Unit", () => {
let mockExit;
let launchMock;
const buffer1 = Buffer.alloc(1);
const buffer2 = Buffer.alloc(1);
const html = "<html><body>{{message}}</body></html>";

beforeEach(() => {
jest.spyOn(Cluster, "launch").mockImplementation(
launchMock = jest.spyOn(Cluster, "launch").mockImplementation(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
jest.fn(() => ({
Expand Down Expand Up @@ -50,6 +51,16 @@ describe("node-html-to-image | Unit", () => {

expect(result).toEqual([buffer1, buffer2]);
});

it("should pass 'timeout' to 'puppeteer-cluster' via options", async () => {
const CLUSTER_TIMEOUT = 60 * 1000;
await nodeHtmlToImage({
html,
timeout: CLUSTER_TIMEOUT,
});

expect(launchMock).toHaveBeenCalledWith(expect.objectContaining({ timeout: CLUSTER_TIMEOUT }))
});
});

jest.mock("puppeteer-cluster");
16 changes: 16 additions & 0 deletions src/screenshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe("beforeScreenshot", () => {
beforeEach(() => {
page = {
setContent: jest.fn(),
setDefaultTimeout: jest.fn(),
$: jest.fn(() => ({ screenshot: jest.fn(() => buffer) })),
};
});
Expand Down Expand Up @@ -50,6 +51,20 @@ describe("beforeScreenshot", () => {
);
});

it("should call 'setDefaultTimeout' with option's timeout", async () => {
const TIMEOUT = 40 * 1000;

await makeScreenshot(page, {
timeout: TIMEOUT,
screenshot: new Screenshot({
html: "<html><body>{{message}}</body></html>",
content: { message: "Hello world!" },
}),
});

expect(page.setDefaultTimeout).toHaveBeenCalledWith(TIMEOUT);
});

it("should not compile a screenshot if content is empty", async () => {
await makeScreenshot(page, {
screenshot: new Screenshot({
Expand Down Expand Up @@ -97,6 +112,7 @@ describe("handlebarsHelpers", () => {
beforeEach(() => {
page = {
setContent: jest.fn(),
setDefaultTimeout: jest.fn(),
$: jest.fn(() => ({ screenshot: jest.fn(() => buffer) })),
};
if (
Expand Down
2 changes: 2 additions & 0 deletions src/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ export async function makeScreenshot(
screenshot,
beforeScreenshot,
waitUntil = "networkidle0",
timeout,
handlebarsHelpers,
}: MakeScreenshotParams
) {
page.setDefaultTimeout(timeout);
const hasHelpers = handlebarsHelpers && typeof handlebarsHelpers === "object";
if (hasHelpers) {
if (
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ export interface Options extends ScreenshotParams {
puppeteer?: any,
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
beforeScreenshot?: (page: Page) => void;
timeout?: number
}

export interface MakeScreenshotParams {
screenshot: Screenshot;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
beforeScreenshot?: (page: Page) => void;
handlebarsHelpers?: { [helpers: string]: (...args: any[]) => any };

Check warning on line 33 in src/types.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

Check warning on line 33 in src/types.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type

timeout?: number
}

0 comments on commit 20110bc

Please sign in to comment.