- Copy admin-dashboard-web → dashboards/admin-web - Copy tracker-dashboard-web → dashboards/tracker-web - Update pnpm-workspace.yaml to include dashboards/* - Replace file: refs with workspace:* for @bytelyst/* packages - Replace all hardcoded LysnrAI/lysnn.com branding with generic platform refs - Make telemetry use NEXT_PUBLIC_PRODUCT_ID / PRODUCT_ID env vars - Update mock credentials, seed data, invitation codes, placeholders - Update READMEs, e2e tests, unit tests for product-agnostic content - Both dashboards pass tsc --noEmit clean
115 lines
4.1 KiB
TypeScript
115 lines
4.1 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
/**
|
|
* E2E tests for the Tracker dashboard.
|
|
*
|
|
* Tests cover login page, authentication redirects, public roadmap,
|
|
* and dashboard structure.
|
|
*/
|
|
|
|
test.describe('Tracker Login Page', () => {
|
|
test('shows login form with correct branding', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await expect(page.getByText('Tracker')).toBeVisible();
|
|
await expect(page.getByText('Feature requests, bugs & task management')).toBeVisible();
|
|
await expect(page.getByLabel('Email')).toBeVisible();
|
|
await expect(page.getByLabel('Password')).toBeVisible();
|
|
await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible();
|
|
});
|
|
|
|
test('shows credentials hint', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await expect(page.getByText(/platform-service credentials/i)).toBeVisible();
|
|
});
|
|
|
|
test('shows error for invalid credentials', async ({ page }) => {
|
|
await page.goto('/login');
|
|
await page.getByLabel('Email').fill('bad@user.com');
|
|
await page.getByLabel('Password').fill('wrongpassword');
|
|
await page.getByRole('button', { name: /sign in/i }).click();
|
|
await expect(page.getByText(/failed|error|invalid/i)).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('shows loading state on submit', async ({ page }) => {
|
|
// Block API to keep loading state visible
|
|
await page.route(
|
|
'**/api/auth/**',
|
|
route => new Promise(resolve => setTimeout(() => resolve(route.abort()), 3000))
|
|
);
|
|
await page.goto('/login');
|
|
await page.getByLabel('Email').fill('test@example.com');
|
|
await page.getByLabel('Password').fill('password123');
|
|
await page.getByRole('button', { name: /sign in/i }).click();
|
|
await expect(page.getByText('Signing in...')).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Tracker — Protected Routes', () => {
|
|
test('/ redirects to login when not authenticated', async ({ page }) => {
|
|
await page.goto('/');
|
|
await expect(page).toHaveURL(/\/login/, { timeout: 10000 });
|
|
});
|
|
|
|
test('/dashboard redirects to login when not authenticated', async ({ page }) => {
|
|
await page.goto('/dashboard');
|
|
await expect(page).toHaveURL(/\/login/, { timeout: 10000 });
|
|
});
|
|
});
|
|
|
|
test.describe('Tracker — Public Roadmap', () => {
|
|
test('roadmap page renders board layout', async ({ page }) => {
|
|
await page.goto('/roadmap');
|
|
// The roadmap page is public — no auth required
|
|
await expect(page.getByText(/roadmap/i).first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('roadmap page has search and filter controls', async ({ page }) => {
|
|
await page.goto('/roadmap');
|
|
// Should have a search input
|
|
await expect(page.getByPlaceholder(/search/i).first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('roadmap page has submit suggestion button', async ({ page }) => {
|
|
await page.goto('/roadmap');
|
|
await expect(page.getByRole('button', { name: /suggest|submit|new/i }).first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('roadmap page shows status columns in board view', async ({ page }) => {
|
|
await page.goto('/roadmap');
|
|
// Board view shows Planned, In Progress, Complete columns
|
|
await expect(page.getByText('Planned').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
await expect(page.getByText('In Progress').first()).toBeVisible();
|
|
await expect(page.getByText('Complete').first()).toBeVisible();
|
|
});
|
|
|
|
test('roadmap page can toggle between board and list view', async ({ page }) => {
|
|
await page.goto('/roadmap');
|
|
// Look for view toggle buttons
|
|
const listBtn = page.getByRole('button', { name: /list/i });
|
|
if (await listBtn.isVisible()) {
|
|
await listBtn.click();
|
|
// Should now show list view
|
|
await expect(page.locator("table, [role='list']").first()).toBeVisible({
|
|
timeout: 5000,
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Tracker — Health', () => {
|
|
test('GET /api/health returns ok', async ({ request }) => {
|
|
const res = await request.get('/api/health');
|
|
expect(res.ok()).toBeTruthy();
|
|
const body = await res.json();
|
|
expect(body.status).toBe('ok');
|
|
});
|
|
});
|