- 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>
96 lines
4 KiB
TypeScript
96 lines
4 KiB
TypeScript
import { test, expect } from '@chromatic-com/playwright';
|
|
import { loginViaAPI, navigateTo } from '../helpers';
|
|
import { testKeyboardNav } from '../helpers/interaction-helpers';
|
|
import { TEST_USERS, ROUTES } from '../design-tokens';
|
|
|
|
test.describe('KEYBOARD — Tab, Enter, Escape fonctionnent sur tout', () => {
|
|
test('Login — le formulaire est entièrement navigable au clavier', async ({ page }) => {
|
|
await navigateTo(page, '/login');
|
|
|
|
// Tab through the form
|
|
const { focusOrder, issues } = await testKeyboardNav(page, 10);
|
|
|
|
// Au moins email, password, submit doivent être focusables
|
|
const hasTags = focusOrder.map(f => f.tag);
|
|
expect(hasTags, 'Le formulaire de login devrait avoir des tab stops (input, button)').not.toHaveLength(0);
|
|
|
|
// Enter sur le bouton submit devrait soumettre le formulaire
|
|
// (testé indirectement via les tests fonctionnels)
|
|
|
|
console.log(`[KEYBOARD] Login — ${focusOrder.length} tab stops, ${issues.length} sans focus visible`);
|
|
});
|
|
|
|
test('Dashboard — Escape ferme les éléments ouverts', async ({ page }) => {
|
|
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
|
|
await navigateTo(page, '/dashboard');
|
|
|
|
// Vérifier que Escape ne cause pas d'erreur sur une page sans modal ouvert
|
|
await page.keyboard.press('Escape');
|
|
await page.waitForTimeout(500);
|
|
|
|
// La page ne devrait pas crasher
|
|
const body = await page.textContent('body') || '';
|
|
expect(body).not.toMatch(/500|Internal Server Error/i);
|
|
});
|
|
|
|
for (const route of ROUTES.listener.slice(0, 8)) {
|
|
test(`${route.name} (${route.path}) — navigation clavier possible`, async ({ page }) => {
|
|
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
|
|
await navigateTo(page, route.path);
|
|
|
|
const { focusOrder, totalTabStops } = await testKeyboardNav(page, 15);
|
|
|
|
console.log(`[KEYBOARD] ${route.path} — ${totalTabStops} tab stops`);
|
|
|
|
// Chaque page devrait avoir au moins 1 élément focusable
|
|
expect(totalTabStops, `Aucun tab stop sur ${route.path} — la page n'est pas navigable au clavier`).toBeGreaterThan(0);
|
|
});
|
|
}
|
|
|
|
test('Enter active les boutons focusés', async ({ page }) => {
|
|
await navigateTo(page, '/login');
|
|
|
|
// Focus sur le bouton submit
|
|
const submit = page.getByTestId('login-submit');
|
|
await submit.waitFor({ state: 'visible', timeout: 10_000 });
|
|
|
|
// Tab jusqu'au submit
|
|
for (let i = 0; i < 10; i++) {
|
|
await page.keyboard.press('Tab');
|
|
const focused = await page.evaluate(() => document.activeElement?.getAttribute('data-testid'));
|
|
if (focused === 'login-submit') break;
|
|
}
|
|
|
|
// Vérifier que l'élément focusé est bien le submit
|
|
const focusedTestId = await page.evaluate(() => document.activeElement?.getAttribute('data-testid'));
|
|
console.log(`[KEYBOARD] Focused element: ${focusedTestId}`);
|
|
});
|
|
|
|
test('Space toggle les checkboxes et switches', async ({ page }) => {
|
|
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
|
|
await navigateTo(page, '/settings');
|
|
|
|
// Chercher un switch ou checkbox
|
|
const toggle = page.locator('[role="switch"]:visible, input[type="checkbox"]:visible').first();
|
|
if (!await toggle.isVisible({ timeout: 5_000 }).catch(() => false)) return;
|
|
|
|
// Capturer l'état initial
|
|
const before = await toggle.evaluate(el => {
|
|
if (el.getAttribute('role') === 'switch') return el.getAttribute('aria-checked');
|
|
return (el as HTMLInputElement).checked ? 'true' : 'false';
|
|
});
|
|
|
|
// Focus + Space
|
|
await toggle.focus();
|
|
await page.keyboard.press('Space');
|
|
await page.waitForTimeout(300);
|
|
|
|
const after = await toggle.evaluate(el => {
|
|
if (el.getAttribute('role') === 'switch') return el.getAttribute('aria-checked');
|
|
return (el as HTMLInputElement).checked ? 'true' : 'false';
|
|
});
|
|
|
|
console.log(`[KEYBOARD] Switch/checkbox: ${before} → ${after}`);
|
|
// Le toggle devrait changer d'état (sauf si disabled)
|
|
});
|
|
});
|