Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(electron): fix the directory app path #19601

Merged
merged 1 commit into from Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/playwright-core/src/client/electron.ts
Expand Up @@ -62,6 +62,7 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
readonly _context: BrowserContext;
private _windows = new Set<Page>();
private _timeoutSettings = new TimeoutSettings();
private _isClosed = false;

static from(electronApplication: channels.ElectronApplicationChannel): ElectronApplication {
return (electronApplication as any)._object;
Expand All @@ -73,7 +74,10 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
for (const page of this._context._pages)
this._onPage(page);
this._context.on(Events.BrowserContext.Page, page => this._onPage(page));
this._channel.on('close', () => this.emit(Events.ElectronApplication.Close));
this._channel.on('close', () => {
this._isClosed = true;
this.emit(Events.ElectronApplication.Close);
});
}

process(): childProcess.ChildProcess {
Expand Down Expand Up @@ -102,6 +106,8 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
}

async close() {
if (this._isClosed)
return;
await this._channel.close();
}

Expand Down
9 changes: 8 additions & 1 deletion packages/playwright-core/src/server/electron/loader.ts
Expand Up @@ -15,6 +15,7 @@
*/

const { app } = require('electron');
const fs = require('fs');
const path = require('path');
const { chromiumSwitches } = require('../chromium/chromiumSwitches');

Expand Down Expand Up @@ -42,7 +43,13 @@ app.emit = (event: string | symbol, ...args: any[]): boolean => {
}
return originalEmit(event, ...args);
};
app.getAppPath = () => path.dirname(appPath);

app.getAppPath = () => {
if (fs.statSync(appPath).isFile())
return path.dirname(appPath);
return appPath;
};

let isReady = false;
let whenReadyCallback: (event: any) => any;
const whenReadyPromise = new Promise<void>(f => whenReadyCallback = f);
Expand Down
2 changes: 2 additions & 0 deletions tests/electron/electron-app-pre-ready-app.js
@@ -0,0 +1,2 @@
const { protocol } = require('electron');
protocol.registerSchemesAsPrivileged([]);
3 changes: 0 additions & 3 deletions tests/electron/electron-app.js
@@ -1,9 +1,6 @@
const { app, protocol } = require('electron');
const path = require('path');

// Test using pre-ready apis.
protocol.registerSchemesAsPrivileged([]);

app.on('window-all-closed', e => e.preventDefault());

app.whenReady().then(() => {
Expand Down
97 changes: 39 additions & 58 deletions tests/electron/electron-app.spec.ts
Expand Up @@ -19,10 +19,8 @@ import path from 'path';
import fs from 'fs';
import { electronTest as test, expect } from './electronTest';

test('should fire close event', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-app.js')],
});
test('should fire close event', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp('electron-app.js');
const events = [];
electronApp.on('close', () => events.push('application'));
electronApp.context().on('close', () => events.push('context'));
Expand All @@ -33,22 +31,16 @@ test('should fire close event', async ({ playwright }) => {
expect(events.join('|')).toBe('context|application');
});

test('should dispatch ready event', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-app-ready-event.js')],
});
try {
const events = await electronApp.evaluate(() => globalThis.__playwrightLog);
expect(events).toEqual([
'isReady == false',
'will-finish-launching fired',
'ready fired',
'whenReady resolved',
'isReady == true',
]);
} finally {
await electronApp.close();
}
test('should dispatch ready event', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp('electron-app-ready-event.js');
const events = await electronApp.evaluate(() => globalThis.__playwrightLog);
expect(events).toEqual([
'isReady == false',
'will-finish-launching fired',
'ready fired',
'whenReady resolved',
'isReady == true',
]);
});

test('should script application', async ({ electronApp }) => {
Expand Down Expand Up @@ -114,28 +106,19 @@ test('should have a clipboard instance', async ({ electronApp }) => {
expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});

test('should test app that opens window fast', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
});
await electronApp.close();
test('should test app that opens window fast', async ({ launchElectronApp }) => {
await launchElectronApp('electron-window-app.js');
});

test('should return browser window', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
});
test('should return browser window', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp('electron-window-app.js');
const page = await electronApp.firstWindow();
const bwHandle = await electronApp.browserWindow(page);
expect(await bwHandle.evaluate((bw: BrowserWindow) => bw.title)).toBe('Electron');
await electronApp.close();
});

test('should bypass csp', async ({ playwright, server }) => {
const app = await playwright._electron.launch({
args: [require('path').join(__dirname, 'electron-app.js')],
bypassCSP: true,
});
test('should bypass csp', async ({ launchElectronApp, server }) => {
const app = await launchElectronApp('electron-app.js', { bypassCSP: true });
await app.evaluate(electron => {
const window = new electron.BrowserWindow({
width: 800,
Expand All @@ -147,13 +130,10 @@ test('should bypass csp', async ({ playwright, server }) => {
await page.goto(server.PREFIX + '/csp.html');
await page.addScriptTag({ content: 'window["__injected"] = 42;' });
expect(await page.evaluate('window["__injected"]')).toBe(42);
await app.close();
});

test('should create page for browser view', async ({ playwright }) => {
const app = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
});
test('should create page for browser view', async ({ launchElectronApp }) => {
const app = await launchElectronApp('electron-window-app.js');
await app.firstWindow();
await app.evaluate(async electron => {
const window = electron.BrowserWindow.getAllWindows()[0];
Expand All @@ -163,13 +143,10 @@ test('should create page for browser view', async ({ playwright }) => {
view.setBounds({ x: 0, y: 0, width: 256, height: 256 });
});
await expect.poll(() => app.windows().length).toBe(2);
await app.close();
});

test('should return same browser window for browser view pages', async ({ playwright }) => {
const app = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app.js')],
});
test('should return same browser window for browser view pages', async ({ launchElectronApp }) => {
const app = await launchElectronApp('electron-window-app.js');
await app.firstWindow();
await app.evaluate(async electron => {
const window = electron.BrowserWindow.getAllWindows()[0];
Expand All @@ -187,12 +164,10 @@ 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')],
test('should record video', async ({ launchElectronApp }, testInfo) => {
const app = await launchElectronApp('electron-window-app.js', {
recordVideo: { dir: testInfo.outputPath('video') }
});
const page = await app.firstWindow();
Expand All @@ -203,25 +178,31 @@ test('should record video', async ({ playwright }, testInfo) => {
expect(fs.statSync(videoPath).size).toBeGreaterThan(0);
});

test('should be able to get the first window when with a delayed navigation', async ({ playwright }) => {
test('should be able to get the first window when with a delayed navigation', async ({ launchElectronApp }) => {
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/17765' });

const app = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-window-app-delayed-loadURL.js')],
});
const app = await launchElectronApp('electron-window-app-delayed-loadURL.js');
const page = await app.firstWindow();
await expect(page).toHaveURL('data:text/html,<h1>Foobar</h1>');
await expect(page.locator('h1')).toHaveText('Foobar');
await app.close();
});

test('should detach debugger on app-initiated exit', async ({ playwright }) => {
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-app.js')],
});
test('should detach debugger on app-initiated exit', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp('electron-app.js');
const closePromise = new Promise(f => electronApp.process().on('close', f));
await electronApp.evaluate(({ app }) => {
app.quit();
});
await closePromise;
});

test('should run pre-ready apis', async ({ launchElectronApp }) => {
await launchElectronApp('electron-pre-ready-app.js');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing this file from the PR?

});

test('should resolve app path for folder apps', async ({ launchElectronApp }) => {
const electronApp = await launchElectronApp('.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here?

const appPath = await electronApp.evaluate(async ({ app }) => app.getAppPath());
expect(appPath).toBe(path.resolve(__dirname));
await electronApp.close();
});
18 changes: 13 additions & 5 deletions tests/electron/electronTest.ts
Expand Up @@ -26,6 +26,7 @@ import { assert } from 'playwright-core/lib/utils';

type ElectronTestFixtures = PageTestFixtures & {
electronApp: ElectronApplication;
launchElectronApp: (appFile: string, options?: any) => Promise<ElectronApplication>;
newWindow: () => Promise<Page>;
};

Expand All @@ -40,14 +41,21 @@ export const electronTest = baseTest.extend<TraceViewerFixtures>(traceViewerFixt
isElectron: [true, { scope: 'worker' }],
isWebView2: [false, { scope: 'worker' }],

electronApp: async ({ playwright }, run) => {
launchElectronApp: async ({ playwright }, use) => {
// This env prevents 'Electron Security Policy' console message.
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
const electronApp = await playwright._electron.launch({
args: [path.join(__dirname, 'electron-app.js')],
const apps: ElectronApplication[] = [];
await use(async (appFile: string, options?: any[]) => {
const app = await playwright._electron.launch({ ...options, args: [path.join(__dirname, appFile)] });
apps.push(app);
return app;
});
await run(electronApp);
await electronApp.close();
for (const app of apps)
await app.close();
},

electronApp: async ({ launchElectronApp }, use) => {
await use(await launchElectronApp('electron-app.js'));
},

newWindow: async ({ electronApp }, run) => {
Expand Down
1 change: 1 addition & 0 deletions tests/electron/index.js
@@ -0,0 +1 @@
require('electron');