mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: add PDF print for BiDi (#9914)
This commit is contained in:
parent
94f680a046
commit
95c99e84b8
@ -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.
|
||||
|
@ -122,7 +122,7 @@ page.off('request', logRequest);
|
||||
| [off(eventName, handler)](./puppeteer.page.off.md) | | |
|
||||
| [on(eventName, handler)](./puppeteer.page.on.md) | | <p>Listen to page events.</p><p>:::note</p><p>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).</p><p>:::</p> |
|
||||
| [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 <code>print</code> 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) | | |
|
||||
|
@ -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
|
||||
@ -15,9 +17,15 @@ class Page {
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | --------------------------------------- | ------------ |
|
||||
| options | [PDFOptions](./puppeteer.pdfoptions.md) | _(Optional)_ |
|
||||
| --------- | --------------------------------------- | -------------------------------------------- |
|
||||
| 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.
|
||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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<Buffer>;
|
||||
async pdf(): Promise<Buffer> {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -184,10 +184,29 @@ export interface PaperFormatDimensions {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const _paperFormats: Record<
|
||||
LowerCasePaperFormat,
|
||||
PaperFormatDimensions
|
||||
> = {
|
||||
export interface ParsedPDFOptionsInterface {
|
||||
width: number;
|
||||
height: number;
|
||||
margin: {
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ParsedPDFOptions = Required<
|
||||
Omit<PDFOptions, 'path' | 'format'> & ParsedPDFOptionsInterface
|
||||
>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const paperFormats: Record<LowerCasePaperFormat, PaperFormatDimensions> =
|
||||
{
|
||||
letter: {width: 8.5, height: 11},
|
||||
legal: {width: 8.5, height: 14},
|
||||
tabloid: {width: 11, height: 17},
|
||||
@ -199,4 +218,4 @@ export const _paperFormats: Record<
|
||||
a4: {width: 8.27, height: 11.7},
|
||||
a5: {width: 5.83, height: 8.27},
|
||||
a6: {width: 4.13, height: 5.83},
|
||||
} as const;
|
||||
} as const;
|
||||
|
@ -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<Readable> {
|
||||
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<string>([
|
||||
'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;
|
||||
}
|
||||
|
@ -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<any, unknown>}; // 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);
|
||||
|
@ -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<Buffer> {
|
||||
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<Readable> {
|
||||
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(
|
||||
|
@ -361,13 +361,15 @@ export async function waitWithTimeout<T>(
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
let fs: typeof import('fs') | null = null;
|
||||
let fs: typeof import('fs/promises') | null = null;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export async function importFS(): Promise<typeof import('fs')> {
|
||||
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<Buffer | null> {
|
||||
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(
|
||||
|
@ -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] *",
|
||||
|
@ -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');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user