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:
parent
407e49cc32
commit
6a8181f33a
42 changed files with 630 additions and 541 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue