mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
1 parent
134b5fb779
commit
aead89e3e6
3 files changed
+102
-10
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import { Locator, Page } from '@playwright/test';
|
||||
import {
|
||||
addXtreamPortal,
|
||||
clickCategoryById,
|
||||
closeElectronApp,
|
||||
expect,
|
||||
launchElectronApp,
|
||||
@@ -205,6 +204,86 @@ test.describe('Electron Xtream Category Management', () => {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
|
||||
test('allows restoring live categories after every category is hidden', async ({
|
||||
dataDir,
|
||||
request,
|
||||
}) => {
|
||||
await resetMockServers(request, ['xtream']);
|
||||
const portalName = 'All Hidden Categories Xtream';
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await addXtreamPortal(app.mainWindow, {
|
||||
name: portalName,
|
||||
});
|
||||
await waitForXtreamWorkspaceReady(app.mainWindow);
|
||||
await openWorkspaceSection(app.mainWindow, 'Live TV');
|
||||
await app.mainWindow.waitForURL(
|
||||
/\/workspace\/xtreams\/[^/]+\/live/
|
||||
);
|
||||
|
||||
await expect
|
||||
.poll(() => readVisibleSidebarCategoryNames(app.mainWindow), {
|
||||
timeout: 15000,
|
||||
})
|
||||
.not.toEqual([]);
|
||||
const initialCategoryNames = await readVisibleSidebarCategoryNames(
|
||||
app.mainWindow
|
||||
);
|
||||
|
||||
let dialog = await openManageCategoriesDialog(app.mainWindow);
|
||||
await dialog
|
||||
.getByRole('button', {
|
||||
name: 'Deselect All',
|
||||
exact: true,
|
||||
})
|
||||
.click();
|
||||
await expect(
|
||||
dialog.locator('.category-item mat-checkbox input:checked')
|
||||
).toHaveCount(0);
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Save', exact: true })
|
||||
.click();
|
||||
await app.mainWindow.waitForSelector('mat-dialog-container', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect
|
||||
.poll(() => readVisibleSidebarCategoryNames(app.mainWindow), {
|
||||
timeout: 15000,
|
||||
})
|
||||
.toEqual([]);
|
||||
|
||||
await expect(
|
||||
app.mainWindow.getByRole('button', {
|
||||
name: 'Manage categories',
|
||||
exact: true,
|
||||
})
|
||||
).toBeEnabled();
|
||||
|
||||
dialog = await openManageCategoriesDialog(app.mainWindow);
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Select All', exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
dialog.locator('.category-item mat-checkbox input:checked')
|
||||
).toHaveCount(initialCategoryNames.length);
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Save', exact: true })
|
||||
.click();
|
||||
await app.mainWindow.waitForSelector('mat-dialog-container', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expectVisibleSidebarCategoryNames(
|
||||
app.mainWindow,
|
||||
initialCategoryNames
|
||||
);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function openManageCategoriesDialog(page: Page) {
|
||||
@@ -266,6 +345,10 @@ async function expectVisibleSidebarCategoryNames(
|
||||
.toEqual(expectedNames);
|
||||
} catch (error) {
|
||||
const actualNames = await readVisibleSidebarCategoryNames(page);
|
||||
if (stringArraysEqual(actualNames, expectedNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Expected visible sidebar categories ${JSON.stringify(
|
||||
expectedNames
|
||||
@@ -300,6 +383,13 @@ async function readVisibleSidebarCategoryNames(page: Page): Promise<string[]> {
|
||||
return actualNames;
|
||||
}
|
||||
|
||||
function stringArraysEqual(left: string[], right: string[]): boolean {
|
||||
return (
|
||||
left.length === right.length &&
|
||||
left.every((value, index) => value === right[index])
|
||||
);
|
||||
}
|
||||
|
||||
async function pickSidebarCategory(
|
||||
page: Page
|
||||
): Promise<{ id: string; itemCount: number; name: string }> {
|
||||
|
||||
@@ -66,14 +66,12 @@ ALTER TABLE categories ADD COLUMN hidden INTEGER DEFAULT 0
|
||||
|
||||
**Integration Points**
|
||||
|
||||
- `xtream-main-container.component.ts` - Movies & Series sections
|
||||
- `live-stream-layout.component.ts` - Live TV section
|
||||
|
||||
Both components:
|
||||
|
||||
1. Add a "tune" icon button in the sidebar header
|
||||
2. Open the dialog with playlist ID and content type
|
||||
3. Call `xtreamStore.reloadCategories()` after dialog closes with changes
|
||||
- `libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts`
|
||||
renders the tune icon button for Xtream Live TV, Movies, and Series sidebars.
|
||||
- The workspace context panel lazy-loads `CategoryManagementDialogComponent`
|
||||
from `@iptvnator/portal/xtream/feature`, opens it with playlist ID, content
|
||||
type, and item counts, then calls `xtreamStore.reloadCategories()` after a
|
||||
successful dialog save.
|
||||
|
||||
### Store
|
||||
|
||||
@@ -87,6 +85,11 @@ Added `reloadCategories()` method to refresh categories from database after visi
|
||||
- **Persistence**: Visibility settings survive playlist refresh (see below)
|
||||
- **Per-playlist, per-type**: Categories are managed per playlist and per content type (live/movies/series)
|
||||
- **No content deletion**: Hiding a category only affects sidebar visibility; the category and its content remain in the database
|
||||
- **All-hidden recovery**: Once the selected Xtream type is loaded, the manage
|
||||
categories button remains available even if every visible category has been
|
||||
hidden. The sidebar category list is filtered, but the dialog reads all
|
||||
categories through `getAllXtreamCategories()` so users can select categories
|
||||
again.
|
||||
|
||||
### Visibility Preservation During Refresh
|
||||
|
||||
|
||||
-1
@@ -101,7 +101,6 @@ export class WorkspaceContextPanelComponent {
|
||||
readonly canManageXtreamCategories = computed(
|
||||
() =>
|
||||
this.isXtreamCategories() &&
|
||||
this.xtreamCategories().length > 0 &&
|
||||
this.xtreamSelectedTypeCountsReady()
|
||||
);
|
||||
readonly xtreamStatusText = computed(() => {
|
||||
|
||||
Reference in new issue
Block a user