Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
399ba0f
tests: add test skeleton
AdrianRomanski Jan 8, 2025
c0adb81
tests: add normalizeCreateNodesOptions spy and test
AdrianRomanski Jan 8, 2025
73d3a51
tests: add isEnvProject spy and test
AdrianRomanski Jan 9, 2025
eb0390d
tests: add isPkgProject spy and test
AdrianRomanski Jan 9, 2025
3a88a54
tests: should return empty object if !isE2eProject and !isPublishable…
AdrianRomanski Jan 9, 2025
8a8fcf5
tests: add logger warn tests
AdrianRomanski Jan 9, 2025
37b7654
tests: add project configuration top level structure
AdrianRomanski Jan 9, 2025
b559c85
tests: add verdaccioTargets spy and test
AdrianRomanski Jan 9, 2025
49f1f3e
tests: add verdaccioTargets spy and test
AdrianRomanski Jan 9, 2025
ba2c40b
tests: add getEnvTargets spy and test
AdrianRomanski Jan 9, 2025
dcc37cd
tests: add object structure
AdrianRomanski Jan 9, 2025
03697f5
tests: add updateEnvTargetNames spy and test
AdrianRomanski Jan 9, 2025
16379ff
tests: code cleanup
AdrianRomanski Jan 9, 2025
d9a12d0
tests: getPkgTargets object structure
AdrianRomanski Jan 9, 2025
8da9a70
docs: add createProjectConfiguration
AdrianRomanski Jan 10, 2025
b8a082d
docs: createProjectConfiguration docs formatting
AdrianRomanski Jan 10, 2025
25501d3
docs: add isPkgProject
AdrianRomanski Jan 10, 2025
b00c0cd
docs: add getPkgTargets
AdrianRomanski Jan 10, 2025
1e3d7d3
self-review: test names polish, add missing structure test
AdrianRomanski Jan 10, 2025
164cf8f
self-review: add regex for executor
AdrianRomanski Jan 10, 2025
5bcc40d
docs: rephrasing, formatting
AdrianRomanski Jan 18, 2025
ae25a1f
tests: create-targets change negations to `is false`
AdrianRomanski Jan 18, 2025
21ecc4b
tests: add is true to boolean variable to remain consistent with is f…
AdrianRomanski Jan 18, 2025
1ab50d6
tests: change empty/not empty to given/not given
AdrianRomanski Jan 18, 2025
7920a85
tests: change `configuration with correct structure` to `a config`, r…
AdrianRomanski Jan 18, 2025
c801c4c
docs: remove implementation details
AdrianRomanski Jan 18, 2025
601038a
docs: change expect.any(Object) to {}, as options are always an empty…
AdrianRomanski Jan 18, 2025
2c1dd13
tests: getPkgTargets add dependsOn check for both targets
AdrianRomanski Jan 18, 2025
0f87763
self-review: grouping of test by categories: generation, unhappy path…
AdrianRomanski Jan 18, 2025
38878f7
self-review: memory optimization - remove consts that were used only …
AdrianRomanski Jan 18, 2025
6724a18
self-review: getPkgTargets memory optimization - remove consts that w…
AdrianRomanski Jan 18, 2025
7de1589
self-review: delete `with correct structure` for consistency with cre…
AdrianRomanski Jan 18, 2025
a39e93e
self-review: make generation tests to check one field of object or wh…
AdrianRomanski Jan 18, 2025
555005f
self-review: nx format
AdrianRomanski Jan 18, 2025
a966b80
self-review: change TARGET_PACKAGE_PUBLISH to TARGET_PACKAGE_INSTALL
AdrianRomanski Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions projects/nx-verdaccio/src/plugin/targets/create-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ import {
} from './environment.targets';
import { getPkgTargets, isPkgProject } from './package.targets';

/**
* Generates a project configuration partial including `targets` and `namedInputs`.
*
* If the project is an environment project, derived by `isEnvProject()`,
* It returns the results of `verdaccioTargets()`, `getEnvTargets()`,
*`updateEnvTargetNames()` under `targets`, and `namedInputs`
*
* If the project is a publishable project, derived by `isPkgProject()`,
* it returns the results of `getPkgTargets()`, and `namedInputs`
*
* Otherwise, it returns an empty object (as early exit)
*
* Additionally, it logs warnings for missing implicit dependencies in environment projects.
*
* @param projectConfiguration
* @param options
* @returns A partial project configuration with `targets` and `namedInputs`.
*/
export function createProjectConfiguration(
projectConfiguration: ProjectConfiguration,
options: NxVerdaccioCreateNodeOptions
Expand All @@ -28,9 +46,10 @@ export function createProjectConfiguration(
}

/**
* Unfortunately namedInputs are not picked up by tasks graph: Error: Input 'build-artifacts' is not defined
* When you pass your own namedInputs (like you would in a project.json file) via the inferred tasks plugin, the tasks pipeline ignores them and throws this error.
* Some Nx plugins use the default namedInput, probably for that reason, but I'm concerned that if developers change those inputs, it might lead to undesired behavior.
* When you pass your own `namedInputs` (like you would in a `project.json` file)
* via the inferred tasks plugin, the tasks pipeline ignores them and throws this error.
* Some Nx plugins use the default `namedInput`, probably for that reason,
* but I'm concerned that if developers change those inputs, it might lead to undesired behaviour.
* @todo investigate if there is a way to pass namedInputs to the tasks graph
*/
const namedInputs: ProjectConfiguration['namedInputs'] = {
Expand Down
249 changes: 249 additions & 0 deletions projects/nx-verdaccio/src/plugin/targets/create-targets.unit-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import { beforeEach, describe, expect, type MockInstance } from 'vitest';
import { type ProjectConfiguration } from '@nx/devkit';

import * as nxDevkitMockModule from '@nx/devkit';

import { createProjectConfiguration } from './create-targets';
import {
TARGET_PACKAGE_INSTALL,
TARGET_PACKAGE_PUBLISH,
} from './package.targets';
import {
TARGET_ENVIRONMENT_E2E,
TARGET_ENVIRONMENT_SETUP,
TARGET_ENVIRONMENT_INSTALL,
TARGET_ENVIRONMENT_TEARDOWN,
TARGET_ENVIRONMENT_BOOTSTRAP,
TARGET_ENVIRONMENT_PUBLISH_ONLY,
TARGET_ENVIRONMENT_VERDACCIO_STOP,
TARGET_ENVIRONMENT_VERDACCIO_START,
} from './environment.targets';

import { type NxVerdaccioCreateNodeOptions } from '../schema';
import { type NormalizedCreateNodeOptions } from '../normalize-create-nodes-options';

import * as packageTargetsSpyModule from './package.targets';
import * as environmentTargetsModule from './environment.targets';
import * as normalizeCreateNodesSpyModule from './../normalize-create-nodes-options';

describe('createProjectConfiguration', (): void => {
const implicitDependencies = ['mock-implicit-dep'];
const projectConfiguration: ProjectConfiguration = {
root: 'mock-root',
name: 'unit-test-project',
targets: { build: {} },
};
const options: NxVerdaccioCreateNodeOptions = {
environments: {
targetNames: ['build'],
},
};
const normalizedOptions: NormalizedCreateNodeOptions = {
environments: {
targetNames: ['build'],
environmentsDir: './environments',
},
packages: {
targetNames: ['test', 'lint'],
environmentsDir: './packages',
},
};

vi.mock('@nx/devkit', () => {
return {
logger: {
warn: vi.fn(),
},
};
});

let normalizeCreateNodesOptionsSpy: MockInstance;
let isEnvProjectSpy: MockInstance;
let isPkgSpy: MockInstance;
let verdaccioTargetsSpy: MockInstance;
let getEnvTargetsSpy: MockInstance;
let updateEnvTargetNamesSpy: MockInstance;

beforeEach((): void => {
normalizeCreateNodesOptionsSpy = vi
.spyOn(normalizeCreateNodesSpyModule, 'normalizeCreateNodesOptions')
.mockReturnValue(normalizedOptions);
isEnvProjectSpy = vi
.spyOn(environmentTargetsModule, 'isEnvProject')
.mockReturnValue(true);
isPkgSpy = vi
.spyOn(packageTargetsSpyModule, 'isPkgProject')
.mockReturnValue(true);
Comment on lines +60 to +76
Copy link
Contributor

@BioPhoton BioPhoton Feb 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General best practices for setup spy's and configuring spy's:

  • setup in describe
  • configuring with potential defaults in beforeEach
  • actual mock return in the it block

verdaccioTargetsSpy = vi.spyOn(
environmentTargetsModule,
'verdaccioTargets'
);
getEnvTargetsSpy = vi.spyOn(environmentTargetsModule, 'getEnvTargets');
updateEnvTargetNamesSpy = vi.spyOn(
environmentTargetsModule,
'updateEnvTargetNames'
);
});

afterEach((): void => {
normalizeCreateNodesOptionsSpy.mockRestore();
isEnvProjectSpy.mockRestore();
isPkgSpy.mockRestore();
verdaccioTargetsSpy.mockRestore();
getEnvTargetsSpy.mockRestore();
updateEnvTargetNamesSpy.mockRestore();
});

it('should generate a config if isE2eProject and isPublishableProject are true', (): void => {
expect(
createProjectConfiguration(projectConfiguration, options)
).toMatchObject({
targets: expect.any(Object),
namedInputs: expect.any(Object),
});
});

it('should generate a config if isE2eProject is true and isPublishableProject is false', (): void => {
isPkgSpy.mockReturnValue(false);
expect(
createProjectConfiguration(projectConfiguration, options)
).toMatchObject({
namedInputs: expect.any(Object),
targets: expect.any(Object),
});
});

it('should generate a config if isE2eProject is false and isPublishableProject is true', (): void => {
isEnvProjectSpy.mockReturnValue(false);
expect(
createProjectConfiguration(projectConfiguration, options)
).toMatchObject({
targets: expect.any(Object),
});
});

it('should generate targets if isE2eProject is true and isPublishableProject is false', (): void => {
isPkgSpy.mockReturnValue(false);
expect(
createProjectConfiguration(projectConfiguration, options)['targets']
).toMatchObject({
build: expect.any(Object),
[TARGET_ENVIRONMENT_VERDACCIO_START]: expect.any(Object),
[TARGET_ENVIRONMENT_VERDACCIO_STOP]: expect.any(Object),
[TARGET_ENVIRONMENT_BOOTSTRAP]: expect.any(Object),
[TARGET_ENVIRONMENT_INSTALL]: expect.any(Object),
[TARGET_ENVIRONMENT_PUBLISH_ONLY]: expect.any(Object),
[TARGET_ENVIRONMENT_SETUP]: expect.any(Object),
[TARGET_ENVIRONMENT_TEARDOWN]: expect.any(Object),
[TARGET_ENVIRONMENT_E2E]: expect.any(Object),
});
});

it('should generate targets if isE2eProject is false and isPublishableProject is true', (): void => {
expect(
createProjectConfiguration(projectConfiguration, options)['targets']
).toMatchObject({
[TARGET_PACKAGE_PUBLISH]: expect.any(Object),
[TARGET_PACKAGE_INSTALL]: expect.any(Object),
});
});

it('should return an empty object if isE2eProject and isPublishableProject are false', (): void => {
isEnvProjectSpy.mockReturnValue(false);
isPkgSpy.mockReturnValue(false);
expect(
createProjectConfiguration(projectConfiguration, options)
).toStrictEqual({});
});

it('should call normalizeCreateNodesOptions ones with projectConfiguration and options', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(
normalizeCreateNodesSpyModule.normalizeCreateNodesOptions
).toHaveBeenCalledOnce();
expect(
normalizeCreateNodesSpyModule.normalizeCreateNodesOptions
).toHaveBeenCalledWith(options);
});

it('should call isEnvProject ones with projectConfiguration and environments', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(environmentTargetsModule.isEnvProject).toHaveBeenCalledOnce();
expect(environmentTargetsModule.isEnvProject).toHaveBeenCalledWith(
projectConfiguration,
normalizedOptions['environments']
);
});

it('should call isPublishableProject ones with projectConfiguration and packages', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(packageTargetsSpyModule.isPkgProject).toHaveBeenCalledOnce();
expect(packageTargetsSpyModule.isPkgProject).toHaveBeenCalledWith(
projectConfiguration,
normalizedOptions['packages']
);
});

it('should call verdaccioTargets ones with correct arguments', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(environmentTargetsModule.verdaccioTargets).toHaveBeenCalledOnce();
expect(environmentTargetsModule.verdaccioTargets).toHaveBeenCalledWith(
projectConfiguration,
{ environmentsDir: normalizedOptions.environments.environmentsDir }
);
});

it('should call getEnvTargets ones with correct arguments', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(environmentTargetsModule.getEnvTargets).toHaveBeenCalledOnce();
expect(environmentTargetsModule.getEnvTargets).toHaveBeenCalledWith(
projectConfiguration,
normalizedOptions.environments
);
});

it('should call updateEnvTargetNames ones with correct arguments', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(
environmentTargetsModule.updateEnvTargetNames
).toHaveBeenCalledOnce();
expect(environmentTargetsModule.updateEnvTargetNames).toHaveBeenCalledWith(
projectConfiguration,
normalizedOptions.environments
);
});

it('should log warn if isE2eProject is true and implicitDependencies are empty', (): void => {
createProjectConfiguration(projectConfiguration, options);
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledOnce();
});

it('should not log warn if isE2eProject is true and implicitDependencies are given', (): void => {
createProjectConfiguration(
{
...projectConfiguration,
implicitDependencies,
},
options
);
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledTimes(0);
});

Comment on lines +229 to +231
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledTimes(0);
});
expect(nxDevkitMockModule.logger.warn).not.toHaveBeenCalled();
});

it('should not log warn if isE2eProject is false and implicitDependencies are given', (): void => {
isEnvProjectSpy.mockReturnValue(false);
createProjectConfiguration(
{
...projectConfiguration,
implicitDependencies,
},
options
);
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledTimes(0);
});
Comment on lines +241 to +242
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledTimes(0);
});
expect(nxDevkitMockModule.logger.warn).not.toHaveBeenCalled();
});


it('should not log warn if isE2eProject is false and implicitDependencies are not given', (): void => {
isEnvProjectSpy.mockReturnValue(false);
createProjectConfiguration(projectConfiguration, options);
expect(nxDevkitMockModule.logger.warn).toHaveBeenCalledTimes(0);
});
});
15 changes: 15 additions & 0 deletions projects/nx-verdaccio/src/plugin/targets/package.targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import { EXECUTOR_PACKAGE_NPM_INSTALL } from '../../executors/pkg-install/consta
export const TARGET_PACKAGE_INSTALL = 'nxv-pkg-install';
export const TARGET_PACKAGE_PUBLISH = 'nxv-pkg-publish';

/**
* Determines if the given project is a `publishable` package.
* A project qualifies as a `publishable` if it's of type 'library'.
* If tag filters are provided only projects passing the filter will return true.
*
* @param projectConfig
* @param options
* @returns `true` if the project is a publishable; otherwise, `false`.
*/
export function isPkgProject(
projectConfig: ProjectConfiguration,
options: NormalizedCreateNodeOptions['packages']
Expand All @@ -29,6 +38,12 @@ export function isPkgProject(
return true;
}

/**
* Creates package-related targets for build pipelines.
* Includes `TARGET_PACKAGE_PUBLISH` and `TARGET_PACKAGE_INSTALL` target configurations.
*
* @returns A record of package targets with their configurations.
*/
export function getPkgTargets(): Record<string, TargetConfiguration> {
return {
[TARGET_PACKAGE_PUBLISH]: {
Expand Down
Loading
Loading