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 20:18:49 +00:00
|
|
|
import { test, expect } from '@chromatic-com/playwright';
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
import { loginViaAPI, CONFIG, navigateTo, playFirstTrack } from './helpers';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* VISUAL BUGS — Tests ciblés pour prévenir les bugs visuels
|
|
|
|
|
* Touch targets, images cassées, overflow, contraste
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
test.describe('VISUAL — Touch targets mobile @visual @a11y @mobile', () => {
|
|
|
|
|
test.use({ viewport: { width: 375, height: 667 } });
|
|
|
|
|
|
|
|
|
|
test('Player controls — touch targets ≥ 44x44px', async ({ page }) => {
|
|
|
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
|
|
|
await navigateTo(page, '/discover');
|
|
|
|
|
await playFirstTrack(page);
|
|
|
|
|
await page.waitForTimeout(2000);
|
|
|
|
|
|
|
|
|
|
const playerBar = page.getByTestId('player-bar').or(page.getByTestId('global-player'));
|
|
|
|
|
if (await playerBar.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
|
|
|
const buttons = await playerBar.locator('button').all();
|
|
|
|
|
const tooSmall: string[] = [];
|
|
|
|
|
|
|
|
|
|
for (const btn of buttons) {
|
|
|
|
|
if (await btn.isVisible().catch(() => false)) {
|
|
|
|
|
const box = await btn.boundingBox();
|
|
|
|
|
if (box && (box.width < 32 || box.height < 32)) {
|
|
|
|
|
const label = await btn.getAttribute('aria-label') || await btn.textContent() || 'unknown';
|
|
|
|
|
tooSmall.push(`${label}: ${box.width}x${box.height}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.
Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
30-marketplace-checkout (1→0), 22-performance (1→0),
31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
33-visual-bugs (0→0)
Total: 139 fake assertions → real expect(), 159 informational logs removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:50:17 +00:00
|
|
|
expect(tooSmall, `Touch targets too small: ${tooSmall.join(', ')}`).toHaveLength(0);
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Sidebar — pas de débordement horizontal sur mobile', async ({ page }) => {
|
|
|
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
|
|
|
await navigateTo(page, '/dashboard');
|
|
|
|
|
|
|
|
|
|
const hasOverflow = await page.evaluate(() => {
|
|
|
|
|
return document.documentElement.scrollWidth > document.documentElement.clientWidth;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(hasOverflow).toBeFalsy();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Login form — centré et pas de débordement', async ({ page }) => {
|
|
|
|
|
await page.goto('/login');
|
|
|
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
|
|
|
|
|
|
const hasOverflow = await page.evaluate(() => {
|
|
|
|
|
return document.documentElement.scrollWidth > document.documentElement.clientWidth;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(hasOverflow).toBeFalsy();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test.describe('VISUAL — Images cassées @visual', () => {
|
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
|
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Discover — aucune image cassée', async ({ page }) => {
|
|
|
|
|
await navigateTo(page, '/discover');
|
|
|
|
|
await page.waitForTimeout(3000);
|
|
|
|
|
|
|
|
|
|
const brokenImages = await page.evaluate(() => {
|
|
|
|
|
const imgs = document.querySelectorAll('img');
|
|
|
|
|
return Array.from(imgs)
|
|
|
|
|
.filter(img => img.src && !img.complete && img.naturalWidth === 0)
|
|
|
|
|
.map(img => ({ src: img.src, alt: img.alt }));
|
|
|
|
|
});
|
|
|
|
|
|
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.
Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
30-marketplace-checkout (1→0), 22-performance (1→0),
31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
33-visual-bugs (0→0)
Total: 139 fake assertions → real expect(), 159 informational logs removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:50:17 +00:00
|
|
|
expect(brokenImages, `Broken images on /discover: ${JSON.stringify(brokenImages)}`).toHaveLength(0);
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Library — aucune image cassée', async ({ page }) => {
|
|
|
|
|
await navigateTo(page, '/library');
|
|
|
|
|
await page.waitForTimeout(3000);
|
|
|
|
|
|
|
|
|
|
const brokenImages = await page.evaluate(() => {
|
|
|
|
|
const imgs = document.querySelectorAll('img');
|
|
|
|
|
return Array.from(imgs)
|
|
|
|
|
.filter(img => img.src && !img.complete && img.naturalWidth === 0)
|
|
|
|
|
.map(img => ({ src: img.src, alt: img.alt }));
|
|
|
|
|
});
|
|
|
|
|
|
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.
Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
30-marketplace-checkout (1→0), 22-performance (1→0),
31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
33-visual-bugs (0→0)
Total: 139 fake assertions → real expect(), 159 informational logs removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:50:17 +00:00
|
|
|
expect(brokenImages, `Broken images on /library: ${JSON.stringify(brokenImages)}`).toHaveLength(0);
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Marketplace — aucune image cassée', async ({ page }) => {
|
|
|
|
|
await navigateTo(page, '/marketplace');
|
|
|
|
|
await page.waitForTimeout(3000);
|
|
|
|
|
|
|
|
|
|
const brokenImages = await page.evaluate(() => {
|
|
|
|
|
const imgs = document.querySelectorAll('img');
|
|
|
|
|
return Array.from(imgs)
|
|
|
|
|
.filter(img => img.src && !img.complete && img.naturalWidth === 0)
|
|
|
|
|
.map(img => ({ src: img.src, alt: img.alt }));
|
|
|
|
|
});
|
|
|
|
|
|
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.
Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
30-marketplace-checkout (1→0), 22-performance (1→0),
31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
33-visual-bugs (0→0)
Total: 139 fake assertions → real expect(), 159 informational logs removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:50:17 +00:00
|
|
|
expect(brokenImages, `Broken images on /marketplace: ${JSON.stringify(brokenImages)}`).toHaveLength(0);
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test.describe('VISUAL — Layout responsive @visual', () => {
|
|
|
|
|
test.setTimeout(60_000);
|
|
|
|
|
|
|
|
|
|
const viewports = [
|
|
|
|
|
{ width: 375, height: 667, name: 'iPhone SE' },
|
|
|
|
|
{ width: 390, height: 844, name: 'iPhone 14' },
|
|
|
|
|
{ width: 768, height: 1024, name: 'iPad' },
|
|
|
|
|
{ width: 1280, height: 720, name: 'Desktop' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const vp of viewports) {
|
|
|
|
|
test(`Pas de débordement horizontal sur ${vp.name} (${vp.width}x${vp.height}) @mobile`, async ({ page }) => {
|
|
|
|
|
await page.setViewportSize({ width: vp.width, height: vp.height });
|
|
|
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
|
|
|
|
|
|
|
|
const pages = ['/dashboard', '/discover', '/library', '/playlists'];
|
|
|
|
|
for (const path of pages) {
|
|
|
|
|
await navigateTo(page, path);
|
|
|
|
|
|
|
|
|
|
const hasOverflow = await page.evaluate(() =>
|
|
|
|
|
document.documentElement.scrollWidth > document.documentElement.clientWidth,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(hasOverflow, `Overflow on ${path} at ${vp.width}x${vp.height}`).toBeFalsy();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test.describe('VISUAL — Contraste et accessibilité @visual @a11y', () => {
|
|
|
|
|
test('Messages d\'erreur sur login ont un contraste suffisant', async ({ page }) => {
|
|
|
|
|
await page.goto('/login');
|
|
|
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
|
|
|
|
|
|
// Submit empty form to trigger validation
|
|
|
|
|
const submitBtn = page.getByRole('button', { name: /sign in|connexion|se connecter/i }).first();
|
|
|
|
|
if (await submitBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
|
|
|
await submitBtn.click();
|
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
|
|
|
|
|
|
// Check error messages exist and are visible
|
|
|
|
|
const errorElements = await page.locator('[class*="destructive"], [role="alert"], [class*="error"]').all();
|
|
|
|
|
for (const el of errorElements) {
|
|
|
|
|
if (await el.isVisible().catch(() => false)) {
|
|
|
|
|
const opacity = await el.evaluate(e => getComputedStyle(e).opacity);
|
|
|
|
|
// Ensure text is not invisible
|
|
|
|
|
expect(parseFloat(opacity)).toBeGreaterThan(0.5);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('Focus ring visible sur les éléments interactifs', async ({ page }) => {
|
|
|
|
|
await page.goto('/login');
|
|
|
|
|
await page.waitForLoadState('networkidle').catch(() => {});
|
|
|
|
|
|
|
|
|
|
// Tab through elements
|
|
|
|
|
await page.keyboard.press('Tab');
|
|
|
|
|
await page.waitForTimeout(200);
|
|
|
|
|
|
|
|
|
|
const focusedElement = page.locator(':focus');
|
|
|
|
|
if (await focusedElement.isVisible().catch(() => false)) {
|
|
|
|
|
const outline = await focusedElement.evaluate(e => {
|
|
|
|
|
const styles = getComputedStyle(e);
|
|
|
|
|
return {
|
|
|
|
|
outline: styles.outline,
|
|
|
|
|
boxShadow: styles.boxShadow,
|
|
|
|
|
border: styles.borderColor,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
// Should have some visible focus indicator
|
|
|
|
|
const hasFocusIndicator = outline.outline !== 'none' ||
|
|
|
|
|
outline.boxShadow !== 'none' ||
|
|
|
|
|
outline.border !== '';
|
test(e2e): convert all remaining 298 console.log to real expect()
Convert 20 files from fake assertions (console.log with ✓/✗) to real
expect() assertions. This completes the conversion started in the
previous session — zero console.log calls remain in the E2E suite.
Files converted (by batch):
Batch 1: 16-forms-validation (38→0), 13-workflows (18→0), 14-edge-cases (8→0)
Batch 2: 15-routes-coverage (8→0), 20-network-errors (5→0), 04-tracks (4→0),
32-deep-pages (4→0), 19-responsive (3→0), 11-accessibility-ethics (3→0)
Batch 3: 25-profile (2→0), 12-api (2→0), 29-chat-functional (2→0),
30-marketplace-checkout (1→0), 22-performance (1→0),
31-auth-sessions (1→0), 26-smoke (1→0), 02-navigation (1→0)
Batch 4: 24-cross-browser (0 fakes, 12 info→0), 34-workflows-empty (0→0),
33-visual-bugs (0→0)
Total: 139 fake assertions → real expect(), 159 informational logs removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:50:17 +00:00
|
|
|
expect(hasFocusIndicator, 'No visible focus indicator on first tab target').toBeTruthy();
|
test: add comprehensive e2e test suite (34 spec files)
New tests/e2e/ suite covering:
- Auth, navigation, player, tracks, playlists
- Search, discover, social, marketplace, chat
- Accessibility, API, workflows, edge cases
- Routes coverage, forms validation, modals
- Empty states, responsive, network errors
- Error boundary, performance, visual regression
- Cross-browser, profile, smoke, upload
- Storybook, deep pages, visual bugs
- Includes fixtures, helpers, global setup/teardown
- Playwright config and coverage map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:36:22 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|