From abb7e5fa41dd1a1cf17147ca05a22be06fb66534 Mon Sep 17 00:00:00 2001 From: 4gray Date: Wed, 30 Sep 2026 22:32:07 +0200 Subject: [PATCH] test(e2e): scope the mock dependency guard to E2E targets Check only the `e2e*` target defaults in nx.json, so an unrelated default may still depend on a mock, and also check every target in the `apps/*-e2e` project.json files, where a mock `serve` dependency would break the same targets. Co-Authored-By: Claude Opus 5.5 --- .../src/app/project-config.spec.ts | 68 +++++++++++++------ docs/architecture/xtream-mock-server.md | 3 +- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/apps/xtream-mock-server/src/app/project-config.spec.ts b/apps/xtream-mock-server/src/app/project-config.spec.ts index b1650df2b..c3b19a1f6 100644 --- a/apps/xtream-mock-server/src/app/project-config.spec.ts +++ b/apps/xtream-mock-server/src/app/project-config.spec.ts @@ -88,39 +88,65 @@ describe('Playwright mock-server launch', () => { ); }); -describe('Nx E2E target defaults', () => { - type DependsOn = Array; - const nxJson = JSON.parse( - readFileSync(join(process.cwd(), 'nx.json'), 'utf8') - ) as { - targetDefaults: Record; - }; +describe('Nx E2E task dependencies', () => { + type Dependency = string | { projects?: string | string[] }; + type TargetConfig = { dependsOn?: Dependency[] }; + const readJson = (path: string) => + JSON.parse(readFileSync(join(process.cwd(), path), 'utf8')) as T; + const nxJson = readJson<{ + targetDefaults: Record; + }>('nx.json'); + const e2eProjectFiles = readdirSync(join(process.cwd(), 'apps')) + .filter((name) => name.endsWith('-e2e')) + .map((name) => `apps/${name}/project.json`); + const dependsOnMock = (dependency: Dependency) => + typeof dependency === 'string' + ? dependency.includes('-mock-server:') + : [dependency.projects ?? []] + .flat() + .some((project) => project.includes('-mock-server')); // The Playwright configs start the mocks themselves, so @nx/playwright - // infers the atomized e2e-ci targets as non-parallel, and Nx refuses to - // run a non-parallel task that depends on a continuous `serve` task. + // infers their E2E targets as non-parallel, and Nx refuses to run a + // non-parallel task that depends on a continuous `serve` task. it('never makes an E2E target depend on a mock-server task', () => { - const mockDependencies = Object.entries(nxJson.targetDefaults) + const e2eDefaults = Object.entries(nxJson.targetDefaults) + .filter(([targetName]) => targetName.startsWith('e2e')) .flatMap(([targetName, config]) => - (config.dependsOn ?? []).map((dependency) => ({ - targetName, - dependency, + [config].flat().map((entry) => ({ + source: `nx.json ${targetName}`, + config: entry, })) - ) - .filter(({ dependency }) => - typeof dependency === 'string' - ? dependency.includes('-mock-server:') - : (dependency.projects ?? []).some((project) => - project.endsWith('-mock-server') - ) ); + const e2eProjectTargets = e2eProjectFiles.flatMap((path) => + Object.entries( + readJson<{ targets: Record }>(path) + .targets + ).map(([targetName, config]) => ({ + source: `${path} ${targetName}`, + config, + })) + ); + const mockDependencies = [...e2eDefaults, ...e2eProjectTargets] + .map(({ source, config }) => ({ + source, + dependencies: (config.dependsOn ?? []).filter(dependsOnMock), + })) + .filter(({ dependencies }) => dependencies.length > 0); + expect(e2eProjectFiles).toEqual( + expect.arrayContaining([ + 'apps/electron-backend-e2e/project.json', + 'apps/web-e2e/project.json', + ]) + ); expect(mockDependencies).toEqual([]); }); it('still builds the Electron app before each per-file E2E target', () => { expect( - nxJson.targetDefaults['e2e-ci--src/*.e2e.ts']?.dependsOn + (nxJson.targetDefaults['e2e-ci--src/*.e2e.ts'] as TargetConfig) + .dependsOn ).toContainEqual({ projects: ['electron-backend'], target: 'build-e2e', diff --git a/docs/architecture/xtream-mock-server.md b/docs/architecture/xtream-mock-server.md index d9ffca8b4..a82e1618f 100644 --- a/docs/architecture/xtream-mock-server.md +++ b/docs/architecture/xtream-mock-server.md @@ -582,7 +582,8 @@ without being listed there. The `serve` targets remain the entry point for starting a mock by hand, and no E2E target may depend on them: with no Nx-launched server, `@nx/playwright` infers the atomized `e2e-ci--*` targets as non-parallel, and Nx refuses to run a non-parallel task that depends on a -continuous `serve` task. The same spec guards `nx.json` `targetDefaults`. The web-e2e `web-backend` entry uses the same +continuous `serve` task. The same spec guards the `e2e*` entries of `nx.json` +`targetDefaults` and every target in the `apps/*-e2e` `project.json` files. The web-e2e `web-backend` entry uses the same launch form; see [PWA web backend](pwa-self-hosted.md#web-backend). ### Request Interception