Le super admin (table super_admins, master DB) ne pouvait pas se connecter via /api/login car ce firewall n'utilisait que le provider tenant. De même, le JWT n'était pas enrichi pour les super admins, l'endpoint /api/me/roles les rejetait, et le frontend redirigeait systématiquement vers /dashboard. Un chain provider (super_admin + tenant) résout l'authentification, le JwtPayloadEnricher et MyRolesProvider gèrent désormais les deux types d'utilisateurs, et le frontend redirige selon le rôle après login.
209 lines
7.0 KiB
TypeScript
209 lines
7.0 KiB
TypeScript
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 projectRoot = join(__dirname, '../..');
|
|
const composeFile = join(projectRoot, 'compose.yaml');
|
|
|
|
// Extract port from PLAYWRIGHT_BASE_URL or use default (4173 matches playwright.config.ts)
|
|
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 SA_PASSWORD = 'SuperAdmin123';
|
|
const REGULAR_PASSWORD = 'TestPassword123';
|
|
|
|
function getSuperAdminEmail(browserName: string): string {
|
|
return `e2e-sadmin-${browserName}@test.com`;
|
|
}
|
|
|
|
function getRegularUserEmail(browserName: string): string {
|
|
return `e2e-sadmin-regular-${browserName}@example.com`;
|
|
}
|
|
|
|
// eslint-disable-next-line no-empty-pattern
|
|
test.beforeAll(async ({}, testInfo) => {
|
|
const browserName = testInfo.project.name;
|
|
const saEmail = getSuperAdminEmail(browserName);
|
|
const regularEmail = getRegularUserEmail(browserName);
|
|
|
|
try {
|
|
// Create a test super admin
|
|
const saResult = execSync(
|
|
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-super-admin --email=${saEmail} --password=${SA_PASSWORD} 2>&1`,
|
|
{ encoding: 'utf-8' }
|
|
);
|
|
console.warn(
|
|
`[${browserName}] Super admin created or exists:`,
|
|
saResult.includes('already exists') ? 'exists' : 'created'
|
|
);
|
|
|
|
// Create a regular user (for access control test)
|
|
const userResult = execSync(
|
|
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --email=${regularEmail} --password=${REGULAR_PASSWORD} 2>&1`,
|
|
{ encoding: 'utf-8' }
|
|
);
|
|
console.warn(
|
|
`[${browserName}] Regular user created or exists:`,
|
|
userResult.includes('already exists') ? 'exists' : 'created'
|
|
);
|
|
} catch (error) {
|
|
console.error(`[${browserName}] Failed to create test users:`, error);
|
|
}
|
|
});
|
|
|
|
async function loginAsSuperAdmin(
|
|
page: import('@playwright/test').Page,
|
|
email: string
|
|
) {
|
|
await page.goto(`${ALPHA_URL}/login`);
|
|
await expect(page.getByRole('heading', { name: /connexion/i })).toBeVisible();
|
|
|
|
await page.locator('#email').fill(email);
|
|
await page.locator('#password').fill(SA_PASSWORD);
|
|
|
|
const submitButton = page.getByRole('button', { name: /se connecter/i });
|
|
await Promise.all([
|
|
page.waitForURL('**/super-admin/dashboard', { timeout: 30000 }),
|
|
submitButton.click()
|
|
]);
|
|
}
|
|
|
|
test.describe('Super Admin', () => {
|
|
test.describe('Login & Redirect', () => {
|
|
test('super admin login redirects to /super-admin/dashboard', async ({ page }, testInfo) => {
|
|
const email = getSuperAdminEmail(testInfo.project.name);
|
|
|
|
await loginAsSuperAdmin(page, email);
|
|
|
|
await expect(page).toHaveURL(/\/super-admin\/dashboard/);
|
|
});
|
|
});
|
|
|
|
test.describe('Dashboard', () => {
|
|
test('dashboard displays stats cards', async ({ page }, testInfo) => {
|
|
const email = getSuperAdminEmail(testInfo.project.name);
|
|
|
|
await loginAsSuperAdmin(page, email);
|
|
|
|
// The dashboard should show stat cards
|
|
await expect(page.locator('.stat-card').first()).toBeVisible({ timeout: 10000 });
|
|
|
|
// Verify dashboard heading
|
|
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Navigation', () => {
|
|
test('navigates to establishments page', async ({ page }, testInfo) => {
|
|
const email = getSuperAdminEmail(testInfo.project.name);
|
|
|
|
await loginAsSuperAdmin(page, email);
|
|
|
|
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
|
|
await Promise.all([
|
|
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
|
|
etablissementsLink.click()
|
|
]);
|
|
|
|
await expect(page).toHaveURL(/\/super-admin\/establishments/);
|
|
await expect(
|
|
page.getByRole('heading', { name: /établissements/i })
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('establishments page has create button', async ({ page }, testInfo) => {
|
|
const email = getSuperAdminEmail(testInfo.project.name);
|
|
|
|
await loginAsSuperAdmin(page, email);
|
|
|
|
// Navigate via SPA link (page.goto would reload and lose in-memory token)
|
|
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
|
|
await Promise.all([
|
|
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
|
|
etablissementsLink.click()
|
|
]);
|
|
|
|
await expect(page.getByRole('heading', { name: /établissements/i })).toBeVisible({
|
|
timeout: 10000
|
|
});
|
|
|
|
// Check "Nouvel établissement" button/link
|
|
await expect(
|
|
page.getByRole('link', { name: /nouvel établissement/i })
|
|
).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Create Establishment Form', () => {
|
|
test('new establishment form has required fields', async ({ page }, testInfo) => {
|
|
const email = getSuperAdminEmail(testInfo.project.name);
|
|
|
|
await loginAsSuperAdmin(page, email);
|
|
|
|
// Navigate via SPA links (page.goto would reload and lose in-memory token)
|
|
const etablissementsLink = page.getByRole('link', { name: /établissements/i });
|
|
await Promise.all([
|
|
page.waitForURL('**/super-admin/establishments', { timeout: 10000 }),
|
|
etablissementsLink.click()
|
|
]);
|
|
|
|
const newLink = page.getByRole('link', { name: /nouvel établissement/i });
|
|
await expect(newLink).toBeVisible({ timeout: 10000 });
|
|
await Promise.all([
|
|
page.waitForURL('**/super-admin/establishments/new', { timeout: 10000 }),
|
|
newLink.click()
|
|
]);
|
|
|
|
// Verify form fields
|
|
await expect(page.locator('#name')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('#subdomain')).toBeVisible();
|
|
await expect(page.locator('#adminEmail')).toBeVisible();
|
|
|
|
// Submit button should be disabled when empty
|
|
const submitButton = page.getByRole('button', {
|
|
name: /créer l'établissement/i
|
|
});
|
|
await expect(submitButton).toBeDisabled();
|
|
|
|
// Fill in the form
|
|
await page.locator('#name').fill('École Test E2E');
|
|
await page.locator('#adminEmail').fill('admin-e2e@test.com');
|
|
|
|
// Subdomain should be auto-generated
|
|
await expect(page.locator('#subdomain')).not.toHaveValue('');
|
|
|
|
// Submit button should be enabled
|
|
await expect(submitButton).toBeEnabled();
|
|
});
|
|
});
|
|
|
|
test.describe('Access Control', () => {
|
|
test('regular user is redirected away from /super-admin', async ({ page }, testInfo) => {
|
|
const regularEmail = getRegularUserEmail(testInfo.project.name);
|
|
|
|
// Login as regular user on alpha tenant
|
|
await page.goto(`${ALPHA_URL}/login`);
|
|
await page.locator('#email').fill(regularEmail);
|
|
await page.locator('#password').fill(REGULAR_PASSWORD);
|
|
|
|
const submitButton = page.getByRole('button', { name: /se connecter/i });
|
|
await Promise.all([
|
|
page.waitForURL('**/dashboard', { timeout: 30000 }),
|
|
submitButton.click()
|
|
]);
|
|
|
|
// Try to navigate to super-admin area
|
|
await page.goto(`${ALPHA_URL}/super-admin/dashboard`);
|
|
|
|
// Should be redirected away (to /dashboard since not super admin)
|
|
await expect(page).not.toHaveURL(/\/super-admin/, { timeout: 10000 });
|
|
});
|
|
});
|
|
});
|