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>
This commit is contained in:
404
plans/01-playwright-scaffolding.md
Normal file
404
plans/01-playwright-scaffolding.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user