fix(electron): make recordVideo work (#10810)

This commit is contained in:
Dmitry Gozman 2021-12-08 17:34:50 -08:00 committed by GitHub
parent a4e68dbac1
commit 4996e184bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 23 deletions

View file

@ -51,12 +51,14 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
extraHTTPHeaders: options.extraHTTPHeaders && headersObjectToArray(options.extraHTTPHeaders),
env: envObjectToArray(options.env ? options.env : process.env),
};
return ElectronApplication.from((await this._channel.launch(params)).electronApplication);
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
app._context._options = params;
return app;
}
}
export class ElectronApplication extends ChannelOwner<channels.ElectronApplicationChannel> implements api.ElectronApplication {
private _context: BrowserContext;
readonly _context: BrowserContext;
private _windows = new Set<Page>();
private _timeoutSettings = new TimeoutSettings();
@ -91,7 +93,7 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
}
context(): BrowserContext {
return this._context! as BrowserContext;
return this._context;
}
async close() {

View file

@ -65,6 +65,7 @@ export abstract class BrowserContext extends SdkObject {
readonly _harRecorder: HarRecorder | undefined;
readonly tracing: Tracing;
readonly fetchRequest: BrowserContextAPIRequestContext;
private _customCloseHandler?: () => Promise<any>;
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
super(browser, 'browser-context');
@ -275,6 +276,10 @@ export abstract class BrowserContext extends SdkObject {
await Promise.all(Array.from(this._downloads).map(download => download.artifact.deleteOnContextClose()));
}
setCustomCloseHandler(handler: (() => Promise<any>) | undefined) {
this._customCloseHandler = handler;
}
async close(metadata: CallMetadata) {
if (this._closedStatus === 'open') {
this.emit(BrowserContext.Events.BeforeClose);
@ -291,7 +296,9 @@ export abstract class BrowserContext extends SdkObject {
promises.push(artifact.finishedPromise());
}
if (this._isPersistentContext) {
if (this._customCloseHandler) {
await this._customCloseHandler();
} else if (this._isPersistentContext) {
// Close all the pages instead of the context,
// because we cannot close the default context.
await Promise.all(this.pages().map(page => page.close(metadata)));
@ -305,6 +312,10 @@ export abstract class BrowserContext extends SdkObject {
promises.push(this._deleteAllDownloads());
await Promise.all(promises);
// Custom handler should trigger didCloseInternal itself.
if (this._customCloseHandler)
return;
// Persistent context should also close the browser.
if (this._isPersistentContext)
await this._browser.close();

View file

@ -26,7 +26,7 @@ import { Page } from '../page';
import { TimeoutSettings } from '../../utils/timeoutSettings';
import { WebSocketTransport } from '../transport';
import { launchProcess, envArrayToObject } from '../../utils/processLauncher';
import { BrowserContext } from '../browserContext';
import { BrowserContext, validateBrowserContextOptions } from '../browserContext';
import type { BrowserWindow } from 'electron';
import { Progress, ProgressController } from '../progress';
import { helper } from '../helper';
@ -37,6 +37,7 @@ import * as readline from 'readline';
import { RecentLogsCollector } from '../../utils/debugLogger';
import { internalCallMetadata, SdkObject } from '../instrumentation';
import * as channels from '../../protocol/channels';
import { BrowserContextOptions } from '../types';
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
@ -69,6 +70,10 @@ export class ElectronApplication extends SdkObject {
}
});
});
this._browserContext.setCustomCloseHandler(async () => {
const electronHandle = await this._nodeElectronHandlePromise;
await electronHandle.evaluate(({ app }) => app.quit());
});
this._nodeSession.send('Runtime.enable', {}).catch(e => {});
}
@ -79,8 +84,7 @@ export class ElectronApplication extends SdkObject {
async close() {
const progressController = new ProgressController(internalCallMetadata(), this);
const closed = progressController.run(progress => helper.waitForEvent(progress, this, ElectronApplication.Events.Close).promise, this._timeoutSettings.timeout({}));
const electronHandle = await this._nodeElectronHandlePromise;
await electronHandle.evaluate(({ app }) => app.quit());
await this._browserContext.close(internalCallMetadata());
this._nodeConnection.close();
await closed;
}
@ -168,26 +172,16 @@ export class Electron extends SdkObject {
close: gracefullyClose,
kill
};
const contextOptions: BrowserContextOptions = {
...options,
noDefaultViewport: true,
};
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
name: 'electron',
isChromium: true,
headful: true,
persistent: {
noDefaultViewport: true,
acceptDownloads: options.acceptDownloads,
bypassCSP: options.bypassCSP,
colorScheme: options.colorScheme,
extraHTTPHeaders: options.extraHTTPHeaders,
geolocation: options.geolocation,
httpCredentials: options.httpCredentials,
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
locale: options.locale,
offline: options.offline,
recordHar: options.recordHar,
recordVideo: options.recordVideo,
timezoneId: options.timezoneId,
},
persistent: contextOptions,
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector,
@ -195,6 +189,7 @@ export class Electron extends SdkObject {
downloadsPath: artifactsDir,
tracesDir: artifactsDir,
};
validateBrowserContextOptions(contextOptions, browserOptions);
const browser = await CRBrowser.connect(chromeTransport, browserOptions);
app = new ElectronApplication(this, browser, nodeConnection);
return app;

View file

@ -16,6 +16,7 @@
import type { BrowserWindow } from 'electron';
import path from 'path';
import fs from 'fs';
import { electronTest as test, expect } from './electronTest';
test('should fire close event', async ({ playwright }) => {
@ -166,3 +167,16 @@ test('should return same browser window for browser view pages', async ({ playwr
expect(firstWindowId).toEqual(secondWindowId);
await app.close();
});
test('should record video', async ({ playwright }, testInfo) => {
const app = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
recordVideo: { dir: testInfo.outputPath('video') }
});
const page = await app.firstWindow();
await page.setContent(`<style>body {background:red}</style>`);
await page.waitForTimeout(1000);
await app.close();
const videoPath = await page.video().path();
expect(fs.statSync(videoPath).size).toBeGreaterThan(0);
});

View file

@ -48,7 +48,9 @@ export const electronTest = baseTest.extend<ElectronTestFixtures, PageWorkerFixt
await run(async () => {
const [ window ] = await Promise.all([
electronApp.waitForEvent('window'),
electronApp.evaluate(electron => {
electronApp.evaluate(async electron => {
// Avoid "Error: Cannot create BrowserWindow before app is ready".
await electron.app.whenReady();
const window = new electron.BrowserWindow({
width: 800,
height: 600,