Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fb09a45
feat: tauri window management
goosewobbler Jan 26, 2026
5831b15
fix: use correct tauri APIs
goosewobbler Jan 27, 2026
138ccce
fix: only create one window
goosewobbler Jan 27, 2026
4a5afc3
chore: add commands to schema
goosewobbler Jan 27, 2026
a7e1636
test: update window label commands, use splash screen env var
goosewobbler Jan 27, 2026
723c9a9
refactor: rework perms
goosewobbler Jan 27, 2026
f41a6bb
chore: ignore autogenerated
goosewobbler Jan 27, 2026
68aabd7
test: remove skip logic
goosewobbler Jan 27, 2026
93368af
chore: update switch to main command
goosewobbler Jan 27, 2026
3ffc8e7
chore: align CI around window testing
goosewobbler Jan 27, 2026
a8458c8
fix: call correct command names
goosewobbler Jan 28, 2026
27207a4
chore: add debug
goosewobbler Jan 28, 2026
4375bd7
refactor: standardise script nomenclature
goosewobbler Jan 28, 2026
7bafb51
refactor: remove env passing, ensure isolation of window specs
goosewobbler Jan 28, 2026
f30f1e0
chore: add window test debug
goosewobbler Jan 28, 2026
8e74198
fix: address code review comments
goosewobbler Jan 28, 2026
f09c955
chore: reinstate env passing, defer main window creation
goosewobbler Feb 1, 2026
6415f91
chore: add debug
goosewobbler Feb 2, 2026
acde63b
chore: explicitly show, focus and close splash
goosewobbler Feb 2, 2026
ab25bf1
chore: include splash in vite conf
goosewobbler Feb 2, 2026
59f15ab
chore: use window.__TAURI__
goosewobbler Feb 2, 2026
afd43ae
chore: match electron behaviour
goosewobbler Feb 2, 2026
492f00e
chore: remove logs
goosewobbler Feb 2, 2026
530cd33
chore: just hide splash
goosewobbler Feb 2, 2026
981728c
feat: add window state management and focus handling
goosewobbler Feb 2, 2026
67baf88
chore: allow get_window_states
goosewobbler Feb 2, 2026
3bc43a1
chore: create splash first
goosewobbler Feb 2, 2026
099df2b
chore: fix perms
goosewobbler Feb 2, 2026
a044a9f
feat: enhance splash screen with Tauri plugin integration and improve…
goosewobbler Feb 3, 2026
bd8fe61
chore: fix windows
goosewobbler Feb 3, 2026
3bd7e68
test: fix function signature mismatch
goosewobbler Feb 3, 2026
49e943e
test: fix return value
goosewobbler Feb 3, 2026
8248ee8
chore: add error handling
goosewobbler Feb 3, 2026
3c9aa60
test: clean up window units
goosewobbler Feb 3, 2026
edc8a72
fix: check each window only once
goosewobbler Feb 3, 2026
d7fa8fa
chore: fix linting errors
goosewobbler Feb 3, 2026
2423edd
fix: make title matching more robust
goosewobbler Feb 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/_ci-e2e-tauri.reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ jobs:
echo "::notice::Tauri binaries: Using pre-built binaries for ${{ runner.os }}"

# Dynamically generate the Tauri test commands to run
# Since we only have one Tauri app (basic), no module type filtering needed
# Uses 'tauri-basic' naming convention to align with Electron's 'builder/forge/script' naming
- name: 🪄 Generate Tauri Test Execution Plan
id: gen-test
uses: actions/github-script@v8
Expand All @@ -227,7 +227,8 @@ jobs:
.split(',')
.map((s) => {
const scenario = s.trim();
const baseCommand = `test:e2e:tauri:${scenario.replace('tauri-', '')}`;
// Use 'tauri-basic' as the app name (single Tauri app variant)
const baseCommand = `test:e2e:tauri-basic`;
return testType !== 'standard'
? `${baseCommand}:${testType}`
: baseCommand;
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/_ci-e2e.reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ jobs:
# Each test builds its own apps with correct environment context
- name: 🧪 Execute E2E Tests
shell: bash
env:
ENABLE_SPLASH_WINDOW: 'true'
run: pnpm exec turbo run ${{ steps.gen-test.outputs.result }} --only --log-order=stream

# Show logs on failure for debugging
Expand Down
31 changes: 29 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,33 @@ jobs:
artifact_size: ${{ needs.build.outputs.artifact_size }}
cache_key: ${{ needs.build.outputs.cache_key }}

# Electron window tests - run on Linux with splash screen enabled
e2e-electron-window:
name: E2E - Electron [Linux] (window)
needs: [detect-changes, build]
if: needs.detect-changes.outputs.run_electron == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
display-name: Linux
scenario: builder
- os: ubuntu-latest
display-name: Linux
scenario: forge
- os: ubuntu-latest
display-name: Linux
scenario: script
uses: ./.github/workflows/_ci-e2e.reusable.yml
with:
os: ${{ matrix.os }}
node-version: '20'
scenario: ${{ matrix.scenario }}
build_id: ${{ needs.build.outputs.build_id }}
artifact_size: ${{ needs.build.outputs.artifact_size }}
cache_key: ${{ needs.build.outputs.cache_key }}

# Tauri E2E tests for Linux - only waits for E2E app build
e2e-tauri-linux-matrix:
name: E2E - Tauri [Linux] - ${{ matrix.scenario }}${{ matrix.test-type != 'standard' && format(' ({0})', matrix.test-type) || '' }}
Expand All @@ -326,7 +353,7 @@ jobs:
fail-fast: false
matrix:
scenario: ['tauri-basic']
test-type: ['standard', 'multiremote', 'standalone']
test-type: ['standard', 'window', 'multiremote', 'standalone']
uses: ./.github/workflows/_ci-e2e-tauri.reusable.yml
with:
os: 'ubuntu-latest'
Expand All @@ -347,7 +374,7 @@ jobs:
fail-fast: false
matrix:
scenario: ['tauri-basic']
test-type: ['standard', 'multiremote', 'standalone']
test-type: ['standard', 'window', 'multiremote', 'standalone']
uses: ./.github/workflows/_ci-e2e-tauri.reusable.yml
with:
os: 'windows-latest'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ coverage

# Tauri build files
**/src-tauri/target/*
**/autogenerated/**/*


12 changes: 7 additions & 5 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@
"logs:follow": "tsx scripts/show-logs.ts --follow",
"test": "tsx scripts/run-matrix.ts",
"test:e2e:electron-builder": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=builder BINARY=true tsx scripts/run-matrix.ts",
"test:e2e:electron-builder:window": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=builder BINARY=true TEST_TYPE=window ENABLE_SPLASH_WINDOW=true tsx scripts/run-matrix.ts",
"test:e2e:electron-forge": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=forge BINARY=true tsx scripts/run-matrix.ts",
"test:e2e:electron-forge:window": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=forge BINARY=true TEST_TYPE=window ENABLE_SPLASH_WINDOW=true tsx scripts/run-matrix.ts",
"test:e2e:electron-script": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=script BINARY=false tsx scripts/run-matrix.ts",
"test:e2e:electron-script:window": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=script BINARY=false TEST_TYPE=window ENABLE_SPLASH_WINDOW=true tsx scripts/run-matrix.ts",
"test:e2e-mac-universal:electron-builder": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=builder BINARY=true MAC_UNIVERSAL=true tsx scripts/run-matrix.ts",
"test:e2e-mac-universal:electron-forge": "cross-env DEBUG=wdio-electron-service:launcher WDIO_VERBOSE=true FRAMEWORK=electron APP=forge BINARY=true MAC_UNIVERSAL=true tsx scripts/run-matrix.ts",
"test:e2e:standard": "cross-env TEST_TYPE=standard tsx scripts/run-matrix.ts",
"test:e2e:window": "cross-env TEST_TYPE=window tsx scripts/run-matrix.ts",
"test:e2e:multiremote": "cross-env TEST_TYPE=multiremote tsx scripts/run-matrix.ts",
"test:e2e:standalone": "cross-env TEST_TYPE=standalone tsx scripts/run-matrix.ts",
"test:e2e:mac-universal": "cross-env MAC_UNIVERSAL=true tsx scripts/run-matrix.ts",
"test:e2e:tauri": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=standard tsx scripts/run-matrix.ts",
"test:e2e:tauri:basic": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=standard tsx scripts/run-matrix.ts",
"test:e2e:tauri:basic:multiremote": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=multiremote tsx scripts/run-matrix.ts",
"test:e2e:tauri:basic:standalone": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=standalone tsx scripts/run-matrix.ts"
"test:e2e:tauri-basic": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=standard tsx scripts/run-matrix.ts",
"test:e2e:tauri-basic:window": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=window ENABLE_SPLASH_WINDOW=true tsx scripts/run-matrix.ts",
"test:e2e:tauri-basic:multiremote": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=multiremote tsx scripts/run-matrix.ts",
"test:e2e:tauri-basic:standalone": "cross-env FRAMEWORK=tauri APP=basic TEST_TYPE=standalone tsx scripts/run-matrix.ts"
},
"dependencies": {
"@wdio/cli": "catalog:default",
Expand Down
23 changes: 23 additions & 0 deletions e2e/test/electron/window.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ import { expect } from '@wdio/globals';

describe('application window tests', () => {
it('should launch the application splash screen window', async () => {
// Check if splash screen is enabled by checking for the switch button
const switchButton = await browser.$('.switch-main-window');
const hasSwitchButton = switchButton !== null;

if (!hasSwitchButton) {
// Splash is not enabled, verify we're on the main window
await expect(browser).toHaveTitle(/Electron.*E2E Test App/);
return;
}

// Splash is enabled, verify we're on splash screen
if (browser.isMultiremote) {
const multi = browser as unknown as WebdriverIO.MultiRemoteBrowser;
const browserA = multi.getInstance('browserA');
Expand All @@ -15,6 +26,18 @@ describe('application window tests', () => {
});

it('should switch to the application main window', async () => {
// Check if splash screen is enabled (has switch button)
const switchButton = await browser.$('.switch-main-window');
const hasSwitchButton = switchButton !== null;

if (!hasSwitchButton) {
// Splash is not enabled, verify we're already on the main window
const title = await browser.getTitle();
expect(title).toMatch(/Electron.*E2E Test App/);
return;
}

// Splash is enabled, click the switch button
if (browser.isMultiremote) {
const multi = browser as unknown as WebdriverIO.MultiRemoteBrowser;
const browserA = multi.getInstance('browserA');
Expand Down
84 changes: 84 additions & 0 deletions e2e/test/tauri/window.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { expect } from '@wdio/globals';
import { browser } from '@wdio/tauri-service';

describe('application window tests', () => {
it('should launch the application splash screen window', async () => {
// Check if splash screen is enabled by checking for the switch button
const switchButton = await browser.$('.switch-main-window');
// Fix: Use isExisting() to properly check if element exists
const hasSwitchButton = await switchButton.isExisting();

// Debug: Log window handles and current state
const windowHandles = await browser.getWindowHandles();
const currentTitle = await browser.getTitle();
console.log('[DEBUG] Window handles:', windowHandles);
console.log('[DEBUG] Current window title:', currentTitle);
console.log('[DEBUG] hasSwitchButton:', hasSwitchButton);

if (!hasSwitchButton) {
// Splash is not enabled, verify we're on the main window
console.log('[DEBUG] Splash not enabled, checking main window title');
await expect(browser).toHaveTitle(/Tauri.*E2E Test App/);
return;
}

// Splash is enabled, verify we're on splash screen
console.log('[DEBUG] Splash enabled, checking splash window title');
if (browser.isMultiremote) {
const multi = browser as unknown as WebdriverIO.MultiRemoteBrowser;
const browserA = multi.getInstance('browserA');
const browserB = multi.getInstance('browserB');
await expect(browserA).toHaveTitle('Splash Screen');
await expect(browserB).toHaveTitle('Splash Screen');
} else {
await expect(browser).toHaveTitle('Splash Screen');
}
});

it('should switch to the application main window', async () => {
// Check if splash screen is enabled (has switch button)
const switchButton = await browser.$('.switch-main-window');
// Fix: Use isExisting() to properly check if element exists
const hasSwitchButton = await switchButton.isExisting();

if (!hasSwitchButton) {
// Splash is not enabled, verify we're already on the main window
console.log('[DEBUG] Splash not enabled, verifying main window');
const title = await browser.getTitle();
expect(title).toMatch(/Tauri.*E2E Test App/);
return;
}

// Splash is enabled, click the switch button
console.log('[DEBUG] Splash enabled, clicking switch button');
if (browser.isMultiremote) {
const multi = browser as unknown as WebdriverIO.MultiRemoteBrowser;
const browserA = multi.getInstance('browserA');
const browserB = multi.getInstance('browserB');
await (await browserA.$('.switch-main-window')).click();
await (await browserB.$('.switch-main-window')).click();
const titleA = await browserA.getTitle();
const titleB = await browserB.getTitle();
expect(titleA).toMatch(/Tauri.*E2E Test App/);
expect(titleB).toMatch(/Tauri.*E2E Test App/);
} else {
const elem = await browser.$('.switch-main-window');
await elem.click();
const title = await browser.getTitle();
expect(title).toMatch(/Tauri.*E2E Test App/);
}
});

it('should list all window labels', async () => {
const windows = (await browser.tauri.execute(({ core }) => core.invoke('plugin:wdio|list_windows'))) as string[];
expect(Array.isArray(windows)).toBe(true);
expect(windows.length).toBeGreaterThanOrEqual(1);
});

it('should get active window label', async () => {
const label = (await browser.tauri.execute(({ core }) =>
core.invoke('plugin:wdio|get_active_window_label'),
)) as string;
expect(typeof label).toBe('string');
});
});
25 changes: 21 additions & 4 deletions e2e/wdio.tauri.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,15 @@ switch (envContext.testType) {
case 'standalone':
specs = ['./test/tauri/standalone/*.spec.ts'];
break;
case 'window':
// Window tests - only window-specific functionality
specs = ['./test/tauri/window.spec.ts'];
break;
default:
// Standard tests - core functionality without specialized test modes
specs = ['./test/tauri/*.spec.ts'];
// Exclude mocking tests for now
exclude = ['./test/tauri/mocking.spec.ts'];
// Exclude mocking tests and window tests (window tests require splash)
exclude = ['./test/tauri/mocking.spec.ts', './test/tauri/window.spec.ts'];
break;
}

Expand All @@ -141,6 +145,7 @@ type TauriCapability = {
'wdio:tauriServiceOptions': {
appBinaryPath: string;
appArgs: string[];
env?: Record<string, string>;
captureBackendLogs?: boolean;
captureFrontendLogs?: boolean;
backendLogLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
Expand All @@ -164,7 +169,12 @@ type StandardCapabilities = TauriCapability[];
let capabilities: MultiremoteCapabilities | StandardCapabilities;

if (envContext.isMultiremote) {
// Tauri multiremote configuration
// Tauri multiremote configuration - create base env for tauri-driver
const baseEnv: Record<string, string> = {};
if (envContext.isSplashEnabled) {
baseEnv.ENABLE_SPLASH_WINDOW = 'true';
}

// The service automatically handles data directory isolation for multiremote instances
capabilities = {
browserA: {
Expand All @@ -178,6 +188,7 @@ if (envContext.isMultiremote) {
'wdio:tauriServiceOptions': {
appBinaryPath: appBinaryPath,
appArgs: ['--browser=A'],
env: baseEnv,
// Enable log capture for logging tests
captureBackendLogs: true,
captureFrontendLogs: true,
Expand All @@ -199,6 +210,7 @@ if (envContext.isMultiremote) {
'wdio:tauriServiceOptions': {
appBinaryPath: appBinaryPath,
appArgs: ['--browser=B'],
env: baseEnv,
// Enable log capture for logging tests
captureBackendLogs: true,
captureFrontendLogs: true,
Expand All @@ -211,7 +223,11 @@ if (envContext.isMultiremote) {
},
};
} else {
// Tauri standard configuration
const baseEnv: Record<string, string> = {};
if (envContext.isSplashEnabled) {
baseEnv.ENABLE_SPLASH_WINDOW = 'true';
}

capabilities = [
{
browserName: 'tauri',
Expand All @@ -223,6 +239,7 @@ if (envContext.isMultiremote) {
'wdio:tauriServiceOptions': {
appBinaryPath: appBinaryPath,
appArgs: ['foo', 'bar=baz'],
env: baseEnv,
// Enable log capture for logging tests
captureBackendLogs: true,
captureFrontendLogs: true,
Expand Down
3 changes: 2 additions & 1 deletion fixtures/e2e-apps/tauri/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri Basic App</title>
<title>Tauri E2E Test App</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
Expand Down Expand Up @@ -94,6 +94,7 @@ <h1>🚀 Tauri Basic App</h1>
<div class="info-section">
<p>This is a basic Tauri application for WebDriverIO testing.</p>
<div class="status" id="status">Ready for testing</div>
<button id="switch-main-window" class="switch-main-window" style="display:none">Continue to Main</button>
</div>
</div>

Expand Down
67 changes: 67 additions & 0 deletions fixtures/e2e-apps/tauri/splash.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Splash Screen</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin: 0;
}
.container {
text-align: center;
padding: 2rem;
background: rgba(255,255,255,0.1);
border-radius: 1rem;
}
button {
padding: 12px 24px;
font-size: 1.1rem;
cursor: pointer;
border: none;
border-radius: 8px;
background: linear-gradient(45deg, #61dafb, #21a1f1);
color: white;
}
</style>
</head>
<body>
<div class="container">
<h1>Tauri E2E Test App</h1>
<p>Splash Screen</p>
<button id="switch-main-window" class="switch-main-window">Continue</button>
</div>

<!-- CRITICAL: Load plugin JS explicitly for Tauri v2 dynamic windows -->
<script type="module">
import '@wdio/tauri-plugin';

// Debug: Verify plugin loaded
console.log('[Splash] wdioTauri available:', !!window.wdioTauri);
console.log('[Splash] __TAURI__ available:', !!window.__TAURI__);
</script>

<script type="module">
document.addEventListener('DOMContentLoaded', () => {
const btn = document.getElementById('switch-main-window');
if (!btn) return;

btn.addEventListener('click', async () => {
if (window.__TAURI__?.core?.invoke) {
try {
await window.__TAURI__.core.invoke('switch_to_main');
console.log('[Splash] switch_to_main invoked successfully');
} catch (e) {
console.error('[Splash] switch_to_main failed:', e);
}
}
});
});
</script>
</body>
</html>
Loading
Loading