agent-orchestrator/tests/e2e/specs/phase-d-settings.test.ts
Hibryda 6a8181f33a 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
2026-03-22 06:33:55 +01:00

228 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { browser, expect } from '@wdio/globals';
import { exec } from '../helpers/execute.ts';
// Phase D — Settings Panel Tests (D1D3)
// Tests the redesigned VS Code-style settings panel with 6+1 category tabs,
// appearance controls, and theme editor.
// ─── Helpers ──────────────────────────────────────────────────────────
async function openSettings(): Promise<void> {
const panel = await browser.$('.settings-panel');
if (!(await panel.isDisplayed().catch(() => false))) {
await exec(() => {
const btn = document.querySelector('[data-testid="settings-btn"]');
if (btn) (btn as HTMLElement).click();
});
await (await browser.$('.sidebar-panel')).waitForDisplayed({ timeout: 5000 });
}
await browser.waitUntil(
async () => (await exec(() => document.querySelectorAll('.settings-panel').length) as number) >= 1,
{ timeout: 5000, timeoutMsg: 'Settings panel did not render within 5s' },
);
await browser.pause(300);
}
async function closeSettings(): Promise<void> {
const panel = await browser.$('.sidebar-panel');
if (await panel.isDisplayed().catch(() => false)) {
await exec(() => {
const btn = document.querySelector('.settings-close') || document.querySelector('.panel-close');
if (btn) (btn as HTMLElement).click();
});
await browser.pause(500);
}
}
async function clickCategory(label: string): Promise<void> {
await exec((lbl) => {
const items = document.querySelectorAll('.sidebar-item');
for (const el of items) {
if (el.textContent?.includes(lbl)) { (el as HTMLElement).click(); return; }
}
}, label);
await browser.pause(300);
}
async function scrollToTop(): Promise<void> {
await exec(() => { document.querySelector('.settings-content')?.scrollTo(0, 0); });
await browser.pause(200);
}
// ─── Scenario D1: Settings Panel Categories ──────────────────────────
describe('Scenario D1 — Settings Panel Categories', () => {
before(async () => { await openSettings(); });
after(async () => { await closeSettings(); });
it('should render settings sidebar with 6+ category buttons', async () => {
const sidebar = await browser.$('.settings-sidebar');
await expect(sidebar).toBeDisplayed();
const items = await browser.$$('.sidebar-item');
expect(items.length).toBeGreaterThanOrEqual(6);
});
it('should switch between all 6 categories', async () => {
for (const cat of ['Appearance', 'Agents', 'Security', 'Projects', 'Orchestration', 'Advanced']) {
await clickCategory(cat);
const content = await browser.$('.settings-content');
await expect(content).toBeDisplayed();
}
await clickCategory('Appearance');
});
it('should highlight active category with blue accent', async () => {
await clickCategory('Agents');
const activeItem = await browser.$('.sidebar-item.active');
await expect(activeItem).toBeExisting();
expect(await activeItem.getText()).toContain('Agents');
await clickCategory('Appearance');
});
it('should show search bar and filter results', async () => {
await expect(await browser.$('.settings-search')).toBeDisplayed();
await exec(() => {
const input = document.querySelector('.settings-search') as HTMLInputElement;
if (input) { input.value = 'font'; input.dispatchEvent(new Event('input', { bubbles: true })); }
});
await browser.pause(500);
const results = await browser.$$('.search-result');
expect(results.length).toBeGreaterThan(0);
const hasFont = await exec(() => {
const labels = document.querySelectorAll('.search-result .sr-label');
return Array.from(labels).some(l => l.textContent?.toLowerCase().includes('font'));
});
expect(hasFont).toBe(true);
// Clear search
await exec(() => {
const input = document.querySelector('.settings-search') as HTMLInputElement;
if (input) { input.value = ''; input.dispatchEvent(new Event('input', { bubbles: true })); }
});
await browser.pause(300);
});
});
// ─── Scenario D2: Appearance Settings ────────────────────────────────
describe('Scenario D2 — Appearance Settings', () => {
before(async () => { await openSettings(); await clickCategory('Appearance'); await scrollToTop(); });
after(async () => { await closeSettings(); });
it('should show theme dropdown with 17+ built-in themes grouped by category', async () => {
await exec(() => {
const btn = document.querySelector('.appearance .custom-dropdown .dropdown-btn');
if (btn) (btn as HTMLElement).click();
});
await browser.pause(500);
const groupLabels = await browser.$$('.theme-menu .group-label');
expect(groupLabels.length).toBeGreaterThanOrEqual(3);
const items = await browser.$$('.theme-menu .dropdown-item');
expect(items.length).toBeGreaterThanOrEqual(17);
// Close dropdown
await exec(() => {
const btn = document.querySelector('.appearance .custom-dropdown .dropdown-btn');
if (btn) (btn as HTMLElement).click();
});
await browser.pause(300);
});
it('should show font size steppers with -/+ buttons', async () => {
const steppers = await browser.$$('.stepper');
expect(steppers.length).toBeGreaterThanOrEqual(1);
const before = await exec(() => document.querySelector('.stepper span')?.textContent ?? '');
const sizeBefore = parseInt(before as string, 10);
await exec(() => {
const btns = document.querySelectorAll('.stepper button');
if (btns.length >= 2) (btns[1] as HTMLElement).click(); // + button
});
await browser.pause(300);
const after = await exec(() => document.querySelector('.stepper span')?.textContent ?? '');
expect(parseInt(after as string, 10)).toBe(sizeBefore + 1);
// Revert
await exec(() => {
const btns = document.querySelectorAll('.stepper button');
if (btns.length >= 1) (btns[0] as HTMLElement).click();
});
await browser.pause(200);
});
it('should show terminal cursor style selector (Block/Line/Underline)', async () => {
await exec(() => {
document.getElementById('setting-cursor-style')?.scrollIntoView({ behavior: 'instant', block: 'center' });
});
await browser.pause(300);
const segmented = await browser.$('.segmented');
await expect(segmented).toBeDisplayed();
const buttons = await browser.$$('.segmented button');
expect(buttons.length).toBe(3);
const activeText = await exec(() =>
document.querySelector('.segmented button.active')?.textContent?.trim() ?? '',
);
expect(activeText).toBe('Block');
});
it('should show scrollback lines input', async () => {
await exec(() => {
document.getElementById('setting-scrollback')?.scrollIntoView({ behavior: 'instant', block: 'center' });
});
await browser.pause(300);
const input = await browser.$('#setting-scrollback input[type="number"]');
await expect(input).toBeExisting();
const num = parseInt(await input.getValue() as string, 10);
expect(num).toBeGreaterThanOrEqual(100);
expect(num).toBeLessThanOrEqual(100000);
});
});
// ─── Scenario D3: Theme Editor ───────────────────────────────────────
describe('Scenario D3 — Theme Editor', () => {
before(async () => { await openSettings(); await clickCategory('Appearance'); await scrollToTop(); });
after(async () => {
await exec(() => {
const btn = Array.from(document.querySelectorAll('.editor .btn'))
.find(b => b.textContent?.trim() === 'Cancel');
if (btn) (btn as HTMLElement).click();
});
await browser.pause(300);
await closeSettings();
});
it('should show "+ New Custom Theme" button', async () => {
const btn = await browser.$('.new-theme-btn');
await expect(btn).toBeDisplayed();
expect(await btn.getText()).toContain('New Custom Theme');
});
it('should open theme editor with color pickers when clicked', async () => {
await exec(() => {
const btn = document.querySelector('.new-theme-btn');
if (btn) (btn as HTMLElement).click();
});
await browser.pause(500);
const editor = await browser.$('.editor');
await expect(editor).toBeDisplayed();
const colorInputs = await browser.$$('.editor input[type="color"]');
expect(colorInputs.length).toBeGreaterThan(0);
const nameInput = await browser.$('.editor .name-input');
await expect(nameInput).toBeExisting();
});
it('should show 26 color pickers grouped in Accents and Neutrals', async () => {
const groups = await browser.$$('.editor details.group');
expect(groups.length).toBe(2);
const colorRows = await browser.$$('.editor .color-row');
expect(colorRows.length).toBe(26);
});
it('should have Cancel and Save buttons', async () => {
const hasCancel = await exec(() =>
Array.from(document.querySelectorAll('.editor .footer .btn')).some(b => b.textContent?.trim() === 'Cancel'),
);
expect(hasCancel).toBe(true);
const hasSave = await exec(() =>
Array.from(document.querySelectorAll('.editor .footer .btn')).some(b => b.textContent?.trim() === 'Save'),
);
expect(hasSave).toBe(true);
});
});