Proyecto App Windows
This commit is contained in:
@@ -0,0 +1,505 @@
|
||||
import {
|
||||
addStalkerPortal,
|
||||
addXtreamPortal,
|
||||
closeElectronApp,
|
||||
createMutableTextServer,
|
||||
deleteSource,
|
||||
dragSourceBefore,
|
||||
expect,
|
||||
expectPlaylistUpdatedToast,
|
||||
expectSourceDialogValues,
|
||||
getVisibleSourceTitles,
|
||||
importM3uPlaylistFromNativeDialog,
|
||||
importM3uPlaylistFromUrl,
|
||||
launchElectronApp,
|
||||
openSources,
|
||||
openSourceEditor,
|
||||
refreshSource,
|
||||
resetMockServers,
|
||||
restartElectronApp,
|
||||
saveSourceDialog,
|
||||
selectSourceSort,
|
||||
selectSourceTypeFilter,
|
||||
sourceRowByTitle,
|
||||
test,
|
||||
updateSourceDialog,
|
||||
waitForM3uCatalog,
|
||||
waitForPortalDebugEvent,
|
||||
waitForXtreamCatalog,
|
||||
waitForSourceRowIdle,
|
||||
writeTemporaryM3uFile,
|
||||
} from './electron-test-fixtures';
|
||||
|
||||
const localSourceFileName = 'alpha-local-source.m3u';
|
||||
const localSourceDisplayName = 'alpha-local-source';
|
||||
const urlSourceFileName = 'omega-url-source.m3u';
|
||||
const xtreamSourceName = 'Charlie Xtream Portal';
|
||||
const stalkerSourceName = 'Bravo Stalker Portal';
|
||||
const editableLocalSourceDisplayName = 'editable-local-source';
|
||||
const deletableLocalSourceDisplayName = 'deletable-local-source';
|
||||
const refreshLocalSourceDisplayName = 'refresh-local-source';
|
||||
|
||||
test.describe('Electron Sources View', () => {
|
||||
test('filters and sorts sources, including persisted custom order', async ({
|
||||
dataDir,
|
||||
request,
|
||||
}) => {
|
||||
await resetMockServers(request, ['xtream', 'stalker']);
|
||||
const localFilePath = writeTemporaryM3uFile(
|
||||
dataDir,
|
||||
localSourceFileName,
|
||||
[
|
||||
{
|
||||
groupTitle: 'News',
|
||||
name: 'Alpha Local News',
|
||||
url: 'https://streams.example.test/local-alpha.m3u8',
|
||||
},
|
||||
]
|
||||
);
|
||||
const urlServer = await createMutableTextServer(
|
||||
`#EXTM3U
|
||||
#EXTINF:-1 group-title="Sports",Omega URL Sports
|
||||
https://streams.example.test/url-omega.m3u8
|
||||
`,
|
||||
{
|
||||
contentType: 'application/vnd.apple.mpegurl',
|
||||
resourcePath: `/${urlSourceFileName}`,
|
||||
}
|
||||
);
|
||||
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromNativeDialog(app, localFilePath);
|
||||
await importM3uPlaylistFromUrl(
|
||||
app.mainWindow,
|
||||
urlServer.resourceUrl
|
||||
);
|
||||
await addXtreamPortal(app.mainWindow, {
|
||||
name: xtreamSourceName,
|
||||
});
|
||||
await addStalkerPortal(app.mainWindow, {
|
||||
name: stalkerSourceName,
|
||||
});
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toHaveLength(4);
|
||||
|
||||
await selectSourceTypeFilter(app.mainWindow, 'M3U');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([urlSourceFileName, localSourceDisplayName]);
|
||||
|
||||
await selectSourceTypeFilter(app.mainWindow, 'Xtream');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([xtreamSourceName]);
|
||||
|
||||
await selectSourceTypeFilter(app.mainWindow, 'Stalker');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([stalkerSourceName]);
|
||||
|
||||
await selectSourceTypeFilter(app.mainWindow, 'All');
|
||||
|
||||
await selectSourceSort(app.mainWindow, 'Date added (Oldest first)');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
localSourceDisplayName,
|
||||
urlSourceFileName,
|
||||
xtreamSourceName,
|
||||
stalkerSourceName,
|
||||
]);
|
||||
|
||||
await selectSourceSort(app.mainWindow, 'Name (A-Z)');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
localSourceDisplayName,
|
||||
stalkerSourceName,
|
||||
xtreamSourceName,
|
||||
urlSourceFileName,
|
||||
]);
|
||||
|
||||
await selectSourceSort(app.mainWindow, 'Name (Z-A)');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
urlSourceFileName,
|
||||
xtreamSourceName,
|
||||
stalkerSourceName,
|
||||
localSourceDisplayName,
|
||||
]);
|
||||
|
||||
await selectSourceSort(app.mainWindow, 'Date added (Newest first)');
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
stalkerSourceName,
|
||||
xtreamSourceName,
|
||||
urlSourceFileName,
|
||||
localSourceDisplayName,
|
||||
]);
|
||||
|
||||
await selectSourceSort(app.mainWindow, 'Custom order');
|
||||
await dragSourceBefore(
|
||||
app.mainWindow,
|
||||
xtreamSourceName,
|
||||
localSourceDisplayName
|
||||
);
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
xtreamSourceName,
|
||||
localSourceDisplayName,
|
||||
urlSourceFileName,
|
||||
stalkerSourceName,
|
||||
]);
|
||||
|
||||
await app.mainWindow
|
||||
.getByRole('link', { name: 'Dashboard', exact: true })
|
||||
.click();
|
||||
await openSources(app.mainWindow);
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
xtreamSourceName,
|
||||
localSourceDisplayName,
|
||||
urlSourceFileName,
|
||||
stalkerSourceName,
|
||||
]);
|
||||
|
||||
const restarted = await restartElectronApp(app, dataDir);
|
||||
app.electronApp = restarted.electronApp;
|
||||
app.mainWindow = restarted.mainWindow;
|
||||
|
||||
await openSources(app.mainWindow);
|
||||
await expect
|
||||
.poll(() => getVisibleSourceTitles(app.mainWindow))
|
||||
.toEqual([
|
||||
xtreamSourceName,
|
||||
localSourceDisplayName,
|
||||
urlSourceFileName,
|
||||
stalkerSourceName,
|
||||
]);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await urlServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('edits M3U, Xtream, and Stalker source details and keeps them after restart', async ({
|
||||
dataDir,
|
||||
request,
|
||||
}) => {
|
||||
await resetMockServers(request, ['xtream', 'stalker']);
|
||||
const localFilePath = writeTemporaryM3uFile(
|
||||
dataDir,
|
||||
'editable-local-source.m3u',
|
||||
[
|
||||
{
|
||||
groupTitle: 'Movies',
|
||||
name: 'Editable Local Channel',
|
||||
url: 'https://streams.example.test/editable-local.m3u8',
|
||||
},
|
||||
]
|
||||
);
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromNativeDialog(app, localFilePath);
|
||||
await addXtreamPortal(app.mainWindow, {
|
||||
name: 'Editable Xtream Source',
|
||||
});
|
||||
await addStalkerPortal(app.mainWindow, {
|
||||
name: 'Editable Stalker Source',
|
||||
});
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
let dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
editableLocalSourceDisplayName
|
||||
);
|
||||
await updateSourceDialog(dialog, {
|
||||
autoRefresh: true,
|
||||
title: 'Edited Local Source',
|
||||
userAgent: 'Electron E2E Local/1.0',
|
||||
});
|
||||
await saveSourceDialog(app.mainWindow, dialog);
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Editable Xtream Source'
|
||||
);
|
||||
await updateSourceDialog(dialog, {
|
||||
password: 'pass1',
|
||||
serverUrl: 'http://127.0.0.1:3211',
|
||||
title: 'Edited Xtream Source',
|
||||
username: 'user1',
|
||||
});
|
||||
await saveSourceDialog(app.mainWindow, dialog);
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Editable Stalker Source'
|
||||
);
|
||||
await updateSourceDialog(dialog, {
|
||||
macAddress: '00:1A:79:00:00:99',
|
||||
portalUrl: 'http://127.0.0.1:3210/portal.php',
|
||||
title: 'Edited Stalker Source',
|
||||
});
|
||||
await saveSourceDialog(app.mainWindow, dialog);
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Local Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
autoRefresh: true,
|
||||
title: 'Edited Local Source',
|
||||
userAgent: 'Electron E2E Local/1.0',
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Close', exact: true })
|
||||
.click();
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Xtream Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
password: 'pass1',
|
||||
serverUrl: 'http://127.0.0.1:3211',
|
||||
title: 'Edited Xtream Source',
|
||||
username: 'user1',
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Close', exact: true })
|
||||
.click();
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Stalker Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
macAddress: '00:1A:79:00:00:99',
|
||||
portalUrl: 'http://127.0.0.1:3210/portal.php',
|
||||
title: 'Edited Stalker Source',
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Close', exact: true })
|
||||
.click();
|
||||
|
||||
const restarted = await restartElectronApp(app, dataDir);
|
||||
app.electronApp = restarted.electronApp;
|
||||
app.mainWindow = restarted.mainWindow;
|
||||
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Local Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
autoRefresh: true,
|
||||
title: 'Edited Local Source',
|
||||
userAgent: 'Electron E2E Local/1.0',
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Close', exact: true })
|
||||
.click();
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Xtream Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
password: 'pass1',
|
||||
serverUrl: 'http://127.0.0.1:3211',
|
||||
title: 'Edited Xtream Source',
|
||||
username: 'user1',
|
||||
});
|
||||
await dialog
|
||||
.getByRole('button', { name: 'Close', exact: true })
|
||||
.click();
|
||||
|
||||
dialog = await openSourceEditor(
|
||||
app.mainWindow,
|
||||
'Edited Stalker Source'
|
||||
);
|
||||
await expectSourceDialogValues(dialog, {
|
||||
macAddress: '00:1A:79:00:00:99',
|
||||
portalUrl: 'http://127.0.0.1:3210/portal.php',
|
||||
title: 'Edited Stalker Source',
|
||||
});
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
|
||||
test('deletes M3U, Stalker, and Xtream sources from the sources view', async ({
|
||||
dataDir,
|
||||
request,
|
||||
}) => {
|
||||
await resetMockServers(request, ['xtream', 'stalker']);
|
||||
const localFilePath = writeTemporaryM3uFile(
|
||||
dataDir,
|
||||
'deletable-local-source.m3u',
|
||||
[
|
||||
{
|
||||
groupTitle: 'Live',
|
||||
name: 'Delete Me Local',
|
||||
url: 'https://streams.example.test/delete-local.m3u8',
|
||||
},
|
||||
]
|
||||
);
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromNativeDialog(app, localFilePath);
|
||||
await addXtreamPortal(app.mainWindow, {
|
||||
name: 'Delete Me Xtream',
|
||||
});
|
||||
await addStalkerPortal(app.mainWindow, {
|
||||
name: 'Delete Me Stalker',
|
||||
});
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
await deleteSource(app.mainWindow, deletableLocalSourceDisplayName);
|
||||
await expect(
|
||||
sourceRowByTitle(
|
||||
app.mainWindow,
|
||||
deletableLocalSourceDisplayName
|
||||
)
|
||||
).toHaveCount(0, {
|
||||
timeout: 20000,
|
||||
});
|
||||
|
||||
await deleteSource(app.mainWindow, 'Delete Me Stalker');
|
||||
await expect(
|
||||
sourceRowByTitle(app.mainWindow, 'Delete Me Stalker')
|
||||
).toHaveCount(0, {
|
||||
timeout: 20000,
|
||||
});
|
||||
|
||||
await deleteSource(app.mainWindow, 'Delete Me Xtream');
|
||||
await expect(
|
||||
sourceRowByTitle(app.mainWindow, 'Delete Me Xtream')
|
||||
).toHaveCount(0, {
|
||||
timeout: 60000,
|
||||
});
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
});
|
||||
|
||||
test('refreshes local-file M3U, URL M3U, and Xtream sources from the sources view', async ({
|
||||
dataDir,
|
||||
request,
|
||||
}) => {
|
||||
await resetMockServers(request, ['xtream']);
|
||||
const localFilePath = writeTemporaryM3uFile(
|
||||
dataDir,
|
||||
'refresh-local-source.m3u',
|
||||
[
|
||||
{
|
||||
groupTitle: 'News',
|
||||
name: 'Original Local Channel',
|
||||
url: 'https://streams.example.test/original-local.m3u8',
|
||||
},
|
||||
]
|
||||
);
|
||||
const urlServer = await createMutableTextServer(
|
||||
`#EXTM3U
|
||||
#EXTINF:-1 group-title="News",Original URL Channel
|
||||
https://streams.example.test/original-url.m3u8
|
||||
`,
|
||||
{
|
||||
contentType: 'application/vnd.apple.mpegurl',
|
||||
resourcePath: '/refresh-url-source.m3u',
|
||||
}
|
||||
);
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromNativeDialog(app, localFilePath);
|
||||
await importM3uPlaylistFromUrl(
|
||||
app.mainWindow,
|
||||
urlServer.resourceUrl
|
||||
);
|
||||
await addXtreamPortal(app.mainWindow, {
|
||||
name: 'Refresh Xtream Source',
|
||||
});
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
writeTemporaryM3uFile(dataDir, 'refresh-local-source.m3u', [
|
||||
{
|
||||
groupTitle: 'News',
|
||||
name: 'Refreshed Local Channel',
|
||||
url: 'https://streams.example.test/refreshed-local.m3u8',
|
||||
},
|
||||
]);
|
||||
await refreshSource(app.mainWindow, refreshLocalSourceDisplayName);
|
||||
await expectPlaylistUpdatedToast(app.mainWindow);
|
||||
await sourceRowByTitle(
|
||||
app.mainWindow,
|
||||
refreshLocalSourceDisplayName
|
||||
)
|
||||
.first()
|
||||
.click();
|
||||
await waitForM3uCatalog(app.mainWindow);
|
||||
await expect(
|
||||
app.mainWindow.getByTestId('channel-item').filter({
|
||||
hasText: 'Refreshed Local Channel',
|
||||
})
|
||||
).toHaveCount(1);
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
urlServer.setBody(
|
||||
`#EXTM3U
|
||||
#EXTINF:-1 group-title="News",Refreshed URL Channel
|
||||
https://streams.example.test/refreshed-url.m3u8
|
||||
`
|
||||
);
|
||||
await refreshSource(app.mainWindow, 'refresh-url-source.m3u');
|
||||
await expectPlaylistUpdatedToast(app.mainWindow);
|
||||
await waitForSourceRowIdle(
|
||||
app.mainWindow,
|
||||
'refresh-url-source.m3u'
|
||||
);
|
||||
await sourceRowByTitle(app.mainWindow, 'refresh-url-source.m3u')
|
||||
.first()
|
||||
.click();
|
||||
await waitForM3uCatalog(app.mainWindow);
|
||||
await expect(
|
||||
app.mainWindow.getByTestId('channel-item').filter({
|
||||
hasText: 'Refreshed URL Channel',
|
||||
})
|
||||
).toHaveCount(1);
|
||||
await openSources(app.mainWindow);
|
||||
|
||||
await refreshSource(app.mainWindow, 'Refresh Xtream Source', {
|
||||
confirm: true,
|
||||
});
|
||||
await waitForXtreamCatalog(app.mainWindow);
|
||||
await waitForPortalDebugEvent(app.mainWindow, {
|
||||
operation: 'get_live_categories',
|
||||
provider: 'xtream',
|
||||
timeoutMs: 30000,
|
||||
});
|
||||
await openSources(app.mainWindow);
|
||||
await waitForSourceRowIdle(app.mainWindow, 'Refresh Xtream Source');
|
||||
await expect(
|
||||
sourceRowByTitle(app.mainWindow, 'Refresh Xtream Source')
|
||||
.first()
|
||||
.locator('.meta')
|
||||
).toContainText('Updated:');
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await urlServer.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user