import { test, expect } from '@playwright/test'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); /** * Docker deployment tests * * These tests verify the application works correctly when deployed in Docker. * They specifically test CSRF protection which requires TASKPLANER_ORIGIN to be set. * * Run against Docker: BASE_URL=http://localhost:3000 npx playwright test */ test.describe('Docker Deployment', () => { test('application loads', async ({ page }) => { await page.goto('/'); await expect(page).toHaveTitle(/TaskPlaner/i); }); test('health endpoint returns ok', async ({ request }) => { const response = await request.get('/health'); expect(response.status()).toBe(200); expect(await response.text()).toBe('ok'); }); test('can create entry via form submission', async ({ page }) => { // This test verifies CSRF protection is properly configured // If TASKPLANER_ORIGIN is not set, form submissions return 403 await page.goto('/'); // Fill in quick capture form (textarea for content) const contentInput = page.locator('textarea[name="content"]'); await contentInput.fill('Test entry from Playwright'); // Submit the form - button text is "Add" const addButton = page.locator('button[type="submit"]:has-text("Add")'); await addButton.click(); // Wait for the entry to appear in the list await expect(page.locator('text=Test entry from Playwright')).toBeVisible({ timeout: 5000 }); }); test('can toggle entry completion', async ({ page }) => { await page.goto('/'); // Create a task entry first const contentInput = page.locator('textarea[name="content"]'); await contentInput.fill('Test task for completion'); // Select task type const typeSelect = page.locator('select[name="type"]'); await typeSelect.selectOption('task'); const addButton = page.locator('button[type="submit"]:has-text("Add")'); await addButton.click(); // Wait for entry to appear await expect(page.locator('text=Test task for completion')).toBeVisible({ timeout: 5000 }); // Find and click the checkbox to mark complete const checkbox = page.locator('input[type="checkbox"]').first(); await checkbox.click(); // Verify the action completed (no 403 error) // The checkbox should now be checked await expect(checkbox).toBeChecked({ timeout: 5000 }); }); test('data persists across container restart', async ({ page }) => { // This test verifies Docker volume persistence const uniqueContent = `Persistence test ${Date.now()}`; // Create an entry await page.goto('/'); const contentInput = page.locator('textarea[name="content"]'); await contentInput.fill(uniqueContent); const addButton = page.locator('button[type="submit"]:has-text("Add")'); await addButton.click(); // Wait for entry to appear await expect(page.locator(`text=${uniqueContent}`)).toBeVisible({ timeout: 5000 }); // Restart the container await execAsync('docker compose restart'); // Wait for container to be healthy again let healthy = false; for (let i = 0; i < 30; i++) { try { const response = await fetch('http://localhost:3000/health'); if (response.ok) { healthy = true; break; } } catch { // Container not ready yet } await new Promise((r) => setTimeout(r, 1000)); } expect(healthy).toBe(true); // Verify entry still exists after restart await page.goto('/'); await expect(page.locator(`text=${uniqueContent}`)).toBeVisible({ timeout: 5000 }); }); });