import { test, expect } from '@playwright/test'; import { execSync } from 'child_process'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const baseUrl = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:4173'; const urlMatch = baseUrl.match(/:(\d+)$/); const PORT = urlMatch ? urlMatch[1] : '4173'; const ALPHA_URL = `http://ecole-alpha.classeo.local:${PORT}`; const ADMIN_EMAIL = 'e2e-blocking-admin@example.com'; const ADMIN_PASSWORD = 'BlockingTest123'; const TARGET_EMAIL = 'e2e-blocking-target@example.com'; const TARGET_PASSWORD = 'TargetUser123'; test.describe('User Blocking', () => { test.describe.configure({ mode: 'serial' }); test.beforeAll(async () => { const projectRoot = join(__dirname, '../..'); const composeFile = join(projectRoot, 'compose.yaml'); // Create admin user execSync( `docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${ADMIN_EMAIL} --password=${ADMIN_PASSWORD} --role=ROLE_ADMIN 2>&1`, { encoding: 'utf-8' } ); // Create target user to be blocked execSync( `docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=ecole-alpha --email=${TARGET_EMAIL} --password=${TARGET_PASSWORD} --role=ROLE_PROF 2>&1`, { encoding: 'utf-8' } ); }); async function loginAsAdmin(page: import('@playwright/test').Page) { await page.goto(`${ALPHA_URL}/login`); await page.locator('#email').fill(ADMIN_EMAIL); await page.locator('#password').fill(ADMIN_PASSWORD); await page.getByRole('button', { name: /se connecter/i }).click(); await expect(page).toHaveURL(/\/dashboard/, { timeout: 10000 }); } test('admin can block a user and sees blocked status', async ({ page }) => { await loginAsAdmin(page); await page.goto(`${ALPHA_URL}/admin/users`); // Wait for users table to load await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 }); // Find the target user row const targetRow = page.locator('tr', { has: page.locator(`text=${TARGET_EMAIL}`) }); await expect(targetRow).toBeVisible(); // Click "Bloquer" button await targetRow.getByRole('button', { name: /bloquer/i }).click(); // Block modal should appear await expect(page.locator('#block-modal-title')).toBeVisible(); // Fill in the reason await page.locator('#block-reason').fill('Comportement inapproprié en E2E'); // Confirm the block await page.getByRole('button', { name: /confirmer le blocage/i }).click(); // Wait for the success message await expect(page.locator('.alert-success')).toBeVisible({ timeout: 5000 }); // Verify the user status changed to "Suspendu" const updatedRow = page.locator('tr', { has: page.locator(`text=${TARGET_EMAIL}`) }); await expect(updatedRow.locator('.status-blocked')).toContainText('Suspendu'); // Verify the reason is displayed await expect(updatedRow.locator('.blocked-reason')).toContainText('Comportement inapproprié en E2E'); }); test('admin can unblock a suspended user', async ({ page }) => { await loginAsAdmin(page); await page.goto(`${ALPHA_URL}/admin/users`); await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 }); // Find the suspended target user row const targetRow = page.locator('tr', { has: page.locator(`text=${TARGET_EMAIL}`) }); await expect(targetRow).toBeVisible(); // "Débloquer" button should be visible for suspended user const unblockButton = targetRow.getByRole('button', { name: /débloquer/i }); await expect(unblockButton).toBeVisible(); // Click unblock await unblockButton.click(); // Wait for the success message await expect(page.locator('.alert-success')).toBeVisible({ timeout: 5000 }); // Verify the user status changed back to "Actif" const updatedRow = page.locator('tr', { has: page.locator(`text=${TARGET_EMAIL}`) }); await expect(updatedRow.locator('.status-active')).toContainText('Actif'); }); test('blocked user sees specific error on login', async ({ page }) => { // First, block the user again await loginAsAdmin(page); await page.goto(`${ALPHA_URL}/admin/users`); await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 }); const targetRow = page.locator('tr', { has: page.locator(`text=${TARGET_EMAIL}`) }); await targetRow.getByRole('button', { name: /bloquer/i }).click(); await page.locator('#block-reason').fill('Bloqué pour test login'); await page.getByRole('button', { name: /confirmer le blocage/i }).click(); await expect(page.locator('.alert-success')).toBeVisible({ timeout: 5000 }); // Logout await page.getByRole('button', { name: /déconnexion/i }).click(); // Try to log in as the blocked user await page.goto(`${ALPHA_URL}/login`); await page.locator('#email').fill(TARGET_EMAIL); await page.locator('#password').fill(TARGET_PASSWORD); await page.getByRole('button', { name: /se connecter/i }).click(); // Should see a suspended account error, not the generic credentials error const errorBanner = page.locator('.error-banner.account-suspended'); await expect(errorBanner).toBeVisible({ timeout: 5000 }); await expect(errorBanner).toContainText(/suspendu|contactez/i); }); test('admin cannot block themselves', async ({ page }) => { await loginAsAdmin(page); await page.goto(`${ALPHA_URL}/admin/users`); await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 }); // Find the admin's own row const adminRow = page.locator('tr', { has: page.locator(`text=${ADMIN_EMAIL}`) }); await expect(adminRow).toBeVisible(); // "Bloquer" button should NOT be present on the admin's own row await expect(adminRow.getByRole('button', { name: /^bloquer$/i })).not.toBeVisible(); }); });