- 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>
124 lines
5.7 KiB
TypeScript
124 lines
5.7 KiB
TypeScript
import { test, expect } from '@chromatic-com/playwright';
|
|
import { loginViaAPI, navigateTo } from '../helpers';
|
|
import { TEST_USERS, ROUTES } from '../design-tokens';
|
|
|
|
test.describe('TEXTE — Pas de texte coupé ni qui déborde de son conteneur', () => {
|
|
// Pages publiques
|
|
for (const route of ROUTES.public) {
|
|
test(`[PUBLIC] ${route.name} — aucun texte ne déborde`, async ({ page }) => {
|
|
await navigateTo(page, route.path);
|
|
|
|
const overflowingText = await page.evaluate(() => {
|
|
const issues: Array<{ text: string; selector: string; containerWidth: number; textWidth: number; overflow: number; fix: string }> = [];
|
|
|
|
document.querySelectorAll('p, span, h1, h2, h3, h4, h5, h6, a, button, label, td, th, li').forEach(el => {
|
|
const text = el.textContent?.trim();
|
|
if (!text || text.length < 3) return;
|
|
if (el.children.length > 2) return;
|
|
const style = getComputedStyle(el);
|
|
if (style.display === 'none' || style.visibility === 'hidden') return;
|
|
if (el.clientWidth === 0) return;
|
|
|
|
const isOverflowing = el.scrollWidth > el.clientWidth + 2;
|
|
const hasEllipsis = style.textOverflow === 'ellipsis' && style.overflow === 'hidden';
|
|
const hasLineClamp = style.webkitLineClamp !== '' && style.webkitLineClamp !== 'none';
|
|
const isPreWrap = style.whiteSpace === 'pre' || style.whiteSpace === 'pre-wrap' || style.whiteSpace === 'nowrap';
|
|
|
|
if (isOverflowing && !hasEllipsis && !hasLineClamp) {
|
|
// Skip elements inside overflow:hidden parents (already clipped)
|
|
let clipped = false;
|
|
let parent = el.parentElement;
|
|
while (parent) {
|
|
const ps = getComputedStyle(parent);
|
|
if (ps.overflow === 'hidden' || ps.overflowX === 'hidden') { clipped = true; break; }
|
|
parent = parent.parentElement;
|
|
}
|
|
if (clipped) return;
|
|
|
|
const className = (typeof el.className === 'string' ? el.className : '').split(' ')[0] || '';
|
|
const testid = el.getAttribute('data-testid');
|
|
const selector = testid ? `[data-testid="${testid}"]` : `${el.tagName.toLowerCase()}.${className}`;
|
|
const overflow = Math.round(el.scrollWidth - el.clientWidth);
|
|
|
|
issues.push({
|
|
text: text.slice(0, 40),
|
|
selector,
|
|
containerWidth: Math.round(el.clientWidth),
|
|
textWidth: Math.round(el.scrollWidth),
|
|
overflow,
|
|
fix: `ÉLÉMENT: ${selector} | PAGE: ${location.pathname} | MESURÉ: scrollWidth ${Math.round(el.scrollWidth)}px > clientWidth ${Math.round(el.clientWidth)}px (+${overflow}px) | FIX TAILWIND: Ajouter truncate (overflow-hidden text-ellipsis whitespace-nowrap) ou line-clamp-2 sur ${selector}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
return issues.slice(0, 20);
|
|
});
|
|
|
|
for (const issue of overflowingText) {
|
|
console.log(`[TEXT OVERFLOW] ${issue.fix}`);
|
|
}
|
|
|
|
expect(overflowingText.length,
|
|
`${overflowingText.length} texte(s) qui déborde(nt) sur ${route.path}:\n` +
|
|
overflowingText.map(i => `• ${i.fix}`).join('\n')
|
|
).toBe(0);
|
|
});
|
|
}
|
|
|
|
// Pages protégées
|
|
for (const route of ROUTES.listener.slice(0, 12)) {
|
|
test(`[PROTECTED] ${route.name} — aucun texte ne déborde`, async ({ page }) => {
|
|
await loginViaAPI(page, TEST_USERS.listener.email, TEST_USERS.listener.password);
|
|
await navigateTo(page, route.path);
|
|
|
|
const overflowingText = await page.evaluate(() => {
|
|
const issues: Array<{ text: string; selector: string; overflow: number; fix: string }> = [];
|
|
|
|
document.querySelectorAll('p, span, h1, h2, h3, h4, h5, h6, a, button, label, td, th, li').forEach(el => {
|
|
const text = el.textContent?.trim();
|
|
if (!text || text.length < 3 || el.children.length > 2) return;
|
|
const style = getComputedStyle(el);
|
|
if (style.display === 'none' || style.visibility === 'hidden' || el.clientWidth === 0) return;
|
|
|
|
const isOverflowing = el.scrollWidth > el.clientWidth + 2;
|
|
const hasEllipsis = style.textOverflow === 'ellipsis' && style.overflow === 'hidden';
|
|
const hasLineClamp = style.webkitLineClamp !== '' && style.webkitLineClamp !== 'none';
|
|
|
|
if (isOverflowing && !hasEllipsis && !hasLineClamp) {
|
|
let clipped = false;
|
|
let parent = el.parentElement;
|
|
while (parent) {
|
|
const ps = getComputedStyle(parent);
|
|
if (ps.overflow === 'hidden' || ps.overflowX === 'hidden') { clipped = true; break; }
|
|
parent = parent.parentElement;
|
|
}
|
|
if (clipped) return;
|
|
|
|
const className = (typeof el.className === 'string' ? el.className : '').split(' ')[0] || '';
|
|
const testid = el.getAttribute('data-testid');
|
|
const selector = testid ? `[data-testid="${testid}"]` : `${el.tagName.toLowerCase()}.${className}`;
|
|
const overflow = Math.round(el.scrollWidth - el.clientWidth);
|
|
|
|
issues.push({
|
|
text: text.slice(0, 40),
|
|
selector,
|
|
overflow,
|
|
fix: `ÉLÉMENT: ${selector} | PAGE: ${location.pathname} | MESURÉ: +${overflow}px de texte débordant | FIX TAILWIND: Ajouter truncate ou line-clamp-2 sur ${selector}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
return issues.slice(0, 20);
|
|
});
|
|
|
|
for (const issue of overflowingText) {
|
|
console.log(`[TEXT OVERFLOW] ${issue.fix}`);
|
|
}
|
|
|
|
expect(overflowingText.length,
|
|
`${overflowingText.length} texte(s) qui déborde(nt) sur ${route.path}:\n` +
|
|
overflowingText.map(i => `• ${i.fix}`).join('\n')
|
|
).toBe(0);
|
|
});
|
|
}
|
|
});
|