diff --git a/tooling/build-env/src/executors/bootstrap/bootstrap-env.ts b/tooling/build-env/src/executors/bootstrap/bootstrap-env.ts new file mode 100644 index 00000000..d1fb591e --- /dev/null +++ b/tooling/build-env/src/executors/bootstrap/bootstrap-env.ts @@ -0,0 +1,66 @@ +import { join } from 'node:path'; +import { + startVerdaccioServer, + type StarVerdaccioOptions, + type VercaddioServerResult, +} from './verdaccio-registry'; +import { writeFile } from 'node:fs/promises'; +import { setupNpmWorkspace } from './npm'; +import { formatInfo } from '../../internal/logging'; +import { VERDACCIO_REGISTRY_JSON } from './constants'; +import { logger } from '@nx/devkit'; +import { configureRegistry, Environment, VERDACCIO_ENV_TOKEN } from './npm'; + +export type BootstrapEnvironmentOptions = Partial< + StarVerdaccioOptions & Environment +> & + Required>; + +export type BootstrapEnvironmentResult = Environment & { + registry: VercaddioServerResult; + stop: () => void; +}; + +export async function bootstrapEnvironment({ + verbose = false, + environmentRoot, + ...opts +}: BootstrapEnvironmentOptions & { + environmentRoot: string; +}): Promise { + const storage = join(environmentRoot, 'storage'); + const registryResult = await startVerdaccioServer({ + storage, + verbose, + ...opts, + }); + + await setupNpmWorkspace(environmentRoot, verbose); + const userconfig = join(environmentRoot, '.npmrc'); + configureRegistry({ ...registryResult.registry, userconfig }, verbose); + + const activeRegistry: BootstrapEnvironmentResult = { + ...registryResult, + root: environmentRoot, + }; + + logger.info( + formatInfo( + `Save active verdaccio registry data to file: ${activeRegistry.root}`, + VERDACCIO_ENV_TOKEN + ) + ); + await writeFile( + join(activeRegistry.root, VERDACCIO_REGISTRY_JSON), + JSON.stringify(activeRegistry.registry, null, 2) + ); + + logger.info( + formatInfo( + `Environment ready under: ${activeRegistry.root}`, + VERDACCIO_ENV_TOKEN + ) + ); + + return activeRegistry; +} diff --git a/tooling/build-env/src/executors/bootstrap/bootstrap-env.unit-test.ts b/tooling/build-env/src/executors/bootstrap/bootstrap-env.unit-test.ts new file mode 100644 index 00000000..5551b321 --- /dev/null +++ b/tooling/build-env/src/executors/bootstrap/bootstrap-env.unit-test.ts @@ -0,0 +1,110 @@ +import { describe, expect, it, vi } from 'vitest'; +import { bootstrapEnvironment } from './bootstrap-env'; +import * as verdaccioRegistryModule from './verdaccio-registry'; +import * as npmModule from './npm'; +import * as fs from 'node:fs/promises'; + +vi.mock('@nx/devkit', async () => { + const actual = await vi.importActual('@nx/devkit'); + return { + ...actual, + logger: { + info: vi.fn(), + }, + readJsonFile: vi.fn().mockResolvedValue({ + pid: 7777, + port: 4387, + url: 'http://localhost:4873', + host: 'localhost', + protocol: 'http', + storage: 'tmp/storage', + }), + }; +}); + +describe('bootstrapEnvironment', () => { + const startVerdaccioServerSpy = vi + .spyOn(verdaccioRegistryModule, 'startVerdaccioServer') + .mockResolvedValue({ + registry: { + host: 'localhost', + pid: 7777, + port: 4387, + protocol: 'http', + storage: 'tmp/storage', + url: 'http://localhost:4873', + }, + stop: vi.fn(), + }); + const setupNpmWorkspaceSpy = vi + .spyOn(npmModule, 'setupNpmWorkspace') + .mockImplementation(vi.fn()); + const configureRegistrySpy = vi + .spyOn(npmModule, 'configureRegistry') + .mockImplementation(vi.fn()); + const writeFileSpy = vi.spyOn(fs, 'writeFile').mockImplementation(vi.fn()); + + it('should create environment', async () => { + await expect( + bootstrapEnvironment({ + projectName: 'my-lib-e2e', + environmentRoot: 'tmp/environments/my-lib-e2e', + }) + ).resolves.toStrictEqual({ + registry: { + host: 'localhost', + pid: 7777, + port: 4387, + protocol: 'http', + storage: 'tmp/storage', + url: 'http://localhost:4873', + }, + root: 'tmp/environments/my-lib-e2e', + stop: expect.any(Function), + }); + + expect(startVerdaccioServerSpy).toHaveBeenCalledTimes(1); + expect(startVerdaccioServerSpy).toHaveBeenCalledWith({ + projectName: 'my-lib-e2e', + storage: 'tmp/environments/my-lib-e2e/storage', + verbose: false, + }); + + expect(setupNpmWorkspaceSpy).toHaveBeenCalledTimes(1); + expect(setupNpmWorkspaceSpy).toHaveBeenCalledWith( + 'tmp/environments/my-lib-e2e', + false + ); + + expect(configureRegistrySpy).toHaveBeenCalledTimes(1); + expect(configureRegistrySpy).toHaveBeenCalledWith( + { + host: 'localhost', + pid: 7777, + port: 4387, + protocol: 'http', + storage: 'tmp/storage', + url: 'http://localhost:4873', + userconfig: 'tmp/environments/my-lib-e2e/.npmrc', + }, + false + ); + + expect(writeFileSpy).toHaveBeenCalledTimes(1); + expect(writeFileSpy).toHaveBeenCalledWith( + 'tmp/environments/my-lib-e2e/verdaccio-registry.json', + JSON.stringify( + { + host: 'localhost', + pid: 7777, + port: 4387, + protocol: 'http', + storage: 'tmp/storage', + url: 'http://localhost:4873', + }, + null, + 2 + ) + ); + }); +}); diff --git a/tooling/build-env/src/executors/bootstrap/executor.ts b/tooling/build-env/src/executors/bootstrap/executor.ts index 9e6b97ee..5c57dee1 100644 --- a/tooling/build-env/src/executors/bootstrap/executor.ts +++ b/tooling/build-env/src/executors/bootstrap/executor.ts @@ -1,6 +1,6 @@ import { type ExecutorContext, logger } from '@nx/devkit'; import type { BootstrapExecutorOptions } from './schema'; -import { bootstrapEnvironment } from './verdaccio-npm-env'; +import { bootstrapEnvironment } from './bootstrap-env'; import { normalizeOptions } from '../internal/normalize-options'; export type BootstrapExecutorOutput = { diff --git a/tooling/build-env/src/executors/bootstrap/executor.unit-test.ts b/tooling/build-env/src/executors/bootstrap/executor.unit-test.ts new file mode 100644 index 00000000..f0dfe702 --- /dev/null +++ b/tooling/build-env/src/executors/bootstrap/executor.unit-test.ts @@ -0,0 +1,154 @@ +import runBootstrapExecutor from './executor'; +import * as bootstrapEnvModule from './bootstrap-env'; +import { beforeEach, expect, vi } from 'vitest'; +import { logger } from '@nx/devkit'; + +vi.mock('@nx/devkit', async () => { + const actual = await vi.importActual('@nx/devkit'); + return { + ...actual, + logger: { + info: vi.fn(), + error: vi.fn(), + }, + }; +}); + +describe('runBootstrapExecutor', () => { + const bootstrapEnvironmentSpy = vi + .spyOn(bootstrapEnvModule, 'bootstrapEnvironment') + .mockResolvedValue({ + registry: { + host: 'localhost', + pid: 7777, + port: 4387, + protocol: 'http', + storage: 'tmp/storage', + url: 'http://localhost:4873', + }, + root: 'tmp/environments/my-lib-e2e', + stop: expect.any(Function), + }); + + beforeEach(() => { + bootstrapEnvironmentSpy.mockReset(); + }); + + it('should bootstrap environment correctly', async () => { + await expect( + runBootstrapExecutor( + {}, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: true, + command: 'Bootstraped environemnt successfully.', + }); + + expect(logger.error).not.toHaveBeenCalled(); + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( + 'Execute @org/build-env:build with options: {}' + ); + + expect(bootstrapEnvironmentSpy).toHaveBeenCalledTimes(1); + expect(bootstrapEnvironmentSpy).toHaveBeenCalledWith({ + projectName: 'my-lib-e2e', + environmentProject: 'my-lib-e2e', + environmentRoot: 'tmp/environments/my-lib-e2e', + readyWhen: 'Environment ready under', + }); + }); + + it('should pass options to bootstrapEnvironment', async () => { + await expect( + runBootstrapExecutor( + { + environmentRoot: 'static-environments/dummy-react-app', + }, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: true, + command: 'Bootstraped environemnt successfully.', + }); + + expect(bootstrapEnvironmentSpy).toHaveBeenCalledWith( + expect.objectContaining({ + environmentRoot: 'static-environments/dummy-react-app', + }) + ); + }); + + it('should throw if bootstrapping environment fails', async () => { + bootstrapEnvironmentSpy.mockRejectedValue( + new Error('Failed to bootstrap environment') + ); + await expect( + runBootstrapExecutor( + {}, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: false, + command: Error('Failed to bootstrap environment'), + }); + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( + 'Execute @org/build-env:build with options: {}' + ); + + expect(logger.error).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledWith( + Error('Failed to bootstrap environment') + ); + + expect(bootstrapEnvironmentSpy).toHaveBeenCalledTimes(1); + expect(bootstrapEnvironmentSpy).toHaveBeenCalledWith({ + projectName: 'my-lib-e2e', + environmentProject: 'my-lib-e2e', + environmentRoot: 'tmp/environments/my-lib-e2e', + readyWhen: 'Environment ready under', + }); + }); +}); diff --git a/tooling/build-env/src/executors/bootstrap/npm.ts b/tooling/build-env/src/executors/bootstrap/npm.ts index 8ea8ebd9..a7abf832 100644 --- a/tooling/build-env/src/executors/bootstrap/npm.ts +++ b/tooling/build-env/src/executors/bootstrap/npm.ts @@ -1,8 +1,10 @@ -import { execFileSync } from 'node:child_process'; +import { execFileSync, execSync } from 'node:child_process'; import { join } from 'node:path'; import { ensureDirectoryExists } from '../../internal/file-system'; import { formatError, formatInfo } from '../../internal/logging'; import { logger } from '@nx/devkit'; +import type { VerdaccioProcessResult } from './verdaccio-registry'; +import { objectToCliArgs } from '../../internal/terminal'; export const NPM_ENV_TOKEN = 'Npm Env: '; @@ -43,3 +45,81 @@ export async function setupNpmWorkspace( chdir(cwd); } } + +export const VERDACCIO_ENV_TOKEN = 'Verdaccio Env: '; + +export type Environment = { + root: string; +}; + +export type ConfigureRegistryOptions = Pick< + VerdaccioProcessResult, + 'port' | 'host' | 'url' +> & { + userconfig?: string; +}; + +export function configureRegistry( + { port, host, url, userconfig }: ConfigureRegistryOptions, + verbose?: boolean +) { + const setRegistry = `npm config set registry="${url}" ${objectToCliArgs({ + userconfig, + }).join(' ')}`; + if (verbose) { + logger.info( + formatInfo(`Set registry:\n${setRegistry}`, VERDACCIO_ENV_TOKEN) + ); + } + execSync(setRegistry); + + /** + * Protocol-Agnostic Configuration: The use of // allows NPM to configure authentication for a registry without tying it to a specific protocol (http: or https:). + * This is particularly useful when the registry might be accessible via both HTTP and HTTPS. + * + * Example: //registry.npmjs.org/:_authToken=your-token + */ + const urlNoProtocol = `//${host}:${port}`; + const token = 'secretVerdaccioToken'; + const setAuthToken = `npm config set ${urlNoProtocol}/:_authToken "${token}" ${objectToCliArgs( + { userconfig } + ).join(' ')}`; + if (verbose) { + logger.info( + formatInfo(`Set authToken:\n${setAuthToken}`, VERDACCIO_ENV_TOKEN) + ); + } + execSync(setAuthToken); +} + +export type UnconfigureRegistryOptions = Pick< + VerdaccioProcessResult, + 'port' | 'host' +> & { + userconfig?: string; +}; +export function unconfigureRegistry( + { port, host, userconfig }: UnconfigureRegistryOptions, + verbose?: boolean +) { + const urlNoProtocol = `//${host}:${port}`; + const setAuthToken = `npm config delete ${urlNoProtocol}/:_authToken ${objectToCliArgs( + { userconfig } + ).join(' ')}`; + if (verbose) { + logger.info( + formatInfo(`Delete authToken:\n${setAuthToken}`, VERDACCIO_ENV_TOKEN) + ); + } + execSync(setAuthToken); + + const setRegistry = `npm config delete registry ${objectToCliArgs({ + userconfig, + }).join(' ')}`; + if (verbose) { + logger.info( + formatInfo(`Delete registry:\n${setRegistry}`, VERDACCIO_ENV_TOKEN) + ); + } + execSync(setRegistry); +} diff --git a/tooling/build-env/src/executors/bootstrap/npm.unit-test.ts b/tooling/build-env/src/executors/bootstrap/npm.unit-test.ts index 86572c48..3d7c92e6 100644 --- a/tooling/build-env/src/executors/bootstrap/npm.unit-test.ts +++ b/tooling/build-env/src/executors/bootstrap/npm.unit-test.ts @@ -1,7 +1,130 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { bold, red } from 'ansis'; import { MEMFS_VOLUME } from '@org/test-utils'; -import { setupNpmWorkspace } from './npm'; +import { + configureRegistry, + ConfigureRegistryOptions, + setupNpmWorkspace, + unconfigureRegistry, + UnconfigureRegistryOptions, + VERDACCIO_ENV_TOKEN, +} from './npm'; +import { execSync } from 'node:child_process'; +import { logger } from '@nx/devkit'; +import { formatInfo } from '../../internal/logging'; + +vi.mock('child_process', async () => { + const actual = await vi.importActual( + 'child_process' + ); + return { + ...actual, + execSync: vi.fn(), + }; +}); + +vi.mock('@nx/devkit', async () => { + const actual = await vi.importActual('@nx/devkit'); + return { + ...actual, + logger: { + info: vi.fn(), + }, + }; +}); + +describe('configureRegistry', () => { + it('should set the npm registry and authToken', () => { + const processResult: ConfigureRegistryOptions = { + port: 4873, + host: 'localhost', + url: 'http://localhost:4873', + userconfig: 'test-config', + }; + + configureRegistry(processResult); + + expect(execSync).toHaveBeenCalledTimes(2); + expect(execSync).toHaveBeenCalledWith( + 'npm config set registry="http://localhost:4873" --userconfig="test-config"' + ); + + expect(execSync).toHaveBeenCalledWith( + 'npm config set //localhost:4873/:_authToken "secretVerdaccioToken" --userconfig="test-config"' + ); + }); + + it('should set and log registry and authToken commands if verbose is true', () => { + const processResult: ConfigureRegistryOptions = { + port: 4873, + host: 'localhost', + url: 'http://localhost:4873', + userconfig: 'test-config', + }; + + configureRegistry(processResult, true); + + expect(execSync).toHaveBeenCalledTimes(2); + expect(logger.info).toHaveBeenCalledWith( + formatInfo( + 'Set registry:\nnpm config set registry="http://localhost:4873" --userconfig="test-config"', + VERDACCIO_ENV_TOKEN + ) + ); + expect(logger.info).toHaveBeenCalledWith( + formatInfo( + 'Set authToken:\nnpm config set //localhost:4873/:_authToken "secretVerdaccioToken" --userconfig="test-config"', + VERDACCIO_ENV_TOKEN + ) + ); + }); +}); + +describe('unconfigureRegistry', () => { + it('should delete the npm registry and authToken', () => { + const processResult: UnconfigureRegistryOptions = { + userconfig: 'test-config', + port: 4873, + host: 'localhost', + }; + + unconfigureRegistry(processResult); + + expect(execSync).toHaveBeenCalledTimes(2); + expect(execSync).toHaveBeenCalledWith( + 'npm config delete registry --userconfig="test-config"' + ); + + expect(execSync).toHaveBeenCalledWith( + 'npm config delete //localhost:4873/:_authToken --userconfig="test-config"' + ); + }); + + it('should delete and log registry and authToken commands if verbose is true', () => { + const processResult: UnconfigureRegistryOptions = { + userconfig: 'test-config', + port: 4873, + host: 'localhost', + }; + + unconfigureRegistry(processResult, true); + + expect(execSync).toHaveBeenCalledTimes(2); + expect(logger.info).toHaveBeenCalledWith( + formatInfo( + 'Delete registry:\nnpm config delete registry --userconfig="test-config"', + VERDACCIO_ENV_TOKEN + ) + ); + + expect(logger.info).toHaveBeenCalledWith( + formatInfo( + 'Delete authToken:\nnpm config delete //localhost:4873/:_authToken --userconfig="test-config"', + VERDACCIO_ENV_TOKEN + ) + ); + }); +}); describe.skip('setupNpmWorkspace', () => { let cwdSpy; diff --git a/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.ts b/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.ts deleted file mode 100644 index 4ef122ba..00000000 --- a/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { join } from 'node:path'; -import { - startVerdaccioServer, - type StarVerdaccioOptions, - type VercaddioServerResult, - type VerdaccioProcessResult, -} from './verdaccio-registry'; -import { writeFile } from 'node:fs/promises'; -import { setupNpmWorkspace } from './npm'; -import { formatInfo } from '../../internal/logging'; -import { objectToCliArgs } from '../../internal/terminal'; -import { execSync } from 'node:child_process'; -import { VERDACCIO_REGISTRY_JSON } from './constants'; -import { logger } from '@nx/devkit'; - -export const VERDACCIO_ENV_TOKEN = 'Verdaccio Env: '; - -export type Environment = { - root: string; -}; - -export type BootstrapEnvironmentOptions = Partial< - StarVerdaccioOptions & Environment -> & - Required>; - -export type BootstrapEnvironmentResult = Environment & { - registry: VercaddioServerResult; - stop: () => void; -}; - -export async function bootstrapEnvironment({ - verbose = false, - environmentRoot, - ...opts -}: BootstrapEnvironmentOptions & { - environmentRoot: string; -}): Promise { - const storage = join(environmentRoot, 'storage'); - const registryResult = await startVerdaccioServer({ - storage, - verbose, - ...opts, - }); - - // set up NPM workspace environment - await setupNpmWorkspace(environmentRoot, verbose); - const userconfig = join(environmentRoot, '.npmrc'); - configureRegistry({ ...registryResult.registry, userconfig }, verbose); - - const activeRegistry: BootstrapEnvironmentResult = { - ...registryResult, - root: environmentRoot, - }; - - logger.info( - formatInfo( - `Save active verdaccio registry data to file: ${activeRegistry.root}`, - VERDACCIO_ENV_TOKEN - ) - ); - await writeFile( - join(activeRegistry.root, VERDACCIO_REGISTRY_JSON), - JSON.stringify(activeRegistry.registry, null, 2) - ); - - logger.info( - formatInfo( - `Environment ready under: ${activeRegistry.root}`, - VERDACCIO_ENV_TOKEN - ) - ); - - return activeRegistry; -} - -export type ConfigureRegistryOptions = Pick< - VerdaccioProcessResult, - 'port' | 'host' | 'url' -> & { - userconfig?: string; -}; - -export function configureRegistry( - { port, host, url, userconfig }: ConfigureRegistryOptions, - verbose?: boolean -) { - const setRegistry = `npm config set registry="${url}" ${objectToCliArgs({ - userconfig, - }).join(' ')}`; - if (verbose) { - logger.info( - formatInfo(`Set registry:\n${setRegistry}`, VERDACCIO_ENV_TOKEN) - ); - } - execSync(setRegistry); - - /** - * Protocol-Agnostic Configuration: The use of // allows NPM to configure authentication for a registry without tying it to a specific protocol (http: or https:). - * This is particularly useful when the registry might be accessible via both HTTP and HTTPS. - * - * Example: //registry.npmjs.org/:_authToken=your-token - */ - const urlNoProtocol = `//${host}:${port}`; - const token = 'secretVerdaccioToken'; - const setAuthToken = `npm config set ${urlNoProtocol}/:_authToken "${token}" ${objectToCliArgs( - { userconfig } - ).join(' ')}`; - if (verbose) { - logger.info( - formatInfo(`Set authToken:\n${setAuthToken}`, VERDACCIO_ENV_TOKEN) - ); - } - execSync(setAuthToken); -} - -export type UnconfigureRegistryOptions = Pick< - VerdaccioProcessResult, - 'port' | 'host' -> & { - userconfig?: string; -}; -export function unconfigureRegistry( - { port, host, userconfig }: UnconfigureRegistryOptions, - verbose?: boolean -) { - const urlNoProtocol = `//${host}:${port}`; - const setAuthToken = `npm config delete ${urlNoProtocol}/:_authToken ${objectToCliArgs( - { userconfig } - ).join(' ')}`; - if (verbose) { - logger.info( - formatInfo(`Delete authToken:\n${setAuthToken}`, VERDACCIO_ENV_TOKEN) - ); - } - execSync(setAuthToken); - - const setRegistry = `npm config delete registry ${objectToCliArgs({ - userconfig, - }).join(' ')}`; - if (verbose) { - logger.info( - formatInfo(`Delete registry:\n${setRegistry}`, VERDACCIO_ENV_TOKEN) - ); - } - execSync(setRegistry); -} diff --git a/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.unit-test.ts b/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.unit-test.ts deleted file mode 100644 index 532f974e..00000000 --- a/tooling/build-env/src/executors/bootstrap/verdaccio-npm-env.unit-test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import { - bootstrapEnvironment, - configureRegistry, - ConfigureRegistryOptions, - unconfigureRegistry, - UnconfigureRegistryOptions, - VERDACCIO_ENV_TOKEN, -} from './verdaccio-npm-env'; -import { execSync } from 'node:child_process'; -import { logger } from '@nx/devkit'; -import { formatInfo } from '../../internal/logging'; - -vi.mock('child_process', async () => { - const actual = await vi.importActual( - 'child_process' - ); - return { - ...actual, - execSync: vi.fn(), - }; -}); - -vi.mock('@nx/devkit', async () => { - const actual = await vi.importActual('@nx/devkit'); - return { - ...actual, - logger: { - info: vi.fn(), - }, - }; -}); - -describe('configureRegistry', () => { - it('should set the npm registry and authToken', () => { - const processResult: ConfigureRegistryOptions = { - port: 4873, - host: 'localhost', - url: 'http://localhost:4873', - userconfig: 'test-config', - }; - - configureRegistry(processResult); - - expect(execSync).toHaveBeenCalledTimes(2); - expect(execSync).toHaveBeenCalledWith( - 'npm config set registry="http://localhost:4873" --userconfig="test-config"' - ); - - expect(execSync).toHaveBeenCalledWith( - 'npm config set //localhost:4873/:_authToken "secretVerdaccioToken" --userconfig="test-config"' - ); - }); - - it('should set and log registry and authToken commands if verbose is true', () => { - const processResult: ConfigureRegistryOptions = { - port: 4873, - host: 'localhost', - url: 'http://localhost:4873', - userconfig: 'test-config', - }; - - configureRegistry(processResult, true); - - expect(execSync).toHaveBeenCalledTimes(2); - expect(logger.info).toHaveBeenCalledWith( - formatInfo( - 'Set registry:\nnpm config set registry="http://localhost:4873" --userconfig="test-config"', - VERDACCIO_ENV_TOKEN - ) - ); - expect(logger.info).toHaveBeenCalledWith( - formatInfo( - 'Set authToken:\nnpm config set //localhost:4873/:_authToken "secretVerdaccioToken" --userconfig="test-config"', - VERDACCIO_ENV_TOKEN - ) - ); - }); -}); - -describe('unconfigureRegistry', () => { - it('should delete the npm registry and authToken', () => { - const processResult: UnconfigureRegistryOptions = { - userconfig: 'test-config', - port: 4873, - host: 'localhost', - }; - - unconfigureRegistry(processResult); - - expect(execSync).toHaveBeenCalledTimes(2); - expect(execSync).toHaveBeenCalledWith( - 'npm config delete registry --userconfig="test-config"' - ); - - expect(execSync).toHaveBeenCalledWith( - 'npm config delete //localhost:4873/:_authToken --userconfig="test-config"' - ); - }); - - it('should delete and log registry and authToken commands if verbose is true', () => { - const processResult: UnconfigureRegistryOptions = { - userconfig: 'test-config', - port: 4873, - host: 'localhost', - }; - - unconfigureRegistry(processResult, true); - - expect(execSync).toHaveBeenCalledTimes(2); - expect(logger.info).toHaveBeenCalledWith( - formatInfo( - 'Delete registry:\nnpm config delete registry --userconfig="test-config"', - VERDACCIO_ENV_TOKEN - ) - ); - - expect(logger.info).toHaveBeenCalledWith( - formatInfo( - 'Delete authToken:\nnpm config delete //localhost:4873/:_authToken --userconfig="test-config"', - VERDACCIO_ENV_TOKEN - ) - ); - }); -}); diff --git a/tooling/build-env/src/executors/bootstrap/verdaccio-registry.ts b/tooling/build-env/src/executors/bootstrap/verdaccio-registry.ts index 9ef921d8..fe340e1a 100644 --- a/tooling/build-env/src/executors/bootstrap/verdaccio-registry.ts +++ b/tooling/build-env/src/executors/bootstrap/verdaccio-registry.ts @@ -1,4 +1,4 @@ -import { bold, gray, red } from 'ansis'; +import { bold } from 'ansis'; import { join } from 'node:path'; import { logger } from '@nx/devkit'; import { objectToCliArgs } from '../../internal/terminal'; diff --git a/tooling/build-env/src/executors/setup/executor.unit-test.ts b/tooling/build-env/src/executors/setup/executor.unit-test.ts new file mode 100644 index 00000000..955a7143 --- /dev/null +++ b/tooling/build-env/src/executors/setup/executor.unit-test.ts @@ -0,0 +1,194 @@ +import runSetupEnvironmentExecutor from './executor'; +import { beforeEach, expect, vi } from 'vitest'; +import * as runBuildExecutorModule from '../bootstrap/executor'; +import * as executeProcessModule from '../../internal/execute-process'; +import * as killProcessExecutorModule from '../kill-process/executor'; +import { logger } from '@nx/devkit'; + +vi.mock('@nx/devkit', async () => { + const actual = await vi.importActual('@nx/devkit'); + return { + ...actual, + logger: { + info: vi.fn(), + error: vi.fn(), + }, + readJsonFile: vi.fn().mockReturnValue({ + pid: 4873, + port: '4873', + url: 'http://localhost:4873', + }), + }; +}); + +describe('runSetupEnvironmentExecutor', () => { + const runBootstrapEnvironmentSpy = vi.spyOn( + runBuildExecutorModule, + 'default' + ); + const executeProcessSpy = vi.spyOn(executeProcessModule, 'executeProcess'); + const runKillProcessExecutorSpy = vi.spyOn( + killProcessExecutorModule, + 'default' + ); + + beforeEach(() => { + runBootstrapEnvironmentSpy.mockReset(); + executeProcessSpy.mockReset(); + }); + + it('should setup environment correctly', async () => { + runBootstrapEnvironmentSpy.mockResolvedValue({ + success: true, + command: 'Bootstraped environemnt successfully.', + }); + runKillProcessExecutorSpy.mockResolvedValue({ + success: true, + command: 'Kill process successfully', + }); + + await expect( + runSetupEnvironmentExecutor( + {}, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: true, + command: 'Environment setup complete.', + }); + + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith({ + args: [ + 'install-env', + 'my-lib-e2e', + '--environmentProject="my-lib-e2e"', + '--environmentRoot="tmp/environments/my-lib-e2e"', + ], + command: 'nx', + cwd: '/test', + verbose: true, + }); + + expect(runKillProcessExecutorSpy).toHaveBeenCalledTimes(1); + expect(runKillProcessExecutorSpy).toHaveBeenCalledWith( + { + filePath: 'tmp/environments/my-lib-e2e/verdaccio-registry.json', + environmentProject: 'my-lib-e2e', + environmentRoot: 'tmp/environments/my-lib-e2e', + }, + { + cwd: 'test', + isVerbose: false, + projectName: 'my-lib-e2e', + projectsConfigurations: { + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + version: 2, + }, + root: 'tmp/environments/test', + } + ); + }); + + it('should catch error', async () => { + runBootstrapEnvironmentSpy.mockRejectedValue( + new Error('Error in runBootstrapEnvironment') + ); + + await expect( + runSetupEnvironmentExecutor( + {}, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: false, + command: Error('Error in runBootstrapEnvironment'), + }); + }); + + it('should keep server running if keepServerRunning is passed', async () => { + runBootstrapEnvironmentSpy.mockResolvedValue({ + success: true, + command: 'Bootstraped environemnt successfully.', + }); + runKillProcessExecutorSpy.mockResolvedValue({ + success: true, + command: 'Kill process successfully', + }); + + await expect( + runSetupEnvironmentExecutor( + { + keepServerRunning: true, + }, + { + cwd: 'test', + isVerbose: false, + root: 'tmp/environments/test', + projectName: 'my-lib-e2e', + projectsConfigurations: { + version: 2, + projects: { + 'my-lib': { + root: 'e2e/my-lib-e2e', + }, + }, + }, + } + ) + ).resolves.toStrictEqual({ + success: true, + command: 'Environment setup complete.', + }); + + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith({ + args: [ + 'install-env', + 'my-lib-e2e', + '--environmentProject="my-lib-e2e"', + '--environmentRoot="tmp/environments/my-lib-e2e"', + ], + command: 'nx', + cwd: '/test', + verbose: true, + }); + + expect(runKillProcessExecutorSpy).toHaveBeenCalledTimes(0); + + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( + 'Verdaccio server kept running under : http://localhost:4873' + ); + }); +});