test(web-e2e): cover self-hosted backend proxy

This commit is contained in:
4gray committed 2026-05-15 11:20:35 +02:00
1 parent eafc95bef0
commit eab5939b7d
2 files changed
+112

No files matched your search

+6
View File
@@ -46,6 +46,12 @@ export default defineConfig({
reuseExistingServer: !process.env['CI'],
cwd: workspaceRoot,
},
{
command: 'pnpm nx run web-backend:serve',
url: 'http://localhost:3333/health',
reuseExistingServer: !process.env['CI'],
cwd: workspaceRoot,
},
],
projects: [
{
+106
View File
@@ -0,0 +1,106 @@
import { expect, type Locator, type Page, test } from '@playwright/test';
const WEB_BACKEND_URL = 'http://localhost:3333';
const XTREAM_MOCK_PORT = process.env['XTREAM_MOCK_PORT'] ?? '3211';
const STALKER_MOCK_PORT = process.env['MOCK_PORT'] ?? '3210';
const XTREAM_MOCK_SERVER = `http://localhost:${XTREAM_MOCK_PORT}`;
const STALKER_MOCK_SERVER = `http://localhost:${STALKER_MOCK_PORT}`;
const STALKER_PORTAL_URL = `${STALKER_MOCK_SERVER}/portal.php`;
const DEFAULT_MAC = '00:1A:79:00:00:01';
async function installRuntimeConfig(page: Page): Promise<void> {
await page.route('**/assets/app-config.js', async (route) => {
await route.fulfill({
contentType: 'application/javascript',
body: `window.__IPTVNATOR_CONFIG__ = { BACKEND_URL: ${JSON.stringify(WEB_BACKEND_URL)} };\n`,
});
});
}
async function setInputValue(input: Locator, value: string): Promise<void> {
await input.fill('');
await input.fill(value);
if ((await input.inputValue()) === value) {
return;
}
await input.click();
await input.press('ControlOrMeta+A');
await input.press('Backspace');
await input.type(value);
await expect(input).toHaveValue(value);
}
async function addXtreamPortal(page: Page): Promise<void> {
await page.getByRole('button', { name: 'Add playlist' }).click();
const dialog = page.locator('mat-dialog-container');
await expect(dialog).toBeVisible();
await dialog.getByRole('tab', { name: 'Xtream', exact: true }).click();
await dialog.locator('#title').fill('Self-hosted Xtream');
await dialog.locator('#serverUrl').fill(XTREAM_MOCK_SERVER);
await dialog.locator('#username').fill('user1');
await dialog.locator('#password').fill('pass1');
await dialog.getByRole('button', { name: 'Add', exact: true }).click();
await page.waitForSelector('mat-dialog-container', { state: 'detached' });
await page.waitForURL(/xtreams.*vod/);
}
async function addStalkerPortal(page: Page): Promise<void> {
await page.getByRole('button', { name: 'Add playlist' }).click();
const dialog = page.locator('mat-dialog-container');
await expect(dialog).toBeVisible();
await dialog.getByRole('tab', { name: 'Stalker', exact: true }).click();
await setInputValue(dialog.locator('input#title'), 'Self-hosted Stalker');
await setInputValue(dialog.locator('input#portalUrl'), STALKER_PORTAL_URL);
await setInputValue(dialog.locator('input#macAddress'), DEFAULT_MAC);
const addButton = dialog.getByRole('button', { name: 'Add', exact: true });
await expect(addButton).toBeEnabled({ timeout: 10_000 });
await addButton.click();
await page.waitForSelector('mat-dialog-container', { state: 'detached' });
await page.waitForURL(/stalker.*vod/);
}
test.beforeEach(async ({ page, request }) => {
await request.post(`${XTREAM_MOCK_SERVER}/reset`);
await request.post(`${STALKER_MOCK_SERVER}/reset`);
await installRuntimeConfig(page);
await page.goto('/');
});
test('@self-hosted runtime config points PWA calls at the monorepo backend', async ({
page,
request,
}) => {
await expect
.poll(() =>
page.evaluate(() => window.__IPTVNATOR_CONFIG__?.BACKEND_URL)
)
.toBe(WEB_BACKEND_URL);
const response = await request.get(`${WEB_BACKEND_URL}/health`);
expect(response.ok()).toBeTruthy();
await expect(response).toBeOK();
});
test('@self-hosted Xtream portal loads through web-backend proxy', async ({
page,
}) => {
await addXtreamPortal(page);
const categoryItems = page.locator('.category-item');
await expect(categoryItems.first()).toBeVisible({ timeout: 15_000 });
});
test('@self-hosted Stalker portal loads through web-backend proxy', async ({
page,
}) => {
await addStalkerPortal(page);
const categoryItems = page.locator('.category-item');
await expect(categoryItems.first()).toBeVisible({ timeout: 15_000 });
});