playwright-electron-config
Configure Playwright for comprehensive Electron application testing. This skill sets up the complete testing infrastructure including E2E tests, visual regression testing, accessibility audits, and cross-platform test matrices with CI/CD integration.
Capabilities
- Configure Playwright for Electron with
_electronfixture - Generate page object models for Electron windows
- Set up visual regression testing with snapshots
- Configure accessibility testing with axe-core
- Create cross-platform test matrices for CI
- Mock Electron APIs (dialog, shell, clipboard)
- Test IPC communication between main and renderer
- Generate test coverage reports
Input Schema
{
"type": "object",
"properties": {
"projectPath": {
"type": "string",
"description": "Path to the Electron project root"
},
"testDir": {
"type": "string",
"default": "tests/e2e"
},
"features": {
"type": "array",
"items": {
"enum": [
"visualRegression",
"accessibility",
"coverage",
"performance",
"ipcTesting",
"multiWindow",
"systemDialogMocks"
]
},
"default": ["visualRegression", "accessibility", "ipcTesting"]
},
"platforms": {
"type": "array",
"items": { "enum": ["windows", "macos", "linux"] },
"default": ["windows", "macos", "linux"]
},
"pageObjects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"selectors": { "type": "object" }
}
},
"description": "Page objects to generate"
},
"ciIntegration": {
"type": "object",
"properties": {
"provider": { "enum": ["github-actions", "azure-devops", "circleci", "gitlab"] },
"parallelization": { "type": "boolean", "default": true },
"sharding": { "type": "number", "description": "Number of shards" }
}
}
},
"required": ["projectPath"]
}
Output Schema
{
"type": "object",
"properties": {
"success": { "type": "boolean" },
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": { "type": "string" },
"type": { "enum": ["config", "fixture", "pageObject", "test", "helper", "ci"] }
}
}
},
"commands": {
"type": "object",
"properties": {
"runTests": { "type": "string" },
"updateSnapshots": { "type": "string" },
"showReport": { "type": "string" }
}
},
"ciWorkflow": {
"type": "string",
"description": "Path to generated CI workflow file"
}
},
"required": ["success", "files"]
}
Generated File Structure
tests/
e2e/
playwright.config.ts # Main Playwright config
fixtures/
electron-app.ts # Electron fixture
test-utils.ts # Test utilities
page-objects/
MainWindow.ts # Page object models
SettingsDialog.ts
specs/
app.spec.ts # Application tests
ipc.spec.ts # IPC tests
visual.spec.ts # Visual regression
a11y.spec.ts # Accessibility tests
mocks/
electron-api-mocks.ts # Electron API mocks
ipc-mocks.ts # IPC mocks
snapshots/ # Visual snapshots
reports/ # Test reports
Code Templates
Playwright Configuration for Electron
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e/specs',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { outputFolder: 'tests/e2e/reports' }],
['json', { outputFile: 'tests/e2e/reports/results.json' }],
],
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{
name: 'electron',
testMatch: '**/*.spec.ts',
},
],
});
Electron Test Fixture
// fixtures/electron-app.ts
import { test as base, ElectronApplication, Page } from '@playwright/test';
import { _electron as electron } from 'playwright';
import path from 'path';
export type TestFixtures = {
electronApp: ElectronApplication;
mainWindow: Page;
};
export const test = base.extend<TestFixtures>({
electronApp: async ({}, use) => {
// Launch Electron app
const electronApp = await electron.launch({
args: [path.join(__dirname, '../../dist/main/main.js')],
env: {
...process.env,
NODE_ENV: 'test',
},
});
// Use the app in tests
await use(electronApp);
// Cleanup
await electronApp.close();
},
mainWindow: async ({ electronApp }, use) => {
// Wait for first window
const window = await electronApp.firstWindow();
// Wait for app to be ready
await window.waitForLoadState('domcontentloaded');
await use(window);
},
});
export { expect } from '@playwright/test';
Page Object Model
// page-objects/MainWindow.ts
import { Page, Locator } from '@playwright/test';
export class MainWindow {
readonly page: Page;
readonly titleBar: Locator;
readonly sidebar: Locator;
readonly mainContent: Locator;
readonly statusBar: Locator;
constructor(page: Page) {
this.page = page;
this.titleBar = page.locator('[data-testid="title-bar"]');
this.sidebar = page.locator('[data-testid="sidebar"]');
this.mainContent = page.locator('[data-testid="main-content"]');
this.statusBar = page.locator('[data-testid="status-bar"]');
}
async getTitle(): Promise<string> {
return this.page.title();
}
async openSettings(): Promise<void> {
await this.page.click('[data-testid="settings-button"]');
await this.page.waitForSelector('[data-testid="settings-dialog"]');
}
async navigateTo(section: string): Promise<void> {
await this.sidebar.locator(`[data-section="${section}"]`).click();
await this.page.waitForLoadState('networkidle');
}
async screenshot(name: string): Promise<Buffer> {
return this.page.screenshot({ path: `tests/e2e/snapshots/${name}.png` });
}
}
IPC Testing
// specs/ipc.spec.ts
import { test, expect } from '../fixtures/electron-app';
test.describe('IPC Communication', () => {
test('should send message to main process', async ({ electronApp, mainWindow }) => {
// Evaluate in main process
const result = await electronApp.evaluate(async ({ ipcMain }) => {
return new Promise((resolve) => {
ipcMain.once('test-channel', (event, data) => {
resolve(data);
});
});
});
// Send from renderer
await mainWindow.evaluate(() => {
window.electronAPI.send('test-channel', { message: 'hello' });
});
// Verify
expect(result).toEqual({ message: 'hello' });
});
test('should receive response from main process', async ({ mainWindow }) => {
const response = await mainWindow.evaluate(async () => {
return window.electronAPI.invoke('get-app-version');
});
expect(response).toMatch(/^\d+\.\d+\.\d+$/);
});
});
Visual Regression Testing
// specs/visual.spec.ts
import { test, expect } from '../fixtures/electron-app';
import { MainWindow } from '../page-objects/MainWindow';
test.describe('Visual Regression', () => {
test('main window matches snapshot', async ({ mainWindow }) => {
const page = new MainWindow(mainWindow);
// Wait for animations to complete
await mainWindow.waitForTimeout(500);
await expect(mainWindow).toHaveScreenshot('main-window.png', {
maxDiffPixels: 100,
});
});
test('dark mode matches snapshot', async ({ electronApp, mainWindow }) => {
//