import { browser, expect } from '@wdio/globals'; import { exec } from '../helpers/execute.ts'; // Phase E — Part 1: Multi-agent orchestration, project tabs, and provider UI. // Tests ProjectBox tab bar, AgentPane state, provider config, status bar fleet state. // ─── Helpers ────────────────────────────────────────────────────────── async function clickTabByText(tabText: string): Promise { await exec((text) => { const tabs = document.querySelectorAll('[data-testid="project-tabs"] .ptab'); for (const tab of tabs) { if (tab.textContent?.trim() === text) { (tab as HTMLElement).click(); return; } } }, tabText); await browser.pause(400); } async function getActiveTabText(): Promise { return exec(() => { const box = document.querySelector('[data-testid="project-box"]'); const active = box?.querySelector('[data-testid="project-tabs"] .ptab.active'); return active?.textContent?.trim() ?? ''; }); } // ─── Scenario E1: ProjectBox Tab Bar ───────────────────────────────── describe('Scenario E1 — ProjectBox Tab Bar', () => { before(async () => { await browser.waitUntil( async () => { const count = await exec(() => document.querySelectorAll('[data-testid="project-box"]').length, ); return (count as number) >= 1; }, { timeout: 10_000, timeoutMsg: 'No project boxes rendered within 10s' }, ); await clickTabByText('Model'); }); it('should render project-level tab bar with at least 7 tabs', async () => { const tabCount = await exec(() => { const box = document.querySelector('[data-testid="project-box"]'); return box?.querySelectorAll('[data-testid="project-tabs"] .ptab')?.length ?? 0; }); expect(tabCount).toBeGreaterThanOrEqual(7); }); it('should include expected base tab labels', async () => { const tabTexts = await exec(() => { const box = document.querySelector('[data-testid="project-box"]'); const tabs = box?.querySelectorAll('[data-testid="project-tabs"] .ptab'); return Array.from(tabs ?? []).map((t) => t.textContent?.trim() ?? ''); }); for (const label of ['Model', 'Docs', 'Context', 'Files', 'SSH', 'Memory', 'Metrics']) { expect(tabTexts).toContain(label); } }); it('should switch to Files tab and activate it', async () => { await clickTabByText('Files'); expect(await getActiveTabText()).toBe('Files'); }); it('should switch to Memory tab and activate it', async () => { await clickTabByText('Memory'); expect(await getActiveTabText()).toBe('Memory'); }); it('should switch back to Model and show agent session pane', async () => { await clickTabByText('Model'); expect(await getActiveTabText()).toBe('Model'); const session = await browser.$('[data-testid="agent-session"]'); await expect(session).toBeDisplayed(); }); it('should use PERSISTED-LAZY mount (Files content persists across tab switches)', async () => { await clickTabByText('Files'); await browser.pause(300); await clickTabByText('Model'); await browser.pause(200); await clickTabByText('Files'); expect(await getActiveTabText()).toBe('Files'); // Content panes should still be in DOM (display toggled, not unmounted) const paneCount = await exec(() => { const box = document.querySelector('[data-testid="project-box"]'); return box?.querySelectorAll('.content-pane')?.length ?? 0; }); expect(paneCount).toBeGreaterThan(1); await clickTabByText('Model'); }); }); // ─── Scenario E2: Agent Session UI ─────────────────────────────────── describe('Scenario E2 — Agent Session UI', () => { before(async () => { await clickTabByText('Model'); await browser.pause(300); }); it('should show agent pane with prompt input area', async () => { const pane = await browser.$('[data-testid="agent-pane"]'); await expect(pane).toBeExisting(); const prompt = await browser.$('[data-testid="agent-prompt"]'); await expect(prompt).toBeExisting(); }); it('should show submit button', async () => { const btn = await browser.$('[data-testid="agent-submit"]'); await expect(btn).toBeExisting(); }); it('should show agent messages area', async () => { const messages = await browser.$('[data-testid="agent-messages"]'); await expect(messages).toBeExisting(); }); it('should show agent pane in idle status initially', async () => { const status = await exec(() => { const el = document.querySelector('[data-testid="agent-pane"]'); return el?.getAttribute('data-agent-status') ?? 'unknown'; }); expect(status).toBe('idle'); }); it('should show CWD in ProjectHeader', async () => { const cwd = await exec(() => { const header = document.querySelector('.project-header'); return header?.querySelector('.info-cwd')?.textContent?.trim() ?? ''; }); expect(cwd.length).toBeGreaterThan(0); }); it('should show profile name in ProjectHeader if configured', async () => { const profileInfo = await exec(() => { const el = document.querySelector('.project-header .info-profile'); return { exists: el !== null, text: el?.textContent?.trim() ?? '' }; }); if (profileInfo.exists) { expect(profileInfo.text.length).toBeGreaterThan(0); } }); }); // ─── Scenario E3: Provider Configuration ───────────────────────────── describe('Scenario E3 — Provider Configuration', () => { before(async () => { await exec(() => { const btn = document.querySelector('[data-testid="settings-btn"]'); if (btn) (btn as HTMLElement).click(); }); const panel = await browser.$('.sidebar-panel'); await panel.waitForDisplayed({ timeout: 5000 }); await browser.pause(500); }); it('should show Providers section in Settings', async () => { const hasProviders = await exec(() => { const headers = document.querySelectorAll('.settings-section h2'); return Array.from(headers).some((h) => h.textContent?.trim() === 'Providers'); }); expect(hasProviders).toBe(true); }); it('should show at least one provider panel', async () => { const count = await exec(() => document.querySelectorAll('.provider-panel').length, ); expect(count).toBeGreaterThanOrEqual(1); }); it('should show provider name in panel header', async () => { const name = await exec(() => { const panel = document.querySelector('.provider-panel'); return panel?.querySelector('.provider-name')?.textContent?.trim() ?? ''; }); expect(name.length).toBeGreaterThan(0); }); it('should expand provider panel to show enabled toggle', async () => { await exec(() => { const header = document.querySelector('.provider-header'); if (header) (header as HTMLElement).click(); }); await browser.pause(300); const hasToggle = await exec(() => { const body = document.querySelector('.provider-body'); return body?.querySelector('.toggle-switch') !== null; }); expect(hasToggle).toBe(true); }); after(async () => { await exec(() => { const header = document.querySelector('.provider-header'); const expanded = document.querySelector('.provider-body'); if (expanded && header) (header as HTMLElement).click(); }); await browser.pause(200); await browser.keys('Escape'); const panel = await browser.$('.sidebar-panel'); await panel.waitForDisplayed({ timeout: 3000, reverse: true }); }); }); // ─── Scenario E4: Status Bar Fleet State ───────────────────────────── describe('Scenario E4 — Status Bar Fleet State', () => { it('should render status bar', async () => { const bar = await browser.$('[data-testid="status-bar"]'); await expect(bar).toBeDisplayed(); }); it('should show project count', async () => { const text = await exec(() => { return document.querySelector('[data-testid="status-bar"]')?.textContent ?? ''; }); expect(text).toMatch(/\d+ projects/); }); it('should show agent state or project info', async () => { const hasState = await exec(() => { const text = document.querySelector('[data-testid="status-bar"]')?.textContent ?? ''; return text.includes('idle') || text.includes('running') || text.includes('projects'); }); expect(hasState).toBe(true); }); it('should not show burn rate when all agents idle', async () => { const burnRate = await exec(() => { const bar = document.querySelector('[data-testid="status-bar"]'); return bar?.querySelector('.burn-rate')?.textContent ?? null; }); if (burnRate !== null) { expect(burnRate).toMatch(/\$0|0\.00/); } }); it('should show notification center bell', async () => { const bell = await browser.$('[data-testid="notification-bell"]'); await expect(bell).toBeExisting(); }); it('should conditionally show attention queue button', async () => { const info = await exec(() => { const btn = document.querySelector('[data-testid="status-bar"] .attention-btn'); return { exists: btn !== null, text: btn?.textContent?.trim() ?? '' }; }); if (info.exists) { expect(info.text).toContain('attention'); } }); });