import { browser, expect } from '@wdio/globals'; // /** Switch to a tab by text content in the first project box. */ async function clickTabByText(tabText: string): Promise { await browser.execute((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); } /** Get the active tab text in the first project box. */ async function getActiveTabText(): Promise { return browser.execute(() => { const box = document.querySelector('[data-testid="project-box"]'); const active = box?.querySelector('[data-testid="project-tabs"] .ptab.active'); return active?.textContent?.trim() ?? ''; }); } /** Check if a tab with given text exists in any project box. */ async function tabExistsWithText(tabText: string): Promise { return browser.execute((text) => { const tabs = document.querySelectorAll('[data-testid="project-tabs"] .ptab'); return Array.from(tabs).some((t) => t.textContent?.trim() === text); }, tabText); } describe('Scenario E5 — Project Health Indicators', () => { before(async () => { await browser.waitUntil( async () => { const count = await browser.execute(() => 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 show status dot in ProjectHeader', async () => { const statusDot = await browser.execute(() => { const header = document.querySelector('.project-header'); const dot = header?.querySelector('.status-dot'); return { exists: dot !== null, classes: dot?.className ?? '', }; }); expect(statusDot.exists).toBe(true); expect(statusDot.classes).toContain('status-dot'); }); it('should show status dot with appropriate state class', async () => { const dotClass = await browser.execute(() => { const header = document.querySelector('.project-header'); const dot = header?.querySelector('.status-dot'); return dot?.className ?? ''; }); expect(dotClass).toContain('status-dot'); }); it('should show CWD path in ProjectHeader info area', async () => { const cwdText = await browser.execute(() => { const header = document.querySelector('.project-header'); const cwd = header?.querySelector('.info-cwd'); return cwd?.textContent?.trim() ?? ''; }); expect(cwdText.length).toBeGreaterThan(0); }); it('should have context pressure info element when pressure exists', async () => { const ctxInfo = await browser.execute(() => { const header = document.querySelector('.project-header'); const ctx = header?.querySelector('.info-ctx'); return { exists: ctx !== null, text: ctx?.textContent?.trim() ?? '', }; }); if (ctxInfo.exists) { expect(ctxInfo.text).toMatch(/ctx \d+%/); } }); it('should have burn rate info element when agents are active', async () => { const rateInfo = await browser.execute(() => { const header = document.querySelector('.project-header'); const rate = header?.querySelector('.info-rate'); return { exists: rate !== null, text: rate?.textContent?.trim() ?? '', }; }); if (rateInfo.exists) { expect(rateInfo.text).toMatch(/\$[\d.]+\/hr/); } }); it('should render ProjectHeader with all structural elements', async () => { const structure = await browser.execute(() => { const header = document.querySelector('.project-header'); return { hasMain: header?.querySelector('.header-main') !== null, hasInfo: header?.querySelector('.header-info') !== null, hasStatusDot: header?.querySelector('.status-dot') !== null, hasIcon: header?.querySelector('.project-icon') !== null, hasName: header?.querySelector('.project-name') !== null, hasCwd: header?.querySelector('.info-cwd') !== null, }; }); expect(structure.hasMain).toBe(true); expect(structure.hasInfo).toBe(true); expect(structure.hasStatusDot).toBe(true); expect(structure.hasIcon).toBe(true); expect(structure.hasName).toBe(true); expect(structure.hasCwd).toBe(true); }); }); describe('Scenario E6 — Metrics Tab', () => { before(async () => { await clickTabByText('Model'); await browser.pause(200); }); it('should show Metrics tab button in project tab bar', async () => { const hasMetrics = await tabExistsWithText('Metrics'); expect(hasMetrics).toBe(true); }); it('should switch to Metrics tab and show metrics panel', async () => { await clickTabByText('Metrics'); const activeTab = await getActiveTabText(); expect(activeTab).toBe('Metrics'); await browser.waitUntil( async () => { const exists = await browser.execute(() => document.querySelector('.metrics-panel') !== null, ); return exists as boolean; }, { timeout: 5000, timeoutMsg: 'Metrics panel did not render within 5s' }, ); }); it('should show Live view with fleet aggregates', async () => { const liveView = await browser.execute(() => { const panel = document.querySelector('.metrics-panel'); const live = panel?.querySelector('.live-view'); const aggBar = panel?.querySelector('.agg-bar'); return { hasLive: live !== null, hasAgg: aggBar !== null, }; }); expect(liveView.hasLive).toBe(true); expect(liveView.hasAgg).toBe(true); }); it('should show fleet badges in aggregates bar', async () => { const badges = await browser.execute(() => { const panel = document.querySelector('.metrics-panel'); const aggBadges = panel?.querySelectorAll('.agg-badge'); return Array.from(aggBadges ?? []).map((b) => b.textContent?.trim() ?? ''); }); expect(badges.length).toBeGreaterThanOrEqual(1); }); it('should show health cards for current project', async () => { const cardLabels = await browser.execute(() => { const panel = document.querySelector('.metrics-panel'); const labels = panel?.querySelectorAll('.hc-label'); return Array.from(labels ?? []).map((l) => l.textContent?.trim() ?? ''); }); expect(cardLabels).toContain('Status'); }); it('should show view tabs for Live and History toggle', async () => { const viewTabs = await browser.execute(() => { const panel = document.querySelector('.metrics-panel'); const tabs = panel?.querySelectorAll('.vtab'); return Array.from(tabs ?? []).map((t) => t.textContent?.trim() ?? ''); }); expect(viewTabs.length).toBeGreaterThanOrEqual(2); }); after(async () => { await clickTabByText('Model'); }); }); describe('Scenario E7 — Conflict Detection UI', () => { it('should NOT show external write badge on fresh launch', async () => { const hasExternalBadge = await browser.execute(() => { const headers = document.querySelectorAll('.project-header'); for (const header of headers) { const ext = header.querySelector('.info-conflict-external'); if (ext) return true; } return false; }); expect(hasExternalBadge).toBe(false); }); it('should NOT show agent conflict badge on fresh launch', async () => { const hasConflictBadge = await browser.execute(() => { const headers = document.querySelectorAll('.project-header'); for (const header of headers) { const conflict = header.querySelector('.info-conflict:not(.info-conflict-external)'); if (conflict) return true; } return false; }); expect(hasConflictBadge).toBe(false); }); it('should NOT show file conflict count in status bar on fresh launch', async () => { const hasConflict = await browser.execute(() => { const bar = document.querySelector('[data-testid="status-bar"]'); const conflictEl = bar?.querySelector('.state-conflict'); return conflictEl !== null; }); expect(hasConflict).toBe(false); }); }); describe('Scenario E8 — Audit Log Tab', () => { it('should show Audit tab only for manager role projects', async () => { const auditTabInfo = await browser.execute(() => { const boxes = document.querySelectorAll('[data-testid="project-box"]'); const results: { projectId: string; hasAudit: boolean }[] = []; for (const box of boxes) { const id = box.getAttribute('data-project-id') ?? ''; const tabs = box.querySelectorAll('[data-testid="project-tabs"] .ptab'); const hasAudit = Array.from(tabs).some((t) => t.textContent?.trim() === 'Audit'); results.push({ projectId: id, hasAudit }); } return results; }); expect(auditTabInfo.length).toBeGreaterThanOrEqual(1); for (const info of auditTabInfo) { expect(typeof info.hasAudit).toBe('boolean'); } }); it('should render audit log content when Audit tab is activated', async () => { const auditProjectId = await browser.execute(() => { const boxes = document.querySelectorAll('[data-testid="project-box"]'); for (const box of boxes) { const tabs = box.querySelectorAll('[data-testid="project-tabs"] .ptab'); const auditTab = Array.from(tabs).find((t) => t.textContent?.trim() === 'Audit'); if (auditTab) { (auditTab as HTMLElement).click(); return box.getAttribute('data-project-id') ?? ''; } } return ''; }); if (!auditProjectId) return; // No manager agent — skip await browser.pause(500); const auditContent = await browser.execute(() => { const tab = document.querySelector('.audit-log-tab'); if (!tab) return { exists: false, hasToolbar: false, hasEntries: false }; return { exists: true, hasToolbar: tab.querySelector('.audit-toolbar') !== null, hasEntries: tab.querySelector('.audit-entries') !== null, }; }); expect(auditContent.exists).toBe(true); expect(auditContent.hasToolbar).toBe(true); expect(auditContent.hasEntries).toBe(true); await clickTabByText('Model'); }); });