diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index f7910b7..0000000 --- a/.dockerignore +++ /dev/null @@ -1,13 +0,0 @@ -target/ -.git/ -node_modules/ -.next/ -web/studio/node_modules/ -web/experience/node_modules/ -web/studio/.next/ -web/experience/.next/ -services/*/target/ -*.log -.env -.DS_Store -.vscode/ diff --git a/CACHED b/CACHED deleted file mode 100644 index e69de29..0000000 diff --git a/CANCELED b/CANCELED deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index 871486b..ed92027 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,12 @@ docker compose up -d --build - **Rendimiento Bajo (Orange)**: P% to P+9% - **Rendimiento Medio (Yellow)**: P+10% to P+15% - **Buen Rendimiento (Green)**: P+16% to 90% + - **Buen Rendimiento (Green)**: P+16% to 90% - **Excelente (Blue)**: 91%+ +- **Automated Certificate Generation**: + - HTML-based customizable certificate templates + - Automatic download button upon passing a course + - PDF generation for print/save ### 📈 Analytics & Insights - **Instructor Analytics Dashboard**: @@ -85,6 +90,10 @@ docker compose up -d --build - **Students**: Course enrollment, lesson consumption, progress tracking - **Service-to-Service Authorization**: Secure internal API calls with token validation - **Audit Logging**: All CMS mutations recorded for compliance and debugging +- **Admin Audit Dashboard**: + - Visual interface to view system logs + - Diff viewer for JSON changes + - Advanced filtering by user and action ### 🚀 Service Integration - **Automatic Sync**: One-click publish from CMS to LMS @@ -218,6 +227,8 @@ openccb/ - ✅ **Dynamic Passing Thresholds**: Customizable pass marks with 5-tier performance visualization - ✅ **Role-Based Access Control**: Admin, Instructor, and Student roles with granular permissions - ✅ **Enhanced Progress Dashboard**: Real-time weighted grades and visual performance bars +- ✅ **Certificate System**: Custom HTML templates and automated generation +- ✅ **Quality Assurance**: Automated End-to-End (E2E) testing pipeline with Playwright ## Contributing diff --git a/[experience b/[experience deleted file mode 100644 index e69de29..0000000 diff --git a/[studio b/[studio deleted file mode 100644 index e69de29..0000000 diff --git a/docker-compose.yml b/docker-compose.yml index 410b532..ccf5c06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,20 @@ services: environment: NEXT_PUBLIC_LMS_API_URL: http://localhost:3002 + e2e: + build: + context: ./e2e + environment: + - STUDIO_URL=http://studio:3000 + - EXPERIENCE_URL=http://experience:3003 + depends_on: + - studio + - experience + volumes: + - ./e2e/tests:/e2e/tests + - ./e2e/playwright-report:/e2e/playwright-report + profiles: [ "test" ] + volumes: postgres_data: uploads_data: diff --git a/e2e/Dockerfile b/e2e/Dockerfile new file mode 100644 index 0000000..d483114 --- /dev/null +++ b/e2e/Dockerfile @@ -0,0 +1,11 @@ +FROM mcr.microsoft.com/playwright:v1.40.0-jammy + +WORKDIR /e2e + +COPY package*.json ./ +RUN npm install + +COPY . . + +# Default command, can be overridden +CMD ["npx", "playwright", "test"] diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000..8db850f --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,14 @@ +{ + "name": "openccb-e2e", + "version": "1.0.0", + "scripts": { + "test": "playwright test", + "test:ui": "playwright test --ui", + "test:debug": "playwright test --debug" + }, + "devDependencies": { + "@playwright/test": "1.40.0", + "@types/node": "^20.10.0", + "typescript": "^5.3.0" + } +} \ No newline at end of file diff --git a/e2e/playwright-report/data/0351703cfc25c7bc4b88d18217ac5aedd94caf5e.png b/e2e/playwright-report/data/0351703cfc25c7bc4b88d18217ac5aedd94caf5e.png new file mode 100644 index 0000000..7a6cde4 Binary files /dev/null and b/e2e/playwright-report/data/0351703cfc25c7bc4b88d18217ac5aedd94caf5e.png differ diff --git a/e2e/playwright-report/data/228833cd27617ccc85c6de2c71fe9ba7c3bf289f.webm b/e2e/playwright-report/data/228833cd27617ccc85c6de2c71fe9ba7c3bf289f.webm new file mode 100644 index 0000000..cb6dfa3 Binary files /dev/null and b/e2e/playwright-report/data/228833cd27617ccc85c6de2c71fe9ba7c3bf289f.webm differ diff --git a/e2e/playwright-report/data/2820038172144c35dc7b48b89215617e6a1e2b6e.webm b/e2e/playwright-report/data/2820038172144c35dc7b48b89215617e6a1e2b6e.webm new file mode 100644 index 0000000..c18d720 Binary files /dev/null and b/e2e/playwright-report/data/2820038172144c35dc7b48b89215617e6a1e2b6e.webm differ diff --git a/e2e/playwright-report/data/2b074415dc91ba2ea942b07a85fcb587a1a0ecf9.png b/e2e/playwright-report/data/2b074415dc91ba2ea942b07a85fcb587a1a0ecf9.png new file mode 100644 index 0000000..780ee79 Binary files /dev/null and b/e2e/playwright-report/data/2b074415dc91ba2ea942b07a85fcb587a1a0ecf9.png differ diff --git a/e2e/playwright-report/data/32f6d3aa594400bc97ba56621573ec594af49a85.webm b/e2e/playwright-report/data/32f6d3aa594400bc97ba56621573ec594af49a85.webm new file mode 100644 index 0000000..8d7e74e Binary files /dev/null and b/e2e/playwright-report/data/32f6d3aa594400bc97ba56621573ec594af49a85.webm differ diff --git a/e2e/playwright-report/data/4739403224b5864965dacca5a0addf4fc64d95d2.webm b/e2e/playwright-report/data/4739403224b5864965dacca5a0addf4fc64d95d2.webm new file mode 100644 index 0000000..0f013e4 Binary files /dev/null and b/e2e/playwright-report/data/4739403224b5864965dacca5a0addf4fc64d95d2.webm differ diff --git a/e2e/playwright-report/data/4a312fe22936983e52b9576af833563f3a12104b.png b/e2e/playwright-report/data/4a312fe22936983e52b9576af833563f3a12104b.png new file mode 100644 index 0000000..6adda4d Binary files /dev/null and b/e2e/playwright-report/data/4a312fe22936983e52b9576af833563f3a12104b.png differ diff --git a/e2e/playwright-report/data/547ddda8a524d490694db731698bca0e0999047b.png b/e2e/playwright-report/data/547ddda8a524d490694db731698bca0e0999047b.png new file mode 100644 index 0000000..396d786 Binary files /dev/null and b/e2e/playwright-report/data/547ddda8a524d490694db731698bca0e0999047b.png differ diff --git a/e2e/playwright-report/data/571dc05dc609d2a91d50e14581bb841e7e7d53cb.webm b/e2e/playwright-report/data/571dc05dc609d2a91d50e14581bb841e7e7d53cb.webm new file mode 100644 index 0000000..21c3eea Binary files /dev/null and b/e2e/playwright-report/data/571dc05dc609d2a91d50e14581bb841e7e7d53cb.webm differ diff --git a/e2e/playwright-report/data/617510e7c753a3f75e7f670f16762f480d8f2160.webm b/e2e/playwright-report/data/617510e7c753a3f75e7f670f16762f480d8f2160.webm new file mode 100644 index 0000000..33eccab Binary files /dev/null and b/e2e/playwright-report/data/617510e7c753a3f75e7f670f16762f480d8f2160.webm differ diff --git a/e2e/playwright-report/data/724f95495a378f8e536ae7f3c35bbac342ae19fd.png b/e2e/playwright-report/data/724f95495a378f8e536ae7f3c35bbac342ae19fd.png new file mode 100644 index 0000000..4335591 Binary files /dev/null and b/e2e/playwright-report/data/724f95495a378f8e536ae7f3c35bbac342ae19fd.png differ diff --git a/e2e/playwright-report/data/81523a66ec93d844208cadf99f23a2ef3fea68ee.png b/e2e/playwright-report/data/81523a66ec93d844208cadf99f23a2ef3fea68ee.png new file mode 100644 index 0000000..55fcd83 Binary files /dev/null and b/e2e/playwright-report/data/81523a66ec93d844208cadf99f23a2ef3fea68ee.png differ diff --git a/e2e/playwright-report/data/a8686d22623518bc85c6aacd60717f28cdabcdc6.webm b/e2e/playwright-report/data/a8686d22623518bc85c6aacd60717f28cdabcdc6.webm new file mode 100644 index 0000000..8a84122 Binary files /dev/null and b/e2e/playwright-report/data/a8686d22623518bc85c6aacd60717f28cdabcdc6.webm differ diff --git a/e2e/playwright-report/data/dc021c92b7d1bef3cd8de6683970260b40c7c7a2.webm b/e2e/playwright-report/data/dc021c92b7d1bef3cd8de6683970260b40c7c7a2.webm new file mode 100644 index 0000000..e7d6dd4 Binary files /dev/null and b/e2e/playwright-report/data/dc021c92b7d1bef3cd8de6683970260b40c7c7a2.webm differ diff --git a/e2e/playwright-report/index.html b/e2e/playwright-report/index.html new file mode 100644 index 0000000..e7b6172 --- /dev/null +++ b/e2e/playwright-report/index.html @@ -0,0 +1,62 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + + \ No newline at end of file diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 0000000..82cdc52 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,44 @@ +import { defineConfig, devices } from '@playwright/test'; + +// Use environment variables for base URLs, with fallbacks for local dev vs docker +const STUDIO_URL = process.env.STUDIO_URL || 'http://studio:3000'; +const EXPERIENCE_URL = process.env.EXPERIENCE_URL || 'http://experience:3003'; + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + }, + + projects: [ + { + name: 'setup', + testMatch: /global\.setup\.ts/, + }, + { + name: 'studio', + use: { + ...devices['Desktop Chrome'], + baseURL: STUDIO_URL, + }, + testMatch: /.*instructor.*\.spec\.ts/, + dependencies: ['setup'], + }, + { + name: 'experience', + use: { + ...devices['Desktop Chrome'], + baseURL: EXPERIENCE_URL, + }, + testMatch: /.*student.*\.spec\.ts/, + dependencies: ['setup'], + }, + ], +}); diff --git a/e2e/tests/global.setup.ts b/e2e/tests/global.setup.ts new file mode 100644 index 0000000..4021109 --- /dev/null +++ b/e2e/tests/global.setup.ts @@ -0,0 +1,8 @@ +import { test as setup } from '@playwright/test'; + +setup('check services are up', async ({ request }) => { + // We could ping health endpoints here + // const studio = await request.get('http://studio:3000'); + // expect(studio.ok()).toBeTruthy(); + console.log('Setup complete - proceeding to tests'); +}); diff --git a/e2e/tests/instructor-flow.spec.ts b/e2e/tests/instructor-flow.spec.ts new file mode 100644 index 0000000..d94a699 --- /dev/null +++ b/e2e/tests/instructor-flow.spec.ts @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Instructor Flow', () => { + test('should login, create course, add content, and publish', async ({ page }) => { + const email = `instructor_${Date.now()}@test.com`; + + // 0. Register (since DB might be empty) + await page.goto('/auth/register'); + await page.fill('[placeholder="Instructor Name"]', 'E2E Instructor'); + await page.fill('[placeholder="instructor@openccb.com"]', email); // or input[type="email"] + await page.fill('[placeholder="••••••••"]', 'password123'); // or input[type="password"] + await page.click('button[type="submit"]'); + + // Wait for navigation - Register automatically logs in and redirects to / + // Increase timeout for cold starts in CI/Docker + await expect(page).toHaveURL('/', { timeout: 15000 }); + + // Verify dashboard loaded + await expect(page.locator('h2')).toContainText('My Courses', { timeout: 10000 }); + + // 2. Create Course + // Usamos manejador de dialogo para el prompt + const courseName = 'Playwright E2E Course ' + Date.now(); + page.on('dialog', dialog => dialog.accept(courseName)); + await page.click('button:has-text("New Course")'); + + // Esperar a que aparezca el nuevo curso y hacer clic + await page.waitForTimeout(1000); // Wait for API + await page.click('text=Playwright E2E Course'); + + // 3. Add Module + await page.click('button:has-text("Add Module")'); + await page.fill('[placeholder="Module Title"]', 'Module 1: Basics'); + await page.click('button:has-text("Create Module")'); + + // 4. Add Lesson + await page.click('button:has-text("Add Lesson")'); + await page.fill('[placeholder="Lesson Title"]', 'Intro Lesson'); + // Select video type (assuming it's default or select dropdown) + await page.click('button:has-text("Create Lesson")'); + + // 5. Publish + await page.click('button:has-text("Publish Course")'); + + // Confirm publish + // Assuming there is a confirmation or toast + // await expect(page.locator('text=Published successfully')).toBeVisible(); + }); +}); diff --git a/e2e/tests/student-flow.spec.ts b/e2e/tests/student-flow.spec.ts new file mode 100644 index 0000000..54a66b5 --- /dev/null +++ b/e2e/tests/student-flow.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Student Flow', () => { + test('should login and view catalog', async ({ page }) => { + // 1. Register/Login + // For simplicity, we assume registration or reuse existing + await page.goto('/auth/login'); + + // Register link? + // await page.click('text=Sign up'); + // ... fill registration ... + + // OR just login if we seed the DB. + // For E2E on fresh DB, we might need to register first. + + // Let's try to register a new user every time to be safe + // Let's try to register a new user every time to be safe + const email = `student_${Date.now()}@test.com`; + await page.goto('/auth/register'); + await page.fill('[placeholder="John Doe"]', 'Test Student'); + await page.fill('[placeholder="name@company.com"]', email); + await page.fill('[placeholder="••••••••"]', 'password123'); + await page.click('button[type="submit"]'); + + // Should redirect to dashboard/catalog + await expect(page).toHaveURL('/', { timeout: 15000 }); + await expect(page.locator('h1')).toContainText('Available Courses', { timeout: 10000 }); + + // Check if the course from instructor flow is visible (might need refresh) + await page.reload(); + // await expect(page.locator('text=Playwright E2E Course')).toBeVisible(); + }); +}); diff --git a/roadmap.md b/roadmap.md index 1239228..236b97a 100644 --- a/roadmap.md +++ b/roadmap.md @@ -32,7 +32,7 @@ - [x] Role-specific permissions and UI - [x] Token-based authorization for protected endpoints - [x] **Audit Logging**: All CMS mutations tracked -- [ ] **Audit UI**: Admin interface to view audit logs +- [x] **Audit UI**: Admin interface to view audit logs ## Phase 4: LMS Experience & Grading ✅ - [x] **Student Portal (Experience)**: @@ -55,7 +55,7 @@ - [x] Configurable passing percentage per course - [x] 5-tier performance visualization - [x] Color-coded feedback (Reprobado to Excelente) -- [ ] **Certificates**: Automated certificate generation upon completion +- [x] **Certificates**: Automated certificate generation upon completion ## Phase 5: Analytics & Insights ✅ - [x] **Instructor Analytics Dashboard**: @@ -122,7 +122,7 @@ - ✅ Enhanced student progress dashboard **Next Priorities**: -1. Automated certificate generation -2. Audit log UI for administrators -3. Multi-tenancy support -4. AI-powered content generation +1. Multi-tenancy support +2. AI-powered content generation +3. Gamification (Badges & Achievements) +4. Advanced analytics & reporting diff --git a/studio@0.1.0 b/studio@0.1.0 deleted file mode 100644 index e69de29..0000000