From 53fe23766bbff08aa3a2a5039259308fb42294b5 Mon Sep 17 00:00:00 2001 From: ParkerES Date: Fri, 20 Feb 2026 01:36:20 -0500 Subject: [PATCH 01/20] =?UTF-8?q?refactor:=20E2E=20infrastructure=20?= =?UTF-8?q?=E2=80=94=20delete=20old=20specs,=20rewrite=20helpers=20for=20c?= =?UTF-8?q?lick-only=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete 9 old spec files that used programmatic navigation (pushState, location.hash) - Rewrite helpers/navigation.ts with click-only patterns (real sidebar button clicks) - Create helpers/page-helpers.ts with shared interaction patterns (tabs, modals, forms) - Update electron.setup.ts with dotenv loading for .env.test credentials Co-Authored-By: Claude Opus 4.6 --- tests/e2e/app-launch.spec.ts | 90 -------- tests/e2e/auth.spec.ts | 107 ---------- tests/e2e/electron.setup.ts | 13 +- tests/e2e/helpers/navigation.ts | 167 ++++++++++++--- tests/e2e/helpers/page-helpers.ts | 135 ++++++++++++ tests/e2e/navigation.spec.ts | 148 -------------- tests/e2e/project-management.spec.ts | 133 ------------ tests/e2e/routes-toplevel.spec.ts | 219 -------------------- tests/e2e/settings-theme.spec.ts | 278 ------------------------- tests/e2e/sidebar-layout.spec.ts | 242 ---------------------- tests/e2e/smoke.spec.ts | 58 ------ tests/e2e/task-grid.spec.ts | 296 --------------------------- 12 files changed, 290 insertions(+), 1596 deletions(-) delete mode 100644 tests/e2e/app-launch.spec.ts delete mode 100644 tests/e2e/auth.spec.ts create mode 100644 tests/e2e/helpers/page-helpers.ts delete mode 100644 tests/e2e/navigation.spec.ts delete mode 100644 tests/e2e/project-management.spec.ts delete mode 100644 tests/e2e/routes-toplevel.spec.ts delete mode 100644 tests/e2e/settings-theme.spec.ts delete mode 100644 tests/e2e/sidebar-layout.spec.ts delete mode 100644 tests/e2e/smoke.spec.ts delete mode 100644 tests/e2e/task-grid.spec.ts diff --git a/tests/e2e/app-launch.spec.ts b/tests/e2e/app-launch.spec.ts deleted file mode 100644 index 53f9952..0000000 --- a/tests/e2e/app-launch.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * E2E Tests: App Launch - * - * Verifies basic application startup behavior: - * - Window visibility - * - Correct app title - * - Sidebar presence - * - No console errors - */ -import { test, expect } from './electron.setup'; - -test.describe('App Launch', () => { - test('app launches successfully with visible window', async ({ electronApp, mainWindow }) => { - // Verify app is running - expect(electronApp).toBeDefined(); - - // Verify window is visible by checking body has content - const bodyLength = await mainWindow.evaluate(() => document.body.innerHTML.length); - expect(bodyLength).toBeGreaterThan(0); - }); - - test('app title contains ADC', async ({ electronApp }) => { - // Get the app name from Electron - const appName = await electronApp.evaluate(({ app }) => app.getName()); - - // App name should contain adc (case-insensitive) - expect(appName.toLowerCase()).toContain('adc'); - }); - - test('app shows sidebar', async ({ mainWindow }) => { - // The sidebar contains "ADC" text in its header - const sidebarHeader = mainWindow.locator('text=ADC'); - await expect(sidebarHeader).toBeVisible({ timeout: 10_000 }); - - // Also verify the aside element exists (sidebar container) - const sidebarAside = mainWindow.locator('aside'); - await expect(sidebarAside).toBeVisible(); - }); - - test('no console errors on startup', async ({ mainWindow }) => { - const consoleErrors: string[] = []; - - // Collect console errors - mainWindow.on('console', (msg) => { - if (msg.type() === 'error') { - consoleErrors.push(msg.text()); - } - }); - - // Wait a bit for any async errors to appear - await mainWindow.waitForTimeout(2000); - - // Filter out known acceptable errors (e.g., network errors in test env) - const criticalErrors = consoleErrors.filter( - (error) => - // Ignore network-related errors that may occur in test environment - !error.includes('net::ERR_') && - !error.includes('Failed to fetch') && - !error.includes('NetworkError') && - // Ignore React DevTools errors (not installed in test) - !error.includes('Download the React DevTools'), - ); - - expect(criticalErrors).toHaveLength(0); - }); - - test('app window has minimum dimensions', async ({ electronApp }) => { - // Get window bounds - const appWindow = await electronApp.firstWindow(); - const width = await appWindow.evaluate(() => globalThis.innerWidth); - const height = await appWindow.evaluate(() => globalThis.innerHeight); - - // Window should have reasonable minimum dimensions - expect(width).toBeGreaterThan(400); - expect(height).toBeGreaterThan(300); - }); - - test('app loads within acceptable time', async ({ electronApp }) => { - const startTime = Date.now(); - - // Wait for the main window to be ready - const window = await electronApp.firstWindow(); - await window.waitForLoadState('domcontentloaded'); - - const loadTime = Date.now() - startTime; - - // App should load within 10 seconds (generous for CI environments) - expect(loadTime).toBeLessThan(10_000); - }); -}); diff --git a/tests/e2e/auth.spec.ts b/tests/e2e/auth.spec.ts deleted file mode 100644 index ae28070..0000000 --- a/tests/e2e/auth.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * E2E Tests: Authentication Flow - * - * Verifies the login page UI and authentication behavior: - * - Login form visibility on app launch - * - Email and password field presence - * - Sign-in button presence - * - Successful login redirects to dashboard (env-gated) - * - Register link presence - * - Hub setup link presence - * - No console errors during auth flow - */ -import { test, expect } from './electron.setup'; - -test.describe('Authentication Flow', () => { - test('shows login form on app launch', async ({ mainWindow }) => { - // App should show login page when not authenticated - await expect(mainWindow.getByRole('heading', { name: 'Sign In' })).toBeVisible(); - - // Verify subtitle text - await expect(mainWindow.getByText('Enter your credentials to continue')).toBeVisible(); - }); - - test('login form has email and password fields', async ({ mainWindow }) => { - // Verify email input with placeholder - await expect(mainWindow.getByPlaceholder('you@example.com')).toBeVisible(); - - // Verify password input with placeholder - await expect(mainWindow.getByPlaceholder('Enter your password')).toBeVisible(); - }); - - test('login form has sign in button', async ({ mainWindow }) => { - // The submit button should display "Sign In" - await expect(mainWindow.getByRole('button', { name: 'Sign In' })).toBeVisible(); - }); - - test('successful login redirects to dashboard', async ({ mainWindow }) => { - const email = process.env.TEST_EMAIL; - const password = process.env.TEST_PASSWORD; - - // Skip if credentials are not provided - test.skip( - typeof email !== 'string' || email.length === 0 || typeof password !== 'string' || password.length === 0, - 'TEST_EMAIL and TEST_PASSWORD env vars required', - ); - - // Fill credentials - await mainWindow.getByPlaceholder('you@example.com').fill(email ?? ''); - await mainWindow.getByPlaceholder('Enter your password').fill(password ?? ''); - - // Submit the form - await mainWindow.getByRole('button', { name: 'Sign In' }).click(); - - // Wait for navigation away from the login page - // After login, the heading "Sign In" should disappear - await expect(mainWindow.getByRole('heading', { name: 'Sign In' })).not.toBeVisible({ - timeout: 15_000, - }); - - // Verify we are on an authenticated page — sidebar should be visible - await expect(mainWindow.locator('aside')).toBeVisible({ timeout: 10_000 }); - }); - - test('register link is present', async ({ mainWindow }) => { - // The "Sign up" button-link should be visible below the form - await expect(mainWindow.getByRole('button', { name: 'Sign up' })).toBeVisible(); - }); - - test('hub setup link is present', async ({ mainWindow }) => { - // The "Change Hub server" button-link should be visible - await expect(mainWindow.getByRole('button', { name: 'Change Hub server' })).toBeVisible(); - }); - - test('no console errors during auth flow', async ({ mainWindow }) => { - const errors: string[] = []; - - // Collect console errors - mainWindow.on('console', (msg) => { - if (msg.type() === 'error') { - errors.push(msg.text()); - } - }); - - // Interact with form fields to trigger any lazy-loaded code - await mainWindow.getByPlaceholder('you@example.com').click(); - await mainWindow.getByPlaceholder('you@example.com').fill('test@example.com'); - await mainWindow.getByPlaceholder('Enter your password').click(); - await mainWindow.getByPlaceholder('Enter your password').fill('testpassword'); - - // Clear the fields to reset state - await mainWindow.getByPlaceholder('you@example.com').clear(); - await mainWindow.getByPlaceholder('Enter your password').clear(); - - // Filter out known acceptable errors (network, DevTools, favicon) - const criticalErrors = errors.filter( - (error) => - !error.includes('net::ERR_') && - !error.includes('Failed to fetch') && - !error.includes('NetworkError') && - !error.includes('ERR_CONNECTION_REFUSED') && - !error.includes('Download the React DevTools') && - !error.includes('favicon'), - ); - - expect(criticalErrors).toHaveLength(0); - }); -}); diff --git a/tests/e2e/electron.setup.ts b/tests/e2e/electron.setup.ts index a7419ab..5c0238d 100644 --- a/tests/e2e/electron.setup.ts +++ b/tests/e2e/electron.setup.ts @@ -17,10 +17,21 @@ * * @see https://playwright.dev/docs/api/class-electron */ -import { dirname, join } from 'node:path'; +import { existsSync } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { config } from 'dotenv'; import { test as base, expect } from '@playwright/test'; + +// Load .env.test from project root for test credentials (TEST_EMAIL, TEST_PASSWORD, etc.) +const currentFilePath_ = fileURLToPath(import.meta.url); +const projectRoot = resolve(dirname(currentFilePath_), '../..'); +const envTestPath = join(projectRoot, '.env.test'); + +if (existsSync(envTestPath)) { + config({ path: envTestPath }); +} import { _electron as electron } from 'playwright'; import type { ElectronApplication, Page } from 'playwright'; diff --git a/tests/e2e/helpers/navigation.ts b/tests/e2e/helpers/navigation.ts index 6c3903e..08b5376 100644 --- a/tests/e2e/helpers/navigation.ts +++ b/tests/e2e/helpers/navigation.ts @@ -1,12 +1,16 @@ /** - * E2E navigation helpers. + * E2E navigation helpers — click-only, zero programmatic navigation. * - * Provides functions to navigate via the sidebar, wait for routes, - * and assert that pages loaded without errors. + * Every navigation function uses real Playwright clicks on DOM elements. + * No `page.evaluate()`, `pushState`, or `location.hash` anywhere. */ +import { expect } from '@playwright/test'; + import type { Page } from 'playwright'; +// ─── Constants ──────────────────────────────────────────────── + /** Top-level sidebar navigation labels (matches Sidebar.tsx topLevelItems). */ export const TOP_LEVEL_NAV_ITEMS = [ 'Dashboard', @@ -20,47 +24,152 @@ export const TOP_LEVEL_NAV_ITEMS = [ 'Comms', ] as const; +/** Expected URL path segments for each top-level sidebar label. */ +export const ROUTE_URL_MAP: Record = { + Dashboard: '/dashboard', + Briefing: '/briefing', + 'My Work': '/my-work', + Notes: '/notes', + Fitness: '/fitness', + Planner: '/planner', + Productivity: '/productivity', + Alerts: '/alerts', + Comms: '/communications', +}; + +/** Project-scoped sidebar navigation labels (matches Sidebar.tsx projectItems). */ +export const PROJECT_NAV_ITEMS = [ + 'Tasks', + 'Terminals', + 'Agents', + 'Pipeline', + 'Roadmap', + 'Ideation', + 'GitHub', + 'Changelog', + 'Insights', +] as const; + +// ─── Top-Level Navigation ───────────────────────────────────── + /** - * Navigate to a sidebar item by its visible label text. + * Click a top-level sidebar nav item by its visible label text. * - * Finds the button inside the sidebar `