import { browser, expect } from '@wdio/globals'; // Phase C — Tab-Based Feature Tests (C5-C9) // Settings panel, project health, metrics tab, context tab, files tab. // --- Helpers --- /** Get all project box IDs currently rendered. */ async function getProjectIds(): Promise { return browser.execute(() => { const boxes = document.querySelectorAll('[data-testid="project-box"]'); return Array.from(boxes).map( (b) => b.getAttribute('data-project-id') ?? '', ).filter(Boolean); }); } /** Switch to a tab in a specific project box. */ async function switchProjectTab(projectId: string, tabIndex: number): Promise { await browser.execute((id, idx) => { const box = document.querySelector(`[data-project-id="${id}"]`); const tabs = box?.querySelectorAll('[data-testid="project-tabs"] .ptab'); if (tabs && tabs[idx]) (tabs[idx] as HTMLElement).click(); }, projectId, tabIndex); await browser.pause(300); } // --- Scenario C5: Settings Panel Sections --- describe('Scenario C5 — Settings Panel Sections', () => { before(async () => { // Close sidebar panel if open const panel = await browser.$('.sidebar-panel'); if (await panel.isDisplayed().catch(() => false)) { await browser.execute(() => { const btn = document.querySelector('.panel-close'); if (btn) (btn as HTMLElement).click(); }); await browser.pause(300); } // Open settings await browser.execute(() => { const btn = document.querySelector('[data-testid="settings-btn"]'); if (btn) (btn as HTMLElement).click(); }); await browser.pause(500); }); it('should show Appearance section with theme dropdown', async () => { const hasTheme = await browser.execute(() => { const panel = document.querySelector('.sidebar-panel .settings-panel'); if (!panel) return false; const text = panel.textContent ?? ''; return text.toLowerCase().includes('theme') || text.toLowerCase().includes('appearance'); }); expect(hasTheme).toBe(true); }); it('should show font settings (UI font and Terminal font)', async () => { const hasFonts = await browser.execute(() => { const panel = document.querySelector('.sidebar-panel .settings-panel'); if (!panel) return false; const text = panel.textContent ?? ''; return text.toLowerCase().includes('font'); }); expect(hasFonts).toBe(true); }); it('should show default shell setting in Agents category', async () => { // Switch to Agents category which contains shell settings const hasShell = await browser.execute(() => { // Check across all settings categories const panel = document.querySelector('.sidebar-panel .settings-panel'); if (!panel) return false; const text = panel.textContent ?? ''; return text.toLowerCase().includes('shell') || text.toLowerCase().includes('agent'); }); expect(hasShell).toBe(true); }); it('should have theme dropdown with many themes', async () => { // Click the theme dropdown const opened = await browser.execute(() => { const btn = document.querySelector('.appearance .custom-dropdown .dropdown-btn'); if (btn) { (btn as HTMLElement).click(); return true; } return false; }); if (opened) { await browser.pause(300); const optionCount = await browser.execute(() => { return document.querySelectorAll('.dropdown-menu .dropdown-item').length; }); expect(optionCount).toBeGreaterThanOrEqual(15); // Close dropdown await browser.execute(() => { const btn = document.querySelector('.appearance .custom-dropdown .dropdown-btn'); if (btn) (btn as HTMLElement).click(); }); await browser.pause(200); } }); after(async () => { // Close settings await browser.execute(() => { const btn = document.querySelector('.panel-close'); if (btn) (btn as HTMLElement).click(); }); await browser.pause(300); }); }); // --- Scenario C6: Project Health Indicators --- describe('Scenario C6 — Project Health Indicators', () => { it('should show status dots on project headers', async () => { const hasDots = await browser.execute(() => { const dots = document.querySelectorAll('.project-header .status-dot'); return dots.length; }); // At least one project should have a status dot expect(hasDots).toBeGreaterThanOrEqual(1); }); it('should show idle status when no agents running', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const dotColor = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const dot = box?.querySelector('.status-dot'); if (!dot) return 'not-found'; const style = window.getComputedStyle(dot); return style.backgroundColor || style.color || 'unknown'; }, ids[0]); // Should have some color value (not 'not-found') expect(dotColor).not.toBe('not-found'); }); it('should show status bar agent counts', async () => { const counts = await browser.execute(() => { const bar = document.querySelector('[data-testid="status-bar"]'); if (!bar) return ''; return bar.textContent ?? ''; }); // Should contain project count at minimum expect(counts).toMatch(/project|idle|running|stalled|\d/i); }); }); // --- Scenario C7: Metrics Tab --- describe('Scenario C7 — Metrics Tab', () => { it('should show Metrics tab in project tab bar', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const hasMetrics = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const tabs = box?.querySelectorAll('[data-testid="project-tabs"] .ptab'); if (!tabs) return false; return Array.from(tabs).some(t => t.textContent?.trim().toLowerCase().includes('metric')); }, ids[0]); expect(hasMetrics).toBe(true); }); it('should render Metrics panel content when tab clicked', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const projectId = ids[0]; // Find and click Metrics tab await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const tabs = box?.querySelectorAll('[data-testid="project-tabs"] .ptab'); if (!tabs) return; for (const tab of tabs) { if (tab.textContent?.trim().toLowerCase().includes('metric')) { (tab as HTMLElement).click(); break; } } }, projectId); await browser.pause(500); const hasContent = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const panel = box?.querySelector('.metrics-panel, .metrics-tab'); return panel !== null; }, projectId); expect(hasContent).toBe(true); // Switch back to Model tab await switchProjectTab(projectId, 0); }); }); // --- Scenario C8: Context Tab --- describe('Scenario C8 — Context Tab Visualization', () => { it('should render Context tab with token meter', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const projectId = ids[0]; // Switch to Context tab (index 2) await switchProjectTab(projectId, 2); const hasContextUI = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const ctx = box?.querySelector('.context-tab, .context-stats, .token-meter, .stat-value'); return ctx !== null; }, projectId); expect(hasContextUI).toBe(true); // Switch back to Model tab await switchProjectTab(projectId, 0); }); }); // --- Scenario C9: Files Tab with Editor --- describe('Scenario C9 — Files Tab & Code Editor', () => { it('should render Files tab with directory tree', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const projectId = ids[0]; // Switch to Files tab (index 3) await switchProjectTab(projectId, 3); await browser.pause(500); const hasTree = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const tree = box?.querySelector('.file-tree, .directory-tree, .files-tab'); return tree !== null; }, projectId); expect(hasTree).toBe(true); }); it('should list files from the project directory', async () => { const ids = await getProjectIds(); if (ids.length < 1) return; const fileNames = await browser.execute((id) => { const box = document.querySelector(`[data-project-id="${id}"]`); const items = box?.querySelectorAll('.tree-name'); return Array.from(items ?? []).map(el => el.textContent?.trim() ?? ''); }, ids[0]); // Test project has README.md and hello.py const hasFiles = fileNames.some(f => f.includes('README') || f.includes('hello') || f.includes('.py') || f.includes('.md'), ); expect(hasFiles).toBe(true); // Switch back to Model tab await switchProjectTab(ids[0], 0); }); });