diff --git a/tests/legacy-cli/e2e/setup/200-create-tmp-dir.ts b/tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts similarity index 96% rename from tests/legacy-cli/e2e/setup/200-create-tmp-dir.ts rename to tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts index ba2a811a580d..f15e504daef1 100644 --- a/tests/legacy-cli/e2e/setup/200-create-tmp-dir.ts +++ b/tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts @@ -17,5 +17,4 @@ export default function () { } console.log(` Using "${tempRoot}" as temporary directory for a new project.`); setGlobalVariable('tmp-root', tempRoot); - process.chdir(tempRoot); } diff --git a/tests/legacy-cli/e2e/setup/002-npm-sandbox.ts b/tests/legacy-cli/e2e/setup/002-npm-sandbox.ts new file mode 100644 index 000000000000..eaca4a166e3f --- /dev/null +++ b/tests/legacy-cli/e2e/setup/002-npm-sandbox.ts @@ -0,0 +1,31 @@ +import { mkdir, writeFile } from 'fs/promises'; +import { delimiter, join } from 'path'; +import { getGlobalVariable } from '../utils/env'; + +/** + * Configure npm to use a unique sandboxed environment. + */ +export default async function () { + const tempRoot: string = getGlobalVariable('tmp-root'); + const npmModulesPrefix = join(tempRoot, 'npm-global'); + const npmrc = join(tempRoot, '.npmrc'); + + // Configure npm to use the sandboxed npm globals and rc file + process.env.NPM_CONFIG_USERCONFIG = npmrc; + process.env.NPM_CONFIG_PREFIX = npmModulesPrefix; + + // Ensure the custom npm global bin is first on the PATH + // https://docs.npmjs.com/cli/v8/configuring-npm/folders#executables + if (process.platform.startsWith('win')) { + process.env.PATH = npmModulesPrefix + delimiter + process.env.PATH; + } else { + process.env.PATH = join(npmModulesPrefix, 'bin') + delimiter + process.env.PATH; + } + + // Ensure the globals directory and npmrc file exist. + // Configure the registry in the npmrc in addition to the environment variable. + await writeFile(npmrc, 'registry=' + getGlobalVariable('package-registry')); + await mkdir(npmModulesPrefix); + + console.log(` Using "${npmModulesPrefix}" as e2e test global npm cache.`); +} diff --git a/tests/legacy-cli/e2e/setup/010-local-publish.ts b/tests/legacy-cli/e2e/setup/010-local-publish.ts index 2a7d31c547ba..41fd0df79e50 100644 --- a/tests/legacy-cli/e2e/setup/010-local-publish.ts +++ b/tests/legacy-cli/e2e/setup/010-local-publish.ts @@ -1,5 +1,5 @@ import { getGlobalVariable } from '../utils/env'; -import { execWithEnv } from '../utils/process'; +import { execWithEnv, extractNpmEnv } from '../utils/process'; import { isPrereleaseCli } from '../utils/project'; export default async function () { @@ -18,7 +18,7 @@ export default async function () { isPrereleaseCli() ? 'next' : 'latest', ], { - ...process.env, + ...extractNpmEnv(), // Also set an auth token value for the local test registry which is required by npm 7+ // even though it is never actually used. 'NPM_CONFIG__AUTH': 'e2e-testing', diff --git a/tests/legacy-cli/e2e/setup/200-create-project-dir.ts b/tests/legacy-cli/e2e/setup/200-create-project-dir.ts new file mode 100644 index 000000000000..1bfb66dde96f --- /dev/null +++ b/tests/legacy-cli/e2e/setup/200-create-project-dir.ts @@ -0,0 +1,19 @@ +import { mkdir } from 'fs/promises'; +import { join } from 'path'; +import { getGlobalVariable, setGlobalVariable } from '../utils/env'; + +/** + * Create a parent directory for test projects to be created within. + * Change the cwd() to that directory in preparation for launching the cli. + */ +export default async function () { + const tempRoot: string = getGlobalVariable('tmp-root'); + const projectsRoot = join(tempRoot, 'e2e-test'); + + setGlobalVariable('projects-root', projectsRoot); + + await mkdir(projectsRoot); + + console.log(` Using "${projectsRoot}" as temporary directory for a new project.`); + process.chdir(projectsRoot); +} diff --git a/tests/legacy-cli/e2e/setup/500-create-project.ts b/tests/legacy-cli/e2e/setup/500-create-project.ts index 1d6ec58f2beb..5b0319cf2941 100644 --- a/tests/legacy-cli/e2e/setup/500-create-project.ts +++ b/tests/legacy-cli/e2e/setup/500-create-project.ts @@ -1,9 +1,9 @@ import { join } from 'path'; import { getGlobalVariable } from '../utils/env'; -import { expectFileToExist, writeFile } from '../utils/fs'; +import { expectFileToExist } from '../utils/fs'; import { gitClean } from '../utils/git'; import { setRegistry as setNPMConfigRegistry } from '../utils/packages'; -import { ng, npm } from '../utils/process'; +import { ng } from '../utils/process'; import { prepareProjectForE2e, updateJsonFile } from '../utils/project'; export default async function () { @@ -18,8 +18,6 @@ export default async function () { await gitClean(); } else { const extraArgs = []; - const testRegistry = getGlobalVariable('package-registry'); - const isCI = getGlobalVariable('ci'); // Ensure local test registry is used when outside a project await setNPMConfigRegistry(true); @@ -28,12 +26,6 @@ export default async function () { await expectFileToExist(join(process.cwd(), 'test-project')); process.chdir('./test-project'); - // If on CI, the user configuration set above will handle project usage - if (!isCI) { - // Ensure local test registry is used inside a project - await writeFile('.npmrc', `registry=${testRegistry}`); - } - // Setup esbuild builder if requested on the commandline const useEsbuildBuilder = !!getGlobalVariable('argv')['esbuild']; if (useEsbuildBuilder) { diff --git a/tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts b/tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts index 23b56464a745..432f21167cdb 100644 --- a/tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts +++ b/tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts @@ -1,5 +1,5 @@ import { expectFileToMatch } from '../../utils/fs'; -import { execWithEnv, ng, silentNpm } from '../../utils/process'; +import { execWithEnv, extractNpmEnv, ng, silentNpm } from '../../utils/process'; import { installPackage, uninstallPackage } from '../../utils/packages'; import { isPrereleaseCli } from '../../utils/project'; @@ -29,7 +29,7 @@ async function publishOutdated(npmSpecifier: string): Promise { '--registry=https://registry.npmjs.org', ); await execWithEnv('npm', ['publish', stdoutPack.trim(), '--tag=outdated'], { - ...process.env, + ...extractNpmEnv(), // Also set an auth token value for the local test registry which is required by npm 7+ // even though it is never actually used. 'NPM_CONFIG__AUTH': 'e2e-testing', diff --git a/tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts b/tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts index 095f333517fa..11040c618bbb 100644 --- a/tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts +++ b/tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts @@ -4,7 +4,7 @@ import { ng, silentGit } from '../../utils/process'; import { prepareProjectForE2e } from '../../utils/project'; export default async function () { - process.chdir(getGlobalVariable('tmp-root')); + process.chdir(getGlobalVariable('projects-root')); await createDir('./subdirectory'); process.chdir('./subdirectory'); diff --git a/tests/legacy-cli/e2e/utils/assets.ts b/tests/legacy-cli/e2e/utils/assets.ts index c7c41ac7b7fe..669f5b48364e 100644 --- a/tests/legacy-cli/e2e/utils/assets.ts +++ b/tests/legacy-cli/e2e/utils/assets.ts @@ -11,7 +11,7 @@ export function assetDir(assetName: string) { } export function copyProjectAsset(assetName: string, to?: string) { - const tempRoot = join(getGlobalVariable('tmp-root'), 'test-project'); + const tempRoot = join(getGlobalVariable('projects-root'), 'test-project'); const sourcePath = assetDir(assetName); const targetPath = join(tempRoot, to || assetName); @@ -20,7 +20,7 @@ export function copyProjectAsset(assetName: string, to?: string) { export function copyAssets(assetName: string, to?: string) { const seed = +Date.now(); - const tempRoot = join(getGlobalVariable('tmp-root'), 'assets', assetName + '-' + seed); + const tempRoot = join(getGlobalVariable('projects-root'), 'assets', assetName + '-' + seed); const root = assetDir(assetName); return Promise.resolve() @@ -30,7 +30,7 @@ export function copyAssets(assetName: string, to?: string) { return allFiles.reduce((promise, filePath) => { const toPath = to !== undefined - ? resolve(getGlobalVariable('tmp-root'), 'test-project', to, filePath) + ? resolve(getGlobalVariable('projects-root'), 'test-project', to, filePath) : join(tempRoot, filePath); return promise.then(() => copyFile(join(root, filePath), toPath)); diff --git a/tests/legacy-cli/e2e/utils/process.ts b/tests/legacy-cli/e2e/utils/process.ts index 7a4100e44822..903e35f15a7b 100644 --- a/tests/legacy-cli/e2e/utils/process.ts +++ b/tests/legacy-cli/e2e/utils/process.ts @@ -15,6 +15,8 @@ interface ExecOptions { cwd?: string; } +const NPM_CONFIG_RE = /^npm_config_/i; + let _processes: child_process.ChildProcess[] = []; export type ProcessOutput = { @@ -138,6 +140,20 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise NPM_CONFIG_RE.test(v)) + .reduce( + (vars, n) => { + vars[n] = process.env[n]; + return vars; + }, + { + PATH: process.env.PATH, + }, + ); +} + export function waitForAnyProcessOutputToMatch( match: RegExp, timeout = 30000, @@ -269,21 +285,22 @@ export function silentNpm( { silent: true, cwd: (options as { cwd?: string } | undefined)?.cwd, + env: extractNpmEnv(), }, 'npm', params, ); } else { - return _exec({ silent: true }, 'npm', args as string[]); + return _exec({ silent: true, env: extractNpmEnv() }, 'npm', args as string[]); } } export function silentYarn(...args: string[]) { - return _exec({ silent: true }, 'yarn', args); + return _exec({ silent: true, env: extractNpmEnv() }, 'yarn', args); } export function npm(...args: string[]) { - return _exec({}, 'npm', args); + return _exec({ env: extractNpmEnv() }, 'npm', args); } export function node(...args: string[]) {