veza/tests/e2e/audit/pixel-perfect/04-spacing-alignment.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

99 lines
3.9 KiB
TypeScript

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, navigateTo } from '../helpers';
import { checkAlignment } from '../helpers/visual-helpers';
import { TEST_USERS, ROUTES } from '../design-tokens';
test.describe('SPACING & ALIGNEMENT — Espacement régulier et éléments alignés', () => {
test.beforeEach(async ({ page }) => {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
});
test('Dashboard — les sections sont alignées et espacées', async ({ page }) => {
await navigateTo(page, '/dashboard');
// Trouver les conteneurs de grille
const gridContainers = await page.locator('main [class*="grid"], main [class*="flex"][class*="gap"]').all();
const allIssues: string[] = [];
for (const container of gridContainers.slice(0, 8)) {
try {
const selector = await container.evaluate(el => {
const testid = el.getAttribute('data-testid');
if (testid) return `[data-testid="${testid}"]`;
const classes = (typeof el.className === 'string' ? el.className : '').split(' ').slice(0, 3).join('.');
return `.${classes}`;
});
const issues = await checkAlignment(page, selector);
for (const issue of issues) {
if (issue.issue.includes('Désalignement')) {
allIssues.push(`${selector}: ${issue.issue}\nFIX: ${issue.fix}`);
console.log(`[ALIGNMENT] ${selector}: ${issue.issue}`);
console.log(` FIX: ${issue.fix}`);
}
}
} catch {
/* skip */
}
}
expect(allIssues.length,
`Problèmes d'alignement sur /dashboard:\n${allIssues.join('\n')}`
).toBe(0);
});
test('Discover — les track cards sont toutes de la même taille', async ({ page }) => {
await navigateTo(page, '/discover');
const cards = await page.locator('[role="article"]').all();
if (cards.length < 2) {
console.log('Moins de 2 track cards sur /discover — test non applicable');
return;
}
const sizes = await Promise.all(cards.slice(0, 20).map(async card => {
const box = await card.boundingBox();
return box ? { width: Math.round(box.width), height: Math.round(box.height) } : null;
}));
const validSizes = sizes.filter(Boolean) as { width: number; height: number }[];
if (validSizes.length < 2) return;
const widths = validSizes.map(s => s.width);
const widthDiff = Math.max(...widths) - Math.min(...widths);
expect(widthDiff,
`Largeurs de cards inconsistantes sur /discover: min=${Math.min(...widths)}px max=${Math.max(...widths)}px diff=${widthDiff}px. ` +
`FIX: Utiliser grid-cols-* avec fr units pour des largeurs uniformes.`
).toBeLessThan(5);
});
// Vérifier le padding du contenu principal sur chaque page
for (const route of ROUTES.listener.slice(0, 10)) {
test(`${route.name} (${route.path}) — le contenu principal a un padding suffisant`, async ({ page }) => {
await navigateTo(page, route.path);
const mainPadding = await page.evaluate(() => {
const main = document.querySelector('main, [role="main"]');
if (!main) return null;
const style = getComputedStyle(main);
return {
top: parseFloat(style.paddingTop),
right: parseFloat(style.paddingRight),
bottom: parseFloat(style.paddingBottom),
left: parseFloat(style.paddingLeft),
};
});
if (mainPadding) {
expect(mainPadding.left,
`Padding gauche insuffisant sur ${route.path}: ${mainPadding.left}px. FIX: Ajouter px-4 (16px) minimum au conteneur principal.`
).toBeGreaterThanOrEqual(8);
expect(mainPadding.right,
`Padding droite insuffisant sur ${route.path}: ${mainPadding.right}px. FIX: Ajouter px-4 (16px) minimum au conteneur principal.`
).toBeGreaterThanOrEqual(8);
}
});
}
});