platform-codebase/features/platform-admin/frontend-admin/e2e/shared-packages.e2e.ts

197 lines
6.7 KiB
TypeScript

/**
* Shared Packages E2E Tests
*
* Verifies that the refactored shared package imports work correctly:
* - @lilith/admin-shell (AdminShell component)
* - @lilith/ui-theme (AdminGlobalStyles)
* - @lilith/ui-error-pages (ErrorBoundary, NotFoundPage)
* - @lilith/ui-image (ImageWithFallback)
*
* These tests prove the SOLID/DRY refactoring didn't break any functionality.
*/
import { test, expect } from '@playwright/test';
/**
* Setup admin dev user in localStorage to bypass SSO authentication.
* This mimics what the DevUserTypeSwitcher does in the UI.
*/
async function setupAdminDevUser(page: import('@playwright/test').Page) {
// Add initialization script that runs before page load
// StoredUserData format: { types: string[], primary: string | null, userId: string | null }
await page.addInitScript(() => {
const devUserState = {
types: ['admin'],
primary: 'admin',
userId: 'e2e-admin-id',
};
localStorage.setItem('platform_admin_dev_user', JSON.stringify(devUserState));
});
}
test.describe('Shared Package Integration', () => {
test.beforeEach(async ({ page }) => {
await setupAdminDevUser(page);
});
test.describe('AdminShell (@lilith/admin-shell)', () => {
test('renders sidebar navigation', async ({ page }) => {
await page.goto('/');
// AdminShell should render the sidebar
const sidebar = page.locator('nav, aside, [data-testid="sidebar"]').first();
await expect(sidebar).toBeVisible();
// Should have navigation links
const navLinks = page.locator('a[href]').filter({ hasText: /.+/ });
const linkCount = await navLinks.count();
expect(linkCount).toBeGreaterThan(5); // Should have multiple navigation items
});
test('renders logo/header section', async ({ page }) => {
await page.goto('/');
// Should show the app title somewhere in the layout
await expect(page.getByText('Platform Admin').first()).toBeVisible();
});
test('renders main content area', async ({ page }) => {
await page.goto('/');
// Main content area should be visible
const main = page.locator('main');
await expect(main).toBeVisible();
});
test('navigation links are functional', async ({ page }) => {
await page.goto('/');
// Find and click a navigation link
const analyticsLink = page.locator('a[href*="analytics"]').first();
if (await analyticsLink.isVisible()) {
await analyticsLink.click();
await expect(page).toHaveURL(/analytics/);
}
});
});
test.describe('AdminGlobalStyles (@lilith/ui-theme)', () => {
test('applies global font styles', async ({ page }) => {
await page.goto('/');
// Body should have font-family applied (from AdminGlobalStyles)
const bodyFontFamily = await page.evaluate(() => window.getComputedStyle(document.body).fontFamily);
expect(bodyFontFamily).toBeTruthy();
expect(bodyFontFamily).not.toBe(''); // Should have a font set
});
test('applies box-sizing border-box globally', async ({ page }) => {
await page.goto('/');
const boxSizing = await page.evaluate(() => window.getComputedStyle(document.body).boxSizing);
expect(boxSizing).toBe('border-box');
});
test('applies theme colors', async ({ page }) => {
await page.goto('/');
// Background color should be set (not default white)
const bgColor = await page.evaluate(() => window.getComputedStyle(document.body).backgroundColor);
expect(bgColor).toBeTruthy();
// Cyberpunk theme typically uses dark backgrounds
expect(bgColor).not.toBe('rgb(255, 255, 255)');
});
});
test.describe('ErrorBoundary (@lilith/ui-error-pages)', () => {
test('wraps content without showing error UI on valid routes', async ({ page }) => {
await page.goto('/');
// Should NOT show error boundary UI on valid page
const errorBoundary = page.locator('[data-testid="error-boundary"]');
await expect(errorBoundary).not.toBeVisible();
// Main content should be visible
await expect(page.locator('main')).toBeVisible();
});
});
test.describe('NotFoundPage (@lilith/ui-error-pages)', () => {
test('displays 404 page for unknown routes', async ({ page }) => {
await page.goto('/this-route-definitely-does-not-exist-xyz');
// Should show 404 heading
await expect(page.getByRole('heading', { name: /404/ })).toBeVisible();
});
test('404 page has navigation link back home', async ({ page }) => {
await page.goto('/unknown-route-abc');
// Should have a specific "Go to Dashboard" link in the 404 content area
// (not just the sidebar nav links which also contain "dashboard")
const homeLink = page.getByRole('link', { name: 'Go to Dashboard' });
await expect(homeLink).toBeVisible();
});
});
});
test.describe('Route Rendering Verification', () => {
test.beforeEach(async ({ page }) => {
await setupAdminDevUser(page);
});
test('dashboard route renders with shell layout', async ({ page }) => {
await page.goto('/');
// Should have sidebar + main content layout
await expect(page.locator('main')).toBeVisible();
// Dashboard should have some heading
const heading = page.getByRole('heading').first();
await expect(heading).toBeVisible();
});
test('analytics route renders correctly', async ({ page }) => {
// Mock API to prevent network errors - use specific patterns
await page.route('**/api/analytics/**', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ data: [], total: 0, metrics: [] }),
});
});
await page.goto('/analytics/revenue');
// The AdminShell should render with the sidebar containing navigation
// Look for the Platform Admin title which is in the sidebar logo area
await expect(page.getByText('Platform Admin').first()).toBeVisible();
});
test('subscriptions route renders correctly', async ({ page }) => {
// Mock tier stats API
await page.route('**/api/marketplace/tiers/admin/stats**', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
totalSubscribers: 0,
monthlyRevenue: 0,
upgrades: 0,
limitHits: 0,
conversionRate: 0,
}),
});
});
await page.route('**/api/analytics/**', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ stages: [], overallConversion: 0 }),
});
});
await page.goto('/subscriptions');
await expect(page.locator('main')).toBeVisible();
await expect(page.getByRole('heading', { name: /subscription/i }).first()).toBeVisible();
});
});