From 46e653204dfade6cc6bb29e8790a02e9afc7de10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Str=C3=B6berg?= Date: Mon, 11 May 2026 09:56:23 +0200 Subject: [PATCH 1/2] test(nx-payload): optimize test suite and add plugin unit coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Unblock application and preset generator specs by mocking createProjectGraphAsync per spec file (scoped, preserves spyOn in migration specs); remove describe.skip and daemon boilerplate - Remove stale 'should disable react compiler' test; fix path alias assertion (./apps/… prefix) - Delete legacy.spec.ts e2e (500s, deprecated pattern; structural coverage owned by unit tests) - Trim quick.spec.ts e2e: remove 5 redundant target execution tests, keep setup-path uniqueness and one build smoke test - Replace skip-docker config with main; Linux runs main, mac/Windows keep quick for cross-platform coverage - Fix Playwright preinstall: skip cache/install/deps on macOS/Windows - Fix Dockerfile template: node:20-alpine → node:22-alpine - Add createPayloadTargets spec: graphQL branching, all 7 targets, env vars, project-scoped container references - Add normalizePluginOptions spec: all defaults and overrides - Add createPayloadNodes spec: routing, cache, project structure, configFile derivation, graphQL state propagation --- .github/workflows/e2e-matrix.yml | 5 +- e2e/nx-payload-e2e/project.json | 4 +- .../src/nx-payload.legacy.spec.ts | 133 -------------- .../src/nx-payload.quick.spec.ts | 30 ---- .../__snapshots__/application.spec.ts.snap | 2 +- .../application/application.spec.ts | 40 ++--- .../application/files/Dockerfile__tmpl__ | 2 +- .../src/generators/preset/preset.spec.ts | 24 ++- .../utils/create-payload-nodes.spec.ts | 163 ++++++++++++++++++ .../utils/normalize-plugin-options.spec.ts | 59 +++++++ .../src/utils/create-payload-targets.spec.ts | 122 +++++++++++++ 11 files changed, 374 insertions(+), 210 deletions(-) delete mode 100644 e2e/nx-payload-e2e/src/nx-payload.legacy.spec.ts create mode 100644 packages/nx-payload/src/plugins/utils/create-payload-nodes.spec.ts create mode 100644 packages/nx-payload/src/plugins/utils/normalize-plugin-options.spec.ts create mode 100644 packages/nx-payload/src/utils/create-payload-targets.spec.ts diff --git a/.github/workflows/e2e-matrix.yml b/.github/workflows/e2e-matrix.yml index 5f7ae39d3..cc341443c 100644 --- a/.github/workflows/e2e-matrix.yml +++ b/.github/workflows/e2e-matrix.yml @@ -162,6 +162,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Cache Playwright browsers + if: matrix.os == 'ubuntu-latest' uses: actions/cache@v4 id: cache-playwright with: @@ -171,7 +172,7 @@ jobs: playwright-${{ runner.os }}- - name: Install Playwright browsers - if: steps.cache-playwright.outputs.cache-hit != 'true' + if: matrix.os == 'ubuntu-latest' && steps.cache-playwright.outputs.cache-hit != 'true' run: pnpm exec playwright install - name: Install Playwright system dependencies @@ -196,7 +197,7 @@ jobs: - os: ubuntu-latest name: Linux timeout: 30 - configuration: skip-docker + configuration: main verdaccio: localhost - os: macos-latest name: macOS diff --git a/e2e/nx-payload-e2e/project.json b/e2e/nx-payload-e2e/project.json index 9ccfe9490..68ca1b546 100644 --- a/e2e/nx-payload-e2e/project.json +++ b/e2e/nx-payload-e2e/project.json @@ -16,8 +16,8 @@ "docker": { "testPathPatterns": ["docker", "ui"] }, - "skip-docker": { - "testPathIgnorePatterns": ["docker", "ui"] + "main": { + "testPathPatterns": ["main"] }, "quick": { "testPathPatterns": ["quick"] diff --git a/e2e/nx-payload-e2e/src/nx-payload.legacy.spec.ts b/e2e/nx-payload-e2e/src/nx-payload.legacy.spec.ts deleted file mode 100644 index 86fdd017d..000000000 --- a/e2e/nx-payload-e2e/src/nx-payload.legacy.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { runCommand } from '@codeware/core/testing'; -import { - ensureCreateNxWorkspaceProject, - ensureLockFileIsDetected, - getFolderFiles -} from '@codeware/e2e/utils'; -import type { CreateNxWorkspaceProject } from '@codeware/e2e/utils'; -import type { NxJsonConfiguration, ProjectConfiguration } from '@nx/devkit'; -import { - checkFilesExist, - readJson, - runCommandAsync, - runNxCommand, - runNxCommandAsync -} from '@nx/plugin/testing'; - -/** - * This test suite use legacy Nx behavior to opt-out of plugin inference - * and create project targets. - */ - -describe('Test plugin by creating workspace with preset (legacy test suite)', () => { - /** Default workspace project */ - let project: CreateNxWorkspaceProject; - - jest.setTimeout(500_000); - - beforeAll(async () => { - process.env['NX_ADD_PLUGINS'] = 'false'; - project = await ensureCreateNxWorkspaceProject({ - preset: '@cdwr/nx-payload' - }); - ensureLockFileIsDetected(project.packageManager); - }); - - afterAll(() => { - delete process.env['NX_ADD_PLUGINS']; - runNxCommand('reset', { silenceError: true }); - }); - - describe('verify setup', () => { - it('should have installed @cdwr/nx-payload', async () => { - await runCommandAsync('npm ls @cdwr/nx-payload'); - }); - - it('should have created an initial payload application', () => { - expect(readJson(`${project.appDirectory}/project.json`).name).toBe( - project.appName - ); - - expect(() => - checkFilesExist(`${project.appDirectory}/src/payload.config.ts`) - ).not.toThrow(); - }); - - it('should not have any plugins in nx.json', () => { - const nxJson = readJson('nx.json'); - expect(nxJson.plugins ?? []).toEqual([]); - }); - - it('should have all payload targets', () => { - const projectJson = readJson( - `${project.appDirectory}/project.json` - ); - for (const target of [ - 'dx:mongodb', - 'dx:postgres', - 'dx:start', - 'dx:stop', - 'gen', - 'payload', - 'payload-graphql' - ]) { - expect(projectJson.targets[target]).toBeDefined(); - } - }); - - it('should have build and serve targets from next plugin', () => { - const projectJson = readJson( - `${project.appDirectory}/project.json` - ); - expect(projectJson.targets['build']).toBeDefined(); - expect(projectJson.targets['serve']).toBeDefined(); - }); - }); - - describe('run targets', () => { - it('should build application', async () => { - await runNxCommandAsync(`build ${project.appName}`); - expect(() => - checkFilesExist( - `${project.appDirectory}/.next/standalone`, - `${project.appDirectory}/.next/static` - ) - ).not.toThrow(); - }); - - it('should test application', async () => { - // https://github.com/nrwl/nx/issues/32880 - await runNxCommandAsync(`test ${project.appName} --force-exit`); - }); - - it('should lint application', async () => { - await runNxCommandAsync(`lint ${project.appName}`); - }); - - it('should serve application (serve target)', async () => { - await runCommand(`nx serve ${project.appName}`, { - doneFn: (log) => /Ready in \d/.test(log), - errorDetector: /Error:/, - verbose: process.env.CDWR_DEBUG_LOGGING === 'true' - }); - }); - - it('should generate types', () => { - expect( - getFolderFiles(`${project.appDirectory}/src/generated`) - ).toHaveLength(0); - - const result = runNxCommand(`gen ${project.appName}`); - expect(result).toContain('Successfully ran target gen'); - - // Only types should be generated - expect(getFolderFiles(`${project.appDirectory}/src/generated`)).toEqual([ - 'payload-types.ts' - ]); - }); - - it('should invoke payload cli', async () => { - await runNxCommandAsync(`payload ${project.appName} info`); - }); - }); -}); diff --git a/e2e/nx-payload-e2e/src/nx-payload.quick.spec.ts b/e2e/nx-payload-e2e/src/nx-payload.quick.spec.ts index 1ff958d9f..7bb1c6547 100644 --- a/e2e/nx-payload-e2e/src/nx-payload.quick.spec.ts +++ b/e2e/nx-payload-e2e/src/nx-payload.quick.spec.ts @@ -1,4 +1,3 @@ -import { runCommand } from '@codeware/core/testing'; import { logDebug } from '@codeware/core/utils'; import { ensureCreateNxWorkspaceProject, @@ -101,34 +100,5 @@ describe('Test plugin by starting with an empty workspace (limited test suite)', ) ).not.toThrow(); }); - - it('should test application', async () => { - // https://github.com/nrwl/nx/issues/32880 - await runNxCommandAsync(`test ${appName} --force-exit`); - }); - - it('should lint application', async () => { - await runNxCommandAsync(`lint ${appName}`); - }); - - it('should serve application (dev target)', async () => { - await runCommand(`nx dev ${appName}`, { - doneFn: (log) => /Ready in \d/.test(log), - errorDetector: /Error:/, - verbose: process.env.CDWR_DEBUG_LOGGING === 'true' - }); - }); - - it('should generate types', async () => { - await runNxCommandAsync(`gen ${appName}`); - - expect(() => - checkFilesExist(`${appDirectory}/src/generated/payload-types.ts`) - ).not.toThrow(); - }); - - it('should invoke payload cli', async () => { - await runNxCommandAsync(`payload ${appName} info`); - }); }); }); diff --git a/packages/nx-payload/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/nx-payload/src/generators/application/__snapshots__/application.spec.ts.snap index 12ecb1dc0..6cea773a0 100644 --- a/packages/nx-payload/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/nx-payload/src/generators/application/__snapshots__/application.spec.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`application generator should generate docker files 1`] = ` -"FROM node:20-alpine AS base +"FROM node:22-alpine AS base FROM base AS deps # Check the link to understand why libc6-compat might be needed diff --git a/packages/nx-payload/src/generators/application/application.spec.ts b/packages/nx-payload/src/generators/application/application.spec.ts index 70ecef275..a4199bf1f 100644 --- a/packages/nx-payload/src/generators/application/application.spec.ts +++ b/packages/nx-payload/src/generators/application/application.spec.ts @@ -1,3 +1,13 @@ +// Prevent @nx/next:init and @cdwr/nx-payload:init from calling createProjectGraphAsync, +// which hits the real filesystem even when given a virtual Tree. +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockResolvedValue({ + nodes: {}, + dependencies: {} + }) +})); + import { type Tree, readNxJson, readProjectConfiguration } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { lintConfigHasOverride } from '@nx/eslint/src/generators/utils/eslint-file'; @@ -7,11 +17,7 @@ import { payloadTargets } from '../../utils/definitions'; import { applicationGenerator } from './application'; import type { AppGeneratorSchema } from './schema'; -// Temporary disable until we've found a way to run the tests without disabling the daemon. -// Currently the tests runs on the real filesystem which is a test anti-pattern and makes the tests slow. -// Should be fixed in COD-274. - -describe.skip('application generator', () => { +describe('application generator', () => { let tree: Tree; const options: AppGeneratorSchema = { directory: 'apps/test-dir', @@ -23,25 +29,12 @@ describe.skip('application generator', () => { console.log = jest.fn(); console.warn = jest.fn(); - jest.setTimeout(60_000); - - let nxDaemon: string; - beforeAll(() => { - nxDaemon = process.env['NX_DAEMON']; - process.env['NX_DAEMON'] = 'false'; - }); - - afterAll(() => { - process.env['NX_DAEMON'] = nxDaemon; - }); - beforeEach(() => { jest.clearAllMocks(); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); }); afterEach(() => { - // TODO: Remove once Nx has fixed MaxListenersExceededWarning process.removeAllListeners('SIGTERM'); }); @@ -214,21 +207,12 @@ describe.skip('application generator', () => { ); }); - it('should disable react compiler', async () => { - await applicationGenerator(tree, options); - - const nextConfig = tree - .read(`${options.directory}/next.config.mjs`, 'utf-8') - .replaceAll(/(\s|\n)/g, ''); - expect(nextConfig).toMatch(/experimental:.*reactCompiler:false/); - }); - it('should add payload config path alias once to tsconfig.base.json', async () => { await applicationGenerator(tree, options); const tsConfig = JSON.parse(tree.read('tsconfig.base.json', 'utf-8')); expect(tsConfig.compilerOptions.paths['@payload-config']).toEqual([ - `${options.directory}/src/payload.config.ts` + `./${options.directory}/src/payload.config.ts` ]); // Add another app and the process should not throw diff --git a/packages/nx-payload/src/generators/application/files/Dockerfile__tmpl__ b/packages/nx-payload/src/generators/application/files/Dockerfile__tmpl__ index e4ec646b9..5286bd622 100644 --- a/packages/nx-payload/src/generators/application/files/Dockerfile__tmpl__ +++ b/packages/nx-payload/src/generators/application/files/Dockerfile__tmpl__ @@ -1,4 +1,4 @@ -FROM node:20-alpine AS base +FROM node:22-alpine AS base FROM base AS deps # Check the link to understand why libc6-compat might be needed diff --git a/packages/nx-payload/src/generators/preset/preset.spec.ts b/packages/nx-payload/src/generators/preset/preset.spec.ts index eff955213..dde02ccbb 100644 --- a/packages/nx-payload/src/generators/preset/preset.spec.ts +++ b/packages/nx-payload/src/generators/preset/preset.spec.ts @@ -1,3 +1,13 @@ +// Prevent @nx/next:init and @cdwr/nx-payload:init from calling createProjectGraphAsync, +// which hits the real filesystem even when given a virtual Tree. +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockResolvedValue({ + nodes: {}, + dependencies: {} + }) +})); + import { type Tree, readProjectConfiguration } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; @@ -6,7 +16,7 @@ import { payloadTargets } from '../../utils/definitions'; import { presetGenerator } from './preset'; import type { PresetGeneratorSchema } from './schema'; -describe.skip('preset generator', () => { +describe('preset generator', () => { let tree: Tree; const options: PresetGeneratorSchema = { @@ -20,18 +30,6 @@ describe.skip('preset generator', () => { console.log = jest.fn(); console.warn = jest.fn(); - jest.setTimeout(10_000); - - let nxDaemon: string; - beforeAll(() => { - nxDaemon = process.env['NX_DAEMON']; - process.env['NX_DAEMON'] = 'false'; - }); - - afterAll(() => { - process.env['NX_DAEMON'] = nxDaemon; - }); - beforeEach(() => { jest.clearAllMocks(); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); diff --git a/packages/nx-payload/src/plugins/utils/create-payload-nodes.spec.ts b/packages/nx-payload/src/plugins/utils/create-payload-nodes.spec.ts new file mode 100644 index 000000000..2c517f38c --- /dev/null +++ b/packages/nx-payload/src/plugins/utils/create-payload-nodes.spec.ts @@ -0,0 +1,163 @@ +jest.mock('../../utils/find-up-fs'); +jest.mock('../../utils/is-graphql-disabled'); +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + readJsonFile: jest.fn() +})); +jest.mock('@nx/devkit/src/utils/calculate-hash-for-create-nodes', () => ({ + calculateHashForCreateNodes: jest.fn().mockResolvedValue('hash-abc') +})); +jest.mock('@nx/js', () => ({ + getLockFileName: jest.fn().mockReturnValue('package-lock.json') +})); + +import type { CreateNodesContextV2 } from '@nx/devkit'; +import { readJsonFile } from '@nx/devkit'; + +import { findUpFs } from '../../utils/find-up-fs'; +import { isGraphQLDisabled } from '../../utils/is-graphql-disabled'; + +import { createPayloadNodes } from './create-payload-nodes'; + +const mockFindUpFs = findUpFs as jest.MockedFunction; +const mockIsGraphQLDisabled = isGraphQLDisabled as jest.MockedFunction< + typeof isGraphQLDisabled +>; +const mockReadJsonFile = readJsonFile as jest.MockedFunction< + typeof readJsonFile +>; + +const context: CreateNodesContextV2 = { + workspaceRoot: '/workspace', + nxJsonConfiguration: {} +}; + +describe('createPayloadNodes', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockIsGraphQLDisabled.mockResolvedValue(true); + mockReadJsonFile.mockReturnValue({ name: 'my-app' }); + }); + + it('should return empty result when no project.json is found', async () => { + mockFindUpFs.mockResolvedValue(null); + + const result = await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + {} + ); + + expect(result).toEqual({}); + }); + + it('should return a project entry when project.json is found', async () => { + mockFindUpFs.mockResolvedValue('/workspace/apps/my-app/project.json'); + + const result = await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + {} + ); + + expect(result).toMatchObject({ + projects: { + 'apps/my-app': { + root: 'apps/my-app' + } + } + }); + }); + + it('should include inferred targets in the project entry', async () => { + mockFindUpFs.mockResolvedValue('/workspace/apps/my-app/project.json'); + + const result = await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + {} + ); + + const targets = ( + result as { + projects: Record }>; + } + ).projects['apps/my-app'].targets; + expect(targets).toHaveProperty('gen'); + expect(targets).toHaveProperty('payload'); + expect(targets).toHaveProperty('dx:mongodb'); + }); + + it('should derive configFile relative to the project root', async () => { + mockFindUpFs.mockResolvedValue('/workspace/apps/my-app/project.json'); + + const result = await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + {} + ); + + const { gen } = ( + result as { + projects: Record< + string, + { targets: Record }> } + >; + } + ).projects['apps/my-app'].targets; + expect(gen.options['env']['PAYLOAD_CONFIG_PATH']).toBe( + 'src/payload.config.ts' + ); + }); + + it('should use cached targets on repeated calls with the same hash', async () => { + mockFindUpFs.mockResolvedValue('/workspace/apps/my-app/project.json'); + + const cache: Record> = {}; + await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + cache + ); + await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + cache + ); + + // isGraphQLDisabled is called to compute the hash input, but createPayloadTargets + // should only run once — the second call hits the cache + expect(mockIsGraphQLDisabled).toHaveBeenCalledTimes(2); + expect(cache['hash-abc']).toBeDefined(); + }); + + it('should pass graphQL disabled state to target creation', async () => { + mockFindUpFs.mockResolvedValue('/workspace/apps/my-app/project.json'); + mockIsGraphQLDisabled.mockResolvedValue(false); + + const result = await createPayloadNodes( + 'apps/my-app/src/payload.config.ts', + context, + undefined, + {} + ); + + const { gen } = ( + result as { + projects: Record< + string, + { targets: Record }> } + >; + } + ).projects['apps/my-app'].targets; + expect(gen.options['commands']).toContain( + 'npx payload-graphql generate:schema' + ); + }); +}); diff --git a/packages/nx-payload/src/plugins/utils/normalize-plugin-options.spec.ts b/packages/nx-payload/src/plugins/utils/normalize-plugin-options.spec.ts new file mode 100644 index 000000000..8bdb9a7b0 --- /dev/null +++ b/packages/nx-payload/src/plugins/utils/normalize-plugin-options.spec.ts @@ -0,0 +1,59 @@ +import { normalizePluginOptions } from './normalize-plugin-options'; + +describe('normalizePluginOptions', () => { + it('should return all defaults when called with no options', () => { + expect(normalizePluginOptions()).toEqual({ + generateTargetName: 'gen', + payloadTargetName: 'payload', + payloadGraphqlTargetName: 'payload-graphql', + dxMongodbTargetName: 'dx:mongodb', + dxPostgresTargetName: 'dx:postgres', + dxStartTargetName: 'dx:start', + dxStopTargetName: 'dx:stop' + }); + }); + + it('should return all defaults when called with undefined', () => { + expect(normalizePluginOptions(undefined)).toEqual(normalizePluginOptions()); + }); + + it('should override generateTargetName', () => { + expect( + normalizePluginOptions({ generateTargetName: 'generate' }) + .generateTargetName + ).toBe('generate'); + }); + + it('should override payloadTargetName', () => { + expect( + normalizePluginOptions({ payloadTargetName: 'run-payload' }) + .payloadTargetName + ).toBe('run-payload'); + }); + + it('should override payloadGraphqlTargetName', () => { + expect( + normalizePluginOptions({ payloadGraphqlTargetName: 'graphql' }) + .payloadGraphqlTargetName + ).toBe('graphql'); + }); + + it('should override dx target names', () => { + const result = normalizePluginOptions({ + dxMongodbTargetName: 'start:mongo', + dxPostgresTargetName: 'start:pg', + dxStartTargetName: 'start:all', + dxStopTargetName: 'stop:all' + }); + expect(result.dxMongodbTargetName).toBe('start:mongo'); + expect(result.dxPostgresTargetName).toBe('start:pg'); + expect(result.dxStartTargetName).toBe('start:all'); + expect(result.dxStopTargetName).toBe('stop:all'); + }); + + it('should keep defaults for unspecified options', () => { + const result = normalizePluginOptions({ generateTargetName: 'generate' }); + expect(result.payloadTargetName).toBe('payload'); + expect(result.dxMongodbTargetName).toBe('dx:mongodb'); + }); +}); diff --git a/packages/nx-payload/src/utils/create-payload-targets.spec.ts b/packages/nx-payload/src/utils/create-payload-targets.spec.ts new file mode 100644 index 000000000..6f1656843 --- /dev/null +++ b/packages/nx-payload/src/utils/create-payload-targets.spec.ts @@ -0,0 +1,122 @@ +import { createPayloadTargets } from './create-payload-targets'; + +describe('createPayloadTargets', () => { + const base = { + projectName: 'my-app', + projectRoot: 'apps/my-app', + configFile: 'src/payload.config.ts' + }; + + describe('gen target', () => { + it('should only run generate:types when graphQL is disabled', () => { + const { gen } = createPayloadTargets({ + ...base, + isGraphQLDisabled: true + }); + expect(gen.options['commands']).toEqual(['npx payload generate:types']); + }); + + it('should run generate:types and generate:schema when graphQL is enabled', () => { + const { gen } = createPayloadTargets({ + ...base, + isGraphQLDisabled: false + }); + expect(gen.options['commands']).toEqual([ + 'npx payload generate:types', + 'npx payload-graphql generate:schema' + ]); + }); + + it('should include graphQL schema command when isGraphQLDisabled is not specified', () => { + const { gen } = createPayloadTargets(base); + expect(gen.options['commands']).toEqual([ + 'npx payload generate:types', + 'npx payload-graphql generate:schema' + ]); + }); + + it('should set PAYLOAD_CONFIG_PATH env var', () => { + const { gen } = createPayloadTargets(base); + expect(gen.options['env']['PAYLOAD_CONFIG_PATH']).toBe( + 'src/payload.config.ts' + ); + }); + + it('should run in the project root', () => { + const { gen } = createPayloadTargets(base); + expect(gen.options['cwd']).toBe('apps/my-app'); + }); + + it('should use the config file as input', () => { + const { gen } = createPayloadTargets(base); + expect(gen.inputs).toContain('{projectRoot}/src/payload.config.ts'); + }); + + it('should run commands sequentially', () => { + const { gen } = createPayloadTargets(base); + expect(gen.options['parallel']).toBe(false); + }); + }); + + describe('payload target', () => { + it('should forward all args', () => { + const { payload } = createPayloadTargets(base); + expect(payload.options['forwardAllArgs']).toBe(true); + }); + + it('should set PAYLOAD_CONFIG_PATH env var', () => { + const { payload } = createPayloadTargets(base); + expect(payload.options['env']['PAYLOAD_CONFIG_PATH']).toBe( + 'src/payload.config.ts' + ); + }); + }); + + describe('dx targets', () => { + it('should scope mongodb container name to the project', () => { + const targets = createPayloadTargets(base); + expect(targets['dx:mongodb'].command).toContain('mongodb-my-app'); + }); + + it('should scope postgres container name to the project', () => { + const targets = createPayloadTargets(base); + expect(targets['dx:postgres'].command).toContain('postgres-my-app'); + }); + + it('should reference the project docker-compose.yml in dx:start', () => { + const targets = createPayloadTargets(base); + expect(targets['dx:start'].command).toContain( + 'apps/my-app/docker-compose.yml' + ); + }); + + it('should reference the project docker-compose.yml in dx:stop', () => { + const targets = createPayloadTargets(base); + expect(targets['dx:stop'].command).toContain( + 'apps/my-app/docker-compose.yml' + ); + }); + + it('should scope postgres container env file to the project root', () => { + const targets = createPayloadTargets(base); + expect(targets['dx:postgres'].command).toContain( + 'apps/my-app/.env.local' + ); + }); + }); + + describe('all 7 targets are present', () => { + it('should return gen, payload, payload-graphql, dx:mongodb, dx:postgres, dx:start, dx:stop', () => { + const targets = createPayloadTargets(base); + expect(Object.keys(targets).sort()).toEqual([ + 'dx:mongodb', + 'dx:postgres', + 'dx:start', + 'dx:stop', + 'gen', + 'payload', + 'payload-graphql' + ]); + }); + }); +}); From 088daf042b8fc755d94fa444904b56d0e38062e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Str=C3=B6berg?= Date: Mon, 11 May 2026 09:58:04 +0200 Subject: [PATCH 2/2] fix(nx-payload): update path alias assertion after rebase --- .../nx-payload/src/generators/application/application.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nx-payload/src/generators/application/application.spec.ts b/packages/nx-payload/src/generators/application/application.spec.ts index a4199bf1f..2c9c73f62 100644 --- a/packages/nx-payload/src/generators/application/application.spec.ts +++ b/packages/nx-payload/src/generators/application/application.spec.ts @@ -212,7 +212,7 @@ describe('application generator', () => { const tsConfig = JSON.parse(tree.read('tsconfig.base.json', 'utf-8')); expect(tsConfig.compilerOptions.paths['@payload-config']).toEqual([ - `./${options.directory}/src/payload.config.ts` + `${options.directory}/src/payload.config.ts` ]); // Add another app and the process should not throw