veza/tests/e2e/audit/interaction/03-forms-validation.spec.ts
senke 6fad0ad68d fix: stabilize frontend — 98 TS errors to 0, align API endpoints, optimize bundle
- Fix 98 TypeScript errors across 37 files:
  - Service layer double-unwrapping (subscriptionService, distributionService, gearService)
  - Self-referencing variables in SearchPageResults
  - FeedView/ExploreView .posts→.items alignment
  - useQueueSync Zustand subscribe API
  - AdminAuditLogsView missing interface fields
  - Toast proxy type, interceptor type narrowing
  - 22 unused imports/variables removed
  - 5 storybook mock data fixes

- Align frontend API calls with backend endpoints:
  - Analytics: useAnalyticsView now calls /creator/analytics/dashboard (was /analytics)
  - Chat: chatService uses /conversations (was mock data), WS URL from backend token
  - Dashboard StatsSection: uses real /dashboard API data (was hardcoded zeros)
  - Settings: suppress 2FA toast error when endpoint unavailable

- Fix marketplace products: seed uses 'active' status (was 'published')
- Enrich seed: admin follows all creators (feed has content)

- Optimize bundle: vendor catch-all 793KB→318KB gzip (-60%)
  Split into vendor-charts, vendor-emoji, vendor-swagger, vendor-media, etc.

- Clean repo: remove ~100 orphaned screenshots, audit reports, logs from root

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:18:49 +01:00

123 lines
5 KiB
TypeScript

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, navigateTo } from '../helpers';
import { getFormFields } from '../helpers/interaction-helpers';
import { TEST_USERS } from '../design-tokens';
test.describe('FORMULAIRES & VALIDATION — Chaque formulaire valide/invalide/vide', () => {
test('Login — soumission vide affiche des erreurs', async ({ page }) => {
await navigateTo(page, '/login');
const submit = page.getByTestId('login-submit');
await submit.waitFor({ state: 'visible', timeout: 10_000 });
// Vider les champs (au cas où ils sont pré-remplis)
const email = page.locator('input[type="email"]');
await email.waitFor({ state: 'visible' });
await email.clear();
const password = page.locator('input[type="password"]');
await password.clear();
// Soumettre vide
await submit.click();
await page.waitForTimeout(2_000);
// Doit rester sur /login
expect(page.url()).toContain('/login');
// Devrait afficher des messages d'erreur (validation HTML5 ou custom)
const body = await page.textContent('body') || '';
const hasValidation = body.match(/required|requis|obligatoire|invalide|invalid|veuillez|please/i) ||
(await page.locator('[class*="error"], [class*="destructive"], [role="alert"]').count()) > 0;
console.log(`[FORM] Login — validation errors visible: ${!!hasValidation}`);
});
test('Register — champs requis sont validés', async ({ page }) => {
await navigateTo(page, '/register');
const form = page.getByTestId('register-form').or(page.locator('form')).first();
await form.waitFor({ state: 'visible', timeout: 10_000 });
// Vérifier les champs
const fields = await getFormFields(page, form.first() ? 'form' : '[data-testid="register-form"]');
console.log(`[FORM] Register — ${fields.length} champs trouvés:`);
for (const field of fields) {
console.log(` ${field.name} (${field.type}) — required: ${field.required}, label: "${field.label}"`);
}
// Au minimum : email, password, username
expect(fields.length, 'Le formulaire d\'inscription devrait avoir au moins 3 champs').toBeGreaterThanOrEqual(3);
// Tester email invalide
const emailInput = page.locator('input[type="email"]');
if (await emailInput.isVisible().catch(() => false)) {
await emailInput.fill('not-an-email');
await page.locator('input[type="password"]').first().fill('a');
const submitBtn = page.locator('button[type="submit"]').first();
if (await submitBtn.isVisible().catch(() => false)) {
await submitBtn.click();
await page.waitForTimeout(2_000);
// Devrait montrer une erreur
expect(page.url()).toContain('/register');
}
}
});
test('Settings — les formulaires de profil sauvegardent correctement', async ({ page }) => {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
await navigateTo(page, '/settings');
// Vérifier que les champs de profil sont pré-remplis
const inputs = await page.locator('input:visible').all();
let prefilledCount = 0;
for (const input of inputs.slice(0, 10)) {
const value = await input.inputValue().catch(() => '');
if (value.length > 0) prefilledCount++;
}
console.log(`[FORM] Settings — ${prefilledCount}/${inputs.length} champs pré-remplis`);
});
test('Forms — pas de double soumission (bouton disabled après clic)', async ({ page }) => {
await navigateTo(page, '/login');
const email = page.locator('input[type="email"]');
await email.waitFor({ state: 'visible', timeout: 10_000 });
await email.fill(TEST_USERS.listener.email);
await page.locator('input[type="password"]').fill(TEST_USERS.listener.password);
const submit = page.getByTestId('login-submit');
await submit.click();
// Après le premier clic, vérifier si le bouton est désactivé ou en état loading
await page.waitForTimeout(500);
const isDisabledOrLoading = await submit.evaluate(el => {
return (el as HTMLButtonElement).disabled ||
el.getAttribute('aria-busy') === 'true' ||
el.classList.contains('loading') ||
el.textContent?.includes('...') || false;
}).catch(() => false);
console.log(`[FORM] Submit button disabled/loading after click: ${isDisabledOrLoading}`);
});
test('Forgot password — le formulaire accepte un email et affiche confirmation', async ({ page }) => {
await navigateTo(page, '/forgot-password');
const emailInput = page.locator('input[type="email"]');
await emailInput.waitFor({ state: 'visible', timeout: 10_000 });
await emailInput.fill('test@example.com');
const submit = page.locator('button[type="submit"]').first();
if (await submit.isVisible().catch(() => false)) {
await submit.click();
await page.waitForTimeout(3_000);
// La page ne devrait pas crasher
const body = await page.textContent('body') || '';
expect(body).not.toMatch(/500|Internal Server Error/i);
}
});
});