refactor: migrate to monorepo structure with Docker, PocketBase, and e2e tests

- Reorganize into apps/, packages/, docs/, e2e/, pocketbase/ directories
- Add Dockerfiles for web, worker, and PocketBase services
- Add docker-compose.yml for local orchestration
- Add turbo.json for monorepo task management
- Add Playwright e2e test infrastructure
- Add PocketBase backend with migrations
- Remove Vite/Next.js/ESLint/PostCSS config files
- Update package.json with workspace dependencies
- Add .env.example and .dockerignore
This commit is contained in:
2026-07-16 06:19:58 -04:00
parent ec14645a4b
commit 8f55626e03
286 changed files with 31992 additions and 9245 deletions
+105
View File
@@ -0,0 +1,105 @@
import { test, expect } from '@playwright/test';
import { login, logout, goToLogin } from './helpers/auth';
import { TEST_USER, INVALID_USER } from './helpers/fixtures';
test.describe('Authentication Flow', () => {
test.describe('Login', () => {
test('should login with valid credentials and redirect to dashboard', async ({ page }) => {
await goToLogin(page);
// Verify login page loaded
await expect(page.getByRole('heading', { name: /project e/i })).toBeVisible();
await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible();
// Fill in credentials
await page.getByLabel('Email').fill(TEST_USER.email);
await page.getByLabel('Password').fill(TEST_USER.password);
await page.getByRole('button', { name: /sign in/i }).click();
// Should redirect to dashboard
await page.waitForURL('**/dashboard', { timeout: 15_000 });
await expect(page).toHaveURL(/\/dashboard/);
// Dashboard should be visible
await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
});
test('should show error with invalid credentials', async ({ page }) => {
await goToLogin(page);
await page.getByLabel('Email').fill(INVALID_USER.email);
await page.getByLabel('Password').fill(INVALID_USER.password);
await page.getByRole('button', { name: /sign in/i }).click();
// Should still be on login page
await expect(page).toHaveURL(/\/login/);
// Should show error message (toast or inline)
// The app uses sonner toasts for errors
await expect(
page.getByText(/login failed|invalid/i),
).toBeVisible({ timeout: 10_000 });
});
test('should prevent submitting empty form', async ({ page }) => {
await goToLogin(page);
const submitButton = page.getByRole('button', { name: /sign in/i });
await expect(submitButton).toBeVisible();
// HTML5 required attribute should prevent submission
// The email field is required
await page.getByLabel('Password').fill('somepassword');
await submitButton.click();
// Should stay on login page (HTML5 validation prevents submit)
await expect(page).toHaveURL(/\/login/);
});
});
test.describe('Logout', () => {
test('should logout and redirect to login page', async ({ page }) => {
// First login
await login(page);
await expect(page).toHaveURL(/\/dashboard/);
// Clear cookies to simulate logout
await logout(page);
// Navigate to dashboard should redirect to login
await page.goto('/dashboard');
await page.waitForURL('**/login', { timeout: 10_000 });
await expect(page).toHaveURL(/\/login/);
});
});
test.describe('Session persistence', () => {
test('should stay logged in after page refresh', async ({ page }) => {
await login(page);
await expect(page).toHaveURL(/\/dashboard/);
// Refresh the page
await page.reload();
// Should still be on dashboard
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
});
});
test.describe('Protected routes', () => {
test('should redirect unauthenticated users from dashboard to login', async ({ page }) => {
await logout(page);
await page.goto('/dashboard');
await page.waitForURL('**/login', { timeout: 10_000 });
await expect(page).toHaveURL(/\/login/);
});
test('should redirect unauthenticated users from tasks to login', async ({ page }) => {
await logout(page);
await page.goto('/tasks');
await page.waitForURL('**/login', { timeout: 10_000 });
await expect(page).toHaveURL(/\/login/);
});
});
});