veza/tests/e2e/audit/pixel-perfect/01-element-overlap.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

111 lines
5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, navigateTo, navigateToPageWithTracks, playFirstTrack } from '../helpers';
import { detectOverlaps } from '../helpers/visual-helpers';
import { TEST_USERS, ROUTES, ALL_PROTECTED_ROUTES } from '../design-tokens';
test.describe('CHEVAUCHEMENTS — Aucun élément interactif ne passe par-dessus un autre', () => {
// --- Pages publiques (pas de login) ---
for (const route of ROUTES.public) {
test(`[PUBLIC] ${route.name} (${route.path}) — zéro chevauchement critique`, async ({ page }) => {
await navigateTo(page, route.path);
const overlaps = await detectOverlaps(page);
const critical = overlaps.filter(o => o.severity === 'critical');
for (const o of overlaps) {
console.log(`[${o.severity.toUpperCase()}] "${o.elementA.text}" ↔ "${o.elementB.text}" : ${o.overlapX}px × ${o.overlapY}px`);
console.log(` FIX: ${o.fix}`);
}
expect(critical.length,
`${critical.length} chevauchement(s) critique(s) sur ${route.path}:\n` +
critical.map(o => `• "${o.elementA.text}" (${o.elementA.rect.x},${o.elementA.rect.y} ${o.elementA.rect.width}×${o.elementA.rect.height}) ↔ "${o.elementB.text}" (${o.elementB.rect.x},${o.elementB.rect.y} ${o.elementB.rect.width}×${o.elementB.rect.height}) overlap: ${o.overlapX}×${o.overlapY}px → ${o.fix}`).join('\n')
).toBe(0);
});
}
// --- Pages protégées (listener) ---
for (const route of ROUTES.listener) {
test(`[LISTENER] ${route.name} (${route.path}) — zéro chevauchement critique`, async ({ page }) => {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
await navigateTo(page, route.path);
const overlaps = await detectOverlaps(page);
const critical = overlaps.filter(o => o.severity === 'critical');
for (const o of overlaps) {
console.log(`[${o.severity.toUpperCase()}] "${o.elementA.text}" ↔ "${o.elementB.text}" : ${o.overlapX}px × ${o.overlapY}px`);
console.log(` FIX: ${o.fix}`);
}
expect(critical.length,
`${critical.length} chevauchement(s) critique(s) sur ${route.path}:\n` +
critical.map(o => `• "${o.elementA.text}" ↔ "${o.elementB.text}" ${o.overlapX}×${o.overlapY}px → ${o.fix}`).join('\n')
).toBe(0);
});
}
// --- Player bar ne recouvre pas le contenu ---
test('Le player bar ne recouvre aucun contenu interactif de la page', async ({ page }) => {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
const hasTracks = await navigateToPageWithTracks(page);
if (!hasTracks) {
console.log('Pas de tracks disponibles — test player bar non applicable');
return;
}
await playFirstTrack(page);
await page.waitForTimeout(2_000);
const playerIssues = await page.evaluate(() => {
const player = document.querySelector('[data-testid="global-player"]');
if (!player) return ['Player bar non trouvé'];
const playerRect = player.getBoundingClientRect();
const issues: string[] = [];
// Vérifier que le main content a assez de padding en bas
const main = document.querySelector('main, [role="main"]');
if (main) {
const mainRect = main.getBoundingClientRect();
const mainPaddingBottom = parseFloat(getComputedStyle(main).paddingBottom);
if (mainPaddingBottom < playerRect.height) {
issues.push(
`Le main content a padding-bottom=${mainPaddingBottom}px mais le player fait ${Math.round(playerRect.height)}px. ` +
`Les derniers éléments du contenu seront cachés sous le player. ` +
`FIX: Ajouter pb-[${Math.ceil(playerRect.height + 16)}px] au conteneur principal.`
);
}
}
// Vérifier qu'aucun bouton du contenu n'est sous le player
document.querySelectorAll('main button, main a, main input').forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
if (getComputedStyle(el).display === 'none') return;
const overlapY = Math.max(0, Math.min(rect.bottom, playerRect.bottom) - Math.max(rect.top, playerRect.top));
const overlapX = Math.max(0, Math.min(rect.right, playerRect.right) - Math.max(rect.left, playerRect.left));
if (overlapX > 0 && overlapY > 10) {
const text = el.textContent?.trim().slice(0, 30) || el.getAttribute('aria-label') || '';
issues.push(
`"${text}" (${el.tagName.toLowerCase()}) est recouvert par le player bar de ${Math.round(overlapY)}px. ` +
`Position: y=${Math.round(rect.top)} vs player.top=${Math.round(playerRect.top)}`
);
}
});
return issues;
});
for (const issue of playerIssues) {
console.log(`[PLAYER OVERLAP] ${issue}`);
}
expect(playerIssues.length,
`Le player bar recouvre du contenu:\n${playerIssues.join('\n')}`
).toBe(0);
});
});