fix(e2e): cross-protocol browser.execute() — works with both WebDriver + CDP

Root cause: WebDriverIO devtools protocol wraps functions in a polyfill
that puts `return` inside eval() (not a function body) → "Illegal return".

Fix: exec() wrapper in helpers/execute.ts converts function args to IIFE
strings before passing to browser.execute(). Works identically on both
WebDriver (Tauri) and CDP/devtools (Electrobun CEF).

- 35 spec files updated (browser.execute → exec)
- 4 config files updated (string-form expressions)
- helpers/actions.ts + assertions.ts updated
- 560 vitest + 116 cargo passing
This commit is contained in:
Hibryda 2026-03-22 06:33:55 +01:00
parent 407e49cc32
commit 6a8181f33a
42 changed files with 630 additions and 541 deletions

View file

@ -1,11 +1,12 @@
import { browser, expect } from '@wdio/globals';
import { exec } from '../helpers/execute.ts';
//
/** Switch to a tab by text content in the first project box. */
async function clickTabByText(tabText: string): Promise<void> {
await browser.execute((text) => {
await exec((text) => {
const tabs = document.querySelectorAll('[data-testid="project-tabs"] .ptab');
for (const tab of tabs) {
if (tab.textContent?.trim() === text) {
@ -19,7 +20,7 @@ async function clickTabByText(tabText: string): Promise<void> {
/** Get the active tab text in the first project box. */
async function getActiveTabText(): Promise<string> {
return browser.execute(() => {
return exec(() => {
const box = document.querySelector('[data-testid="project-box"]');
const active = box?.querySelector('[data-testid="project-tabs"] .ptab.active');
return active?.textContent?.trim() ?? '';
@ -28,7 +29,7 @@ async function getActiveTabText(): Promise<string> {
/** Check if a tab with given text exists in any project box. */
async function tabExistsWithText(tabText: string): Promise<boolean> {
return browser.execute((text) => {
return exec((text) => {
const tabs = document.querySelectorAll('[data-testid="project-tabs"] .ptab');
return Array.from(tabs).some((t) => t.textContent?.trim() === text);
}, tabText);
@ -39,7 +40,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
before(async () => {
await browser.waitUntil(
async () => {
const count = await browser.execute(() =>
const count = await exec(() =>
document.querySelectorAll('[data-testid="project-box"]').length,
);
return (count as number) >= 1;
@ -50,7 +51,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should show status dot in ProjectHeader', async () => {
const statusDot = await browser.execute(() => {
const statusDot = await exec(() => {
const header = document.querySelector('.project-header');
const dot = header?.querySelector('.status-dot');
return {
@ -63,7 +64,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should show status dot with appropriate state class', async () => {
const dotClass = await browser.execute(() => {
const dotClass = await exec(() => {
const header = document.querySelector('.project-header');
const dot = header?.querySelector('.status-dot');
return dot?.className ?? '';
@ -72,7 +73,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should show CWD path in ProjectHeader info area', async () => {
const cwdText = await browser.execute(() => {
const cwdText = await exec(() => {
const header = document.querySelector('.project-header');
const cwd = header?.querySelector('.info-cwd');
return cwd?.textContent?.trim() ?? '';
@ -81,7 +82,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should have context pressure info element when pressure exists', async () => {
const ctxInfo = await browser.execute(() => {
const ctxInfo = await exec(() => {
const header = document.querySelector('.project-header');
const ctx = header?.querySelector('.info-ctx');
return {
@ -95,7 +96,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should have burn rate info element when agents are active', async () => {
const rateInfo = await browser.execute(() => {
const rateInfo = await exec(() => {
const header = document.querySelector('.project-header');
const rate = header?.querySelector('.info-rate');
return {
@ -109,7 +110,7 @@ describe('Scenario E5 — Project Health Indicators', () => {
});
it('should render ProjectHeader with all structural elements', async () => {
const structure = await browser.execute(() => {
const structure = await exec(() => {
const header = document.querySelector('.project-header');
return {
hasMain: header?.querySelector('.header-main') !== null,
@ -148,7 +149,7 @@ describe('Scenario E6 — Metrics Tab', () => {
await browser.waitUntil(
async () => {
const exists = await browser.execute(() =>
const exists = await exec(() =>
document.querySelector('.metrics-panel') !== null,
);
return exists as boolean;
@ -158,7 +159,7 @@ describe('Scenario E6 — Metrics Tab', () => {
});
it('should show Live view with fleet aggregates', async () => {
const liveView = await browser.execute(() => {
const liveView = await exec(() => {
const panel = document.querySelector('.metrics-panel');
const live = panel?.querySelector('.live-view');
const aggBar = panel?.querySelector('.agg-bar');
@ -172,7 +173,7 @@ describe('Scenario E6 — Metrics Tab', () => {
});
it('should show fleet badges in aggregates bar', async () => {
const badges = await browser.execute(() => {
const badges = await exec(() => {
const panel = document.querySelector('.metrics-panel');
const aggBadges = panel?.querySelectorAll('.agg-badge');
return Array.from(aggBadges ?? []).map((b) => b.textContent?.trim() ?? '');
@ -181,7 +182,7 @@ describe('Scenario E6 — Metrics Tab', () => {
});
it('should show health cards for current project', async () => {
const cardLabels = await browser.execute(() => {
const cardLabels = await exec(() => {
const panel = document.querySelector('.metrics-panel');
const labels = panel?.querySelectorAll('.hc-label');
return Array.from(labels ?? []).map((l) => l.textContent?.trim() ?? '');
@ -190,7 +191,7 @@ describe('Scenario E6 — Metrics Tab', () => {
});
it('should show view tabs for Live and History toggle', async () => {
const viewTabs = await browser.execute(() => {
const viewTabs = await exec(() => {
const panel = document.querySelector('.metrics-panel');
const tabs = panel?.querySelectorAll('.vtab');
return Array.from(tabs ?? []).map((t) => t.textContent?.trim() ?? '');
@ -206,7 +207,7 @@ describe('Scenario E6 — Metrics Tab', () => {
describe('Scenario E7 — Conflict Detection UI', () => {
it('should NOT show external write badge on fresh launch', async () => {
const hasExternalBadge = await browser.execute(() => {
const hasExternalBadge = await exec(() => {
const headers = document.querySelectorAll('.project-header');
for (const header of headers) {
const ext = header.querySelector('.info-conflict-external');
@ -218,7 +219,7 @@ describe('Scenario E7 — Conflict Detection UI', () => {
});
it('should NOT show agent conflict badge on fresh launch', async () => {
const hasConflictBadge = await browser.execute(() => {
const hasConflictBadge = await exec(() => {
const headers = document.querySelectorAll('.project-header');
for (const header of headers) {
const conflict = header.querySelector('.info-conflict:not(.info-conflict-external)');
@ -230,7 +231,7 @@ describe('Scenario E7 — Conflict Detection UI', () => {
});
it('should NOT show file conflict count in status bar on fresh launch', async () => {
const hasConflict = await browser.execute(() => {
const hasConflict = await exec(() => {
const bar = document.querySelector('[data-testid="status-bar"]');
const conflictEl = bar?.querySelector('.state-conflict');
return conflictEl !== null;
@ -242,7 +243,7 @@ describe('Scenario E7 — Conflict Detection UI', () => {
describe('Scenario E8 — Audit Log Tab', () => {
it('should show Audit tab only for manager role projects', async () => {
const auditTabInfo = await browser.execute(() => {
const auditTabInfo = await exec(() => {
const boxes = document.querySelectorAll('[data-testid="project-box"]');
const results: { projectId: string; hasAudit: boolean }[] = [];
for (const box of boxes) {
@ -261,7 +262,7 @@ describe('Scenario E8 — Audit Log Tab', () => {
});
it('should render audit log content when Audit tab is activated', async () => {
const auditProjectId = await browser.execute(() => {
const auditProjectId = await exec(() => {
const boxes = document.querySelectorAll('[data-testid="project-box"]');
for (const box of boxes) {
const tabs = box.querySelectorAll('[data-testid="project-tabs"] .ptab');
@ -277,7 +278,7 @@ describe('Scenario E8 — Audit Log Tab', () => {
if (!auditProjectId) return; // No manager agent — skip
await browser.pause(500);
const auditContent = await browser.execute(() => {
const auditContent = await exec(() => {
const tab = document.querySelector('.audit-log-tab');
if (!tab) return { exists: false, hasToolbar: false, hasEntries: false };
return {