agent-orchestrator/tests/e2e/specs/phase-d-settings.test.ts
Hibryda 56971c3f27 test(e2e): add Phase D/E/F specs covering new architecture (54 tests)
Phase D — Settings & Error Handling:
- D1: Settings panel 6-category tabs, search, active highlighting
- D2: Appearance settings (themes, fonts, cursor, scrollback)
- D3: Theme Editor (color pickers, groups, save/cancel)
- D4: Toast notifications, notification center bell/dropdown
- D5: Error states (no loadError warnings, status bar)

Phase E — Agents & Health:
- E1: ProjectBox tab bar (7+ tabs, PERSISTED-LAZY switching)
- E2: Agent session UI (prompt input, context meter, cost)
- E3: Provider configuration (panels, capabilities, toggles)
- E4: Status bar fleet state (counts, cost, attention queue)
- E5: Project health indicators (status dot, CWD, pressure, burn rate)
- E6: Metrics tab (fleet aggregates, health cards, Live/History)
- E7: Conflict detection (no false badges on fresh launch)
- E8: Audit log (manager-only tab, toolbar, entries)

Phase F — Search & LLM Quality:
- F1: Search overlay (Ctrl+Shift+F, input, empty state, close)
- F2: Context tab & anchors (visualization, budget scale)
- F3: SSH tab (connection list, add button)
- F4-F7: LLM-judged quality (settings completeness, theme editor,
  error messages, overall UI consistency)
2026-03-18 03:20:37 +01:00

227 lines
9.5 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';
// 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 browser.execute(() => {
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 browser.execute(() => 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 browser.execute(() => {
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 browser.execute((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 browser.execute(() => { 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 browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() => document.querySelector('.stepper span')?.textContent ?? '');
const sizeBefore = parseInt(before as string, 10);
await browser.execute(() => {
const btns = document.querySelectorAll('.stepper button');
if (btns.length >= 2) (btns[1] as HTMLElement).click(); // + button
});
await browser.pause(300);
const after = await browser.execute(() => document.querySelector('.stepper span')?.textContent ?? '');
expect(parseInt(after as string, 10)).toBe(sizeBefore + 1);
// Revert
await browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() =>
document.querySelector('.segmented button.active')?.textContent?.trim() ?? '',
);
expect(activeText).toBe('Block');
});
it('should show scrollback lines input', async () => {
await browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() => {
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 browser.execute(() =>
Array.from(document.querySelectorAll('.editor .footer .btn')).some(b => b.textContent?.trim() === 'Cancel'),
);
expect(hasCancel).toBe(true);
const hasSave = await browser.execute(() =>
Array.from(document.querySelectorAll('.editor .footer .btn')).some(b => b.textContent?.trim() === 'Save'),
);
expect(hasSave).toBe(true);
});
});