nx/packages/nest/src/generators/application/application.spec.ts
Leosvel Pérez Espinosa cbf80c18d1
feat(misc): add useProjectJson flag to project generators (#30319)
Add a `useProjectJson` option to project generators to allow users to
opt in/out of generating the Nx configuration in a `project.json` file.

## Current Behavior

## Expected Behavior

## Related Issue(s)

Fixes #
2025-03-11 12:12:03 -04:00

461 lines
13 KiB
TypeScript

import {
readJson,
readProjectConfiguration,
updateJson,
writeJson,
type Tree,
} from '@nx/devkit';
import * as devkit from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { applicationGenerator } from './application';
describe('application generator', () => {
let tree: Tree;
const appDirectory = 'my-node-app';
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
jest.clearAllMocks();
});
it('should generate project configurations', async () => {
await applicationGenerator(tree, {
directory: appDirectory,
addPlugin: true,
});
const projectConfigurations = devkit.getProjects(tree);
const project = projectConfigurations.get(appDirectory);
expect(projectConfigurations.get(`${appDirectory}-e2e`)).toBeTruthy();
expect(project).toMatchInlineSnapshot(`
{
"$schema": "../node_modules/nx/schemas/project-schema.json",
"name": "my-node-app",
"projectType": "application",
"root": "my-node-app",
"sourceRoot": "my-node-app/src",
"tags": [],
"targets": {
"build": {
"configurations": {
"development": {
"args": [
"node-env=development",
],
},
},
"executor": "nx:run-commands",
"options": {
"args": [
"node-env=production",
],
"command": "webpack-cli build",
},
},
"serve": {
"configurations": {
"development": {
"buildTarget": "my-node-app:build:development",
},
"production": {
"buildTarget": "my-node-app:build:production",
},
},
"defaultConfiguration": "development",
"dependsOn": [
"build",
],
"executor": "@nx/js:node",
"options": {
"buildTarget": "my-node-app:build",
"runBuildTargetDependencies": false,
},
},
"test": {
"options": {
"passWithNoTests": true,
},
},
},
}
`);
});
it('should generate files', async () => {
await applicationGenerator(tree, {
directory: appDirectory,
addPlugin: true,
});
expect(tree.exists(`${appDirectory}/src/main.ts`)).toBeTruthy();
expect(
tree.exists(`${appDirectory}/src/app/app.controller.spec.ts`)
).toBeTruthy();
expect(
tree.exists(`${appDirectory}/src/app/app.controller.ts`)
).toBeTruthy();
expect(tree.exists(`${appDirectory}/src/app/app.module.ts`)).toBeTruthy();
expect(
tree.exists(`${appDirectory}/src/app/app.service.spec.ts`)
).toBeTruthy();
expect(tree.exists(`${appDirectory}/src/app/app.service.ts`)).toBeTruthy();
});
it('should configure tsconfig correctly', async () => {
await applicationGenerator(tree, {
directory: appDirectory,
addPlugin: true,
});
const tsConfig = devkit.readJson(tree, `${appDirectory}/tsconfig.app.json`);
expect(tsConfig.compilerOptions.emitDecoratorMetadata).toBe(true);
expect(tsConfig.compilerOptions.target).toBe('es2021');
expect(tsConfig.exclude).toEqual([
'jest.config.ts',
'src/**/*.spec.ts',
'src/**/*.test.ts',
]);
});
it('should add strict checks with --strict', async () => {
await applicationGenerator(tree, {
directory: appDirectory,
strict: true,
addPlugin: true,
});
const tsConfig = devkit.readJson(tree, `${appDirectory}/tsconfig.app.json`);
expect(tsConfig.compilerOptions.strictNullChecks).toBeTruthy();
expect(tsConfig.compilerOptions.noImplicitAny).toBeTruthy();
expect(tsConfig.compilerOptions.strictBindCallApply).toBeTruthy();
expect(
tsConfig.compilerOptions.forceConsistentCasingInFileNames
).toBeTruthy();
expect(tsConfig.compilerOptions.noFallthroughCasesInSwitch).toBeTruthy();
});
describe('--skipFormat', () => {
it('should format files', async () => {
jest.spyOn(devkit, 'formatFiles');
await applicationGenerator(tree, {
directory: appDirectory,
addPlugin: true,
});
expect(devkit.formatFiles).toHaveBeenCalled();
});
it('should not format files when --skipFormat=true', async () => {
jest.spyOn(devkit, 'formatFiles');
await applicationGenerator(tree, {
directory: appDirectory,
skipFormat: true,
addPlugin: true,
});
expect(devkit.formatFiles).not.toHaveBeenCalled();
});
});
describe('--e2e-test-runner none', () => {
it('should not generate e2e test project', async () => {
await applicationGenerator(tree, {
directory: appDirectory,
e2eTestRunner: 'none',
addPlugin: true,
});
const projectConfigurations = devkit.getProjects(tree);
expect(projectConfigurations.get(`${appDirectory}-e2e`)).toBeUndefined();
});
});
describe('TS solution setup', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
updateJson(tree, 'package.json', (json) => {
json.workspaces = ['packages/*', 'apps/*'];
return json;
});
writeJson(tree, 'tsconfig.base.json', {
compilerOptions: {
composite: true,
declaration: true,
},
});
writeJson(tree, 'tsconfig.json', {
extends: './tsconfig.base.json',
files: [],
references: [],
});
});
it('should add project references when using TS solution', async () => {
await applicationGenerator(tree, {
directory: 'myapp',
unitTestRunner: 'jest',
addPlugin: true,
useProjectJson: false,
});
expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(`
[
{
"path": "./myapp-e2e",
},
{
"path": "./myapp",
},
]
`);
expect(readJson(tree, 'myapp/package.json')).toMatchInlineSnapshot(`
{
"name": "@proj/myapp",
"nx": {
"targets": {
"build": {
"configurations": {
"development": {
"args": [
"node-env=development",
],
},
},
"executor": "nx:run-commands",
"options": {
"args": [
"node-env=production",
],
"command": "webpack-cli build",
},
},
"serve": {
"configurations": {
"development": {
"buildTarget": "@proj/myapp:build:development",
},
"production": {
"buildTarget": "@proj/myapp:build:production",
},
},
"defaultConfiguration": "development",
"dependsOn": [
"build",
],
"executor": "@nx/js:node",
"options": {
"buildTarget": "@proj/myapp:build",
"runBuildTargetDependencies": false,
},
},
"test": {
"options": {
"passWithNoTests": true,
},
},
},
},
"private": true,
"version": "0.0.1",
}
`);
expect(readJson(tree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(`
{
"extends": "../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json",
},
{
"path": "./tsconfig.spec.json",
},
],
}
`);
expect(readJson(tree, 'myapp/tsconfig.app.json')).toMatchInlineSnapshot(`
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "dist",
"rootDir": "src",
"target": "es2021",
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo",
"types": [
"node",
],
},
"exclude": [
"out-tsc",
"dist",
"jest.config.ts",
"src/**/*.spec.ts",
"src/**/*.test.ts",
"eslint.config.js",
"eslint.config.cjs",
"eslint.config.mjs",
],
"extends": "../tsconfig.base.json",
"include": [
"src/**/*.ts",
],
}
`);
expect(readJson(tree, 'myapp/tsconfig.spec.json')).toMatchInlineSnapshot(`
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "./out-tsc/jest",
"types": [
"jest",
"node",
],
},
"extends": "../tsconfig.base.json",
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts",
],
"references": [
{
"path": "./tsconfig.app.json",
},
],
}
`);
});
it('should respect the provided name', async () => {
await applicationGenerator(tree, {
directory: 'myapp',
name: 'myapp',
unitTestRunner: 'jest',
addPlugin: true,
useProjectJson: false,
skipFormat: true,
});
const packageJson = readJson(tree, 'myapp/package.json');
expect(packageJson.name).toBe('@proj/myapp');
expect(packageJson.nx.name).toBe('myapp');
// Make sure keys are in idiomatic order
expect(Object.keys(packageJson)).toMatchInlineSnapshot(`
[
"name",
"version",
"private",
"nx",
]
`);
});
it('should generate project.json if useProjectJson is true', async () => {
await applicationGenerator(tree, {
directory: 'myapp',
e2eTestRunner: 'jest',
useProjectJson: true,
addPlugin: true,
skipFormat: true,
});
expect(tree.exists('myapp/project.json')).toBeTruthy();
expect(readProjectConfiguration(tree, '@proj/myapp'))
.toMatchInlineSnapshot(`
{
"$schema": "../node_modules/nx/schemas/project-schema.json",
"name": "@proj/myapp",
"projectType": "application",
"root": "myapp",
"sourceRoot": "myapp/src",
"tags": [],
"targets": {
"build": {
"configurations": {
"development": {
"args": [
"node-env=development",
],
},
},
"executor": "nx:run-commands",
"options": {
"args": [
"node-env=production",
],
"command": "webpack-cli build",
},
},
"serve": {
"configurations": {
"development": {
"buildTarget": "@proj/myapp:build:development",
},
"production": {
"buildTarget": "@proj/myapp:build:production",
},
},
"defaultConfiguration": "development",
"dependsOn": [
"build",
],
"executor": "@nx/js:node",
"options": {
"buildTarget": "@proj/myapp:build",
"runBuildTargetDependencies": false,
},
},
"test": {
"options": {
"passWithNoTests": true,
},
},
},
}
`);
expect(readJson(tree, 'myapp/package.json').nx).toBeUndefined();
expect(tree.exists('myapp-e2e/project.json')).toBeTruthy();
expect(readProjectConfiguration(tree, '@proj/myapp-e2e'))
.toMatchInlineSnapshot(`
{
"$schema": "../node_modules/nx/schemas/project-schema.json",
"implicitDependencies": [
"@proj/myapp",
],
"name": "@proj/myapp-e2e",
"projectType": "application",
"root": "myapp-e2e",
"targets": {
"e2e": {
"dependsOn": [
"@proj/myapp:build",
],
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "myapp-e2e/jest.config.ts",
"passWithNoTests": true,
},
"outputs": [
"{workspaceRoot}/coverage/{e2eProjectRoot}",
],
},
},
}
`);
expect(readJson(tree, 'myapp-e2e/package.json').nx).toBeUndefined();
});
});
});