Skip to content

Commit 4fa9601

Browse files
Merge pull request #29 from qavajs/electron-steps
added electron steps
2 parents fb20520 + cbd615c commit 4fa9601

File tree

11 files changed

+184
-29
lines changed

11 files changed

+184
-29
lines changed

.github/workflows/pull-request.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,24 @@ jobs:
2828
with:
2929
report_paths: './test-e2e/report/report.xml'
3030
fail_on_failure: true
31+
32+
test-electron:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
- uses: actions/setup-node@v4
37+
with:
38+
node-version: 24
39+
- run: npm ci
40+
- run: npm run build
41+
- name: setup virtual display
42+
run: |
43+
export DISPLAY=:99
44+
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
45+
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" npm run test:e2e:electron
46+
- name: junit report (electron)
47+
uses: mikepenz/action-junit-report@v4
48+
if: always()
49+
with:
50+
report_paths: './test-e2e/report/report.xml'
51+
fail_on_failure: true

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
1010
:pencil: - chore
1111
:microscope: - experimental
1212

13+
## [3.4.0]
14+
- :rocket: added `electron` world property to interact with main electron process
15+
- :rocket: added step to interact with electron app menu
16+
```gherkin
17+
When I click 'Test > Open Page' electron menu
18+
```
19+
20+
- :rocket: added capability to execute script on electron main process
21+
```gherkin
22+
Scenario: evaluate script on main process
23+
When I execute '$js(async ({ app }) => app.showAboutPanel())' script on electron app
24+
25+
Scenario: evaluate script on main process and save result to memory
26+
When I execute '$js(async ({ app }) => app.getAppPath())' script on electron app and save result as 'appPath'
27+
Then I expect '$appPath' memory value to contain 'test-e2e/apps/electron'
28+
```
29+
1330
## [3.3.0]
1431
- :rocket: added `to satisfy` validation to verify user-defined expectation provided as predicate
1532
```Gherkin

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@qavajs/playwright",
3-
"version": "3.3.0",
3+
"version": "3.4.0",
44
"description": "steps to interact with playwright",
55
"main": "./index.js",
66
"scripts": {
@@ -26,8 +26,8 @@
2626
"homepage": "https://github.com/qavajs/playwright#readme",
2727
"devDependencies": {
2828
"@types/express": "^5.0.3",
29-
"@types/node": "^24.3.0",
30-
"electron": "^37.3.1",
29+
"@types/node": "^24.3.1",
30+
"electron": "^38.0.0",
3131
"express": "^5.1.0",
3232
"ts-node": "^10.9.2",
3333
"typescript": "^5.9.2"

src/QavajsPlaywrightElectronWorld.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { QavajsPlaywrightWorld } from './QavajsPlaywrightWorld';
2-
import { _electron, ElectronApplication, test, WorkerInfo } from '@playwright/test';
2+
import { _electron, Browser, BrowserContext, ElectronApplication, Page, test, WorkerInfo } from '@playwright/test';
33

44
type ElectronFixture = {
55
browser: ElectronApplication
@@ -22,7 +22,17 @@ const electron = test.extend<ElectronFixture>({
2222

2323
export class QavajsPlaywrightElectronWorld extends QavajsPlaywrightWorld {
2424
test = electron;
25+
electron!: ElectronApplication;
26+
context!: BrowserContext;
27+
page!: Page;
2528
constructor(options: any) {
2629
super(options);
2730
}
31+
32+
init = ({ browser, context, page }: { browser: ElectronApplication, context: BrowserContext, page: Page } ) => {
33+
this.browser = browser as unknown as Browser;
34+
this.electron = browser;
35+
this.context = context;
36+
this.page = page;
37+
}
2838
}

src/electron.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { When } from '@qavajs/playwright-runner-adapter';
2+
import { MemoryValue } from './types';
3+
import { QavajsPlaywrightElectronWorld } from './QavajsPlaywrightElectronWorld';
4+
5+
/**
6+
* Execute client function on electron process and save result into memory
7+
* @param {string} functionKey - memory key of function
8+
* @param {string} memoryKey - memory key to store result
9+
* @example I execute '$fn' function and save result as 'result' // fn is function reference
10+
* @example I execute '$js(async ({ app }) => app.getAppPath())' function and save result as 'scroll'
11+
*/
12+
When('I execute {value} function/script on electron app', async function (this: QavajsPlaywrightElectronWorld, fn: MemoryValue) {
13+
await this.electron.evaluate(await fn.value());
14+
});
15+
16+
/**
17+
* Execute client function on electron process and save result into memory
18+
* @param {string} functionKey - memory key of function
19+
* @param {string} memoryKey - memory key to store result
20+
* @example I execute '$fn' function and save result as 'result' // fn is function reference
21+
* @example I execute '$js(async ({ app }) => app.getAppPath())' function on electron app and save result as 'result'
22+
*/
23+
When('I execute {value} function/script on electron app and save result as {value}', async function (this: QavajsPlaywrightElectronWorld, fn: MemoryValue, memoryKey: MemoryValue) {
24+
memoryKey.set(await this.electron.evaluate(await fn.value()));
25+
});
26+
27+
/**
28+
* Click electron menu
29+
* @param {string} menuPath - menu path
30+
* @example I click 'File > Edit' electron menu
31+
*/
32+
When('I click {value} electron menu', async function (this: QavajsPlaywrightElectronWorld, menu: MemoryValue) {
33+
await this.electron.evaluate(async ({ Menu }: { Menu: any }, { menuPath }: { menuPath: string }) => {
34+
const path = menuPath.split(/\s*>\s*/) as string[];
35+
const menu = Menu.getApplicationMenu();
36+
if (!menu) throw new Error('Menu is not set');
37+
const firstMenu = path.shift() as string;
38+
const findItemPredicate = (item: string) => (menu: any) => menu.label === item || menu.role === item;
39+
let currentMenu = menu.items.find(findItemPredicate(firstMenu));
40+
if (!currentMenu) throw new Error(`Menu '${firstMenu}' is not found`);
41+
for (const pathItem of path) {
42+
if (!currentMenu?.submenu) throw new Error(`Menu '${pathItem}' does not have submenu`);
43+
currentMenu = currentMenu.submenu.items.find(findItemPredicate(pathItem));
44+
if (!currentMenu) throw new Error(`Menu '${pathItem}' is not found`);
45+
}
46+
currentMenu.click()
47+
}, { menuPath: await menu.value() });
48+
});

test-e2e/apps/electron/main.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
const { app, BrowserWindow, ipcMain } = require('electron')
1+
const { app, BrowserWindow, ipcMain, Menu } = require('electron')
22
const path = require('node:path')
3+
const menuTemplate = require('./menuTemplate');
4+
const handleOpenNewWindow = require('./newWindow');
35

46
function createWindow () {
57
const mainWindow = new BrowserWindow({
@@ -10,20 +12,9 @@ function createWindow () {
1012
}
1113
})
1214

13-
mainWindow.loadFile('index.html')
15+
return mainWindow.loadFile('index.html')
1416
}
1517

16-
function handleOpenNewWindow() {
17-
const newWindow = new BrowserWindow({
18-
width: 800,
19-
height: 600,
20-
webPreferences: {
21-
preload: path.join(__dirname, 'preload.js')
22-
}
23-
})
24-
25-
newWindow.loadFile('newWindow.html')
26-
}
2718
app.whenReady().then(() => {
2819
createWindow()
2920

@@ -38,6 +29,9 @@ app.whenReady().then(() => {
3829
}
3930
event.returnValue = null
4031
})
32+
33+
const menu = Menu.buildFromTemplate(menuTemplate);
34+
Menu.setApplicationMenu(menu);
4135
})
4236

4337
app.on('window-all-closed', function () {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const handleOpenNewWindow = require("./newWindow");
2+
3+
module.exports = [
4+
{
5+
label: 'Default',
6+
submenu: [
7+
{
8+
label: 'Open Page',
9+
click: () => {
10+
handleOpenNewWindow()
11+
}
12+
}
13+
]
14+
},
15+
{
16+
label: 'Test',
17+
submenu: [
18+
{
19+
label: 'Open Page',
20+
click: () => {
21+
handleOpenNewWindow()
22+
}
23+
}
24+
]
25+
},
26+
{
27+
label: 'Help',
28+
submenu: [
29+
{
30+
role: 'about',
31+
}
32+
]
33+
}
34+
];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { BrowserWindow } = require('electron')
2+
const path = require('node:path')
3+
4+
module.exports = function handleOpenNewWindow() {
5+
const newWindow = new BrowserWindow({
6+
width: 800,
7+
height: 600,
8+
webPreferences: {
9+
preload: path.join(__dirname, 'preload.js')
10+
}
11+
})
12+
13+
newWindow.loadFile('newWindow.html')
14+
}

test-e2e/features/electron/electron.feature

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,16 @@ Feature: electron
44
* I click 'Open New Window Electron Button'
55
* I switch to 'qavajs electron app new window' window
66
* I click 'Close Current Window Electron Button'
7-
* I expect current url to contain 'newWindow.html'
7+
* I expect current url to contain 'newWindow.html'
8+
9+
Scenario: evaluate script on main process
10+
* I execute '$js(async ({ app }) => app.showAboutPanel())' script on electron app
11+
12+
Scenario: evaluate script on main process and save result to memory
13+
* I execute '$js(async ({ app }) => app.getAppPath())' script on electron app and save result as 'appPath'
14+
* I expect '$appPath' to contain 'test-e2e/apps/electron'
15+
16+
Scenario: open menu
17+
* I click 'Test > Open Page' electron menu
18+
* I switch to 'qavajs electron app new window' window
19+
* I click 'Close Current Window Electron Button'

0 commit comments

Comments
 (0)