workspace/projects/gamilit/apps/frontend/e2e/navigation.spec.ts
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

198 lines
5.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
/**
* Navigation and Routing E2E Tests
*
* Tests basic navigation, routing, and page loads
*/
test.describe('Basic Navigation', () => {
test('should load homepage without errors', async ({ page }) => {
// Navigate to homepage
const response = await page.goto('/');
// Page should load successfully
expect(response?.status()).toBe(200);
// Page should have a title
await expect(page).toHaveTitle(/GAMILIT|Gamificación/i);
// Page should not have console errors
const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
// Wait a bit for any async errors
await page.waitForTimeout(2000);
// Should have minimal or no errors
expect(errors.length).toBeLessThan(5); // Allow for some dev warnings
});
test('should handle 404 pages', async ({ page }) => {
const response = await page.goto('/this-page-does-not-exist');
// Should either show 404 page or redirect
const url = page.url();
const statusCode = response?.status();
// Accept either a 404 status OR redirect to home/login
const isValid =
statusCode === 404 ||
url.includes('/login') ||
url === 'http://localhost:3005/';
expect(isValid).toBeTruthy();
});
test('should have responsive navigation menu', async ({ page }) => {
await page.goto('/');
// Check if navigation exists (either as sidebar or header)
const hasNav =
(await page.locator('nav').count()) > 0 ||
(await page.locator('[role="navigation"]').count()) > 0;
expect(hasNav).toBeTruthy();
});
test('should load without JavaScript errors', async ({ page }) => {
const jsErrors: Error[] = [];
page.on('pageerror', (error) => {
jsErrors.push(error);
});
await page.goto('/');
// Wait for page to fully load
await page.waitForLoadState('networkidle');
// Should have no JavaScript errors
expect(jsErrors.length).toBe(0);
});
test('should have proper meta tags', async ({ page }) => {
await page.goto('/');
// Check for viewport meta tag (important for mobile)
const viewport = await page.locator('meta[name="viewport"]').getAttribute('content');
expect(viewport).toContain('width=device-width');
// Check for charset
const hasCharset =
(await page.locator('meta[charset]').count()) > 0 ||
(await page.locator('meta[charset="utf-8"]').count()) > 0 ||
(await page.locator('meta[charset="UTF-8"]').count()) > 0;
expect(hasCharset).toBeTruthy();
});
});
test.describe('Protected Routes', () => {
test('should redirect to login when accessing protected routes unauthenticated', async ({
page,
}) => {
// Try to access a protected route (e.g., dashboard)
await page.goto('/dashboard');
// Should redirect to login page
await expect(page).toHaveURL(/\/login|\//);
});
test('should redirect to login when accessing student pages unauthenticated', async ({
page,
}) => {
await page.goto('/student/dashboard');
// Should redirect to login
await expect(page).toHaveURL(/\/login|\//);
});
test('should redirect to login when accessing teacher pages unauthenticated', async ({
page,
}) => {
await page.goto('/teacher/dashboard');
// Should redirect to login
await expect(page).toHaveURL(/\/login|\//);
});
});
test.describe('Performance', () => {
test('should load initial page within acceptable time', async ({ page }) => {
const startTime = Date.now();
await page.goto('/');
await page.waitForLoadState('domcontentloaded');
const loadTime = Date.now() - startTime;
// Should load within 5 seconds (generous for E2E)
expect(loadTime).toBeLessThan(5000);
});
test('should not have memory leaks on navigation', async ({ page }) => {
await page.goto('/');
// Navigate between pages multiple times
for (let i = 0; i < 3; i++) {
await page.goto('/');
await page.waitForLoadState('networkidle');
// Try to navigate to other pages if they exist
const links = await page.locator('a[href^="/"]').all();
if (links.length > 0) {
await links[0].click();
await page.waitForLoadState('networkidle');
}
await page.goBack();
}
// If no errors thrown, test passes
expect(true).toBeTruthy();
});
});
test.describe('Accessibility Basics', () => {
test('should have proper heading hierarchy', async ({ page }) => {
await page.goto('/');
// Check for h1
const h1Count = await page.locator('h1').count();
expect(h1Count).toBeGreaterThan(0);
expect(h1Count).toBeLessThanOrEqual(1); // Should have only one h1
});
test('should have skip to content link for keyboard users', async ({ page }) => {
await page.goto('/');
// Check for skip link (common accessibility practice)
const skipLink = page.locator('a[href="#main-content"], a[href="#content"]');
// Skip link may or may not be present, but if it is, it should work
if ((await skipLink.count()) > 0) {
await expect(skipLink).toBeAttached();
}
});
test('should be keyboard navigable', async ({ page }) => {
await page.goto('/');
// Press Tab key to navigate
await page.keyboard.press('Tab');
// Check if focus is visible somewhere
const focusedElement = await page.evaluate(() => {
return document.activeElement?.tagName;
});
// Should have moved focus to some element
expect(focusedElement).toBeTruthy();
});
});