workspace/projects/gamilit/apps/frontend/e2e/student-journey.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

301 lines
10 KiB
TypeScript

import { test, expect } from '@playwright/test';
/**
* Student Journey E2E Tests
*
* Critical user flow: Login → Dashboard → Browse Modules → Start Exercise → Complete → Earn Rewards
*/
test.describe('Student Dashboard', () => {
test.skip('should display student dashboard after login', async ({ page }) => {
// NOTE: Requires test user setup
// Login as student user
// await loginAsStudent(page);
// Should see dashboard
await expect(page).toHaveURL(/\/student\/dashboard/);
// Dashboard should have key sections
await expect(page.locator('text=/progreso|progress/i')).toBeVisible();
await expect(page.locator('text=/módulos|modules/i')).toBeVisible();
});
test.skip('should display user stats on dashboard', async ({ page }) => {
// Login as student
// await loginAsStudent(page);
// Should show XP
await expect(page.locator('text=/XP|experiencia/i')).toBeVisible();
// Should show level
await expect(page.locator('text=/nivel|level|rango/i')).toBeVisible();
// Should show progress indicators
const progressBars = await page.locator('[role="progressbar"], progress, .progress').count();
expect(progressBars).toBeGreaterThan(0);
});
});
test.describe('Module Selection', () => {
test.skip('should display available modules', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/modules');
// Should show list of modules
const modules = await page
.locator('[data-testid="module-card"], .module-card, article')
.count();
expect(modules).toBeGreaterThan(0);
// Each module should have:
// - Title
// - Description or preview
// - Start/Continue button
});
test.skip('should show module difficulty levels', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/modules');
// Should display difficulty indicators
const hasDifficulty =
(await page.locator('text=/fácil|easy|básico/i').count()) > 0 ||
(await page.locator('text=/intermedio|medium/i').count()) > 0 ||
(await page.locator('text=/difícil|hard|avanzado/i').count()) > 0;
expect(hasDifficulty).toBeTruthy();
});
test.skip('should allow filtering modules', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/modules');
// Look for filter controls
const hasFilters =
(await page.locator('select, [role="combobox"]').count()) > 0 ||
(await page.locator('button:has-text("Filtrar"), button:has-text("Filter")').count()) > 0;
expect(hasFilters).toBeTruthy();
});
});
test.describe('Exercise Flow', () => {
test.skip('should start an exercise', async ({ page }) => {
// await loginAsStudent(page);
// await navigateToModule(page);
// Click start exercise button
await page
.getByRole('button', { name: /iniciar|start|comenzar/i })
.first()
.click();
// Should navigate to exercise page
await expect(page).toHaveURL(/\/exercise|\/ejercicio/);
// Should show exercise content
await expect(page.locator('[data-testid="exercise-content"], .exercise-content')).toBeVisible();
});
test.skip('should display exercise question', async ({ page }) => {
// await loginAsStudent(page);
// await startExercise(page);
// Should show question text
const hasQuestion =
(await page.locator('[data-testid="question"], .question, h2, h3').count()) > 0;
expect(hasQuestion).toBeTruthy();
// Should show options (for multiple choice)
const options = await page.locator('input[type="radio"], button[role="radio"]').count();
expect(options).toBeGreaterThanOrEqual(2); // At least 2 options
});
test.skip('should allow answer selection', async ({ page }) => {
// await loginAsStudent(page);
// await startExercise(page);
// Select an option
const firstOption = page.locator('input[type="radio"], button[role="radio"]').first();
await firstOption.click();
// Option should be selected
await expect(firstOption).toBeChecked();
// Submit button should be enabled
const submitButton = page.getByRole('button', { name: /enviar|submit|confirmar/i });
await expect(submitButton).toBeEnabled();
});
test.skip('should submit answer and show feedback', async ({ page }) => {
// await loginAsStudent(page);
// await startExercise(page);
// Select answer
await page.locator('input[type="radio"], button[role="radio"]').first().click();
// Submit
await page.getByRole('button', { name: /enviar|submit/i }).click();
// Should show feedback
await expect(
page.locator('text=/correcto|correct|incorrecto|incorrect|bien|mal/i'),
).toBeVisible({ timeout: 5000 });
// Should show continue/next button
await expect(page.getByRole('button', { name: /siguiente|next|continuar/i })).toBeVisible();
});
test.skip('should track exercise progress', async ({ page }) => {
// await loginAsStudent(page);
// await startExercise(page);
// Should show progress indicator (e.g., "Question 1 of 10")
const hasProgress =
(await page.locator('text=/\\d+ de \\d+|\\d+ of \\d+/i').count()) > 0 ||
(await page.locator('[role="progressbar"]').count()) > 0;
expect(hasProgress).toBeTruthy();
});
test.skip('should complete exercise and show results', async ({ page }) => {
// await loginAsStudent(page);
// await completeExercise(page);
// Should show completion screen
await expect(page.locator('text=/completado|completed|finalizado|terminado/i')).toBeVisible();
// Should show score
await expect(page.locator('text=/puntuación|score|resultado/i')).toBeVisible();
// Should show XP earned
await expect(page.locator('text=/XP|experiencia ganada/i')).toBeVisible();
});
});
test.describe('Gamification Elements', () => {
test.skip('should display XP gain animation', async ({ page }) => {
// await loginAsStudent(page);
// await completeExercise(page);
// Should show XP animation or notification
// This might be a toast, modal, or animated number
const hasXPNotification =
(await page.locator('[data-testid="xp-notification"], .xp-earned, .toast').count()) > 0;
expect(hasXPNotification).toBeTruthy();
});
test.skip('should show achievement unlock', async ({ page }) => {
// NOTE: This would require completing specific conditions
// to unlock an achievement
// await loginAsStudent(page);
// await performActionThatUnlocksAchievement(page);
// Should show achievement modal/notification
await expect(page.locator('text=/logro|achievement|desbloquear/i')).toBeVisible({
timeout: 10000,
});
});
test.skip('should display leaderboard', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/leaderboard');
// Should show leaderboard table
await expect(page.locator('table, [role="table"]')).toBeVisible();
// Should have columns: Rank, Name, XP, Level
await expect(page.locator('text=/rango|rank|posición/i')).toBeVisible();
await expect(page.locator('text=/nombre|name|usuario/i')).toBeVisible();
await expect(page.locator('text=/XP|puntos/i')).toBeVisible();
});
test.skip('should highlight current user in leaderboard', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/leaderboard');
// Current user row should be highlighted
const highlightedRow = page.locator(
'[data-testid="current-user-row"], .current-user, .highlight',
);
await expect(highlightedRow).toBeVisible();
});
});
test.describe('Progress Tracking', () => {
test.skip('should show module completion status', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/progress');
// Should show completed modules
const completedModules = await page.locator('text=/completado|completed|✓/i').count();
// Should show in-progress modules
const inProgressModules = await page.locator('text=/en progreso|in progress/i').count();
// Should have at least one status shown
expect(completedModules + inProgressModules).toBeGreaterThan(0);
});
test.skip('should display overall progress percentage', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/progress');
// Should show percentage (e.g., "75% completed")
const hasPercentage = await page.locator('text=/\\d+%/').count();
expect(hasPercentage).toBeGreaterThan(0);
});
test.skip('should show recent activity', async ({ page }) => {
// await loginAsStudent(page);
await page.goto('/student/dashboard');
// Should show recent activity section
await expect(page.locator('text=/actividad reciente|recent activity/i')).toBeVisible();
// Should list recent exercises or modules
const activityItems = await page
.locator('[data-testid="activity-item"], .activity-item, li')
.count();
expect(activityItems).toBeGreaterThan(0);
});
});
/**
* Helper functions (to be implemented with actual test data)
* Prefixed with _ to indicate they are prepared for future use
*/
async function _loginAsStudent(page: import('@playwright/test').Page) {
await page.goto('/login');
await page.getByLabel(/email/i).fill('student@test.com');
await page.getByLabel(/password/i).fill('Test123!');
await page.getByRole('button', { name: /login/i }).click();
await page.waitForURL(/\/student\/dashboard/);
}
async function _navigateToModule(page: import('@playwright/test').Page) {
await page.goto('/student/modules');
await page.locator('[data-testid="module-card"]').first().click();
}
async function _startExercise(page: import('@playwright/test').Page) {
await _navigateToModule(page);
await page.getByRole('button', { name: /start/i }).click();
}
async function _completeExercise(page: import('@playwright/test').Page) {
await _startExercise(page);
// Answer all questions (simplified - assumes 5 questions)
for (let i = 0; i < 5; i++) {
await page.locator('input[type="radio"]').first().click();
await page.getByRole('button', { name: /submit|next/i }).click();
await page.waitForTimeout(500);
}
}