Proyecto App Linux
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
import { Page } from '@playwright/test';
|
||||
import {
|
||||
buildM3uContent,
|
||||
channelItemByTitle,
|
||||
closeElectronApp,
|
||||
createMutableTextServer,
|
||||
expect,
|
||||
importM3uPlaylistFromUrl,
|
||||
launchElectronApp,
|
||||
openWorkspaceSection,
|
||||
openSettings,
|
||||
test,
|
||||
} from './electron-test-fixtures';
|
||||
|
||||
const fallbackLogoSvg = Buffer.from(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96"><rect width="96" height="96" rx="20" fill="#16436b"/><text x="48" y="58" text-anchor="middle" font-size="28" font-family="Arial" fill="#f5f7fb">GN</text></svg>`
|
||||
).toString('base64');
|
||||
const fallbackLogoDataUrl = `data:image/svg+xml;base64,${fallbackLogoSvg}`;
|
||||
const epgFixtureXml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tv>
|
||||
<channel id="guide-news">
|
||||
<display-name>Guide News</display-name>
|
||||
</channel>
|
||||
<programme start="20260328070000 +0000" stop="20260328080000 +0000" channel="guide-news">
|
||||
<title>Guide Bulletin</title>
|
||||
<desc>EPG refresh smoke test.</desc>
|
||||
</programme>
|
||||
</tv>
|
||||
`;
|
||||
|
||||
test.describe('Electron EPG', () => {
|
||||
test('adds an EPG source, fetches guide data, removes the source row, and clears stored EPG data', async ({
|
||||
dataDir,
|
||||
}) => {
|
||||
const epgServer = await createMutableTextServer(epgFixtureXml, {
|
||||
contentType: 'application/xml; charset=utf-8',
|
||||
resourcePath: '/guide.xml',
|
||||
});
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await openSettings(app.mainWindow);
|
||||
await app.mainWindow
|
||||
.getByRole('button', { name: 'Add EPG source' })
|
||||
.click();
|
||||
await app.mainWindow
|
||||
.locator('.epg-source-row input')
|
||||
.first()
|
||||
.fill(epgServer.resourceUrl);
|
||||
|
||||
await app.mainWindow.locator('.epg-source-row button').first().click();
|
||||
await expect(
|
||||
app.mainWindow.locator('.epg-progress-panel')
|
||||
).toBeVisible();
|
||||
await expect
|
||||
.poll(() => getEpgChannelCount(app.mainWindow), {
|
||||
timeout: 30000,
|
||||
})
|
||||
.toBeGreaterThan(0);
|
||||
await expect(
|
||||
app.mainWindow.locator('.epg-progress-panel .stat-badge').first()
|
||||
).toBeVisible();
|
||||
|
||||
await app.mainWindow.locator('.epg-source-row button').nth(1).click();
|
||||
await expect(app.mainWindow.locator('.epg-source-row')).toHaveCount(0);
|
||||
|
||||
await app.mainWindow
|
||||
.getByRole('button', { name: 'Clear EPG data' })
|
||||
.click();
|
||||
const dialog = app.mainWindow.locator('mat-dialog-container');
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole('button', { name: 'Yes', exact: true }).click();
|
||||
await app.mainWindow.waitForSelector('mat-dialog-container', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect
|
||||
.poll(() => getEpgChannelCount(app.mainWindow), {
|
||||
timeout: 20000,
|
||||
})
|
||||
.toBe(0);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await epgServer.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('uses the XMLTV channel icon as a fallback when the playlist has no tvg-logo', async ({
|
||||
dataDir,
|
||||
}) => {
|
||||
const playlistServer = await createMutableTextServer(
|
||||
buildM3uContent([
|
||||
{
|
||||
name: 'Guide News Live',
|
||||
tvgId: 'guide-news',
|
||||
url: 'https://example.com/live/guide-news.m3u8',
|
||||
},
|
||||
]),
|
||||
{
|
||||
contentType: 'application/x-mpegurl; charset=utf-8',
|
||||
resourcePath: '/playlist.m3u',
|
||||
}
|
||||
);
|
||||
const epgServer = await createMutableTextServer(
|
||||
`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tv>
|
||||
<channel id="guide-news">
|
||||
<display-name>Guide News Live</display-name>
|
||||
<icon src="${fallbackLogoDataUrl}"/>
|
||||
</channel>
|
||||
<programme start="20260328070000 +0000" stop="20260328080000 +0000" channel="guide-news">
|
||||
<title>Guide Bulletin</title>
|
||||
<desc>EPG logo fallback test.</desc>
|
||||
</programme>
|
||||
</tv>
|
||||
`,
|
||||
{
|
||||
contentType: 'application/xml; charset=utf-8',
|
||||
resourcePath: '/guide.xml',
|
||||
}
|
||||
);
|
||||
const app = await launchElectronApp(dataDir);
|
||||
|
||||
try {
|
||||
await importM3uPlaylistFromUrl(
|
||||
app.mainWindow,
|
||||
playlistServer.resourceUrl
|
||||
);
|
||||
|
||||
await openSettings(app.mainWindow);
|
||||
await app.mainWindow
|
||||
.getByRole('button', { name: 'Add EPG source' })
|
||||
.click();
|
||||
await app.mainWindow
|
||||
.locator('.epg-source-row input')
|
||||
.first()
|
||||
.fill(epgServer.resourceUrl);
|
||||
await app.mainWindow.locator('.epg-source-row button').first().click();
|
||||
|
||||
await expect
|
||||
.poll(() => getEpgChannelCount(app.mainWindow), {
|
||||
timeout: 30000,
|
||||
})
|
||||
.toBeGreaterThan(0);
|
||||
|
||||
await openWorkspaceSection(app.mainWindow, 'All channels');
|
||||
|
||||
const channelItem = channelItemByTitle(
|
||||
app.mainWindow,
|
||||
'Guide News Live'
|
||||
);
|
||||
await expect(channelItem).toBeVisible();
|
||||
await expect(channelItem.locator('.channel-logo')).toHaveAttribute(
|
||||
'src',
|
||||
fallbackLogoDataUrl
|
||||
);
|
||||
await expect(
|
||||
channelItem.locator('.channel-logo-fallback')
|
||||
).toHaveCount(0);
|
||||
} finally {
|
||||
await closeElectronApp(app);
|
||||
await playlistServer.close();
|
||||
await epgServer.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function getEpgChannelCount(page: Page): Promise<number> {
|
||||
return page.evaluate(async () => {
|
||||
const channels = await window.electron?.getEpgChannelsByRange?.(0, 20);
|
||||
return Array.isArray(channels) ? channels.length : 0;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user