diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 06a0b92..29f0982 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -8,7 +8,7 @@ on: - v1 jobs: - test: + test-web: strategy: matrix: version: [ 20, 22, 24 ] @@ -23,9 +23,30 @@ jobs: - run: npm run test - run: npm run install:browsers - run: npm run test:e2e - - name: junit report + - name: junit report (web) uses: mikepenz/action-junit-report@v4 if: always() with: report_paths: './test-e2e/report.xml' fail_on_failure: true + test-electron: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + - run: npm ci + - run: npm run build + - run: npm run test + - name: setup virtual display + run: | + export DISPLAY=:99 + sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" npm run test:e2e:electron + - name: junit report (electron) + uses: mikepenz/action-junit-report@v4 + if: always() + with: + report_paths: './test-e2e/report.xml' + fail_on_failure: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ed0505e..bbd08da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how :microscope: - experimental +## [Unreleased] +- :rocket: added step to interact with electron app menu +```gherkin +When I click 'Test > Open Page' electron menu +``` + ## [2.9.0] - :rocket: added capability to execute script on electron main process ```gherkin diff --git a/package.json b/package.json index e6a4afe..6a238df 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "scripts": { "build": "tsc", "test": "vitest --coverage run", - "install:browsers": "playwright install", + "install:browsers": "playwright install chromium", "test:e2e": "qavajs run --config test-e2e/webui.ts", "test:e2e:electron": "qavajs run --config test-e2e/webui.ts --profile electron", - "debug:e2e:electron": "qavajs run --config test-e2e/webui.ts --profile electron", + "debug:e2e:electron": "qavajs run --config test-e2e/webui.ts --profile debugElectron", "debug:e2e": "qavajs run --config test-e2e/webui.ts --profile debug" }, "repository": { diff --git a/src/electron.ts b/src/electron.ts index 77be9f6..ab25799 100644 --- a/src/electron.ts +++ b/src/electron.ts @@ -20,4 +20,27 @@ When('I execute {value} function/script on electron app', async function (fn: Me */ When('I execute {value} function/script on electron app and save result as {value}', async function (fn: MemoryValue, memoryKey: MemoryValue) { memoryKey.set(await this.playwright.driver.evaluate(await fn.value())); +}); + +/** + * Click electron menu + * @param {string} menuPath - menu path + * @example I click 'File > Edit' electron menu + */ +When('I click {value} electron menu', async function (menu: MemoryValue) { + await this.playwright.driver.evaluate(async ({ Menu }: { Menu: any }, { menuPath }: { menuPath: string }) => { + const path = menuPath.split(/\s*>\s*/) as string[]; + const menu = Menu.getApplicationMenu(); + if (!menu) throw new Error('Menu is not set'); + const firstMenu = path.shift() as string; + const findItemPredicate = (item: string) => (menu: any) => menu.label === item || menu.role === item; + let currentMenu = menu.items.find(findItemPredicate(firstMenu)); + if (!currentMenu) throw new Error(`Menu '${firstMenu}' is not found`); + for (const pathItem of path) { + if (!currentMenu?.submenu) throw new Error(`Menu '${pathItem}' does not have submenu`); + currentMenu = currentMenu.submenu.items.find(findItemPredicate(pathItem)); + if (!currentMenu) throw new Error(`Menu '${pathItem}' is not found`); + } + currentMenu.click() + }, { menuPath: await menu.value() }); }); \ No newline at end of file diff --git a/test-e2e/apps/electron/main.js b/test-e2e/apps/electron/main.js index 03fad56..b269178 100644 --- a/test-e2e/apps/electron/main.js +++ b/test-e2e/apps/electron/main.js @@ -1,5 +1,7 @@ -const { app, BrowserWindow, ipcMain } = require('electron') +const { app, BrowserWindow, ipcMain, Menu } = require('electron') const path = require('node:path') +const menuTemplate = require('./menuTemplate'); +const handleOpenNewWindow = require('./newWindow'); function createWindow () { const mainWindow = new BrowserWindow({ @@ -10,20 +12,9 @@ function createWindow () { } }) - mainWindow.loadFile('index.html') + return mainWindow.loadFile('index.html') } -function handleOpenNewWindow() { - const newWindow = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - preload: path.join(__dirname, 'preload.js') - } - }) - - newWindow.loadFile('newWindow.html') -} app.whenReady().then(() => { createWindow() @@ -38,6 +29,9 @@ app.whenReady().then(() => { } event.returnValue = null }) + + const menu = Menu.buildFromTemplate(menuTemplate); + Menu.setApplicationMenu(menu); }) app.on('window-all-closed', function () { diff --git a/test-e2e/apps/electron/menuTemplate.js b/test-e2e/apps/electron/menuTemplate.js new file mode 100644 index 0000000..8b21de0 --- /dev/null +++ b/test-e2e/apps/electron/menuTemplate.js @@ -0,0 +1,34 @@ +const handleOpenNewWindow = require("./newWindow"); + +module.exports = [ + { + label: 'Default', + submenu: [ + { + label: 'Open Page', + click: () => { + handleOpenNewWindow() + } + } + ] + }, + { + label: 'Test', + submenu: [ + { + label: 'Open Page', + click: () => { + handleOpenNewWindow() + } + } + ] + }, + { + label: 'Help', + submenu: [ + { + role: 'about', + } + ] + } +]; \ No newline at end of file diff --git a/test-e2e/apps/electron/newWindow.js b/test-e2e/apps/electron/newWindow.js new file mode 100644 index 0000000..e25a3b3 --- /dev/null +++ b/test-e2e/apps/electron/newWindow.js @@ -0,0 +1,14 @@ +const { BrowserWindow } = require('electron') +const path = require('node:path') + +module.exports = function handleOpenNewWindow() { + const newWindow = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js') + } + }) + + newWindow.loadFile('newWindow.html') +} \ No newline at end of file diff --git a/test-e2e/features/electron/electron.feature b/test-e2e/features/electron/electron.feature index f33b974..460b070 100644 --- a/test-e2e/features/electron/electron.feature +++ b/test-e2e/features/electron/electron.feature @@ -10,4 +10,9 @@ Feature: electron Scenario: evaluate script on main process and save result to memory * I execute '$js(async ({ app }) => app.getAppPath())' script on electron app and save result as 'appPath' - * I expect '$appPath' memory value to contain 'test-e2e/apps/electron' \ No newline at end of file + * I expect '$appPath' memory value to contain 'test-e2e/apps/electron' + + Scenario: open menu + * I click 'Test > Open Page' electron menu + * I switch to 'qavajs electron app new window' window + * I click 'Close Current Window Electron Button' \ No newline at end of file diff --git a/test-e2e/webui.ts b/test-e2e/webui.ts index bb40ce5..04a5d21 100644 --- a/test-e2e/webui.ts +++ b/test-e2e/webui.ts @@ -24,7 +24,7 @@ const common = { video: { event: ['onFail'], dir: 'video', - size: { width: 800, height: 600 }, + size: {width: 800, height: 600}, attach: true }, screenshot: { @@ -34,7 +34,7 @@ const common = { }, format: [ '@qavajs/console-formatter', - 'junit:test-e2e/report.xml', + ['junit', 'test-e2e/report.xml'], ['@qavajs/html-formatter', 'test-e2e/report.html'] ], formatOptions: { @@ -76,7 +76,7 @@ export const debug = { video: { event: ['afterScenario'], dir: 'video', - size: { width: 800, height: 600 }, + size: {width: 800, height: 600}, attach: true } }, @@ -87,7 +87,6 @@ export const electron = { ...common, paths: ['test-e2e/features/electron/*.feature'], retry: 0, - // tags: '@debug', browser: { logLevel: 'warn', timeout: { @@ -95,9 +94,14 @@ export const electron = { }, capabilities: { browserName: 'electron', - args: ['test-e2e/apps/electron/main.js'], + args: ['test-e2e/apps/electron/main.js', '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu'], headless: false } }, parallel: 1 +} + +export const debugElectron = { + ...electron, + tags: '@debug', } \ No newline at end of file