mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(screenshot): create directories for screenshot file
This commit is contained in:
parent
0e9793c452
commit
dfa1f103af
@ -20,6 +20,7 @@ import { Protocol } from './protocol';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
|
import { mkdirIfNeeded } from '../helper';
|
||||||
|
|
||||||
export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string {
|
export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string {
|
||||||
if (exceptionDetails.exception)
|
if (exceptionDetails.exception)
|
||||||
@ -42,8 +43,10 @@ export async function releaseObject(client: CRSession, objectId: string) {
|
|||||||
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<Buffer> {
|
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<Buffer> {
|
||||||
let eof = false;
|
let eof = false;
|
||||||
let fd: number | undefined;
|
let fd: number | undefined;
|
||||||
if (path)
|
if (path) {
|
||||||
|
await mkdirIfNeeded(path);
|
||||||
fd = await util.promisify(fs.open)(path, 'w');
|
fd = await util.promisify(fs.open)(path, 'w');
|
||||||
|
}
|
||||||
const bufs = [];
|
const bufs = [];
|
||||||
while (!eof) {
|
while (!eof) {
|
||||||
const response = await client.send('IO.read', {handle});
|
const response = await client.send('IO.read', {handle});
|
||||||
|
@ -20,7 +20,7 @@ import * as util from 'util';
|
|||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { assert } from './helper';
|
import { assert, mkdirIfNeeded } from './helper';
|
||||||
|
|
||||||
export class Download {
|
export class Download {
|
||||||
private _downloadsPath: string;
|
private _downloadsPath: string;
|
||||||
@ -92,8 +92,7 @@ export class Download {
|
|||||||
|
|
||||||
async _saveAs(downloadPath: string) {
|
async _saveAs(downloadPath: string) {
|
||||||
const fileName = path.join(this._downloadsPath, this._uuid);
|
const fileName = path.join(this._downloadsPath, this._uuid);
|
||||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
await mkdirIfNeeded(downloadPath);
|
||||||
await util.promisify(fs.mkdir)(path.dirname(downloadPath), {recursive: true}).catch(() => {});
|
|
||||||
await util.promisify(fs.copyFile)(fileName, downloadPath);
|
await util.promisify(fs.copyFile)(fileName, downloadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,12 +21,14 @@ import * as fs from 'fs';
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as removeFolder from 'rimraf';
|
import * as removeFolder from 'rimraf';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
import * as path from 'path';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { Progress } from './progress';
|
import { Progress } from './progress';
|
||||||
import * as debug from 'debug';
|
import * as debug from 'debug';
|
||||||
|
|
||||||
const removeFolderAsync = util.promisify(removeFolder);
|
const removeFolderAsync = util.promisify(removeFolder);
|
||||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||||
|
const mkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||||
|
|
||||||
export type RegisteredListener = {
|
export type RegisteredListener = {
|
||||||
emitter: EventEmitter;
|
emitter: EventEmitter;
|
||||||
@ -353,6 +355,12 @@ export function getFromENV(name: string) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function mkdirIfNeeded(filePath: string) {
|
||||||
|
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||||
|
await mkdirAsync(path.dirname(filePath), {recursive: true}).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);
|
const escapeGlobChars = new Set(['/', '$', '^', '+', '.', '(', ')', '=', '!', '|']);
|
||||||
|
|
||||||
export const helper = Helper;
|
export const helper = Helper;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { assert, helper, Listener } from '../../helper';
|
import { assert, helper, Listener, mkdirIfNeeded } from '../../helper';
|
||||||
import { TimeoutSettings } from '../../timeoutSettings';
|
import { TimeoutSettings } from '../../timeoutSettings';
|
||||||
import { BindingCallChannel, BindingCallInitializer, PageChannel, PageInitializer, PagePdfParams, FrameWaitForSelectorOptions, FrameDispatchEventOptions, FrameSetContentOptions, FrameGotoOptions, PageReloadOptions, PageGoBackOptions, PageGoForwardOptions, PageScreenshotOptions, FrameClickOptions, FrameDblclickOptions, FrameFillOptions, FrameFocusOptions, FrameTextContentOptions, FrameInnerTextOptions, FrameInnerHTMLOptions, FrameGetAttributeOptions, FrameHoverOptions, FrameSetInputFilesOptions, FrameTypeOptions, FramePressOptions, FrameCheckOptions, FrameUncheckOptions } from '../channels';
|
import { BindingCallChannel, BindingCallInitializer, PageChannel, PageInitializer, PagePdfParams, FrameWaitForSelectorOptions, FrameDispatchEventOptions, FrameSetContentOptions, FrameGotoOptions, PageReloadOptions, PageGoBackOptions, PageGoForwardOptions, PageScreenshotOptions, FrameClickOptions, FrameDblclickOptions, FrameFillOptions, FrameFocusOptions, FrameTextContentOptions, FrameInnerTextOptions, FrameInnerHTMLOptions, FrameGetAttributeOptions, FrameHoverOptions, FrameSetInputFilesOptions, FrameTypeOptions, FramePressOptions, FrameCheckOptions, FrameUncheckOptions } from '../channels';
|
||||||
import { parseError, serializeError } from '../serializers';
|
import { parseError, serializeError } from '../serializers';
|
||||||
@ -425,8 +425,10 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
|||||||
async screenshot(options: PageScreenshotOptions & { path?: string } = {}): Promise<Buffer> {
|
async screenshot(options: PageScreenshotOptions & { path?: string } = {}): Promise<Buffer> {
|
||||||
return this._wrapApiCall('page.screenshot', async () => {
|
return this._wrapApiCall('page.screenshot', async () => {
|
||||||
const buffer = Buffer.from((await this._channel.screenshot(options)).binary, 'base64');
|
const buffer = Buffer.from((await this._channel.screenshot(options)).binary, 'base64');
|
||||||
if (options.path)
|
if (options.path) {
|
||||||
|
await mkdirIfNeeded(options.path);
|
||||||
await fsWriteFileAsync(options.path, buffer);
|
await fsWriteFileAsync(options.path, buffer);
|
||||||
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import * as fs from 'fs';
|
|||||||
import * as mime from 'mime';
|
import * as mime from 'mime';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as dom from './dom';
|
import * as dom from './dom';
|
||||||
import { assert, helper } from './helper';
|
import { assert, helper, mkdirIfNeeded } from './helper';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { rewriteErrorMessage } from './utils/stackTrace';
|
import { rewriteErrorMessage } from './utils/stackTrace';
|
||||||
@ -153,8 +153,10 @@ export class Screenshotter {
|
|||||||
if (shouldSetDefaultBackground)
|
if (shouldSetDefaultBackground)
|
||||||
await this._page._delegate.setBackgroundColor();
|
await this._page._delegate.setBackgroundColor();
|
||||||
progress.throwIfAborted(); // Avoid side effects.
|
progress.throwIfAborted(); // Avoid side effects.
|
||||||
if (options.path)
|
if (options.path) {
|
||||||
|
await mkdirIfNeeded(options.path);
|
||||||
await util.promisify(fs.writeFile)(options.path, buffer);
|
await util.promisify(fs.writeFile)(options.path, buffer);
|
||||||
|
}
|
||||||
if ((options as any).__testHookAfterScreenshot)
|
if ((options as any).__testHookAfterScreenshot)
|
||||||
await (options as any).__testHookAfterScreenshot();
|
await (options as any).__testHookAfterScreenshot();
|
||||||
return buffer;
|
return buffer;
|
||||||
|
@ -42,6 +42,14 @@ it.skip(!CHROMIUM)('should output a trace', async({browser, page, server, output
|
|||||||
expect(fs.existsSync(outputFile)).toBe(true);
|
expect(fs.existsSync(outputFile)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.skip(!CHROMIUM)('should create directories as needed', async({browser, page, server, tmpDir}) => {
|
||||||
|
const filePath = path.join(tmpDir, 'these', 'are', 'directories');
|
||||||
|
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath});
|
||||||
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
|
await (browser as ChromiumBrowser).stopTracing();
|
||||||
|
expect(fs.existsSync(filePath)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it.skip(!CHROMIUM)('should run with custom categories if provided', async({browser, page, outputFile}) => {
|
it.skip(!CHROMIUM)('should run with custom categories if provided', async({browser, page, outputFile}) => {
|
||||||
await (browser as ChromiumBrowser).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
|
await (browser as ChromiumBrowser).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
|
||||||
await (browser as ChromiumBrowser).stopTracing();
|
await (browser as ChromiumBrowser).stopTracing();
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
import './base.fixture';
|
import './base.fixture';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
const { HEADLESS } = testOptions;
|
const { HEADLESS } = testOptions;
|
||||||
|
|
||||||
// Firefox headful produces a different image.
|
// Firefox headful produces a different image.
|
||||||
@ -246,3 +249,19 @@ it.skip(ffheadful)('should work with iframe in shadow', async({page, server, gol
|
|||||||
await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html');
|
await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html');
|
||||||
expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png'));
|
expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('path option should work', async({page, server, golden, tmpDir}) => {
|
||||||
|
await page.setViewportSize({width: 500, height: 500});
|
||||||
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
|
const outputPath = path.join(tmpDir, 'screenshot.png');
|
||||||
|
await page.screenshot({path: outputPath});
|
||||||
|
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('path option should create subdirectories', async({page, server, golden, tmpDir}) => {
|
||||||
|
await page.setViewportSize({width: 500, height: 500});
|
||||||
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
|
const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png');
|
||||||
|
await page.screenshot({path: outputPath});
|
||||||
|
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user