Replace 105+ fake assertions across 8 E2E test files that used
console.log('✓'/'✗') instead of expect(), causing tests to always
pass even when features were broken. Now 87 tests correctly fail,
exposing real application bugs.
Files converted:
- 09-chat-notifications-settings.spec.ts (33 fakes → real)
- 18-empty-states.spec.ts (14 fakes → real)
- 17-modals-dialogs.spec.ts (15 fakes → real)
- 07-social.spec.ts (12 fakes → real)
- 06-search-discover.spec.ts (12 fakes → real)
- 05-playlists.spec.ts (6 fakes → real)
- 08-marketplace.spec.ts (8 fakes → real)
- 10-features.spec.ts (5 fakes → real)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
191 lines
7.1 KiB
TypeScript
191 lines
7.1 KiB
TypeScript
import { test, expect } from '@chromatic-com/playwright';
|
|
import { loginViaAPI, CONFIG, navigateTo, SELECTORS } from './helpers';
|
|
|
|
/**
|
|
* Helper to find the search input on /search page with multiple fallbacks.
|
|
*/
|
|
async function findSearchInput(page: import('@playwright/test').Page) {
|
|
const searchInput = page.locator('input[role="combobox"][aria-label="Search"]')
|
|
.or(page.getByPlaceholder(/search/i))
|
|
.or(page.locator(SELECTORS.searchInput))
|
|
.or(page.locator('input[type="search"]'))
|
|
.or(page.locator('input[type="text"]').first());
|
|
return searchInput.first();
|
|
}
|
|
|
|
test.describe('SEARCH — Recherche unifiée', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
});
|
|
|
|
test('01. Le champ de recherche est accessible dans le header @critical', async ({ page }) => {
|
|
await navigateTo(page, '/dashboard');
|
|
|
|
const headerSearch = page.locator('[data-testid="search-input"]')
|
|
.or(page.locator(SELECTORS.searchInput));
|
|
const headerVisible = await headerSearch.first().isVisible().catch(() => false);
|
|
|
|
await navigateTo(page, '/search');
|
|
const pageSearch = await findSearchInput(page);
|
|
const pageSearchVisible = await pageSearch.isVisible().catch(() => false);
|
|
|
|
// At least one of the two search inputs should be accessible
|
|
expect(headerVisible || pageSearchVisible).toBeTruthy();
|
|
});
|
|
|
|
test('02. Taper une requête affiche des résultats @critical', async ({ page }) => {
|
|
await navigateTo(page, '/search');
|
|
|
|
const searchInput = await findSearchInput(page);
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await searchInput.fill('test');
|
|
await page.waitForTimeout(1_500);
|
|
|
|
const body = await page.textContent('body') || '';
|
|
const hasResults = body.length > 500;
|
|
const hasNoResults = /no results|aucun résultat|nothing found/i.test(body);
|
|
|
|
expect(hasResults || hasNoResults).toBeTruthy();
|
|
});
|
|
|
|
test('03. L\'autocomplete fonctionne (suggestions pendant la frappe)', async ({ page }) => {
|
|
await navigateTo(page, '/search');
|
|
|
|
const searchInput = await findSearchInput(page);
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await searchInput.fill('tes');
|
|
await page.waitForTimeout(1_000);
|
|
|
|
const suggestions = page.locator('[role="listbox"]');
|
|
await expect(suggestions).toBeVisible();
|
|
});
|
|
|
|
test('04. Les résultats de recherche sont catégorisés (tabs: All, Tracks, Artists, Playlists)', async ({ page }) => {
|
|
await navigateTo(page, '/search');
|
|
|
|
const searchInput = await findSearchInput(page);
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await searchInput.fill('music');
|
|
await page.waitForTimeout(2_000);
|
|
|
|
const expectedTabs = ['All Results', 'Tracks', 'Artists', 'Playlists'];
|
|
for (const tabName of expectedTabs) {
|
|
const tab = page.getByRole('tab', { name: new RegExp(tabName, 'i') });
|
|
await expect(tab).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('05. Recherche vide ne crash pas', async ({ page }) => {
|
|
await navigateTo(page, '/search');
|
|
|
|
const searchInput = await findSearchInput(page);
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await searchInput.fill('');
|
|
await page.waitForTimeout(1_000);
|
|
|
|
const body = await page.textContent('body') || '';
|
|
expect(body).not.toMatch(/500|Internal Server Error|crash|TypeError|Unhandled/i);
|
|
});
|
|
|
|
test('05b. Recherche via URL params ?q= fonctionne', async ({ page }) => {
|
|
await navigateTo(page, '/search?q=test');
|
|
await page.waitForTimeout(1_500);
|
|
|
|
const body = await page.textContent('body') || '';
|
|
expect(body).not.toMatch(/500|Internal Server Error|crash|TypeError|Unhandled/i);
|
|
expect(body.length).toBeGreaterThan(50);
|
|
});
|
|
});
|
|
|
|
test.describe('DISCOVER — Exploration éthique', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
|
|
});
|
|
|
|
test('06. Page /discover affiche les genres @critical', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const heading = page.getByRole('heading', { name: /découvrir|discover/i });
|
|
await expect(heading.first()).toBeVisible();
|
|
|
|
const genreButtons = page.locator('button').filter({ has: page.locator('.font-heading.font-bold') });
|
|
let genreCount = await genreButtons.count();
|
|
|
|
if (genreCount === 0) {
|
|
const altGenreButtons = page.locator('button').filter({ hasText: /rock|pop|jazz|hip.?hop|electro|classical|r&b|reggae|metal|folk|blues|soul|country|latin/i });
|
|
genreCount = await altGenreButtons.count();
|
|
}
|
|
expect(genreCount).toBeGreaterThan(0);
|
|
|
|
const body = await page.textContent('body') || '';
|
|
expect(body).not.toMatch(/500|Internal Server Error|crash|TypeError|Unhandled/i);
|
|
});
|
|
|
|
test('07. Cliquer sur un genre filtre les résultats', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const genreButtons = page.locator('button').filter({ has: page.locator('.font-heading.font-bold') });
|
|
await expect(genreButtons.first()).toBeVisible();
|
|
|
|
await genreButtons.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
expect(page.url()).toContain('genre=');
|
|
|
|
const backBtn = page.getByRole('button', { name: /retour/i });
|
|
await expect(backBtn).toBeVisible();
|
|
|
|
const body = await page.textContent('body') || '';
|
|
expect(body.length).toBeGreaterThan(200);
|
|
});
|
|
|
|
test('08. Playlists éditoriales affichées sur /discover', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const editorialHeading = page.getByRole('heading', { name: /playlists éditoriales/i });
|
|
await expect(editorialHeading).toBeVisible();
|
|
|
|
const editorialCards = page.locator('[role="article"][aria-label^="Playlist:"]');
|
|
expect(await editorialCards.count()).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test('09. Pas de sections "trending" ou "for you" (design éthique)', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const body = await page.textContent('body') || '';
|
|
expect(body).not.toMatch(/pour vous|for you|recommended|recommandé|trending/i);
|
|
});
|
|
|
|
test('10. Pas de métriques de popularité publiques visibles', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const publicCounters = page.locator('[class*="play-count"], [class*="like-count"]')
|
|
.filter({ hasText: /\d+\s*(plays?|écoutes?|likes?|vues?)/i });
|
|
|
|
expect(await publicCounters.count()).toBe(0);
|
|
});
|
|
|
|
test('11. Bouton retour depuis genre revient à la liste des genres', async ({ page }) => {
|
|
await navigateTo(page, '/discover');
|
|
|
|
const genreButtons = page.locator('button').filter({ has: page.locator('.font-heading.font-bold') });
|
|
await expect(genreButtons.first()).toBeVisible();
|
|
|
|
await genreButtons.first().click();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const backBtn = page.getByRole('button', { name: /retour/i });
|
|
await expect(backBtn).toBeVisible();
|
|
await backBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
expect(page.url()).not.toContain('genre=');
|
|
|
|
const genreHeading = page.getByRole('heading', { name: /par genre/i });
|
|
await expect(genreHeading).toBeVisible();
|
|
});
|
|
});
|