Files
app/plans/01-playwright-scaffolding.md
Albert b96159ec02 docs: Add comprehensive implementation plans for all todo items
Created detailed markdown plans for all items in todo.md:

1. 01-playwright-scaffolding.md - Base Playwright infrastructure
2. 02-magnitude-tests-comprehensive.md - Complete test coverage
3. 03-stream-ai-to-deepgram-tts.md - TTS latency optimization
4. 04-fix-galaxy-node-clicking.md - Galaxy navigation bugs
5. 05-dark-light-mode-theme.md - Dark/light mode with dynamic favicons
6. 06-fix-double-border-desktop.md - UI polish
7. 07-delete-backup-files.md - Code cleanup
8. 08-ai-transition-to-edit.md - Intelligent node creation flow
9. 09-umap-minimum-nodes-analysis.md - Technical analysis

Each plan includes:
- Detailed problem analysis
- Proposed solutions with code examples
- Manual Playwright MCP testing strategy
- Magnitude test specifications
- Implementation steps
- Success criteria

Ready to implement in sequence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 21:07:42 +00:00

405 lines
10 KiB
Markdown

# Plan: Add Playwright Scaffolding Files
**Priority:** CRITICAL - Must be done first
**Dependencies:** None
**Affects:** All future testing work
## Overview
Create base Playwright scaffolding files to improve efficiency of both manual Playwright MCP testing and Magnitude test automation. This infrastructure will make it easier to write, run, and maintain browser-based tests.
## Current State
- No Playwright configuration file exists
- No test helpers or utilities for common operations
- No fixtures for authenticated states
- Manual testing with Playwright MCP is inefficient
- Magnitude tests will lack reusable components
## Proposed Solution
Create a complete Playwright testing infrastructure with:
### 1. Core Configuration Files
#### `playwright.config.ts`
```typescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/playwright',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
// Setup authentication
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
// Desktop browsers
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'tests/playwright/.auth/user.json',
},
dependencies: ['setup'],
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
storageState: 'tests/playwright/.auth/user.json',
},
dependencies: ['setup'],
},
// Mobile browsers
{
name: 'mobile-chrome',
use: {
...devices['Pixel 5'],
storageState: 'tests/playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
webServer: {
command: 'pnpm dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
```
#### `.gitignore` updates
```
# Playwright
tests/playwright/.auth/
test-results/
playwright-report/
playwright/.cache/
```
### 2. Authentication Setup
#### `tests/playwright/auth.setup.ts`
```typescript
import { test as setup, expect } from '@playwright/test';
const authFile = 'tests/playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
// Check if auth already exists and is valid
const authCookie = process.env.PLAYWRIGHT_AUTH_COOKIE;
if (authCookie) {
// Use existing auth from environment
await page.context().addCookies([{
name: 'ponderants-auth',
value: authCookie,
domain: 'localhost',
path: '/',
}]);
} else {
// Perform OAuth login
await page.goto('/');
await page.click('text=Log in with Bluesky');
// Wait for OAuth redirect
await page.waitForURL(/bsky\.social/);
// Fill in credentials
await page.fill('input[name="identifier"]', process.env.TEST_USER_HANDLE!);
await page.fill('input[name="password"]', process.env.TEST_USER_PASSWORD!);
await page.click('button[type="submit"]');
// Wait for redirect back to app
await page.waitForURL(/localhost:3000/);
// Verify we're authenticated
await expect(page.getByRole('button', { name: /profile/i })).toBeVisible();
}
// Save signed-in state
await page.context().storageState({ path: authFile });
});
```
### 3. Test Fixtures and Helpers
#### `tests/playwright/fixtures.ts`
```typescript
import { test as base, Page } from '@playwright/test';
type Fixtures = {
authenticatedPage: Page;
chatPage: Page;
galaxyPage: Page;
};
export const test = base.extend<Fixtures>({
authenticatedPage: async ({ page }, use) => {
// Already authenticated via setup
await page.goto('/');
await use(page);
},
chatPage: async ({ page }, use) => {
await page.goto('/chat');
await use(page);
},
galaxyPage: async ({ page }, use) => {
await page.goto('/galaxy');
await use(page);
},
});
export { expect } from '@playwright/test';
```
#### `tests/playwright/helpers/chat.ts`
```typescript
import { Page, expect } from '@playwright/test';
export class ChatHelper {
constructor(private page: Page) {}
async sendMessage(message: string) {
await this.page.fill('textarea[placeholder*="Type"]', message);
await this.page.press('textarea[placeholder*="Type"]', 'Enter');
}
async waitForAIResponse() {
// Wait for typing indicator to disappear
await this.page.waitForSelector('[data-testid="typing-indicator"]', {
state: 'hidden',
timeout: 30000,
});
}
async createNode() {
await this.page.click('button:has-text("Create Node")');
}
async getLastMessage() {
const messages = this.page.locator('[data-testid="chat-message"]');
const count = await messages.count();
return messages.nth(count - 1);
}
}
```
#### `tests/playwright/helpers/galaxy.ts`
```typescript
import { Page, expect } from '@playwright/test';
export class GalaxyHelper {
constructor(private page: Page) {}
async waitForGalaxyLoad() {
// Wait for canvas to be visible
await this.page.waitForSelector('canvas', { timeout: 10000 });
// Wait for nodes to be loaded (check for at least one node)
await this.page.waitForFunction(() => {
const canvas = document.querySelector('canvas');
return canvas && canvas.getContext('2d') !== null;
});
}
async clickNode(nodeId: string) {
// Find and click on a specific node
await this.page.evaluate((id) => {
const event = new CustomEvent('node-click', { detail: { nodeId: id } });
window.dispatchEvent(event);
}, nodeId);
}
async getNodeCount() {
return await this.page.evaluate(() => {
// Access R3F scene data
return window.__galaxyNodes?.length || 0;
});
}
}
```
#### `tests/playwright/helpers/node.ts`
```typescript
import { Page, expect } from '@playwright/test';
export class NodeHelper {
constructor(private page: Page) {}
async createNode(title: string, body: string) {
// Navigate to chat
await this.page.goto('/chat');
// Trigger node creation
await this.page.fill('input[placeholder*="Title"]', title);
await this.page.fill('textarea[placeholder*="Body"]', body);
await this.page.click('button:has-text("Publish")');
// Wait for success
await expect(this.page.getByText(/published/i)).toBeVisible();
}
async waitForUMAPCalculation() {
// Poll /api/galaxy until nodes have coords_3d
await this.page.waitForFunction(async () => {
const response = await fetch('/api/galaxy');
const data = await response.json();
return data.nodes.every((n: any) => n.coords_3d !== null);
}, { timeout: 60000 });
}
}
```
### 4. Example Tests
#### `tests/playwright/smoke.spec.ts`
```typescript
import { test, expect } from './fixtures';
test.describe('Smoke Tests', () => {
test('homepage loads', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Ponderants/);
});
test('authenticated user can access chat', async ({ chatPage }) => {
await expect(chatPage.getByRole('textbox')).toBeVisible();
});
test('authenticated user can access galaxy', async ({ galaxyPage }) => {
await expect(galaxyPage.locator('canvas')).toBeVisible();
});
});
```
### 5. Environment Variables
#### `.env.test` (template)
```bash
# Test user credentials
TEST_USER_HANDLE=your-test-handle.bsky.social
TEST_USER_PASSWORD=your-test-password
# Optional: Pre-authenticated cookie for faster tests
PLAYWRIGHT_AUTH_COOKIE=
# Playwright settings
PLAYWRIGHT_BASE_URL=http://localhost:3000
```
## Implementation Steps
1. **Create directory structure**
```bash
mkdir -p tests/playwright/.auth
mkdir -p tests/playwright/helpers
```
2. **Install dependencies**
```bash
pnpm add -D @playwright/test
```
3. **Create configuration files**
- playwright.config.ts
- .gitignore updates
- .env.test template
4. **Create authentication setup**
- auth.setup.ts
5. **Create fixtures and helpers**
- fixtures.ts
- helpers/chat.ts
- helpers/galaxy.ts
- helpers/node.ts
6. **Create example tests**
- smoke.spec.ts
7. **Update package.json scripts**
```json
{
"scripts": {
"test:playwright": "playwright test",
"test:playwright:ui": "playwright test --ui",
"test:playwright:debug": "playwright test --debug"
}
}
```
## Testing Plan
### Manual Playwright MCP Testing
1. Open Playwright MCP browser
2. Navigate to http://localhost:3000
3. Test authentication flow
4. Test chat interaction
5. Test node creation
6. Test galaxy visualization
7. Test node detail modal
### Magnitude Test Integration
After manual testing succeeds, create magnitude tests that use the same patterns:
```typescript
import { test } from 'magnitude-test';
test('User can create and view nodes in galaxy', async (agent) => {
await agent.open('http://localhost:3000');
await agent.act('Log in with test credentials');
await agent.check('User is authenticated');
await agent.act('Navigate to chat');
await agent.act('Create a new node with title "Test Node" and body "Test content"');
await agent.check('Node is published to Bluesky');
await agent.act('Navigate to galaxy');
await agent.check('Can see the new node in 3D visualization');
});
```
## Success Criteria
- ✅ Playwright configuration file exists
- ✅ Authentication setup works (both OAuth and cookie-based)
- ✅ Helper classes for chat, galaxy, and node operations exist
- ✅ Example smoke tests pass
- ✅ Can run `pnpm test:playwright` successfully
- ✅ Manual Playwright MCP testing is efficient with helpers
- ✅ Foundation exists for Magnitude test creation
## Files to Create
1. `playwright.config.ts` - Main configuration
2. `tests/playwright/auth.setup.ts` - Authentication setup
3. `tests/playwright/fixtures.ts` - Custom fixtures
4. `tests/playwright/helpers/chat.ts` - Chat helper functions
5. `tests/playwright/helpers/galaxy.ts` - Galaxy helper functions
6. `tests/playwright/helpers/node.ts` - Node helper functions
7. `tests/playwright/smoke.spec.ts` - Example smoke tests
8. `.env.test` - Environment variables template
## Files to Update
1. `.gitignore` - Add Playwright artifacts
2. `package.json` - Add test scripts
3. `README.md` - Document testing approach (optional)