From 95c99e84b8550e2e37fa57be989858f5c86d37c0 Mon Sep 17 00:00:00 2001
From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com>
Date: Mon, 27 Mar 2023 11:39:40 +0200
Subject: [PATCH] chore: add PDF print for BiDi (#9914)
---
docs/api/puppeteer.page.createpdfstream.md | 2 -
docs/api/puppeteer.page.md | 2 +-
docs/api/puppeteer.page.pdf.md | 14 ++-
package-lock.json | 16 +--
packages/puppeteer-core/package.json | 2 +-
packages/puppeteer-core/src/api/Page.ts | 95 ++++++++++++++-
packages/puppeteer-core/src/common/Frame.ts | 6 +-
.../puppeteer-core/src/common/PDFOptions.ts | 51 +++++---
packages/puppeteer-core/src/common/Page.ts | 114 ++++--------------
.../src/common/bidi/Connection.ts | 8 +-
.../puppeteer-core/src/common/bidi/Page.ts | 63 +++++++++-
packages/puppeteer-core/src/common/util.ts | 12 +-
test/TestExpectations.json | 20 ++-
test/src/page.spec.ts | 22 +---
14 files changed, 266 insertions(+), 161 deletions(-)
diff --git a/docs/api/puppeteer.page.createpdfstream.md b/docs/api/puppeteer.page.createpdfstream.md
index a4a5a650c86..e84764bd54d 100644
--- a/docs/api/puppeteer.page.createpdfstream.md
+++ b/docs/api/puppeteer.page.createpdfstream.md
@@ -26,8 +26,6 @@ Promise<Readable>
## Remarks
-NOTE: PDF generation is only supported in Chrome headless mode.
-
To generate a PDF with the `screen` media type, call [\`page.emulateMediaType('screen')\`](./puppeteer.page.emulatemediatype.md) before calling `page.pdf()`.
By default, `page.pdf()` generates a pdf with modified colors for printing. Use the [\`-webkit-print-color-adjust\`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust) property to force rendering of exact colors.
diff --git a/docs/api/puppeteer.page.md b/docs/api/puppeteer.page.md
index a8bcd6278aa..e73ba4f149f 100644
--- a/docs/api/puppeteer.page.md
+++ b/docs/api/puppeteer.page.md
@@ -122,7 +122,7 @@ page.off('request', logRequest);
| [off(eventName, handler)](./puppeteer.page.off.md) | | |
| [on(eventName, handler)](./puppeteer.page.on.md) | |
Listen to page events.
:::note
This method exists to define event typings and handle proper wireup of cooperative request interception. Actual event listening and dispatching is delegated to [EventEmitter](./puppeteer.eventemitter.md).
:::
|
| [once(eventName, handler)](./puppeteer.page.once.md) | | |
-| [pdf(options)](./puppeteer.page.pdf.md) | | |
+| [pdf(options)](./puppeteer.page.pdf.md) | | Generates a PDF of the page with the print
CSS media type. |
| [queryObjects(prototypeHandle)](./puppeteer.page.queryobjects.md) | | This method iterates the JavaScript heap and finds all objects with the given prototype. |
| [reload(options)](./puppeteer.page.reload.md) | | |
| [screenshot(options)](./puppeteer.page.screenshot.md) | | |
diff --git a/docs/api/puppeteer.page.pdf.md b/docs/api/puppeteer.page.pdf.md
index 0a7b357b271..e96e341e9e3 100644
--- a/docs/api/puppeteer.page.pdf.md
+++ b/docs/api/puppeteer.page.pdf.md
@@ -4,6 +4,8 @@ sidebar_label: Page.pdf
# Page.pdf() method
+Generates a PDF of the page with the `print` CSS media type.
+
#### Signature:
```typescript
@@ -14,10 +16,16 @@ class Page {
## Parameters
-| Parameter | Type | Description |
-| --------- | --------------------------------------- | ------------ |
-| options | [PDFOptions](./puppeteer.pdfoptions.md) | _(Optional)_ |
+| Parameter | Type | Description |
+| --------- | --------------------------------------- | -------------------------------------------- |
+| options | [PDFOptions](./puppeteer.pdfoptions.md) | _(Optional)_ options for generating the PDF. |
**Returns:**
Promise<Buffer>
+
+## Remarks
+
+To generate a PDF with the `screen` media type, call [\`page.emulateMediaType('screen')\`](./puppeteer.page.emulatemediatype.md) before calling `page.pdf()`.
+
+By default, `page.pdf()` generates a pdf with modified colors for printing. Use the [\`-webkit-print-color-adjust\`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust) property to force rendering of exact colors.
diff --git a/package-lock.json b/package-lock.json
index 70d2fd85f6b..48a5f3c0680 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2587,9 +2587,9 @@
"license": "ISC"
},
"node_modules/chromium-bidi": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.5.tgz",
- "integrity": "sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==",
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.6.tgz",
+ "integrity": "sha512-TQOkWRaLI/IWvoP8XC+7jO4uHTIiAUiklXU1T0qszlUFEai9LgKXIBXy3pOS3EnQZ3bQtMbKUPkug0fTAEHCSw==",
"dependencies": {
"mitt": "3.0.0"
},
@@ -9481,7 +9481,7 @@
"version": "19.8.0",
"license": "Apache-2.0",
"dependencies": {
- "chromium-bidi": "0.4.5",
+ "chromium-bidi": "0.4.6",
"cross-fetch": "3.1.5",
"debug": "4.3.4",
"devtools-protocol": "0.0.1107588",
@@ -11371,9 +11371,9 @@
"version": "1.1.4"
},
"chromium-bidi": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.5.tgz",
- "integrity": "sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg==",
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.6.tgz",
+ "integrity": "sha512-TQOkWRaLI/IWvoP8XC+7jO4uHTIiAUiklXU1T0qszlUFEai9LgKXIBXy3pOS3EnQZ3bQtMbKUPkug0fTAEHCSw==",
"requires": {
"mitt": "3.0.0"
}
@@ -14462,7 +14462,7 @@
"puppeteer-core": {
"version": "file:packages/puppeteer-core",
"requires": {
- "chromium-bidi": "0.4.5",
+ "chromium-bidi": "0.4.6",
"cross-fetch": "3.1.5",
"debug": "4.3.4",
"devtools-protocol": "0.0.1107588",
diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json
index 2ed554a4e90..66bbb73ffe6 100644
--- a/packages/puppeteer-core/package.json
+++ b/packages/puppeteer-core/package.json
@@ -131,7 +131,7 @@
"author": "The Chromium Authors",
"license": "Apache-2.0",
"dependencies": {
- "chromium-bidi": "0.4.5",
+ "chromium-bidi": "0.4.6",
"cross-fetch": "3.1.5",
"debug": "4.3.4",
"devtools-protocol": "0.0.1107588",
diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts
index d092a6c1c35..912442b1863 100644
--- a/packages/puppeteer-core/src/api/Page.ts
+++ b/packages/puppeteer-core/src/api/Page.ts
@@ -43,7 +43,12 @@ import type {
import type {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
import type {PuppeteerLifeCycleEvent} from '../common/LifecycleWatcher.js';
import type {Credentials, NetworkConditions} from '../common/NetworkManager.js';
-import type {PDFOptions} from '../common/PDFOptions.js';
+import {
+ LowerCasePaperFormat,
+ ParsedPDFOptions,
+ PDFOptions,
+ paperFormats,
+} from '../common/PDFOptions.js';
import type {Viewport} from '../common/PuppeteerViewport.js';
import type {Target} from '../common/Target.js';
import type {Tracing} from '../common/Tracing.js';
@@ -53,7 +58,9 @@ import type {
HandleFor,
NodeFor,
} from '../common/types.js';
+import {isNumber, isString} from '../common/util.js';
import type {WebWorker} from '../common/WebWorker.js';
+import {assert} from '../util/assert.js';
import type {Browser} from './Browser.js';
import type {BrowserContext} from './BrowserContext.js';
@@ -2136,12 +2143,58 @@ export class Page extends EventEmitter {
throw new Error('Not implemented');
}
+ /**
+ * @internal
+ */
+ _getPDFOptions(options: PDFOptions = {}): ParsedPDFOptions {
+ const defaults = {
+ scale: 1,
+ displayHeaderFooter: false,
+ headerTemplate: '',
+ footerTemplate: '',
+ printBackground: false,
+ landscape: false,
+ pageRanges: '',
+ preferCSSPageSize: false,
+ omitBackground: false,
+ timeout: 30000,
+ };
+
+ let width = 8.5;
+ let height = 11;
+ if (options.format) {
+ const format =
+ paperFormats[options.format.toLowerCase() as LowerCasePaperFormat];
+ assert(format, 'Unknown paper format: ' + options.format);
+ width = format.width;
+ height = format.height;
+ } else {
+ width = convertPrintParameterToInches(options.width) ?? width;
+ height = convertPrintParameterToInches(options.height) ?? height;
+ }
+
+ const margin = {
+ top: convertPrintParameterToInches(options.margin?.top) || 0,
+ left: convertPrintParameterToInches(options.margin?.left) || 0,
+ bottom: convertPrintParameterToInches(options.margin?.bottom) || 0,
+ right: convertPrintParameterToInches(options.margin?.right) || 0,
+ };
+
+ const output = {
+ ...defaults,
+ ...options,
+ width,
+ height,
+ margin,
+ };
+
+ return output;
+ }
+
/**
* Generates a PDF of the page with the `print` CSS media type.
* @remarks
*
- * NOTE: PDF generation is only supported in Chrome headless mode.
- *
* To generate a PDF with the `screen` media type, call
* {@link Page.emulateMediaType | `page.emulateMediaType('screen')`} before
* calling `page.pdf()`.
@@ -2159,8 +2212,7 @@ export class Page extends EventEmitter {
}
/**
- * @param options -
- * @returns
+ * {@inheritDoc Page.createPDFStream}
*/
async pdf(options?: PDFOptions): Promise;
async pdf(): Promise {
@@ -2619,3 +2671,36 @@ export const unitToPixels = {
cm: 37.8,
mm: 3.78,
};
+
+function convertPrintParameterToInches(
+ parameter?: string | number
+): number | undefined {
+ if (typeof parameter === 'undefined') {
+ return undefined;
+ }
+ let pixels;
+ if (isNumber(parameter)) {
+ // Treat numbers as pixel values to be aligned with phantom's paperSize.
+ pixels = parameter;
+ } else if (isString(parameter)) {
+ const text = parameter;
+ let unit = text.substring(text.length - 2).toLowerCase();
+ let valueText = '';
+ if (unit in unitToPixels) {
+ valueText = text.substring(0, text.length - 2);
+ } else {
+ // In case of unknown unit try to parse the whole parameter as number of pixels.
+ // This is consistent with phantom's paperSize behavior.
+ unit = 'px';
+ valueText = text;
+ }
+ const value = Number(valueText);
+ assert(!isNaN(value), 'Failed to parse parameter value: ' + text);
+ pixels = value * unitToPixels[unit as keyof typeof unitToPixels];
+ } else {
+ throw new Error(
+ 'page.pdf() Cannot handle parameter type: ' + typeof parameter
+ );
+ }
+ return pixels / 96;
+}
diff --git a/packages/puppeteer-core/src/common/Frame.ts b/packages/puppeteer-core/src/common/Frame.ts
index 07a86c6be6a..158977d5cdf 100644
--- a/packages/puppeteer-core/src/common/Frame.ts
+++ b/packages/puppeteer-core/src/common/Frame.ts
@@ -40,7 +40,7 @@ import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
import {LazyArg} from './LazyArg.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from './types.js';
-import {importFS} from './util.js';
+import {importFSPromises} from './util.js';
/**
* @public
@@ -883,9 +883,9 @@ export class Frame {
}
if (path) {
- let fs: typeof import('fs').promises;
+ let fs: typeof import('fs/promises');
try {
- fs = (await importFS()).promises;
+ fs = await importFSPromises();
} catch (error) {
if (error instanceof TypeError) {
throw new Error(
diff --git a/packages/puppeteer-core/src/common/PDFOptions.ts b/packages/puppeteer-core/src/common/PDFOptions.ts
index c11c6c6814f..bca6d9eb159 100644
--- a/packages/puppeteer-core/src/common/PDFOptions.ts
+++ b/packages/puppeteer-core/src/common/PDFOptions.ts
@@ -184,19 +184,38 @@ export interface PaperFormatDimensions {
/**
* @internal
*/
-export const _paperFormats: Record<
- LowerCasePaperFormat,
- PaperFormatDimensions
-> = {
- letter: {width: 8.5, height: 11},
- legal: {width: 8.5, height: 14},
- tabloid: {width: 11, height: 17},
- ledger: {width: 17, height: 11},
- a0: {width: 33.1, height: 46.8},
- a1: {width: 23.4, height: 33.1},
- a2: {width: 16.54, height: 23.4},
- a3: {width: 11.7, height: 16.54},
- a4: {width: 8.27, height: 11.7},
- a5: {width: 5.83, height: 8.27},
- a6: {width: 4.13, height: 5.83},
-} as const;
+export interface ParsedPDFOptionsInterface {
+ width: number;
+ height: number;
+ margin: {
+ top: number;
+ bottom: number;
+ left: number;
+ right: number;
+ };
+}
+
+/**
+ * @internal
+ */
+export type ParsedPDFOptions = Required<
+ Omit & ParsedPDFOptionsInterface
+>;
+
+/**
+ * @internal
+ */
+export const paperFormats: Record =
+ {
+ letter: {width: 8.5, height: 11},
+ legal: {width: 8.5, height: 14},
+ tabloid: {width: 11, height: 17},
+ ledger: {width: 17, height: 11},
+ a0: {width: 33.1, height: 46.8},
+ a1: {width: 23.4, height: 33.1},
+ a2: {width: 16.54, height: 23.4},
+ a3: {width: 11.7, height: 16.54},
+ a4: {width: 8.27, height: 11.7},
+ a5: {width: 5.83, height: 8.27},
+ a6: {width: 4.13, height: 5.83},
+ } as const;
diff --git a/packages/puppeteer-core/src/common/Page.ts b/packages/puppeteer-core/src/common/Page.ts
index 21c13514d71..3e93dbf7f3d 100644
--- a/packages/puppeteer-core/src/common/Page.ts
+++ b/packages/puppeteer-core/src/common/Page.ts
@@ -70,7 +70,7 @@ import {
NetworkConditions,
NetworkManagerEmittedEvents,
} from './NetworkManager.js';
-import {LowerCasePaperFormat, PDFOptions, _paperFormats} from './PDFOptions.js';
+import {PDFOptions} from './PDFOptions.js';
import {Viewport} from './PuppeteerViewport.js';
import {Target} from './Target.js';
import {TargetManagerEmittedEvents} from './TargetManager.js';
@@ -91,8 +91,7 @@ import {
getExceptionMessage,
getReadableAsBuffer,
getReadableFromProtocolStream,
- importFS,
- isNumber,
+ importFSPromises,
isString,
pageBindingInitString,
releaseObject,
@@ -1448,7 +1447,7 @@ export class CDPPage extends Page {
if (options.path) {
try {
- const fs = (await importFS()).promises;
+ const fs = await importFSPromises();
await fs.writeFile(options.path, buffer);
} catch (error) {
if (error instanceof TypeError) {
@@ -1471,68 +1470,37 @@ export class CDPPage extends Page {
}
override async createPDFStream(options: PDFOptions = {}): Promise {
- const {
- scale = 1,
- displayHeaderFooter = false,
- headerTemplate = '',
- footerTemplate = '',
- printBackground = false,
- landscape = false,
- pageRanges = '',
- preferCSSPageSize = false,
- margin = {},
- omitBackground = false,
- timeout = 30000,
- } = options;
+ const params = this._getPDFOptions(options);
- let paperWidth = 8.5;
- let paperHeight = 11;
- if (options.format) {
- const format =
- _paperFormats[options.format.toLowerCase() as LowerCasePaperFormat];
- assert(format, 'Unknown paper format: ' + options.format);
- paperWidth = format.width;
- paperHeight = format.height;
- } else {
- paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
- paperHeight =
- convertPrintParameterToInches(options.height) || paperHeight;
- }
-
- const marginTop = convertPrintParameterToInches(margin.top) || 0;
- const marginLeft = convertPrintParameterToInches(margin.left) || 0;
- const marginBottom = convertPrintParameterToInches(margin.bottom) || 0;
- const marginRight = convertPrintParameterToInches(margin.right) || 0;
-
- if (omitBackground) {
+ if (params.omitBackground) {
await this.#setTransparentBackgroundColor();
}
const printCommandPromise = this.#client.send('Page.printToPDF', {
transferMode: 'ReturnAsStream',
- landscape,
- displayHeaderFooter,
- headerTemplate,
- footerTemplate,
- printBackground,
- scale,
- paperWidth,
- paperHeight,
- marginTop,
- marginBottom,
- marginLeft,
- marginRight,
- pageRanges,
- preferCSSPageSize,
+ landscape: params.landscape,
+ displayHeaderFooter: params.displayHeaderFooter,
+ headerTemplate: params.headerTemplate,
+ footerTemplate: params.footerTemplate,
+ printBackground: params.printBackground,
+ scale: params.scale,
+ paperWidth: params.width,
+ paperHeight: params.height,
+ marginTop: params.margin.top,
+ marginBottom: params.margin.bottom,
+ marginLeft: params.margin.left,
+ marginRight: params.margin.right,
+ pageRanges: params.pageRanges,
+ preferCSSPageSize: params.preferCSSPageSize,
});
const result = await waitWithTimeout(
printCommandPromise,
'Page.printToPDF',
- timeout
+ params.timeout
);
- if (omitBackground) {
+ if (params.omitBackground) {
await this.#resetDefaultBackgroundColor();
}
@@ -1688,43 +1656,3 @@ const supportedMetrics = new Set([
'JSHeapUsedSize',
'JSHeapTotalSize',
]);
-
-const unitToPixels = {
- px: 1,
- in: 96,
- cm: 37.8,
- mm: 3.78,
-};
-
-function convertPrintParameterToInches(
- parameter?: string | number
-): number | undefined {
- if (typeof parameter === 'undefined') {
- return undefined;
- }
- let pixels;
- if (isNumber(parameter)) {
- // Treat numbers as pixel values to be aligned with phantom's paperSize.
- pixels = parameter;
- } else if (isString(parameter)) {
- const text = parameter;
- let unit = text.substring(text.length - 2).toLowerCase();
- let valueText = '';
- if (unit in unitToPixels) {
- valueText = text.substring(0, text.length - 2);
- } else {
- // In case of unknown unit try to parse the whole parameter as number of pixels.
- // This is consistent with phantom's paperSize behavior.
- unit = 'px';
- valueText = text;
- }
- const value = Number(valueText);
- assert(!isNaN(value), 'Failed to parse parameter value: ' + text);
- pixels = value * unitToPixels[unit as keyof typeof unitToPixels];
- } else {
- throw new Error(
- 'page.pdf() Cannot handle parameter type: ' + typeof parameter
- );
- }
- return pixels / 96;
-}
diff --git a/packages/puppeteer-core/src/common/bidi/Connection.ts b/packages/puppeteer-core/src/common/bidi/Connection.ts
index 05de314acf0..ff90c2902d1 100644
--- a/packages/puppeteer-core/src/common/bidi/Connection.ts
+++ b/packages/puppeteer-core/src/common/bidi/Connection.ts
@@ -55,6 +55,10 @@ interface Commands {
params: Bidi.BrowsingContext.NavigateParameters;
returnType: Bidi.BrowsingContext.NavigateResult;
};
+ 'browsingContext.print': {
+ params: Bidi.BrowsingContext.PrintParameters;
+ returnType: Bidi.BrowsingContext.PrintResult;
+ };
'session.new': {
params: {capabilities?: Record}; // TODO: Update Types in chromium bidi
@@ -152,10 +156,10 @@ export class Connection extends EventEmitter {
#maybeEmitOnContext(event: Bidi.Message.EventMessage) {
let context: Context | undefined;
// Context specific events
- if ('context' in event.params) {
+ if ('context' in event.params && event.params.context) {
context = this.#contexts.get(event.params.context);
// `log.entryAdded` specific context
- } else if ('source' in event.params && !!event.params.source.context) {
+ } else if ('source' in event.params && event.params.source.context) {
context = this.#contexts.get(event.params.source.context);
}
context?.emit(event.method, event.params);
diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts
index 4fbafd86459..9da38978117 100644
--- a/packages/puppeteer-core/src/common/bidi/Page.ts
+++ b/packages/puppeteer-core/src/common/bidi/Page.ts
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import type {Readable} from 'stream';
+
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {HTTPResponse} from '../../api/HTTPResponse.js';
@@ -25,8 +27,9 @@ import {
import {isErrorLike} from '../../util/ErrorLike.js';
import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js';
import {Handler} from '../EventEmitter.js';
+import {PDFOptions} from '../PDFOptions.js';
import {EvaluateFunc, HandleFor} from '../types.js';
-import {debugError} from '../util.js';
+import {debugError, importFSPromises, waitWithTimeout} from '../util.js';
import {Context, getBidiHandle} from './Context.js';
import {BidiSerializer} from './Serializer.js';
@@ -199,6 +202,64 @@ export class Page extends PageBase {
return retVal;
});
}
+
+ override async pdf(options: PDFOptions = {}): Promise {
+ const {path = undefined} = options;
+ const params = this._getPDFOptions(options);
+ const {result} = await waitWithTimeout(
+ this.#context.connection.send('browsingContext.print', {
+ context: this.#context._contextId,
+ background: params.printBackground,
+ margin: params.margin,
+ orientation: params.landscape ? 'landscape' : 'portrait',
+ page: {
+ width: params.width,
+ height: params.height,
+ },
+ pageRanges: params.pageRanges.split(', '),
+ scale: params.scale,
+ shrinkToFit: !params.preferCSSPageSize,
+ }),
+ 'browsingContext.print',
+ params.timeout
+ );
+
+ const buffer = Buffer.from(result.data, 'base64');
+
+ try {
+ if (path) {
+ const fs = await importFSPromises();
+
+ await fs.writeFile(path, buffer);
+ }
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Can only pass a file path in a Node-like environment.'
+ );
+ }
+ throw error;
+ }
+
+ return buffer;
+ }
+
+ override async createPDFStream(
+ options?: PDFOptions | undefined
+ ): Promise {
+ const buffer = await this.pdf(options);
+ try {
+ const {Readable} = await import('stream');
+ return Readable.from(buffer);
+ } catch (error) {
+ if (error instanceof TypeError) {
+ throw new Error(
+ 'Can only pass a file path in a Node-like environment.'
+ );
+ }
+ throw error;
+ }
+ }
}
function isConsoleLogEntry(
diff --git a/packages/puppeteer-core/src/common/util.ts b/packages/puppeteer-core/src/common/util.ts
index 77f05497590..200b559f787 100644
--- a/packages/puppeteer-core/src/common/util.ts
+++ b/packages/puppeteer-core/src/common/util.ts
@@ -361,13 +361,15 @@ export async function waitWithTimeout(
/**
* @internal
*/
-let fs: typeof import('fs') | null = null;
+let fs: typeof import('fs/promises') | null = null;
/**
* @internal
*/
-export async function importFS(): Promise {
+export async function importFSPromises(): Promise<
+ typeof import('fs/promises')
+> {
if (!fs) {
- fs = await import('fs');
+ fs = await import('fs/promises');
}
return fs;
}
@@ -381,9 +383,9 @@ export async function getReadableAsBuffer(
): Promise {
const buffers = [];
if (path) {
- let fs: typeof import('fs').promises;
+ let fs: typeof import('fs/promises');
try {
- fs = (await importFS()).promises;
+ fs = await importFSPromises();
} catch (error) {
if (error instanceof TypeError) {
throw new Error(
diff --git a/test/TestExpectations.json b/test/TestExpectations.json
index ca642c01db6..e163144a9dc 100644
--- a/test/TestExpectations.json
+++ b/test/TestExpectations.json
@@ -59,6 +59,12 @@
"parameters": ["webDriverBiDi"],
"expectations": ["PASS", "TIMEOUT"]
},
+ {
+ "testIdPattern": "[page.spec] Page Page.pdf *",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
{
"testIdPattern": "[page.spec] Page Page.setContent *",
"platforms": ["darwin", "linux", "win32"],
@@ -297,7 +303,13 @@
"testIdPattern": "[navigation.spec] navigation Page.goto should navigate to URL with hash and fire requests without hash",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
+ "expectations": ["FAIL", "TIMEOUT"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should not leak listeners during navigation of 11 pages",
+ "platforms": ["darwin"],
+ "parameters": ["chrome", "webDriverBiDi"],
+ "expectations": ["PASS", "TIMEOUT"]
},
{
"testIdPattern": "[navigation.spec] navigation Page.goto should not throw an error for a 404 response with an empty body",
@@ -315,7 +327,7 @@
"testIdPattern": "[navigation.spec] navigation Page.goto should return last response in redirect chain",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
+ "expectations": ["FAIL", "TIMEOUT"]
},
{
"testIdPattern": "[navigation.spec] navigation Page.goto should return response when page changes its URL after load",
@@ -351,7 +363,7 @@
"testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to valid url",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
+ "expectations": ["FAIL", "TIMEOUT"]
},
{
"testIdPattern": "[navigation.spec] navigation Page.goto should work when page calls history API in beforeunload",
@@ -369,7 +381,7 @@
"testIdPattern": "[navigation.spec] navigation Page.reload should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
- "expectations": ["FAIL"]
+ "expectations": ["FAIL", "TIMEOUT"]
},
{
"testIdPattern": "[oopif.spec] *",
diff --git a/test/src/page.spec.ts b/test/src/page.spec.ts
index 31722363809..fb35fa98034 100644
--- a/test/src/page.spec.ts
+++ b/test/src/page.spec.ts
@@ -2051,28 +2051,19 @@ describe('Page', function () {
});
});
- describe('printing to PDF', function () {
+ describe('Page.pdf', function () {
it('can print to PDF and save to file', async () => {
- // Printing to pdf is currently only supported in headless
- const {isHeadless, page} = getTestState();
-
- if (!isHeadless) {
- return;
- }
+ const {page, server} = getTestState();
const outputFile = __dirname + '/../assets/output.pdf';
+ await page.goto(server.PREFIX + '/pdf.html');
await page.pdf({path: outputFile});
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
fs.unlinkSync(outputFile);
});
it('can print to PDF and stream the result', async () => {
- // Printing to pdf is currently only supported in headless
- const {isHeadless, page} = getTestState();
-
- if (!isHeadless) {
- return;
- }
+ const {page} = getTestState();
const stream = await page.createPDFStream();
let size = 0;
@@ -2083,10 +2074,7 @@ describe('Page', function () {
});
it('should respect timeout', async () => {
- const {isHeadless, page, server} = getTestState();
- if (!isHeadless) {
- return;
- }
+ const {page, server} = getTestState();
await page.goto(server.PREFIX + '/pdf.html');