veza/apps/web/e2e/qa-audit.spec.ts

324 lines
10 KiB
TypeScript
Raw Normal View History

import { test, expect, Page } from '@playwright/test';
const BASE_URL = process.env.VITE_API_URL || 'http://localhost';
const API_URL = process.env.VITE_API_URL || 'http://localhost:8080/api/v1';
interface TestResult {
test: string;
status: 'pass' | 'fail' | 'skip';
error?: string;
details?: any;
}
const results: TestResult[] = [];
// Helper pour capturer les erreurs console
async function captureConsoleErrors(page: Page): Promise<string[]> {
const errors: string[] = [];
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
return errors;
}
// Helper pour capturer les erreurs réseau
async function captureNetworkErrors(page: Page): Promise<any[]> {
const networkErrors: any[] = [];
page.on('response', response => {
if (response.status() >= 400) {
networkErrors.push({
url: response.url(),
status: response.status(),
statusText: response.statusText(),
});
}
});
return networkErrors;
}
test.describe('QA E2E Audit - Veza Frontend', () => {
let page: Page;
let consoleErrors: string[] = [];
let networkErrors: any[] = [];
const testUser = {
email: `test.veza.qa+${Date.now()}@example.com`,
password: 'Test1234!@#',
username: `qa_test_user_${Date.now()}`,
};
test.beforeEach(async ({ page: testPage }) => {
page = testPage;
consoleErrors = [];
networkErrors = [];
// Capturer les erreurs
page.on('console', msg => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
page.on('response', response => {
if (response.status() >= 400) {
networkErrors.push({
url: response.url(),
status: response.status(),
statusText: response.statusText(),
});
}
});
});
test('1. Health Check - Backend API', async () => {
const response = await page.request.get(`${API_URL}/health`);
expect(response.status()).toBe(200);
results.push({
test: 'Backend API Health',
status: response.status() === 200 ? 'pass' : 'fail',
details: await response.json(),
});
});
test('2.1. Register - Test complet', async () => {
await page.goto(`${BASE_URL}/register`);
await page.waitForLoadState('networkidle');
// Test 1: Inscription avec données valides
await page.fill('input[name="email"]', testUser.email);
await page.fill('input[name="username"]', testUser.username);
await page.fill('input[name="password"]', testUser.password);
await page.fill('input[name="confirmPassword"]', testUser.password);
// Accepter les termes si checkbox existe
const termsCheckbox = page.locator('input[type="checkbox"]').first();
if (await termsCheckbox.isVisible()) {
await termsCheckbox.check();
}
// Capturer la réponse avant soumission
const responsePromise = page.waitForResponse(
response => response.url().includes('/auth/register') && response.request().method() === 'POST'
);
await page.click('button[type="submit"]');
try {
const response = await responsePromise;
const status = response.status();
const body = await response.json().catch(() => ({}));
results.push({
test: 'Register - Valid data',
status: status === 200 || status === 201 ? 'pass' : 'fail',
error: status >= 400 ? `Status ${status}: ${JSON.stringify(body)}` : undefined,
details: { status, body, consoleErrors: [...consoleErrors], networkErrors: [...networkErrors] },
});
if (status === 200 || status === 201) {
// Vérifier redirection vers dashboard
await page.waitForURL('**/dashboard', { timeout: 5000 }).catch(() => {});
}
} catch (error: any) {
results.push({
test: 'Register - Valid data',
status: 'fail',
error: error.message,
details: { consoleErrors: [...consoleErrors], networkErrors: [...networkErrors] },
});
}
});
test('2.2. Register - Validation errors', async () => {
await page.goto(`${BASE_URL}/register`);
await page.waitForLoadState('networkidle');
// Test email invalide
await page.fill('input[name="email"]', 'invalid-email');
await page.fill('input[name="password"]', 'short');
await page.click('button[type="submit"]');
const emailError = await page.locator('text=/email|Email/i').first().isVisible().catch(() => false);
results.push({
test: 'Register - Email validation',
status: emailError ? 'pass' : 'fail',
details: { emailError },
});
// Test mot de passe court
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'short');
await page.click('button[type="submit"]');
const passwordError = await page.locator('text=/password|mot de passe/i').first().isVisible().catch(() => false);
results.push({
test: 'Register - Password validation',
status: passwordError ? 'pass' : 'fail',
details: { passwordError },
});
});
test('2.3. Login - Test complet', async () => {
await page.goto(`${BASE_URL}/login`);
await page.waitForLoadState('networkidle');
// Test login avec mauvais mot de passe
await page.fill('input[name="email"]', testUser.email);
await page.fill('input[name="password"]', 'wrongpassword');
const responsePromise = page.waitForResponse(
response => response.url().includes('/auth/login') && response.request().method() === 'POST'
);
await page.click('button[type="submit"]');
try {
const response = await responsePromise;
const status = response.status();
results.push({
test: 'Login - Wrong password',
status: status === 401 ? 'pass' : 'fail',
error: status !== 401 ? `Expected 401, got ${status}` : undefined,
details: { status },
});
} catch (error: any) {
results.push({
test: 'Login - Wrong password',
status: 'fail',
error: error.message,
});
}
// Test login valide
await page.fill('input[name="password"]', testUser.password);
const loginResponsePromise = page.waitForResponse(
response => response.url().includes('/auth/login') && response.request().method() === 'POST'
);
await page.click('button[type="submit"]');
try {
const response = await loginResponsePromise;
const status = response.status();
const body = await response.json().catch(() => ({}));
results.push({
test: 'Login - Valid credentials',
status: status === 200 ? 'pass' : 'fail',
error: status !== 200 ? `Status ${status}: ${JSON.stringify(body)}` : undefined,
details: { status, body },
});
if (status === 200) {
await page.waitForURL('**/dashboard', { timeout: 5000 }).catch(() => {});
}
} catch (error: any) {
results.push({
test: 'Login - Valid credentials',
status: 'fail',
error: error.message,
});
}
});
test('3. Navigation - Toutes les pages', async () => {
// Se connecter d'abord
await page.goto(`${BASE_URL}/login`);
await page.fill('input[name="email"]', testUser.email);
await page.fill('input[name="password"]', testUser.password);
await page.click('button[type="submit"]');
await page.waitForURL('**/dashboard', { timeout: 10000 }).catch(() => {});
const pages = [
{ name: 'Dashboard', path: '/dashboard' },
{ name: 'Chat', path: '/chat' },
{ name: 'Library', path: '/library' },
{ name: 'Profile', path: '/profile' },
{ name: 'Settings', path: '/settings' },
{ name: 'Marketplace', path: '/marketplace' },
];
for (const pageInfo of pages) {
await page.goto(`${BASE_URL}${pageInfo.path}`);
await page.waitForLoadState('networkidle');
const title = await page.title();
const url = page.url();
const hasErrors = consoleErrors.length > 0 || networkErrors.length > 0;
results.push({
test: `Navigation - ${pageInfo.name}`,
status: url.includes(pageInfo.path) && !hasErrors ? 'pass' : 'fail',
error: hasErrors ? `Console errors: ${consoleErrors.length}, Network errors: ${networkErrors.length}` : undefined,
details: { url, title, consoleErrors: [...consoleErrors], networkErrors: [...networkErrors] },
});
// Reset errors pour la prochaine page
consoleErrors = [];
networkErrors = [];
}
});
test('4. Buttons and Actions - Dashboard', async () => {
await page.goto(`${BASE_URL}/dashboard`);
await page.waitForLoadState('networkidle');
// Tester tous les boutons visibles
const buttons = await page.locator('button').all();
const buttonTests: any[] = [];
for (const button of buttons.slice(0, 10)) { // Limiter à 10 pour éviter trop de tests
const text = await button.textContent().catch(() => '');
const isVisible = await button.isVisible().catch(() => false);
const isEnabled = await button.isEnabled().catch(() => false);
buttonTests.push({ text, isVisible, isEnabled });
}
results.push({
test: 'Dashboard - Buttons',
status: 'pass',
details: { buttons: buttonTests },
});
});
test('5. Logout', async () => {
await page.goto(`${BASE_URL}/dashboard`);
await page.waitForLoadState('networkidle');
// Ouvrir le menu utilisateur
const userMenuButton = page.locator('button[aria-label*="user" i], button:has-text("User")').first();
if (await userMenuButton.isVisible()) {
await userMenuButton.click();
await page.waitForTimeout(500);
// Cliquer sur logout
const logoutButton = page.locator('text=/logout|déconnexion/i').first();
if (await logoutButton.isVisible()) {
await logoutButton.click();
await page.waitForURL('**/login', { timeout: 5000 }).catch(() => {});
results.push({
test: 'Logout',
status: page.url().includes('/login') ? 'pass' : 'fail',
details: { finalUrl: page.url() },
});
}
}
});
test.afterAll(async () => {
// Générer le rapport
console.log('\n=== QA AUDIT RESULTS ===\n');
results.forEach(result => {
const icon = result.status === 'pass' ? '✅' : result.status === 'fail' ? '❌' : '⏭️';
console.log(`${icon} ${result.test}: ${result.status}`);
if (result.error) {
console.log(` Error: ${result.error}`);
}
});
});
});