Skip to content

Commit 8232497

Browse files
authored
feat(electron): expose app process(), detach on exit (#13280)
1 parent 3636d85 commit 8232497

File tree

8 files changed

+46
-3
lines changed

8 files changed

+46
-3
lines changed

docs/src/api/class-electronapplication.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ Typically your script will start with:
112112
// ...
113113
```
114114

115+
## method: ElectronApplication.process
116+
- returns: <[ChildProcess]>
117+
118+
Returns the main process for this Electron Application.
119+
115120
## async method: ElectronApplication.waitForEvent
116121
- returns: <[any]>
117122

packages/playwright-core/src/client/channelOwner.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
135135
}
136136
}
137137

138+
_toImpl(): any {
139+
return this._connection.toImpl?.(this);
140+
}
141+
138142
private toJSON() {
139143
// Jest's expect library tries to print objects sometimes.
140144
// RPC objects can contain links to lots of other objects,

packages/playwright-core/src/client/connection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export class Connection extends EventEmitter {
6767
private _rootObject: Root;
6868
private _closedErrorMessage: string | undefined;
6969
private _isRemote = false;
70+
// Some connections allow resolving in-process dispatchers.
71+
toImpl: ((client: ChannelOwner) => any) | undefined;
7072

7173
constructor() {
7274
super();

packages/playwright-core/src/client/electron.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import type { BrowserWindow } from 'electron';
18+
import * as childProcess from 'child_process';
1819
import * as structs from '../../types/structs';
1920
import * as api from '../../types/types';
2021
import * as channels from '../protocol/channels';
@@ -75,6 +76,10 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
7576
this._channel.on('close', () => this.emit(Events.ElectronApplication.Close));
7677
}
7778

79+
process(): childProcess.ChildProcess {
80+
return this._toImpl().process();
81+
}
82+
7883
_onPage(page: Page) {
7984
this._windows.add(page);
8085
this.emit(Events.ElectronApplication.Window, page);

packages/playwright-core/src/inProcessFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export function createInProcessPlaywright(): PlaywrightAPI {
4444
dispatcherConnection.onmessage = message => setImmediate(() => clientConnection.dispatch(message));
4545
clientConnection.onmessage = message => setImmediate(() => dispatcherConnection.dispatch(message));
4646

47-
(playwrightAPI as any)._toImpl = (x: any) => dispatcherConnection._dispatchers.get(x._guid)!._object;
47+
clientConnection.toImpl = (x: any) => dispatcherConnection._dispatchers.get(x._guid)!._object;
48+
(playwrightAPI as any)._toImpl = clientConnection.toImpl;
4849
return playwrightAPI;
4950
}

packages/playwright-core/src/server/electron/electron.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ export class ElectronApplication extends SdkObject {
5252
private _nodeExecutionContext: js.ExecutionContext | undefined;
5353
_nodeElectronHandlePromise: Promise<js.JSHandle<any>>;
5454
readonly _timeoutSettings = new TimeoutSettings();
55+
private _process: childProcess.ChildProcess;
5556

56-
constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection) {
57+
constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection, process: childProcess.ChildProcess) {
5758
super(parent, 'electron-app');
59+
this._process = process;
5860
this._browserContext = browser._defaultContext as CRBrowserContext;
5961
this._browserContext.on(BrowserContext.Events.Close, () => {
6062
// Emit application closed after context closed.
@@ -77,6 +79,10 @@ export class ElectronApplication extends SdkObject {
7779
this._nodeSession.send('Runtime.enable', {}).catch(e => {});
7880
}
7981

82+
process(): childProcess.ChildProcess {
83+
return this._process;
84+
}
85+
8086
context(): BrowserContext {
8187
return this._browserContext;
8288
}
@@ -166,6 +172,10 @@ export class Electron extends SdkObject {
166172
const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]);
167173
const nodeConnection = new CRConnection(nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
168174

175+
// Immediately release exiting process under debug.
176+
waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./).then(() => {
177+
nodeTransport.close();
178+
}).catch(() => {});
169179
const chromeMatch = await Promise.race([
170180
waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/),
171181
waitForXserverError,
@@ -196,7 +206,7 @@ export class Electron extends SdkObject {
196206
};
197207
validateBrowserContextOptions(contextOptions, browserOptions);
198208
const browser = await CRBrowser.connect(chromeTransport, browserOptions);
199-
app = new ElectronApplication(this, browser, nodeConnection);
209+
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
200210
return app;
201211
}, TimeoutSettings.timeout(options));
202212
}

packages/playwright-core/types/types.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10921,6 +10921,11 @@ export interface ElectronApplication {
1092110921
*/
1092210922
firstWindow(): Promise<Page>;
1092310923

10924+
/**
10925+
* Returns the main process for this Electron Application.
10926+
*/
10927+
process(): ChildProcess;
10928+
1092410929
/**
1092510930
* This event is issued when the application closes.
1092610931
*/

tests/electron/electron-app.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,14 @@ test('should record video', async ({ playwright }, testInfo) => {
180180
const videoPath = await page.video().path();
181181
expect(fs.statSync(videoPath).size).toBeGreaterThan(0);
182182
});
183+
184+
test('should detach debugger on app-initiated exit', async ({ playwright }) => {
185+
const electronApp = await playwright._electron.launch({
186+
args: [path.join(__dirname, 'electron-app.js')],
187+
});
188+
const closePromise = new Promise(f => electronApp.process().on('close', f));
189+
await electronApp.evaluate(({ app }) => {
190+
app.quit();
191+
});
192+
await closePromise;
193+
});

0 commit comments

Comments
 (0)