veza/tests/e2e/audit/pixel-perfect/18-dark-mode.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

101 lines
4.1 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 } from '../helpers';
import { TEST_USERS, ROUTES } from '../design-tokens';
test.describe('DARK MODE — Cohérence complète, aucun flash blanc', () => {
// Public pages
for (const route of ROUTES.public) {
test(`[PUBLIC] ${route.name} — aucun élément avec fond clair en dark mode`, async ({ page }) => {
await navigateTo(page, route.path);
const lightElements = await findLightBackgrounds(page);
for (const issue of lightElements) {
console.log(`[DARK MODE] ${issue}`);
}
expect(lightElements.length,
`Éléments trop clairs en dark mode sur ${route.path}:\n${lightElements.join('\n')}`
).toBe(0);
});
}
// Protected pages
for (const route of ROUTES.listener.slice(0, 10)) {
test(`[PROTECTED] ${route.name} — aucun élément avec fond clair en dark mode`, async ({ page }) => {
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
await navigateTo(page, route.path);
const lightElements = await findLightBackgrounds(page);
for (const issue of lightElements) {
console.log(`[DARK MODE] ${issue}`);
}
// Tolérance: certains éléments intentionnels (badges, pills) peuvent être clairs
expect(lightElements.length,
`Éléments trop clairs en dark mode sur ${route.path}:\n${lightElements.join('\n')}`
).toBeLessThanOrEqual(2);
});
}
test('Le body a la bonne couleur de fond SUMI void', async ({ page }) => {
await navigateTo(page, '/login');
const bodyBg = await page.evaluate(() => {
return getComputedStyle(document.body).backgroundColor;
});
console.log(`[DARK MODE] body background-color: ${bodyBg}`);
// SUMI bg-base is #121215 → rgb(18, 18, 21)
const match = bodyBg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
const luminance = 0.299 * parseInt(match[1]) + 0.587 * parseInt(match[2]) + 0.114 * parseInt(match[3]);
expect(luminance,
`ÉLÉMENT: body | MESURÉ: bg ${bodyBg} (luminance ${Math.round(luminance)}) | ATTENDU: luminance < 30 (dark theme) | FIX CSS: body background doit être var(--sumi-bg-base) (#121215)`
).toBeLessThan(50);
}
});
});
async function findLightBackgrounds(page: import('@playwright/test').Page): Promise<string[]> {
return page.evaluate(() => {
const issues: string[] = [];
const seen = new Set<string>();
document.querySelectorAll('*').forEach(el => {
const bg = getComputedStyle(el).backgroundColor;
const match = bg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (!match) return;
const [r, g, b] = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
// Skip elements that are intentionally light (foreground elements on dark bg, badges, etc.)
if (luminance <= 200) return;
const tag = el.tagName.toLowerCase();
// Skip images, SVG, video, canvas
if (['img', 'svg', 'video', 'canvas', 'path', 'circle', 'rect'].includes(tag)) return;
const rect = el.getBoundingClientRect();
// Only care about visible, sizeable elements
if (rect.width < 20 || rect.height < 20) return;
if (getComputedStyle(el).display === 'none' || getComputedStyle(el).visibility === 'hidden') return;
if (parseFloat(getComputedStyle(el).opacity) < 0.1) return;
const className = (typeof el.className === 'string' ? el.className : '').split(' ')[0] || '';
const key = `${tag}.${className}`;
if (seen.has(key)) return;
seen.add(key);
const testid = el.getAttribute('data-testid');
const selector = testid ? `[data-testid="${testid}"]` : `${tag}.${className}`;
issues.push(
`ÉLÉMENT: ${selector} (${Math.round(rect.width)}×${Math.round(rect.height)}px) | PAGE: ${location.pathname} | MESURÉ: bg ${bg} (luminance ${Math.round(luminance)}) | ATTENDU: luminance < 60 (dark theme) | FIX TAILWIND: Changer bg-white en bg-background ou bg-card sur ${selector}`
);
});
return issues.slice(0, 8);
});
}