Files
Atomizer/atomizer-dashboard/frontend/tests/e2e/home.spec.ts
Anto01 3193831340 feat: Add DevLoop automation and HTML Reports
## DevLoop - Closed-Loop Development System
- Orchestrator for plan → build → test → analyze cycle
- Gemini planning via OpenCode CLI
- Claude implementation via CLI bridge
- Playwright browser testing integration
- Test runner with API, filesystem, and browser tests
- Persistent state in .devloop/ directory
- CLI tool: tools/devloop_cli.py

Usage:
  python tools/devloop_cli.py start 'Create new feature'
  python tools/devloop_cli.py plan 'Fix bug in X'
  python tools/devloop_cli.py test --study support_arm
  python tools/devloop_cli.py browser --level full

## HTML Reports (optimization_engine/reporting/)
- Interactive Plotly-based reports
- Convergence plot, Pareto front, parallel coordinates
- Parameter importance analysis
- Self-contained HTML (offline-capable)
- Tailwind CSS styling

## Playwright E2E Tests
- Home page tests
- Test results in test-results/

## LAC Knowledge Base Updates
- Session insights (failures, workarounds, patterns)
- Optimization memory for arm support study
2026-01-24 21:18:18 -05:00

172 lines
6.8 KiB
TypeScript

import { test, expect } from '@playwright/test';
/**
* Home Page E2E Tests
*
* Tests the study list page at /
* Covers: study loading, topic expansion, navigation
*/
test.describe('Home Page - Study List', () => {
test.beforeEach(async ({ page }) => {
// Navigate to home page
await page.goto('/');
});
test('displays page header', async ({ page }) => {
// Check header is visible
await expect(page.locator('header')).toBeVisible();
// Check for key header elements - Studies heading (exact match to avoid Inbox Studies)
await expect(page.getByRole('heading', { name: 'Studies', exact: true })).toBeVisible({ timeout: 10000 });
});
test('shows aggregate statistics cards', async ({ page }) => {
// Wait for stats to load
await expect(page.getByText('Total Studies')).toBeVisible();
await expect(page.getByText('Running')).toBeVisible();
await expect(page.getByText('Total Trials')).toBeVisible();
await expect(page.getByText('Best Overall')).toBeVisible();
});
test('loads studies table with topic folders', async ({ page }) => {
// Wait for studies section (exact match to avoid Inbox Studies)
await expect(page.getByRole('heading', { name: 'Studies', exact: true })).toBeVisible();
// Wait for loading to complete - either see folders or empty state
// Folders have "trials" text in them
const folderLocator = page.locator('button:has-text("trials")');
const emptyStateLocator = page.getByText('No studies found');
// Wait for either studies loaded or empty state (10s timeout)
await expect(folderLocator.first().or(emptyStateLocator)).toBeVisible({ timeout: 10000 });
});
test('expands topic folder to show studies', async ({ page }) => {
// Wait for folders to load
const folderButton = page.locator('button:has-text("trials")').first();
// Wait for folder to be visible (studies loaded)
await expect(folderButton).toBeVisible({ timeout: 10000 });
// Click to expand
await folderButton.click();
// After expansion, study rows should be visible (they have status badges)
// Status badges contain: running, completed, idle, paused, not_started
const statusBadges = page.locator('span:has-text("running"), span:has-text("completed"), span:has-text("idle"), span:has-text("paused"), span:has-text("not_started")');
await expect(statusBadges.first()).toBeVisible({ timeout: 5000 });
});
test('clicking study shows preview panel', async ({ page }) => {
// Wait for and expand first folder
const folderButton = page.locator('button:has-text("trials")').first();
await expect(folderButton).toBeVisible({ timeout: 10000 });
await folderButton.click();
// Wait for expanded content and click first study row
const studyRow = page.locator('.bg-dark-850\\/50 > div').first();
await expect(studyRow).toBeVisible({ timeout: 5000 });
await studyRow.click();
// Preview panel should show with buttons - use exact match to avoid header nav button
await expect(page.getByRole('button', { name: 'Canvas', exact: true })).toBeVisible({ timeout: 5000 });
await expect(page.getByRole('button', { name: 'Open' })).toBeVisible();
});
test('Open button navigates to dashboard', async ({ page }) => {
// Wait for and expand first folder
const folderButton = page.locator('button:has-text("trials")').first();
await expect(folderButton).toBeVisible({ timeout: 10000 });
await folderButton.click();
// Wait for and click study row
const studyRow = page.locator('.bg-dark-850\\/50 > div').first();
await expect(studyRow).toBeVisible({ timeout: 5000 });
await studyRow.click();
// Wait for and click Open button
const openButton = page.getByRole('button', { name: 'Open' });
await expect(openButton).toBeVisible({ timeout: 5000 });
await openButton.click();
// Should navigate to dashboard
await expect(page).toHaveURL(/\/dashboard/);
});
test('Canvas button navigates to canvas view', async ({ page }) => {
// Wait for and expand first folder
const folderButton = page.locator('button:has-text("trials")').first();
await expect(folderButton).toBeVisible({ timeout: 10000 });
await folderButton.click();
// Wait for and click study row
const studyRow = page.locator('.bg-dark-850\\/50 > div').first();
await expect(studyRow).toBeVisible({ timeout: 5000 });
await studyRow.click();
// Wait for and click Canvas button (exact match to avoid header nav)
const canvasButton = page.getByRole('button', { name: 'Canvas', exact: true });
await expect(canvasButton).toBeVisible({ timeout: 5000 });
await canvasButton.click();
// Should navigate to canvas
await expect(page).toHaveURL(/\/canvas\//);
});
test('refresh button reloads studies', async ({ page }) => {
// Find the main studies section refresh button (the one with visible text "Refresh")
const refreshButton = page.getByText('Refresh');
await expect(refreshButton).toBeVisible({ timeout: 5000 });
// Click refresh
await refreshButton.click();
// Should show loading state or complete quickly
// Just verify no errors occurred (exact match to avoid Inbox Studies)
await expect(page.getByRole('heading', { name: 'Studies', exact: true })).toBeVisible();
});
});
/**
* Inbox Section Tests
*
* Tests the new study intake workflow
*/
test.describe('Home Page - Inbox Section', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('displays inbox section with header', async ({ page }) => {
// Check for Study Inbox heading (section is expanded by default)
const inboxHeading = page.getByRole('heading', { name: 'Study Inbox' });
await expect(inboxHeading).toBeVisible({ timeout: 10000 });
});
test('inbox section shows pending count', async ({ page }) => {
// Section should show pending studies count
const pendingText = page.getByText(/\d+ pending studies/);
await expect(pendingText).toBeVisible({ timeout: 10000 });
});
test('inbox has new study button', async ({ page }) => {
// Section is expanded by default, look for the New Study button
const newStudyButton = page.getByRole('button', { name: /New Study/ });
await expect(newStudyButton).toBeVisible({ timeout: 10000 });
});
test('clicking new study shows create form', async ({ page }) => {
// Click the New Study button
const newStudyButton = page.getByRole('button', { name: /New Study/ });
await expect(newStudyButton).toBeVisible({ timeout: 10000 });
await newStudyButton.click();
// Form should expand with input fields
const studyNameInput = page.getByPlaceholder(/my_study/i).or(page.locator('input[type="text"]').first());
await expect(studyNameInput).toBeVisible({ timeout: 5000 });
});
});