mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(e2e): fix category-management navigation race and grid card click race
Two separate root causes:
1. category-management.e2e.ts: openWorkspaceSection('Live TV') was not
awaited — pickSidebarCategory ran before the route settled on the live
section, so it read VOD categories from the stale sidebar (e.g. 'Action'
instead of a live category). The manage-categories dialog opens for the
live section, so searching for a VOD name found nothing.
Fix: add waitForURL(/\/workspace\/xtreams\/[^/]+\/live/) after both
openWorkspaceSection('Live TV') calls.
2. recent.e2e.ts / favorites.e2e.ts: waitForFirstGridListCardTitle read
the first card's title, but clickGridListCardByTitle then did a fresh
DOM search by that title. Between those two calls the grid could
re-sort/paginate, making the card unfindable.
Fix: new clickFirstGridListCard helper atomically reads the title and
clicks the same card reference, replacing the two-step pattern.
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
This commit is contained in:
4 files changed
+41
-14
No files matched your search
@@ -30,6 +30,12 @@ test.describe('Electron Xtream Category Management', () => {
|
||||
});
|
||||
await waitForXtreamWorkspaceReady(app.mainWindow);
|
||||
await openWorkspaceSection(app.mainWindow, 'Live TV');
|
||||
// Wait for the route to settle on the live TV section so that
|
||||
// the sidebar shows live categories (not VOD/series from a
|
||||
// previous section) before we read from it.
|
||||
await app.mainWindow.waitForURL(
|
||||
/\/workspace\/xtreams\/[^/]+\/live/
|
||||
);
|
||||
|
||||
const targetCategory = await pickSidebarCategory(app.mainWindow);
|
||||
|
||||
@@ -73,6 +79,9 @@ test.describe('Electron Xtream Category Management', () => {
|
||||
await sourceRowByTitle(app.mainWindow, portalName).first().click();
|
||||
await waitForXtreamWorkspaceReady(app.mainWindow);
|
||||
await openWorkspaceSection(app.mainWindow, 'Live TV');
|
||||
await app.mainWindow.waitForURL(
|
||||
/\/workspace\/xtreams\/[^/]+\/live/
|
||||
);
|
||||
await expect(sidebarCategoryById(app.mainWindow, targetCategory.id)).toHaveCount(
|
||||
0
|
||||
);
|
||||
|
||||
@@ -635,6 +635,25 @@ export async function waitForFirstGridListCardTitle(page: Page): Promise<string>
|
||||
return ((await card.locator('.title').textContent()) ?? '').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the first grid card to appear with a non-empty title, clicks it,
|
||||
* and returns the title. Use this instead of waitForFirstGridListCardTitle +
|
||||
* clickGridListCardByTitle to avoid a race condition where the grid re-renders
|
||||
* between the title read and the subsequent search-by-title click.
|
||||
*/
|
||||
export async function clickFirstGridListCard(page: Page): Promise<string> {
|
||||
const card = page.locator('.category-content-layout mat-card').first();
|
||||
await expect(card).toBeVisible({ timeout: 20000 });
|
||||
const titleEl = card.locator('.title');
|
||||
let title = '';
|
||||
await expect(async () => {
|
||||
title = ((await titleEl.textContent()) ?? '').trim();
|
||||
expect(title.length).toBeGreaterThan(0);
|
||||
}).toPass({ timeout: 10000 });
|
||||
await card.click();
|
||||
return title;
|
||||
}
|
||||
|
||||
export async function clickGridListCardByTitle(
|
||||
page: Page,
|
||||
title: string
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
channelItemByTitle,
|
||||
clickCategoryById,
|
||||
clickCategoryByNameExact,
|
||||
clickGridListCardByTitle,
|
||||
clickFirstGridListCard,
|
||||
closeElectronApp,
|
||||
contentCardByTitle,
|
||||
defaultXtreamPassword,
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
switchUnifiedCollectionContent,
|
||||
switchUnifiedCollectionScope,
|
||||
test,
|
||||
waitForFirstGridListCardTitle,
|
||||
waitForM3uCatalog,
|
||||
waitForStalkerCatalog,
|
||||
waitForXtreamWorkspaceReady,
|
||||
@@ -138,10 +137,11 @@ 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);
|
||||
// Use clickFirstGridListCard: grid sorts by date-desc so fixture order ≠
|
||||
// display order; this atomically reads the first card's title and clicks
|
||||
// it, avoiding a race where the grid re-renders between the title read
|
||||
// and a separate search-by-title click.
|
||||
const movieTitle = await clickFirstGridListCard(app.mainWindow);
|
||||
await addCurrentDetailToFavorites(app.mainWindow);
|
||||
await goBackFromDetail(app.mainWindow);
|
||||
|
||||
@@ -150,8 +150,7 @@ test.describe('Electron Favorites', () => {
|
||||
app.mainWindow,
|
||||
seriesFixture.categoryName
|
||||
);
|
||||
const seriesTitle = await waitForFirstGridListCardTitle(app.mainWindow);
|
||||
await clickGridListCardByTitle(app.mainWindow, seriesTitle);
|
||||
const seriesTitle = await clickFirstGridListCard(app.mainWindow);
|
||||
await addCurrentDetailToFavorites(app.mainWindow);
|
||||
await goBackFromDetail(app.mainWindow);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
channelItemByTitle,
|
||||
clickCategoryById,
|
||||
clickCategoryByNameExact,
|
||||
clickGridListCardByTitle,
|
||||
clickFirstGridListCard,
|
||||
closeElectronApp,
|
||||
contentCardByTitle,
|
||||
defaultXtreamPassword,
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
switchUnifiedCollectionContent,
|
||||
switchUnifiedCollectionScope,
|
||||
test,
|
||||
waitForFirstGridListCardTitle,
|
||||
waitForM3uCatalog,
|
||||
waitForStalkerCatalog,
|
||||
waitForXtreamWorkspaceReady,
|
||||
@@ -142,8 +141,10 @@ test.describe('Electron Recently Viewed', () => {
|
||||
);
|
||||
// Pick from the displayed grid: fixture order ≠ display order (date-desc
|
||||
// sort + pagination), so the first fixture item may not be on page 1.
|
||||
const movieTitle = await waitForFirstGridListCardTitle(app.mainWindow);
|
||||
await clickGridListCardByTitle(app.mainWindow, movieTitle);
|
||||
// Use clickFirstGridListCard to atomically read + click the first visible
|
||||
// card, avoiding a race where the grid re-renders between title read and
|
||||
// a separate search-by-title click.
|
||||
const movieTitle = await clickFirstGridListCard(app.mainWindow);
|
||||
await playCurrentDetail(app.mainWindow);
|
||||
await goBackFromDetail(app.mainWindow);
|
||||
|
||||
@@ -152,8 +153,7 @@ test.describe('Electron Recently Viewed', () => {
|
||||
app.mainWindow,
|
||||
seriesFixture.categoryName
|
||||
);
|
||||
const seriesTitle = await waitForFirstGridListCardTitle(app.mainWindow);
|
||||
await clickGridListCardByTitle(app.mainWindow, seriesTitle);
|
||||
const seriesTitle = await clickFirstGridListCard(app.mainWindow);
|
||||
await playFirstSeriesEpisode(app.mainWindow);
|
||||
|
||||
await openPlaylistRecent(app.mainWindow);
|
||||
|
||||
Reference in new issue
Block a user