diff --git a/test/electron/electron-app.spec.ts b/test/electron/electron-app.spec.ts
deleted file mode 100644
index 9e3cd9a313..0000000000
--- a/test/electron/electron-app.spec.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Copyright (c) Microsoft Corporation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import path from 'path';
-import { folio } from './electron.fixture';
-const { it, expect, describe } = folio;
-
-describe('electron app', (suite, { browserName }) => {
- suite.skip(browserName !== 'chromium');
-}, () => {
- it('should fire close event', async ({ playwright }) => {
- const electronApp = await playwright._electron.launch({
- args: [path.join(__dirname, 'testApp.js')],
- });
- const events = [];
- electronApp.on('close', () => events.push('application'));
- electronApp.context().on('close', () => events.push('context'));
- await electronApp.close();
- expect(events.join('|')).toBe('context|application');
- // Give it some time to fire more events - there should not be any.
- await new Promise(f => setTimeout(f, 1000));
- expect(events.join('|')).toBe('context|application');
- });
-
- it('should script application', async ({ electronApp }) => {
- const appPath = await electronApp.evaluate(async ({ app }) => app.getAppPath());
- expect(appPath).toContain('electron');
- });
-
- it('should return windows', async ({ electronApp, newWindow }) => {
- const window = await newWindow();
- expect(electronApp.windows()).toEqual([window]);
- });
-
- it('should evaluate handle', async ({ electronApp }) => {
- const appHandle = await electronApp.evaluateHandle(({ app }) => app);
- expect(await electronApp.evaluate(({ app }, appHandle) => app === appHandle, appHandle)).toBeTruthy();
- });
-
- it('should route network', async ({ electronApp, newWindow }) => {
- await electronApp.context().route('**/empty.html', (route, request) => {
- route.fulfill({
- status: 200,
- contentType: 'text/html',
- body: '
Hello World',
- });
- });
- const window = await newWindow();
- await window.goto('https://localhost:1000/empty.html');
- expect(await window.title()).toBe('Hello World');
- });
-
- it('should support init script', async ({ electronApp, newWindow }) => {
- await electronApp.context().addInitScript('window.magic = 42;');
- const window = await newWindow();
- await window.goto('data:text/html,');
- expect(await window.evaluate(() => window['copy'])).toBe(42);
- });
-
- it('should expose function', async ({ electronApp, newWindow }) => {
- await electronApp.context().exposeFunction('add', (a, b) => a + b);
- const window = await newWindow();
- await window.goto('data:text/html,');
- expect(await window.evaluate(() => window['result'])).toBe(42);
- });
-
- it('should wait for first window', async ({ electronApp }) => {
- await electronApp.evaluate(({ BrowserWindow }) => {
- const window = new BrowserWindow({ width: 800, height: 600 });
- window.loadURL('data:text/html,Hello World!');
- });
- const window = await electronApp.firstWindow();
- expect(await window.title()).toBe('Hello World!');
- });
-
- it('should have a clipboard instance', async ({ electronApp }) => {
- const clipboardContentToWrite = 'Hello from Playwright';
- await electronApp.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
- const clipboardContentRead = await electronApp.evaluate(async ({clipboard}) => clipboard.readText());
- expect(clipboardContentRead).toEqual(clipboardContentToWrite);
- });
-});
diff --git a/test/electron/electron-window.spec.ts b/test/electron/electron-window.spec.ts
deleted file mode 100644
index d0737c7fb0..0000000000
--- a/test/electron/electron-window.spec.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright (c) Microsoft Corporation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { folio } from './electron.fixture';
-const { it, expect, describe } = folio;
-
-describe('electron window', (suite, { browserName }) => {
- suite.skip(browserName !== 'chromium');
-}, () => {
- it('should click the button', async ({window, server}) => {
- await window.goto(server.PREFIX + '/input/button.html');
- await window.click('button');
- expect(await window.evaluate('result')).toBe('Clicked');
- });
-
- it('should check the box', async ({window}) => {
- await window.setContent(``);
- await window.check('input');
- expect(await window.evaluate('checkbox.checked')).toBe(true);
- });
-
- it('should not check the checked box', async ({window}) => {
- await window.setContent(``);
- await window.check('input');
- expect(await window.evaluate('checkbox.checked')).toBe(true);
- });
-
- it('should type into a textarea', async ({window, server}) => {
- await window.evaluate(() => {
- const textarea = document.createElement('textarea');
- document.body.appendChild(textarea);
- textarea.focus();
- });
- const text = 'Hello world. I am the text that was typed!';
- await window.keyboard.type(text);
- expect(await window.evaluate(() => document.querySelector('textarea').value)).toBe(text);
- });
-});
diff --git a/test/electron/electron.fixture.ts b/test/electron/electron.fixture.ts
deleted file mode 100644
index 528448ac53..0000000000
--- a/test/electron/electron.fixture.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright Microsoft Corporation. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { folio as base } from '../fixtures';
-import path from 'path';
-import { ElectronApplication, Page } from '../..';
-
-type TestState = {
- electronApp: ElectronApplication;
- window: Page;
- newWindow: () => Promise;
-};
-const fixtures = base.extend();
-
-fixtures.electronApp.init(async ({ playwright }, run) => {
- const application = await playwright._electron.launch({
- args: [path.join(__dirname, 'testApp.js')],
- });
- await run(application);
- await application.close();
-});
-
-fixtures.newWindow.init(async ({ electronApp }, run) => {
- const windows = [];
- const newWindow = async () => {
- const [ window ] = await Promise.all([
- electronApp.waitForEvent('window'),
- electronApp.evaluate(electron => {
- const window = new electron.BrowserWindow({ width: 800, height: 600 });
- window.loadURL('data:text/html,Hello World 1');
- })
- ]);
- windows.push(window);
- return window;
- };
- await run(newWindow);
- for (const window of windows)
- await window.close();
-});
-
-fixtures.window.init(async ({ newWindow }, run) => {
- await run(await newWindow());
-});
-
-export const folio = fixtures.build();
diff --git a/tests/config/default.config.ts b/tests/config/default.config.ts
index d16d236f3c..b56032b18d 100644
--- a/tests/config/default.config.ts
+++ b/tests/config/default.config.ts
@@ -19,8 +19,10 @@ import * as path from 'path';
import { test as playwrightTest, slowTest as playwrightSlowTest } from './playwrightTest';
import { test as browserTest } from './browserTest';
import { test as pageTest } from './pageTest';
+import { test as electronTest } from './electronTest';
import { PlaywrightEnv, BrowserEnv, PageEnv, BrowserName } from './browserEnv';
import { ServerEnv } from './serverEnv';
+import { ElectronEnv } from './electronEnv';
const config: Config = {
testDir: path.join(__dirname, '..'),
@@ -48,8 +50,9 @@ const serverEnv = new ServerEnv();
const browsers = ['chromium', 'webkit', 'firefox'] as BrowserName[];
for (const browserName of browsers) {
const executablePath = getExecutablePath(browserName);
+ const mode = (process.env.PWMODE || 'default') as ('default' | 'driver' | 'service');
const options = {
- mode: (process.env.PWMODE || 'default') as ('default' | 'driver' | 'service'),
+ mode,
executablePath,
trace: !!process.env.PWTRACE,
headless: !process.env.HEADFUL,
@@ -60,4 +63,6 @@ for (const browserName of browsers) {
playwrightSlowTest.runWith(browserName, serverEnv, new PlaywrightEnv(browserName, options), { timeout: config.timeout * 3 });
browserTest.runWith(browserName, serverEnv, new BrowserEnv(browserName, options), {});
pageTest.runWith(browserName, serverEnv, new PageEnv(browserName, options), {});
+ if (browserName === 'chromium')
+ electronTest.runWith(browserName, serverEnv, new ElectronEnv({ mode }));
}
diff --git a/test/electron/testApp.js b/tests/config/electron-app.js
similarity index 100%
rename from test/electron/testApp.js
rename to tests/config/electron-app.js
diff --git a/tests/config/electronEnv.ts b/tests/config/electronEnv.ts
new file mode 100644
index 0000000000..d991cd73e7
--- /dev/null
+++ b/tests/config/electronEnv.ts
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Env, TestInfo } from '../folio/out';
+import { PlaywrightEnv } from './browserEnv';
+import * as path from 'path';
+import { ElectronTestArgs } from './electronTest';
+import { ElectronApplication, Page } from '../../index';
+
+export class ElectronEnv extends PlaywrightEnv implements Env {
+ private _electronApp: ElectronApplication | undefined;
+ private _windows: Page[] = [];
+
+ constructor(options: { mode: 'default' | 'driver' | 'service' }) {
+ super('chromium', options);
+ }
+
+ private async _newWindow() {
+ const [ window ] = await Promise.all([
+ this._electronApp!.waitForEvent('window'),
+ this._electronApp!.evaluate(electron => {
+ const window = new electron.BrowserWindow({ width: 800, height: 600 });
+ window.loadURL('data:text/html,Hello World 1');
+ })
+ ]);
+ this._windows.push(window);
+ return window;
+ }
+
+ async beforeEach(testInfo: TestInfo) {
+ const result = await super.beforeEach(testInfo);
+ this._electronApp = await result.playwright._electron.launch({
+ args: [path.join(__dirname, 'electron-app.js')],
+ });
+ return {
+ ...result,
+ electronApp: this._electronApp,
+ newWindow: this._newWindow.bind(this),
+ };
+ }
+
+ async afterEach(testInfo: TestInfo) {
+ for (const window of this._windows)
+ await window.close();
+ this._windows = [];
+ if (this._electronApp) {
+ await this._electronApp.close();
+ this._electronApp = undefined;
+ }
+ await super.afterEach(testInfo);
+ }
+}
diff --git a/tests/config/electronTest.ts b/tests/config/electronTest.ts
new file mode 100644
index 0000000000..cd17ff3214
--- /dev/null
+++ b/tests/config/electronTest.ts
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { newTestType } from '../folio/out';
+import { ElectronApplication, Page } from '../../index';
+import type { CommonTestArgs } from './pageTest';
+import type { ServerTestArgs } from './serverTest';
+export { expect } from 'folio';
+
+export type ElectronTestArgs = CommonTestArgs & {
+ electronApp: ElectronApplication;
+ newWindow: () => Promise;
+};
+
+export const test = newTestType();
diff --git a/tests/electron/electron-app.spec.ts b/tests/electron/electron-app.spec.ts
new file mode 100644
index 0000000000..0d37fb54be
--- /dev/null
+++ b/tests/electron/electron-app.spec.ts
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import path from 'path';
+import { test, expect } from '../config/electronTest';
+
+test('should fire close event', async ({ playwright }) => {
+ const electronApp = await playwright._electron.launch({
+ args: [path.join(__dirname, '..', 'config', 'electron-app.js')],
+ });
+ const events = [];
+ electronApp.on('close', () => events.push('application'));
+ electronApp.context().on('close', () => events.push('context'));
+ await electronApp.close();
+ expect(events.join('|')).toBe('context|application');
+ // Give it some time to fire more events - there should not be any.
+ await new Promise(f => setTimeout(f, 1000));
+ expect(events.join('|')).toBe('context|application');
+});
+
+test('should script application', async ({ electronApp }) => {
+ const appPath = await electronApp.evaluate(async ({ app }) => app.getAppPath());
+ expect(appPath).toBe(path.resolve(__dirname, '..', 'config'));
+});
+
+test('should return windows', async ({ electronApp, newWindow }) => {
+ const window = await newWindow();
+ expect(electronApp.windows()).toEqual([window]);
+});
+
+test('should evaluate handle', async ({ electronApp }) => {
+ const appHandle = await electronApp.evaluateHandle(({ app }) => app);
+ expect(await electronApp.evaluate(({ app }, appHandle) => app === appHandle, appHandle)).toBeTruthy();
+});
+
+test('should route network', async ({ electronApp, newWindow }) => {
+ await electronApp.context().route('**/empty.html', (route, request) => {
+ route.fulfill({
+ status: 200,
+ contentType: 'text/html',
+ body: 'Hello World',
+ });
+ });
+ const window = await newWindow();
+ await window.goto('https://localhost:1000/empty.html');
+ expect(await window.title()).toBe('Hello World');
+});
+
+test('should support init script', async ({ electronApp, newWindow }) => {
+ await electronApp.context().addInitScript('window.magic = 42;');
+ const window = await newWindow();
+ await window.goto('data:text/html,');
+ expect(await window.evaluate(() => window['copy'])).toBe(42);
+});
+
+test('should expose function', async ({ electronApp, newWindow }) => {
+ await electronApp.context().exposeFunction('add', (a, b) => a + b);
+ const window = await newWindow();
+ await window.goto('data:text/html,');
+ expect(await window.evaluate(() => window['result'])).toBe(42);
+});
+
+test('should wait for first window', async ({ electronApp }) => {
+ await electronApp.evaluate(({ BrowserWindow }) => {
+ const window = new BrowserWindow({ width: 800, height: 600 });
+ window.loadURL('data:text/html,Hello World!');
+ });
+ const window = await electronApp.firstWindow();
+ expect(await window.title()).toBe('Hello World!');
+});
+
+test('should have a clipboard instance', async ({ electronApp }) => {
+ const clipboardContentToWrite = 'Hello from Playwright';
+ await electronApp.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
+ const clipboardContentRead = await electronApp.evaluate(async ({clipboard}) => clipboard.readText());
+ expect(clipboardContentRead).toEqual(clipboardContentToWrite);
+});
diff --git a/tests/electron/electron-window.spec.ts b/tests/electron/electron-window.spec.ts
new file mode 100644
index 0000000000..3d9b889626
--- /dev/null
+++ b/tests/electron/electron-window.spec.ts
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { test, expect } from '../config/electronTest';
+
+test('should click the button', async ({newWindow, server}) => {
+ const window = await newWindow();
+ await window.goto(server.PREFIX + '/input/button.html');
+ await window.click('button');
+ expect(await window.evaluate('result')).toBe('Clicked');
+});
+
+test('should check the box', async ({newWindow}) => {
+ const window = await newWindow();
+ await window.setContent(``);
+ await window.check('input');
+ expect(await window.evaluate('checkbox.checked')).toBe(true);
+});
+
+test('should not check the checked box', async ({newWindow}) => {
+ const window = await newWindow();
+ await window.setContent(``);
+ await window.check('input');
+ expect(await window.evaluate('checkbox.checked')).toBe(true);
+});
+
+test('should type into a textarea', async ({newWindow}) => {
+ const window = await newWindow();
+ await window.evaluate(() => {
+ const textarea = document.createElement('textarea');
+ document.body.appendChild(textarea);
+ textarea.focus();
+ });
+ const text = 'Hello world. I am the text that was typed!';
+ await window.keyboard.type(text);
+ expect(await window.evaluate(() => document.querySelector('textarea').value)).toBe(text);
+});