mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
fix(e2e): stop the web backend from outliving Playwright runs (#1747)
This commit is contained in:
1 parent
d60c80746b
commit
4e17fbed4e
6 files changed
+107
-11
No files matched your search
@@ -0,0 +1,63 @@
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
describe('Playwright web-backend launch', () => {
|
||||
const appsDirectory = join(process.cwd(), 'apps');
|
||||
const launchCommand = 'node --import tsx apps/web-backend/src/main.ts';
|
||||
const configsStartingBackend = ['apps/web-e2e/playwright.config.ts'];
|
||||
const readSource = (path: string) =>
|
||||
readFileSync(join(process.cwd(), path), 'utf8');
|
||||
const serveEnv = (
|
||||
JSON.parse(readSource('apps/web-backend/project.json')) as {
|
||||
targets: { serve: { options: { env: Record<string, string> } } };
|
||||
}
|
||||
).targets.serve.options.env;
|
||||
|
||||
it('knows every Playwright config that starts the web backend', () => {
|
||||
const configs = readdirSync(appsDirectory, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.flatMap((entry) =>
|
||||
readdirSync(join(appsDirectory, entry.name))
|
||||
.filter((name) =>
|
||||
/^playwright(\..+)?\.config\.ts$/.test(name)
|
||||
)
|
||||
.map((name) => `apps/${entry.name}/${name}`)
|
||||
)
|
||||
.filter((configPath) =>
|
||||
readSource(configPath).includes('web-backend')
|
||||
);
|
||||
|
||||
expect(configs.sort()).toEqual(configsStartingBackend);
|
||||
});
|
||||
|
||||
// Nx starts a run-commands child in its own detached process group, and
|
||||
// Playwright stops a webServer with a process-group SIGKILL, so a backend
|
||||
// launched through Nx outlived the run and kept port 3333.
|
||||
it.each(configsStartingBackend)(
|
||||
'starts the backend in %s as a single node process',
|
||||
(configPath) => {
|
||||
const source = readSource(configPath);
|
||||
const start = source.indexOf(`'${launchCommand}'`);
|
||||
|
||||
expect(source).not.toMatch(/\bnx\b[^'"`\n]*web-backend/);
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
expect(
|
||||
source.indexOf(launchCommand, start + launchCommand.length)
|
||||
).toBe(-1);
|
||||
|
||||
// WEB_BACKEND_PORT relocates the run; its default is the target's.
|
||||
const { PORT: port, ...fixedEnv } = serveEnv;
|
||||
const entry = source.slice(start, source.indexOf('url:', start));
|
||||
for (const [name, value] of Object.entries(fixedEnv)) {
|
||||
expect(entry).toContain(`${name}: '${value}'`);
|
||||
}
|
||||
expect(source).toContain(
|
||||
`process.env['WEB_BACKEND_PORT'] ?? '${port}'`
|
||||
);
|
||||
expect(entry).toContain('PORT: webBackendPort,');
|
||||
expect(source).toContain(
|
||||
'url: `http://localhost:${webBackendPort}/health`'
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -15,6 +15,7 @@ const webServerCommand =
|
||||
? `pnpm nx run web:serve-static --port=${staticPwaPort}`
|
||||
: 'pnpm nx run web:serve';
|
||||
const reuseExistingWebServer = isStaticPwaE2E ? false : !process.env['CI'];
|
||||
const webBackendPort = process.env['WEB_BACKEND_PORT'] ?? '3333';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
@@ -57,7 +58,8 @@ export default defineConfig({
|
||||
* both mock servers honour them as a fallback for PORT (their serve
|
||||
* targets no longer pin PORT, so an explicit shell value reaches the
|
||||
* process). That is what lets two worktrees run E2E side by side when one
|
||||
* already holds 3210/3211.
|
||||
* already holds 3210/3211. WEB_BACKEND_PORT does the same for the web
|
||||
* backend and self-hosted.e2e.ts, which is the only spec that calls it.
|
||||
*/
|
||||
webServer: [
|
||||
{
|
||||
@@ -89,9 +91,18 @@ export default defineConfig({
|
||||
reuseExistingServer: !process.env['CI'],
|
||||
cwd: workspaceRoot,
|
||||
},
|
||||
/* Same single-process launch for the backend; `env` mirrors the
|
||||
* `web-backend:serve` target, which stays the manual entry point. */
|
||||
{
|
||||
command: 'pnpm nx run web-backend:serve',
|
||||
url: 'http://localhost:3333/health',
|
||||
command: 'node --import tsx apps/web-backend/src/main.ts',
|
||||
env: {
|
||||
PORT: webBackendPort,
|
||||
CLIENT_URL: 'http://localhost:4200',
|
||||
BACKEND_URL: '/api',
|
||||
IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS: '1',
|
||||
TSX_TSCONFIG_PATH: 'tsconfig.base.json',
|
||||
},
|
||||
url: `http://localhost:${webBackendPort}/health`,
|
||||
reuseExistingServer: !process.env['CI'],
|
||||
cwd: workspaceRoot,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,8 @@ import { openSourceEditor, sourceRowByTitle } from './sources-pwa.helpers';
|
||||
import { postWithRetry, setInputValue } from './e2e-helpers';
|
||||
import { expect, test } from './fixtures';
|
||||
|
||||
const WEB_BACKEND_URL = 'http://localhost:3333';
|
||||
const WEB_BACKEND_PORT = process.env['WEB_BACKEND_PORT'] ?? '3333';
|
||||
const WEB_BACKEND_URL = `http://localhost:${WEB_BACKEND_PORT}`;
|
||||
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}`;
|
||||
|
||||
@@ -70,17 +70,20 @@ describe('Playwright mock-server launch', () => {
|
||||
const source = readSource(configPath);
|
||||
|
||||
expect(source).not.toMatch(/\bnx\b[^'"`\n]*mock-server/);
|
||||
// Other single-process webServers (web-e2e's web-backend) also set
|
||||
// TSX_TSCONFIG_PATH, so check it inside each mock's own entry.
|
||||
for (const mock of mocks) {
|
||||
expect(source).toContain(
|
||||
`'node --import tsx apps/${mock}-mock-server/src/main.ts'`
|
||||
);
|
||||
const launch = `'node --import tsx apps/${mock}-mock-server/src/main.ts'`;
|
||||
const start = source.indexOf(launch);
|
||||
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
expect(
|
||||
source.slice(start, source.indexOf('url:', start))
|
||||
).toContain("TSX_TSCONFIG_PATH: 'tsconfig.base.json'");
|
||||
}
|
||||
expect(
|
||||
source.match(/node --import tsx apps\/[\w-]+-mock-server\//g)
|
||||
).toHaveLength(mocks.length);
|
||||
expect(
|
||||
source.match(/TSX_TSCONFIG_PATH: 'tsconfig\.base\.json'/g)
|
||||
).toHaveLength(mocks.length);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -73,6 +73,23 @@ The current self-hosted PWA uses these `apps/web-backend` routes:
|
||||
- `GET /xtream?targetId=<id>&username=<u>&password=<p>&action=<action>`
|
||||
- `GET /stalker?targetId=<id>&macAddress=<mac>&action=<action>`
|
||||
|
||||
`pnpm nx run web-backend:serve` starts the backend by hand on port 3333 with
|
||||
`TSX_TSCONFIG_PATH=tsconfig.base.json`, which `tsx` needs for the
|
||||
`@iptvnator/*` path aliases. `apps/web-e2e/playwright.config.ts` does not use
|
||||
that target: it launches `node --import tsx apps/web-backend/src/main.ts` with
|
||||
an `env` that mirrors the target's. Nx `run-commands` starts its command in a
|
||||
detached process group, and Playwright stops a `webServer` with a
|
||||
process-group `SIGKILL` (`taskkill /T /F` on Windows). The kill therefore
|
||||
reached only `pnpm`/`nx`, and the orphaned server kept port 3333 after the run.
|
||||
`apps/web-backend/src/app/project-config.spec.ts` fails if a Playwright config
|
||||
launches the backend through Nx or its `env` drifts from the `serve` target.
|
||||
`WEB_BACKEND_PORT` (default 3333) relocates the E2E backend and the
|
||||
`self-hosted.e2e.ts` URLs, like `MOCK_PORT` does for the mocks. Use it when
|
||||
another worktree holds 3333. The `web:serve` entry can stay on Nx:
|
||||
`@angular/build:dev-server` runs inside the Nx process, so the group kill
|
||||
stops it. The mock servers follow the same
|
||||
rule; see [Xtream mock Playwright integration](xtream-mock-server.md#playwright-integration).
|
||||
|
||||
The PWA continues to use `PwaService`; only the backend base URL is resolved at
|
||||
runtime. Electron routes remain owned by the Electron backend and preload
|
||||
bridge.
|
||||
|
||||
@@ -579,7 +579,8 @@ for the serve target's `--tsconfig` flag and is required for the `@iptvnator/*`
|
||||
path aliases. `project-config.spec.ts` pins which configs start which mock,
|
||||
and fails if any of them launches a mock through Nx or a new config starts one
|
||||
without being listed there. The `serve` targets remain the entry point
|
||||
for starting a mock by hand.
|
||||
for starting a mock by hand. The web-e2e `web-backend` entry uses the same
|
||||
launch form; see [PWA web backend](pwa-self-hosted.md#web-backend).
|
||||
|
||||
### Request Interception
|
||||
|
||||
|
||||
Reference in new issue
Block a user