mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
test(web-e2e): mock provider target registration
This commit is contained in:
1 parent
90c5c30eae
commit
db19d7d9f4
3 files changed
+129
-13
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import { type Page } from '@playwright/test';
|
||||
|
||||
interface ProviderTargetPayload {
|
||||
readonly url: string;
|
||||
}
|
||||
|
||||
const BACKEND_ORIGIN = 'http://localhost:3000';
|
||||
const CORS_HEADERS = {
|
||||
'access-control-allow-headers': 'content-type',
|
||||
'access-control-allow-methods': 'POST, OPTIONS',
|
||||
'access-control-allow-origin': 'http://localhost:4200',
|
||||
} as const;
|
||||
|
||||
export async function interceptProviderTargetRegistration(
|
||||
page: Page
|
||||
): Promise<Map<string, string>> {
|
||||
const providerTargets = new Map<string, string>();
|
||||
|
||||
await page.route(`${BACKEND_ORIGIN}/provider-targets**`, async (route) => {
|
||||
if (route.request().method() === 'OPTIONS') {
|
||||
await route.fulfill({
|
||||
body: '',
|
||||
headers: CORS_HEADERS,
|
||||
status: 200,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const providerUrl = getProviderUrl(route.request().postDataJSON());
|
||||
if (!providerUrl) {
|
||||
await route.fulfill({
|
||||
body: JSON.stringify({
|
||||
message: 'Missing url',
|
||||
status: 400,
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
headers: CORS_HEADERS,
|
||||
status: 400,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const targetId = Buffer.from(providerUrl).toString('base64url');
|
||||
providerTargets.set(targetId, providerUrl);
|
||||
|
||||
await route.fulfill({
|
||||
body: JSON.stringify({ targetId }),
|
||||
contentType: 'application/json',
|
||||
headers: CORS_HEADERS,
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
return providerTargets;
|
||||
}
|
||||
|
||||
export function getRegisteredProviderUrl(
|
||||
url: URL,
|
||||
providerTargets: ReadonlyMap<string, string>
|
||||
): string | null {
|
||||
const targetId = url.searchParams.get('targetId');
|
||||
if (!targetId) {
|
||||
return url.searchParams.get('url');
|
||||
}
|
||||
|
||||
return providerTargets.get(targetId) ?? null;
|
||||
}
|
||||
|
||||
function getProviderUrl(value: unknown): string | null {
|
||||
if (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
'url' in value &&
|
||||
typeof (value as Partial<ProviderTargetPayload>).url === 'string'
|
||||
) {
|
||||
return (value as ProviderTargetPayload).url;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -5,6 +5,10 @@ import {
|
||||
type Page,
|
||||
test,
|
||||
} from '@playwright/test';
|
||||
import {
|
||||
getRegisteredProviderUrl,
|
||||
interceptProviderTargetRegistration,
|
||||
} from './provider-target-route';
|
||||
|
||||
/**
|
||||
* Stalker Portal E2E Tests
|
||||
@@ -42,11 +46,25 @@ const MINIMAL_MAC = '00:1A:79:00:00:03';
|
||||
* any app environment configuration.
|
||||
*/
|
||||
async function interceptStalkerRequests(page: Page): Promise<void> {
|
||||
const providerTargets = await interceptProviderTargetRegistration(page);
|
||||
|
||||
await page.route('**/localhost:3000/stalker**', async (route) => {
|
||||
const originalUrl = new URL(route.request().url());
|
||||
const mockUrl = new URL(BACKEND_PROXY);
|
||||
// Forward all query params unchanged
|
||||
const providerUrl = getRegisteredProviderUrl(
|
||||
originalUrl,
|
||||
providerTargets
|
||||
);
|
||||
|
||||
if (providerUrl) {
|
||||
mockUrl.searchParams.set('url', providerUrl);
|
||||
}
|
||||
|
||||
originalUrl.searchParams.forEach((value, key) => {
|
||||
if (key === 'targetId') {
|
||||
return;
|
||||
}
|
||||
|
||||
mockUrl.searchParams.set(key, value);
|
||||
});
|
||||
await route.continue({ url: mockUrl.toString() });
|
||||
@@ -183,9 +201,8 @@ test('@stalker VOD — content list loads after selecting a category', async ({
|
||||
const contentItems = page.locator(
|
||||
'.content-card, [data-test-id="channel-item"], mat-card'
|
||||
);
|
||||
await expect(contentItems).not.toHaveCount(0, { timeout: 10_000 });
|
||||
await expect(contentItems.first()).toBeVisible({ timeout: 10_000 });
|
||||
const itemCount = await contentItems.count();
|
||||
expect(itemCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('@stalker minimal scenario — correct item counts', async ({ page }) => {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { APIRequestContext, test, expect, Page } from '@playwright/test';
|
||||
import {
|
||||
getRegisteredProviderUrl,
|
||||
interceptProviderTargetRegistration,
|
||||
} from './provider-target-route';
|
||||
|
||||
/**
|
||||
* Xtream Codes E2E Tests
|
||||
@@ -33,10 +37,25 @@ const EPG_PASSWORD = 'epg';
|
||||
* to the mock server. This avoids any real backend requirement.
|
||||
*/
|
||||
async function interceptXtreamRequests(page: Page): Promise<void> {
|
||||
const providerTargets = await interceptProviderTargetRegistration(page);
|
||||
|
||||
await page.route('**/localhost:3000/xtream**', async (route) => {
|
||||
const originalUrl = new URL(route.request().url());
|
||||
const mockUrl = new URL(`${MOCK_SERVER}/xtream`);
|
||||
const providerUrl = getRegisteredProviderUrl(
|
||||
originalUrl,
|
||||
providerTargets
|
||||
);
|
||||
|
||||
if (providerUrl) {
|
||||
mockUrl.searchParams.set('url', providerUrl);
|
||||
}
|
||||
|
||||
originalUrl.searchParams.forEach((value, key) => {
|
||||
if (key === 'targetId') {
|
||||
return;
|
||||
}
|
||||
|
||||
mockUrl.searchParams.set(key, value);
|
||||
});
|
||||
await route.continue({ url: mockUrl.toString() });
|
||||
@@ -289,21 +308,19 @@ test('@xtream epg fixture — short epg starts at the current program, respects
|
||||
expect(decodeXtreamText(nextListing.title)).toBe('Market Wrap');
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
expect(Number.parseInt(currentListing.start_timestamp, 10)).toBeLessThanOrEqual(
|
||||
now
|
||||
);
|
||||
expect(Number.parseInt(currentListing.stop_timestamp, 10)).toBeGreaterThanOrEqual(
|
||||
now
|
||||
);
|
||||
expect(
|
||||
Number.parseInt(currentListing.start_timestamp, 10)
|
||||
).toBeLessThanOrEqual(now);
|
||||
expect(
|
||||
Number.parseInt(currentListing.stop_timestamp, 10)
|
||||
).toBeGreaterThanOrEqual(now);
|
||||
expect(currentListing.start).not.toBe(
|
||||
formatXtreamDateTime(
|
||||
Number.parseInt(currentListing.start_timestamp, 10)
|
||||
)
|
||||
);
|
||||
expect(currentListing.end).not.toBe(
|
||||
formatXtreamDateTime(
|
||||
Number.parseInt(currentListing.stop_timestamp, 10)
|
||||
)
|
||||
formatXtreamDateTime(Number.parseInt(currentListing.stop_timestamp, 10))
|
||||
);
|
||||
});
|
||||
|
||||
@@ -344,7 +361,9 @@ test('@xtream epg fixture — full epg and legacy alias return the same ordered
|
||||
decodeXtreamText(listing.title) === 'Late Edition'
|
||||
);
|
||||
if (!boundaryListing) {
|
||||
throw new Error('Expected the full EPG fixture to include Late Edition.');
|
||||
throw new Error(
|
||||
'Expected the full EPG fixture to include Late Edition.'
|
||||
);
|
||||
}
|
||||
|
||||
const boundaryStart = new Date(
|
||||
|
||||
Reference in new issue
Block a user