- 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>
101 lines
4.1 KiB
TypeScript
101 lines
4.1 KiB
TypeScript
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);
|
||
});
|
||
}
|