mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
test(e2e): enhance Electron E2E tests with new build configurations and clear favorites functionality
Entire-Checkpoint: c6e522b4276c
This commit is contained in:
1 parent
8ca2e927a2
commit
973423a2b5
7 files changed
+187
-8
No files matched your search
@@ -10,7 +10,7 @@
|
||||
],
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"dependsOn": ["electron-backend:build"]
|
||||
"dependsOn": ["electron-backend:build-e2e"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,10 @@ export const electronMainPath = join(
|
||||
workspaceRoot,
|
||||
'dist/apps/electron-backend/main.js'
|
||||
);
|
||||
export const packagedRendererIndexPath = join(
|
||||
workspaceRoot,
|
||||
'dist/apps/web/index.html'
|
||||
);
|
||||
export const m3uFixturePath = join(
|
||||
workspaceRoot,
|
||||
'apps/web-e2e/src/fixtures/test.m3u'
|
||||
@@ -124,6 +128,7 @@ export async function launchElectronApp(
|
||||
`Electron build not found at ${electronMainPath}. Run the build before executing the E2E suite.`
|
||||
);
|
||||
}
|
||||
assertPackagedRendererBuildIsElectronSafe();
|
||||
|
||||
const args = [electronMainPath];
|
||||
|
||||
@@ -164,6 +169,24 @@ export async function closeElectronApp(
|
||||
}
|
||||
}
|
||||
|
||||
function assertPackagedRendererBuildIsElectronSafe(): void {
|
||||
if (!existsSync(packagedRendererIndexPath)) {
|
||||
throw new Error(
|
||||
`Renderer build not found at ${packagedRendererIndexPath}. Run pnpm nx run electron-backend:build-e2e before executing the Electron E2E suite.`
|
||||
);
|
||||
}
|
||||
|
||||
const indexHtml = readFileSync(packagedRendererIndexPath, 'utf8');
|
||||
const baseHrefMatch = indexHtml.match(/<base\s+href="([^"]*)"/i);
|
||||
const baseHref = baseHrefMatch?.[1] ?? '<missing>';
|
||||
|
||||
if (baseHref !== './') {
|
||||
throw new Error(
|
||||
`Renderer build at ${packagedRendererIndexPath} is not file-safe for packaged Electron. Found base href ${JSON.stringify(baseHref)}. Run pnpm nx run electron-backend:build-e2e or use an Electron E2E Nx target so dist/apps/web is rebuilt with the electron-e2e configuration.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function findMainWindow(app: ElectronApplication): Promise<Page> {
|
||||
await new Promise((resolvePromise) => setTimeout(resolvePromise, 2000));
|
||||
|
||||
@@ -183,14 +206,37 @@ async function findMainWindow(app: ElectronApplication): Promise<Page> {
|
||||
async function waitForAppReady(page: Page): Promise<void> {
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForSelector('app-root', { timeout: 30000 });
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const appRoot = document.querySelector('app-root');
|
||||
try {
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const appRoot = document.querySelector('app-root');
|
||||
|
||||
return Boolean(appRoot && appRoot.innerHTML.trim().length > 0);
|
||||
},
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
return Boolean(appRoot && appRoot.innerHTML.trim().length > 0);
|
||||
},
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
} catch (error) {
|
||||
const diagnostics = await page.evaluate(() => ({
|
||||
appRootLength:
|
||||
document.querySelector('app-root')?.innerHTML.trim().length ?? 0,
|
||||
baseHref:
|
||||
document
|
||||
.querySelector('base')
|
||||
?.getAttribute('href') ?? '<missing>',
|
||||
readyState: document.readyState,
|
||||
title: document.title,
|
||||
url: location.href,
|
||||
}));
|
||||
|
||||
const reason =
|
||||
error instanceof Error ? error.message : 'unknown startup error';
|
||||
|
||||
throw new Error(
|
||||
`Electron app did not render visible app-root within 30000ms. ${reason}. Diagnostics: ${JSON.stringify(
|
||||
diagnostics
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function openAddPlaylistDialog(page: Page): Promise<void> {
|
||||
@@ -665,6 +711,21 @@ export async function switchUnifiedCollectionContent(
|
||||
await clickButtonToggleOption(toggleGroup, contentLabel);
|
||||
}
|
||||
|
||||
export async function clearCurrentUnifiedCollection(
|
||||
page: Page
|
||||
): Promise<void> {
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: /Clear .* (favorites|recently viewed)/i,
|
||||
})
|
||||
.click();
|
||||
|
||||
const dialog = page.locator('mat-dialog-container').last();
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole('button', { name: /^Yes$/i }).click();
|
||||
await expect(dialog).toBeHidden();
|
||||
}
|
||||
|
||||
async function clickButtonToggleOption(
|
||||
toggleGroup: Locator,
|
||||
label: string
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
addStalkerPortal,
|
||||
addXtreamPortal,
|
||||
channelItemByTitle,
|
||||
clearCurrentUnifiedCollection,
|
||||
clickCategoryById,
|
||||
clickGridListCardByTitle,
|
||||
clickCategoryByNameExact,
|
||||
@@ -105,6 +106,72 @@ test.describe('Electron Favorites', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('keeps playlist M3U favorites cleared after navigating away and back', async ({
|
||||
dataDir,
|
||||
}) => {
|
||||
const playlistTitle = 'm3u-clear-favorites-source.m3u';
|
||||
const playlistSourceTitle = 'm3u-clear-favorites-source';
|
||||
const filePath = writeTemporaryM3uFile(dataDir, playlistTitle, [
|
||||
{
|
||||
groupTitle: 'News',
|
||||
name: 'M3U Clear Favorite One',
|
||||
url: 'https://streams.example.test/m3u-clear-favorite-one.m3u8',
|
||||
},
|
||||
{
|
||||
groupTitle: 'News',
|
||||
name: 'M3U Clear Favorite Two',
|
||||
url: 'https://streams.example.test/m3u-clear-favorite-two.m3u8',
|
||||
},
|
||||
]);
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromNativeDialog(app, filePath);
|
||||
await waitForM3uCatalog(app.mainWindow);
|
||||
await toggleFavoriteForChannel(
|
||||
app.mainWindow,
|
||||
'M3U Clear Favorite One'
|
||||
);
|
||||
await toggleFavoriteForChannel(
|
||||
app.mainWindow,
|
||||
'M3U Clear Favorite Two'
|
||||
);
|
||||
await openPlaylistFavorites(app.mainWindow);
|
||||
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite One')
|
||||
).toHaveCount(1);
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite Two')
|
||||
).toHaveCount(1);
|
||||
|
||||
await clearCurrentUnifiedCollection(app.mainWindow);
|
||||
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite One')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite Two')
|
||||
).toHaveCount(0);
|
||||
|
||||
await openSources(app.mainWindow);
|
||||
await sourceRowByTitle(app.mainWindow, playlistSourceTitle)
|
||||
.first()
|
||||
.click();
|
||||
await waitForM3uCatalog(app.mainWindow);
|
||||
await openPlaylistFavorites(app.mainWindow);
|
||||
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite One')
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
channelItemByTitle(app.mainWindow, 'M3U Clear Favorite Two')
|
||||
).toHaveCount(0);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
|
||||
test('shows Xtream live, movie, and series favorites in playlist and all-playlists scope, and preserves them after restart', async ({
|
||||
dataDir,
|
||||
request,
|
||||
|
||||
@@ -41,6 +41,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"build-e2e": {
|
||||
"dependsOn": [
|
||||
"electron-backend:build-worker",
|
||||
{
|
||||
"projects": ["web"],
|
||||
"target": "build-e2e"
|
||||
},
|
||||
{
|
||||
"projects": ["remote-control-web"],
|
||||
"target": "build-e2e"
|
||||
}
|
||||
],
|
||||
"executor": "nx-electron:build",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/electron-backend",
|
||||
"main": "apps/electron-backend/src/main.ts",
|
||||
"tsConfig": "apps/electron-backend/tsconfig.app.json",
|
||||
"assets": ["apps/electron-backend/src/assets"]
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
|
||||
@@ -57,6 +57,12 @@
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"build-e2e": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "pnpm nx run remote-control-web:build:development"
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
"executor": "@angular/build:dev-server",
|
||||
|
||||
@@ -102,10 +102,22 @@
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"electron-e2e": {
|
||||
"baseHref": "./",
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"build-e2e": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "pnpm nx run web:build:electron-e2e"
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
"executor": "@angular/build:dev-server",
|
||||
|
||||
@@ -50,6 +50,18 @@
|
||||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["production", "^production"]
|
||||
},
|
||||
"e2e-ci--src/*.e2e.ts": {
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": ["electron-backend"],
|
||||
"target": "build-e2e"
|
||||
},
|
||||
{
|
||||
"projects": ["stalker-mock-server", "xtream-mock-server"],
|
||||
"target": "serve"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
|
||||
Reference in new issue
Block a user