Files
iptvnator/apps/web-e2e/playwright.config.ts
T
6b855eb73b fix(e2e): stop mock servers from outliving Playwright runs (#1710)
* fix(e2e): stop mock servers from outliving Playwright runs

Playwright stops a webServer with a SIGKILL to the process group it
spawned, but `nx run-commands` starts its command in a detached process
group of its own. Launching the Xtream/Stalker mocks through
`pnpm nx run *-mock-server:serve` therefore left the tsx server running
(reparented to PID 1) and holding its port after every run, so the next
run failed with "…/health is already used" or silently reused a stale
server.

Every Playwright config now starts the mocks as a single
`node --import tsx apps/<mock>/src/main.ts` process with
TSX_TSCONFIG_PATH=tsconfig.base.json, which stays in Playwright's group.
A project-config spec guards all playwright*.config.ts files against
regressing to the Nx launch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(e2e): read sidebar categories atomically; tighten mock launch guard

- category-management: readVisibleSidebarCategoryNames read items one by
  one; when Save removed an item between isVisible() and textContent(),
  textContent() auto-waited for the gone label through the whole 15 s
  poll, so expect.poll never retried (ubuntu shard 1 failed 3/3 while the
  UI already showed "No categories available"). Take one snapshot with
  filter({ visible: true }).evaluateAll() instead.
- project-config.spec: pin which Playwright configs start which mock,
  reject any Nx form that mentions a mock server, and fail when a new
  config starts a mock without being listed (the old count check passed
  vacuously on zero matches).
- docs: state which configs start which mock instead of "every config
  starts both".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* revert(e2e): leave the sidebar category read race to #1728

#1728 fixes the same readVisibleSidebarCategoryNames race with a shared
helper; keeping a second copy here would only conflict. This PR stays
about mock-server lifecycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:57:46 +02:00

136 lines
4.4 KiB
TypeScript

import { defineConfig, devices } from '@playwright/test';
import { nxE2EPreset } from '@nx/playwright/preset';
import { workspaceRoot } from '@nx/devkit';
const isStaticPwaE2E = process.env['IPTVNATOR_E2E_STATIC_PWA'] === '1';
const staticPwaPort = process.env['IPTVNATOR_E2E_STATIC_PORT'] ?? '4300';
// For CI, you may want to set BASE_URL to the deployed application.
const baseURL =
process.env['BASE_URL'] ||
(isStaticPwaE2E
? `http://localhost:${staticPwaPort}`
: 'http://localhost:4200');
const webServerCommand =
isStaticPwaE2E
? `pnpm nx run web:serve-static --port=${staticPwaPort}`
: 'pnpm nx run web:serve';
const reuseExistingWebServer = isStaticPwaE2E ? false : !process.env['CI'];
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
...nxE2EPreset(__filename, { testDir: './src' }),
testMatch: ['**/*.e2e.ts'],
reporter: [
['list'],
[
'html',
{
outputFolder: '../../dist/playwright-report/web-e2e',
},
],
[
'json',
{
outputFile: '../../dist/test-results/web-e2e/results.json',
},
],
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
baseURL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Run local dev servers before starting the tests.
* Both the Angular app and the Stalker mock server start in parallel.
*
* MOCK_PORT / XTREAM_MOCK_PORT relocate a mock for the whole run: the
* health checks below and the specs' MOCK_SERVER constants read them, and
* 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.
*/
webServer: [
{
command: webServerCommand,
url: baseURL,
reuseExistingServer: reuseExistingWebServer,
cwd: workspaceRoot,
},
/* The mocks run as one node process, not through `nx run …:serve`:
* Nx starts its command in a detached process group, so Playwright's
* process-group kill missed it and the server kept its port. */
{
command: 'node --import tsx apps/stalker-mock-server/src/main.ts',
env: {
NODE_ENV: 'development',
TSX_TSCONFIG_PATH: 'tsconfig.base.json',
},
url: `http://localhost:${process.env['MOCK_PORT'] ?? '3210'}/health`,
reuseExistingServer: !process.env['CI'],
cwd: workspaceRoot,
},
{
command: 'node --import tsx apps/xtream-mock-server/src/main.ts',
env: {
NODE_ENV: 'development',
TSX_TSCONFIG_PATH: 'tsconfig.base.json',
},
url: `http://localhost:${process.env['XTREAM_MOCK_PORT'] ?? '3211'}/health`,
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: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Uncomment for mobile browsers support
/* {
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
}, */
// Uncomment for branded browsers
/* {
name: 'Microsoft Edge',
use: { ...devices['Desktop Edge'], channel: 'msedge' },
},
{
name: 'Google Chrome',
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
} */
],
});