From b7d05a03183d35a12f7a71dac71ed47873ec3c7f Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Wed, 21 Jun 2023 17:04:56 -0400 Subject: [PATCH] feat(nx-plugin): update e2e tests to use local-registry and be simpler (#17639) --- e2e/plugin/src/nx-plugin.test.ts | 13 ++- .../setup-verdaccio/files/config.yml | 9 +- .../generators/setup-verdaccio/files/htpasswd | 1 - .../create-package/create-package.ts | 10 +++ .../files/e2e/__cliName__.spec.ts__tmpl__ | 73 ++++++++++------ .../src/generators/e2e-project/e2e.spec.ts | 4 + .../plugin/src/generators/e2e-project/e2e.ts | 17 +++- .../tests/__pluginName__.spec.ts__tmpl__ | 85 +++++++++++++------ 8 files changed, 149 insertions(+), 63 deletions(-) delete mode 100644 packages/js/src/generators/setup-verdaccio/files/htpasswd diff --git a/e2e/plugin/src/nx-plugin.test.ts b/e2e/plugin/src/nx-plugin.test.ts index 829dba3a13c6f..13c5f07269680 100644 --- a/e2e/plugin/src/nx-plugin.test.ts +++ b/e2e/plugin/src/nx-plugin.test.ts @@ -31,7 +31,9 @@ describe('Nx Plugin', () => { it('should be able to generate a Nx Plugin ', async () => { const plugin = uniq('plugin'); - runCLI(`generate @nx/plugin:plugin ${plugin} --linter=eslint`); + runCLI( + `generate @nx/plugin:plugin ${plugin} --linter=eslint --e2eTestRunner=jest --publishable` + ); const lintResults = runCLI(`lint ${plugin}`); expect(lintResults).toContain('All files pass linting.'); @@ -45,6 +47,7 @@ describe('Nx Plugin', () => { expect(project).toMatchObject({ tags: [], }); + runCLI(`e2e ${plugin}-e2e`); }, 90000); it('should be able to generate a migration', async () => { @@ -405,9 +408,11 @@ describe('Nx Plugin', () => { it('should be able to generate a create-package plugin ', async () => { const plugin = uniq('plugin'); const createAppName = `create-${plugin}-app`; - runCLI(`generate @nx/plugin:plugin ${plugin}`); runCLI( - `generate @nx/plugin:create-package ${createAppName} --project=${plugin}` + `generate @nx/plugin:plugin ${plugin} --e2eTestRunner jest --publishable` + ); + runCLI( + `generate @nx/plugin:create-package ${createAppName} --project=${plugin} --e2eProject=${plugin}-e2e` ); const buildResults = runCLI(`build ${createAppName}`); @@ -418,6 +423,8 @@ describe('Nx Plugin', () => { `libs/${createAppName}`, `dist/libs/${createAppName}/bin/index.js` ); + + runCLI(`e2e ${plugin}-e2e`); }); it('should throw an error when run create-package for an invalid plugin ', async () => { diff --git a/packages/js/src/generators/setup-verdaccio/files/config.yml b/packages/js/src/generators/setup-verdaccio/files/config.yml index 6efc9446a080f..1c97ff62b00ab 100644 --- a/packages/js/src/generators/setup-verdaccio/files/config.yml +++ b/packages/js/src/generators/setup-verdaccio/files/config.yml @@ -1,10 +1,6 @@ # path to a directory with all packages storage: ../tmp/local-registry/storage -auth: - htpasswd: - file: ./htpasswd - # a list of other known repositories we can talk to uplinks: npmjs: @@ -26,4 +22,7 @@ packages: logs: type: stdout format: pretty - level: http + level: warn + +publish: + allow_offline: true # set offline to true to allow publish offline diff --git a/packages/js/src/generators/setup-verdaccio/files/htpasswd b/packages/js/src/generators/setup-verdaccio/files/htpasswd deleted file mode 100644 index 8391cd4b0a901..0000000000000 --- a/packages/js/src/generators/setup-verdaccio/files/htpasswd +++ /dev/null @@ -1 +0,0 @@ -test:$6FrCaT/v0dwE:autocreated 2020-03-25T19:10:50.254Z diff --git a/packages/plugin/src/generators/create-package/create-package.ts b/packages/plugin/src/generators/create-package/create-package.ts index 1a3ece600339a..bf488b4ccdf33 100644 --- a/packages/plugin/src/generators/create-package/create-package.ts +++ b/packages/plugin/src/generators/create-package/create-package.ts @@ -12,6 +12,8 @@ import { runTasksInSerial, joinPathFragments, getProjects, + detectPackageManager, + getPackageManagerCommand, } from '@nx/devkit'; import { libraryGenerator as jsLibraryGenerator } from '@nx/js'; import { nxVersion } from 'nx/src/utils/versions'; @@ -19,6 +21,7 @@ import generatorGenerator from '../generator/generator'; import { CreatePackageSchema } from './schema'; import { NormalizedSchema, normalizeSchema } from './utils/normalize-schema'; import { hasGenerator } from '../../utils/has-generator'; +import { join } from 'path'; export async function createPackageGenerator( host: Tree, @@ -157,6 +160,11 @@ function addE2eProject(host: Tree, options: NormalizedSchema) { host, options.e2eProject ); + const projectConfiguration = readProjectConfiguration(host, options.project); + const { name: pluginPackageName } = readJson( + host, + join(projectConfiguration.root, 'package.json') + ); generateFiles( host, @@ -165,6 +173,8 @@ function addE2eProject(host: Tree, options: NormalizedSchema) { { pluginName: options.project, cliName: options.name, + packageManagerCommands: getPackageManagerCommand('npm'), + pluginPackageName, tmpl: '', } ); diff --git a/packages/plugin/src/generators/create-package/files/e2e/__cliName__.spec.ts__tmpl__ b/packages/plugin/src/generators/create-package/files/e2e/__cliName__.spec.ts__tmpl__ index b9f7d17227545..817d450766355 100644 --- a/packages/plugin/src/generators/create-package/files/e2e/__cliName__.spec.ts__tmpl__ +++ b/packages/plugin/src/generators/create-package/files/e2e/__cliName__.spec.ts__tmpl__ @@ -1,31 +1,56 @@ -import { - checkFilesExist, - removeTmpProject, - tmpFolder, - uniq, -} from '@nx/plugin/testing'; import { execSync } from 'child_process'; -import { join } from 'path'; - -describe('<%= cliName %> e2e', () => { - let project: string; - beforeAll(async () => { - // create a workspace with cli - project = uniq('<%= cliName %>'); - execSync(`npx <%= cliName %>@1.0.0 ${project}`, { - cwd: tmpFolder(), - env: process.env, - }); - }, 240_000); +import { join, dirname } from 'path'; +import { mkdirSync, rmSync } from 'fs'; + +describe('<%= cliName %>', () => { + let projectDirectory: string; afterAll(() => { - // Remove the generated project from the file system - removeTmpProject(project); + // Cleanup the test project + rmSync(projectDirectory, { + recursive: true, + force: true, + }); }); - it('should create project using <%= cliName %>', () => { - expect(() => - checkFilesExist(join(tmpFolder(), project, 'package.json')) - ).not.toThrow(); + + it('should be installed', () => { + projectDirectory = createTestProject(); + + // npm ls will fail if the package is not installed properly + execSync('<%= packageManagerCommands.list %> <%= pluginPackageName %>', { + cwd: projectDirectory, + stdio: 'inherit', + }); }); }); + +/** + * Creates a test project with create-nx-workspace and installs the plugin + * @returns The directory where the test project was created + */ +function createTestProject(extraArgs: string = '') { + const projectName = 'test-project'; + const projectDirectory = join(process.cwd(), 'tmp', projectName); + + // Ensure projectDirectory is empty + rmSync(projectDirectory, { + recursive: true, + force: true, + }); + mkdirSync(dirname(projectDirectory), { + recursive: true, + }); + + execSync( + `<%= packageManagerCommands.exec %> --yes <%= cliName %>@e2e ${projectName} ${extraArgs}`, + { + cwd: dirname(projectDirectory), + stdio: 'inherit', + env: process.env, + } + ); + console.log(`Created test project in "${projectDirectory}"`); + + return projectDirectory; +} diff --git a/packages/plugin/src/generators/e2e-project/e2e.spec.ts b/packages/plugin/src/generators/e2e-project/e2e.spec.ts index e08e46e7501ee..c3fbd1da9f8bc 100644 --- a/packages/plugin/src/generators/e2e-project/e2e.spec.ts +++ b/packages/plugin/src/generators/e2e-project/e2e.spec.ts @@ -4,6 +4,7 @@ import { readProjectConfiguration, readJson, getProjects, + writeJson, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { e2eProjectGenerator } from './e2e'; @@ -18,6 +19,9 @@ describe('NxPlugin e2e-project Generator', () => { root: 'libs/my-plugin', targets: {}, }); + writeJson(tree, 'libs/my-plugin/package.json', { + name: 'my-plugin', + }); }); it('should validate the plugin name', async () => { diff --git a/packages/plugin/src/generators/e2e-project/e2e.ts b/packages/plugin/src/generators/e2e-project/e2e.ts index ce799a7af313c..bbe6e4e45d59c 100644 --- a/packages/plugin/src/generators/e2e-project/e2e.ts +++ b/packages/plugin/src/generators/e2e-project/e2e.ts @@ -6,10 +6,12 @@ import { formatFiles, generateFiles, GeneratorCallback, + getPackageManagerCommand, getWorkspaceLayout, joinPathFragments, names, offsetFromRoot, + readJson, readProjectConfiguration, runTasksInSerial, updateProjectConfiguration, @@ -27,7 +29,6 @@ interface NormalizedSchema extends Schema { projectRoot: string; projectName: string; pluginPropertyName: string; - npmScope: string; linter: Linter; } @@ -35,7 +36,7 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { const { layoutDirectory, projectDirectory } = extractLayoutDirectory( options.projectDirectory ); - const { npmScope, appsDir: defaultAppsDir } = getWorkspaceLayout(host); + const { appsDir: defaultAppsDir } = getWorkspaceLayout(host); const appsDir = layoutDirectory ?? defaultAppsDir; const projectName = options.rootProject ? 'e2e' : `${options.pluginName}-e2e`; @@ -53,7 +54,6 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { linter: options.linter ?? Linter.EsLint, pluginPropertyName, projectRoot, - npmScope, }; } @@ -66,10 +66,21 @@ function validatePlugin(host: Tree, pluginName: string) { } function addFiles(host: Tree, options: NormalizedSchema) { + const projectConfiguration = readProjectConfiguration( + host, + options.pluginName + ); + const { name: pluginPackageName } = readJson( + host, + join(projectConfiguration.root, 'package.json') + ); + generateFiles(host, join(__dirname, './files'), options.projectRoot, { ...options, tmpl: '', rootTsConfigPath: getRelativePathToRootTsConfig(host, options.projectRoot), + packageManagerCommands: getPackageManagerCommand('npm'), + pluginPackageName, }); } diff --git a/packages/plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ b/packages/plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ index ddeb7327dd957..1949d319db51e 100644 --- a/packages/plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ +++ b/packages/plugin/src/generators/e2e-project/files/tests/__pluginName__.spec.ts__tmpl__ @@ -1,35 +1,66 @@ -import { - checkFilesExist, - ensureNxProject, - runNxCommandAsync, - runNxCommand, -} from '@nx/plugin/testing'; - -describe('<%= pluginName %> e2e', () => { - // Setting up individual workspaces per - // test can cause e2e runs to take a long time. - // For this reason, we recommend each suite only - // consumes 1 workspace. The tests should each operate - // on a unique project in the workspace, such that they - // are not dependant on one another. +import { execSync } from 'child_process'; +import { join, dirname } from 'path'; +import { mkdirSync, rmSync } from 'fs'; + +describe('<%= pluginName %>', () => { + let projectDirectory: string; + beforeAll(() => { - ensureNxProject('<%= npmPackageName %>', '<%= pluginOutputPath %>'); + projectDirectory = createTestProject(); + + // The plugin has been built and published to a local registry in the jest globalSetup + // Install the plugin built with the latest source code into the test repo + execSync(`<%= packageManagerCommands.install %> <%= pluginPackageName %>@e2e`, { + cwd: projectDirectory, + stdio: 'inherit', + env: process.env, + }); }); - afterAll(async () => { - // `nx reset` kills the daemon, and performs - // some work which can help clean up e2e leftovers - await runNxCommandAsync('reset'); + afterAll(() => { + // Cleanup the test project + rmSync(projectDirectory, { + recursive: true, + force: true, + }); }); - // Add some tests here to check that your plugin functionality works as expected. - // A sample test is included below to give you some ideas. - xit('should be able to build generated projects', async () => { - const name = 'proj'; - const generator = 'PLACEHOLDER'; - await runNxCommandAsync(`generate <%= npmPackageName %>:${generator} --name ${name}`); - expect(() => runNxCommand('build ${proj}')).not.toThrow(); - expect(() => checkFilesExist(`dist/${name}/index.js`)).not.toThrow(); + it('should be installed', () => { + // npm ls will fail if the package is not installed properly + execSync('<%= packageManagerCommands.list %> <%= pluginPackageName %>', { + cwd: projectDirectory, + stdio: 'inherit', + }); }); }); + +/** + * Creates a test project with create-nx-workspace and installs the plugin + * @returns The directory where the test project was created + */ +function createTestProject() { + const projectName = 'test-project'; + const projectDirectory = join(process.cwd(), 'tmp', projectName); + + // Ensure projectDirectory is empty + rmSync(projectDirectory, { + recursive: true, + force: true, + }); + mkdirSync(dirname(projectDirectory), { + recursive: true, + }); + + execSync( + `<%= packageManagerCommands.exec %> --yes create-nx-workspace@latest ${projectName} --preset empty --no-nxCloud --no-interactive`, + { + cwd: dirname(projectDirectory), + stdio: 'inherit', + env: process.env, + } + ); + console.log(`Created test project in "${projectDirectory}"`); + + return projectDirectory; +}