mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
* test(db): cover playback positions, recently viewed, and connection migrations Add specs for the previously untested persistence paths: playback-position and recently-viewed operations (upsert/dedup/ordering/scoped deletes), shared-database path-utils, createTables and the tolerant column/index migrations incl. Xtream cache deduplication. Exposes createTables through the existing __databaseConnectionTestHooks object. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(xtream): cover the Electron DB-first data source and favorites guards Add specs for electron-xtream-data-source (DB-hit vs cold-cache paths, concurrent request dedup, error propagation, full method delegation) and extend the favorites feature spec with the invalid-input and content-not-found guard paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(stalker): cover favorites and recent store features Add specs for with-stalker-favorites and with-stalker-recent: payload normalization and id/title fallbacks, series-mode category forcing, meta sync dispatches, snackbar/callback side effects, and error paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(epg): cover archive/summary utils and the EPG worker service Add specs for the pure catch-up window and summary-progress helpers shared by the EPG panels, and for epg-worker.service: in-flight dedup by URL, double-settle guard, progress-aware timeouts, worker lifecycle and error broadcasting. Also settle an interrupted fetch in epg.events.spec that caused "Cannot log after tests are done" in longer runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(player): cover the VLC session service Mirror the MPV session spec patterns for VLC: enqueue-command building and RC response parsing, launch argv construction, instance reuse over the RC socket, exit-code handling, and the retry-without-RC fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(e2e): add downloads page and EPG timeline interaction coverage Downloads: empty state without sources, and a full lifecycle - authorize a folder via a stubbed native dialog, download from a local server, verify the completed item and file on disk, remove it from the UI. Timeline: zoom changes block widths and the on-air info affordance opens the programme dialog with the correct title and watch-live action. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: split oversized specs to meet the file-size guideline Address review feedback: extract shared drizzle mocks into operations.test-helpers.ts and split the Electron data-source delegation spec into delegation + user-data files. Pure reorganization - test counts and assertions unchanged (29/13/10), all files now under 300 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(e2e): wait for DB readiness before opening the downloads page On slow CI runners (macOS) the renderer can query SQLite while the DB worker is still creating tables, leaving the downloads page on its skeleton state forever. Poll a playlist read until it succeeds before navigating on a cold profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(build): exclude *.test-helpers.ts from the electron-backend app tsconfig The new operations.test-helpers.ts uses jest globals and broke the webpack build and tsc typecheck, which compile every non-spec file in the app. Exclude the test-helpers pattern alongside the existing spec exclusions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
import {
|
|
addXtreamPortal,
|
|
channelItemByTitle,
|
|
clickCategoryByNameExact,
|
|
closeElectronApp,
|
|
expect,
|
|
launchElectronApp,
|
|
openWorkspaceSection,
|
|
resetMockServers,
|
|
test,
|
|
waitForXtreamWorkspaceReady,
|
|
} from './electron-test-fixtures';
|
|
import { fetchXtreamEpgFixture } from './portal-mock-fixtures';
|
|
|
|
const epgCredentials = {
|
|
username: 'epg',
|
|
password: 'epg',
|
|
};
|
|
|
|
test('@epg @xtream @electron opens the programme dialog from a timeline block and reacts to zoom', async ({
|
|
dataDir,
|
|
request,
|
|
}) => {
|
|
await resetMockServers(request, ['xtream']);
|
|
const fixture = await fetchXtreamEpgFixture(request, epgCredentials);
|
|
const currentProgram = fixture.shortEpg[0];
|
|
if (!currentProgram) {
|
|
throw new Error(
|
|
'Expected the Xtream EPG fixture to include a current program.'
|
|
);
|
|
}
|
|
const app = await launchElectronApp(dataDir, { env: { TZ: 'UTC' } });
|
|
|
|
try {
|
|
await addXtreamPortal(app.mainWindow, {
|
|
name: 'Xtream Timeline Interaction',
|
|
username: epgCredentials.username,
|
|
password: epgCredentials.password,
|
|
});
|
|
await waitForXtreamWorkspaceReady(app.mainWindow);
|
|
await openWorkspaceSection(app.mainWindow, 'Live TV');
|
|
await clickCategoryByNameExact(app.mainWindow, fixture.categoryName);
|
|
|
|
const channelRow = channelItemByTitle(
|
|
app.mainWindow,
|
|
fixture.stream.name ?? ''
|
|
).first();
|
|
await expect(channelRow).toBeVisible({ timeout: 20000 });
|
|
await channelRow.click();
|
|
|
|
const timeline = app.mainWindow.locator('app-epg-timeline');
|
|
await expect(timeline).toBeVisible({ timeout: 20000 });
|
|
|
|
const nowBlock = timeline
|
|
.locator('.epg-timeline__block.is-now')
|
|
.first();
|
|
await expect(nowBlock).toBeVisible();
|
|
|
|
// Zooming re-renders the ribbon: block widths grow with px/minute.
|
|
const zoomInput = timeline.locator(
|
|
'.epg-timeline__zoom input[type="range"]'
|
|
);
|
|
await expect(zoomInput).toBeVisible();
|
|
|
|
const blockWidthAt = async (zoom: 'min' | 'max') => {
|
|
await zoomInput.evaluate((element, target) => {
|
|
const input = element as HTMLInputElement;
|
|
input.value = target === 'min' ? input.min : input.max;
|
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
}, zoom);
|
|
const box = await nowBlock.boundingBox();
|
|
return box?.width ?? 0;
|
|
};
|
|
|
|
const minZoomWidth = await blockWidthAt('min');
|
|
const maxZoomWidth = await blockWidthAt('max');
|
|
expect(maxZoomWidth).toBeGreaterThan(minZoomWidth);
|
|
|
|
// At max zoom the on-air block is wide enough to expose the info
|
|
// affordance (hidden on narrow/micro tiers), which opens the shared
|
|
// programme-details dialog with the programme metadata.
|
|
await nowBlock.locator('.epg-timeline__info').click();
|
|
|
|
const dialog = app.mainWindow.locator('.epg-dialog');
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog.locator('.epg-dialog__title')).toHaveText(
|
|
currentProgram.title
|
|
);
|
|
// An on-air programme offers "watch live" as the primary action.
|
|
await expect(
|
|
dialog.locator('.epg-dialog__btn--primary')
|
|
).toBeVisible();
|
|
|
|
await dialog.locator('.epg-dialog__close').click();
|
|
await app.mainWindow.waitForSelector('.epg-dialog', {
|
|
state: 'detached',
|
|
});
|
|
} finally {
|
|
await closeElectronApp(app);
|
|
}
|
|
});
|