veza/tests/e2e/audit/pixel-perfect/02-hover-states.spec.ts
senke 7b39efa176 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

62 lines
2.8 KiB
TypeScript

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, navigateTo } from '../helpers';
import { checkHoverState } from '../helpers/visual-helpers';
import { TEST_USERS, ROUTES } from '../design-tokens';
test.describe('HOVER — Chaque bouton/lien a un feedback visuel au survol', () => {
const pagesToCheck = [
{ path: '/login', auth: false, name: 'Login' },
{ path: '/register', auth: false, name: 'Register' },
{ path: '/dashboard', auth: true, name: 'Dashboard' },
{ path: '/discover', auth: true, name: 'Discover' },
{ path: '/library', auth: true, name: 'Library' },
{ path: '/settings', auth: true, name: 'Settings' },
{ path: '/playlists', auth: true, name: 'Playlists' },
{ path: '/marketplace', auth: true, name: 'Marketplace' },
{ path: '/feed', auth: true, name: 'Feed' },
{ path: '/profile', auth: true, name: 'Profile' },
{ path: '/notifications', auth: true, name: 'Notifications' },
];
for (const p of pagesToCheck) {
test(`${p.name} (${p.path}) — tous les boutons changent visuellement au hover`, async ({ page }) => {
if (p.auth) {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
}
await navigateTo(page, p.path);
const buttons = await page.locator('button:visible').all();
const links = await page.locator('a:visible').all();
const allInteractive = [...buttons, ...links];
const issues: string[] = [];
for (const btn of allInteractive.slice(0, 30)) {
try {
// Skip éléments trop petits ou hors viewport
const box = await btn.boundingBox();
if (!box || box.width < 10 || box.height < 10) continue;
if (box.y < 0 || box.y > 900) continue;
const report = await checkHoverState(page, btn);
if (report.issues.length > 0) {
issues.push(`"${report.text}" (${report.selector}): ${report.issues.join(', ')}`);
console.log(`[HOVER ISSUE] "${report.text}"`);
console.log(` Before: bg=${report.before.bg}, color=${report.before.color}, cursor=${report.before.cursor}`);
console.log(` After: bg=${report.after.bg}, color=${report.after.color}, cursor=${report.after.cursor}`);
console.log(` Issues: ${report.issues.join(', ')}`);
}
// Reset mouse
await page.mouse.move(0, 0);
} catch {
/* élément détaché — skip */
}
}
// Tolérance : max 15% de boutons sans hover
const tolerance = Math.max(3, Math.ceil(allInteractive.length * 0.15));
expect(issues.length,
`${issues.length}/${allInteractive.length} bouton(s) sans hover visible sur ${p.path}:\n${issues.join('\n')}`
).toBeLessThanOrEqual(tolerance);
});
}
});