diff --git a/apps/electron-backend-e2e/src/category-management.e2e.ts b/apps/electron-backend-e2e/src/category-management.e2e.ts index ab0be6178..b50f0b92a 100644 --- a/apps/electron-backend-e2e/src/category-management.e2e.ts +++ b/apps/electron-backend-e2e/src/category-management.e2e.ts @@ -158,28 +158,17 @@ async function toggleManagedCategory( name: string; } ): Promise { - const categories = dialog.locator('.category-item'); - const count = await categories.count(); + // Use Playwright's built-in filter so it retries until Angular re-renders + // the filtered list after the search field is populated. + const categoryRow = dialog + .locator('.category-item') + .filter({ + has: dialog.locator('.category-name', { + hasText: targetCategory.name, + }), + }) + .first(); - for (let index = 0; index < count; index += 1) { - const categoryRow = categories.nth(index); - const name = - (await categoryRow.locator('.category-name').textContent())?.trim() ?? - ''; - const countText = - (await categoryRow.locator('.item-count').textContent())?.trim() ?? ''; - const itemCount = Number.parseInt(countText.replace(/[()]/g, ''), 10) || 0; - - if (name !== targetCategory.name) { - continue; - } - - await expect(categoryRow).toBeVisible(); - await categoryRow.click(); - return; - } - - throw new Error( - `Could not find category "${targetCategory.name}" with count ${targetCategory.itemCount} in the management dialog.` - ); + await expect(categoryRow).toBeVisible({ timeout: 5000 }); + await categoryRow.click(); } diff --git a/apps/electron-backend-e2e/src/electron-test-fixtures.ts b/apps/electron-backend-e2e/src/electron-test-fixtures.ts index fd3d5a592..e05ecb13a 100644 --- a/apps/electron-backend-e2e/src/electron-test-fixtures.ts +++ b/apps/electron-backend-e2e/src/electron-test-fixtures.ts @@ -624,6 +624,17 @@ export function gridListCardByTitle(page: Page, title: string): Locator { }); } +/** + * Waits for the first grid card to appear (skeleton loading done) and returns + * its display title. Use this instead of picking from fixture order, since the + * grid sorts by date-desc and may paginate items off the first page. + */ +export async function waitForFirstGridListCardTitle(page: Page): Promise { + const card = page.locator('.category-content-layout mat-card').first(); + await expect(card).toBeVisible({ timeout: 20000 }); + return ((await card.locator('.title').textContent()) ?? '').trim(); +} + export async function clickGridListCardByTitle( page: Page, title: string diff --git a/apps/electron-backend-e2e/src/favorites.e2e.ts b/apps/electron-backend-e2e/src/favorites.e2e.ts index e79b9e07f..bdba8d573 100644 --- a/apps/electron-backend-e2e/src/favorites.e2e.ts +++ b/apps/electron-backend-e2e/src/favorites.e2e.ts @@ -22,6 +22,7 @@ import { switchUnifiedCollectionContent, switchUnifiedCollectionScope, test, + waitForFirstGridListCardTitle, waitForM3uCatalog, waitForStalkerCatalog, waitForXtreamWorkspaceReady, @@ -116,8 +117,6 @@ test.describe('Electron Favorites', () => { xtreamCredentials ); const [liveTitle] = pickDistinctTitles(liveFixture.items, getXtreamTitle); - const [movieTitle] = pickDistinctTitles(vodFixture.items, getXtreamTitle); - const [seriesTitle] = pickDistinctTitles(seriesFixture.items, getXtreamTitle); const portalTitle = 'Xtream Favorites Source'; const app = await launchElectronApp(dataDir); @@ -139,6 +138,9 @@ test.describe('Electron Favorites', () => { app.mainWindow, vodFixture.categoryName ); + // Pick the title from the first displayed card (grid sorts by date-desc, + // so fixture order ≠ display order; pagination may hide some items). + const movieTitle = await waitForFirstGridListCardTitle(app.mainWindow); await clickGridListCardByTitle(app.mainWindow, movieTitle); await addCurrentDetailToFavorites(app.mainWindow); await goBackFromDetail(app.mainWindow); @@ -148,6 +150,7 @@ test.describe('Electron Favorites', () => { app.mainWindow, seriesFixture.categoryName ); + const seriesTitle = await waitForFirstGridListCardTitle(app.mainWindow); await clickGridListCardByTitle(app.mainWindow, seriesTitle); await addCurrentDetailToFavorites(app.mainWindow); await goBackFromDetail(app.mainWindow);