chore(node): decrease e2e times (#20666)

This commit is contained in:
Jason Jean 2023-12-08 16:19:47 -05:00 committed by GitHub
parent 5a305d41de
commit 4ec134f9d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 202 additions and 121 deletions

View File

@ -6,20 +6,21 @@ import {
promisifiedTreeKill, promisifiedTreeKill,
readFile, readFile,
runCLI, runCLI,
runCLIAsync,
runCommandUntil, runCommandUntil,
setMaxWorkers, setMaxWorkers,
tmpProjPath,
uniq, uniq,
updateFile, updateFile,
} from '@nx/e2e/utils'; } from '@nx/e2e/utils';
import { execSync } from 'child_process';
import { join } from 'path'; import { join } from 'path';
describe('Node Applications + esbuild', () => { describe('Node Applications + esbuild', () => {
beforeEach(() => newProject()); beforeAll(() =>
newProject({
packages: ['@nx/node'],
})
);
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
it('should generate an app using esbuild', async () => { it('should generate an app using esbuild', async () => {
const app = uniq('nodeapp'); const app = uniq('nodeapp');

View File

@ -17,11 +17,13 @@ import { join } from 'path';
describe('Node Applications + webpack', () => { describe('Node Applications + webpack', () => {
let proj: string; let proj: string;
beforeEach(() => { beforeAll(() => {
proj = newProject(); proj = newProject({
packages: ['@nx/node'],
});
}); });
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
function addLibImport(appName: string, libName: string, importPath?: string) { function addLibImport(appName: string, libName: string, importPath?: string) {
const content = readFile(`apps/${appName}/src/main.ts`); const content = readFile(`apps/${appName}/src/main.ts`);

View File

@ -17,9 +17,13 @@ import { execSync } from 'child_process';
import { join } from 'path'; import { join } from 'path';
describe('Node Applications + webpack', () => { describe('Node Applications + webpack', () => {
beforeEach(() => newProject()); beforeAll(() =>
newProject({
packages: ['@nx/node'],
})
);
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
it('should generate an app using webpack', async () => { it('should generate an app using webpack', async () => {
const app = uniq('nodeapp'); const app = uniq('nodeapp');

View File

@ -50,9 +50,13 @@ function getData(port, path = '/api'): Promise<any> {
} }
describe('Node Applications', () => { describe('Node Applications', () => {
beforeEach(() => newProject()); beforeAll(() =>
newProject({
packages: ['@nx/node', '@nx/express', '@nx/nest'],
})
);
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
it('should be able to generate an empty application', async () => { it('should be able to generate an empty application', async () => {
const nodeapp = uniq('nodeapp'); const nodeapp = uniq('nodeapp');
@ -331,12 +335,16 @@ describe('Node Applications', () => {
}); });
describe('Build Node apps', () => { describe('Build Node apps', () => {
beforeEach(() => newProject()); let scope: string;
beforeAll(() => {
scope = newProject({
packages: ['@nx/node', '@nx/express', '@nx/nest'],
});
});
afterEach(() => cleanupProject()); afterAll(() => cleanupProject());
it('should generate a package.json with the `--generatePackageJson` flag', async () => { it('should generate a package.json with the `--generatePackageJson` flag', async () => {
const scope = newProject();
const packageManager = detectPackageManager(tmpProjPath()); const packageManager = detectPackageManager(tmpProjPath());
const nestapp = uniq('nestapp'); const nestapp = uniq('nestapp');
runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`); runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`);
@ -523,7 +531,6 @@ ${jslib}();
describe('NestJS', () => { describe('NestJS', () => {
it('should have plugin output if specified in `tsPlugins`', async () => { it('should have plugin output if specified in `tsPlugins`', async () => {
newProject();
const nestapp = uniq('nestapp'); const nestapp = uniq('nestapp');
runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`); runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`);
setMaxWorkers(join('apps', nestapp, 'project.json')); setMaxWorkers(join('apps', nestapp, 'project.json'));
@ -576,11 +583,8 @@ ${jslib}();
expect(mainJs).toContain('_OPENAPI_METADATA_FACTORY'); expect(mainJs).toContain('_OPENAPI_METADATA_FACTORY');
}, 300000); }, 300000);
}); });
});
describe('nest libraries', function () { describe('nest libraries', function () {
beforeEach(() => newProject());
it('should be able to generate a nest library', async () => { it('should be able to generate a nest library', async () => {
const nestlib = uniq('nestlib'); const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib}`); runCLI(`generate @nx/nest:lib ${nestlib}`);
@ -637,7 +641,6 @@ describe('nest libraries', function () {
}, 200000); }, 200000);
it('should have plugin output if specified in `transformers`', async () => { it('should have plugin output if specified in `transformers`', async () => {
newProject();
const nestlib = uniq('nestlib'); const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib} --buildable`); runCLI(`generate @nx/nest:lib ${nestlib} --buildable`);
@ -688,3 +691,4 @@ exports.FooModel = FooModel;
await expectJestTestsToPass('@nx/node:lib'); await expectJestTestsToPass('@nx/node:lib');
}, 100000); }, 100000);
}); });
});

View File

@ -21,6 +21,7 @@ import { angularCliVersion as defaultAngularCliVersion } from '@nx/workspace/src
import { dump } from '@zkochan/js-yaml'; import { dump } from '@zkochan/js-yaml';
import { execSync, ExecSyncOptions } from 'child_process'; import { execSync, ExecSyncOptions } from 'child_process';
import { performance, PerformanceMeasure } from 'perf_hooks';
import { logError, logInfo } from './log-utils'; import { logError, logInfo } from './log-utils';
import { import {
getPackageManagerCommand, getPackageManagerCommand,
@ -28,49 +29,15 @@ import {
RunCmdOpts, RunCmdOpts,
runCommand, runCommand,
} from './command-utils'; } from './command-utils';
import { NxJsonConfiguration, output } from '@nx/devkit'; import { output } from '@nx/devkit';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { resetWorkspaceContext } from 'nx/src/utils/workspace-context'; import { resetWorkspaceContext } from 'nx/src/utils/workspace-context';
let projName: string; let projName: string;
/**
* Sets up a new project in the temporary project path
* for the currently selected CLI.
*/
export function newProject({
name = uniq('proj'),
packageManager = getSelectedPackageManager(),
unsetProjectNameAndRootFormat = true,
} = {}): string {
try {
const projScope = 'proj';
if (!directoryExists(tmpBackupProjPath())) {
runCreateWorkspace(projScope, {
preset: 'apps',
packageManager,
});
if (unsetProjectNameAndRootFormat) {
console.warn(
'ATTENTION: The workspace generated for this e2e test does not use the new as-provided project name/root format. Please update this test'
);
createFile('apps/.gitkeep');
createFile('libs/.gitkeep');
}
// Temporary hack to prevent installing with `--frozen-lockfile`
if (isCI && packageManager === 'pnpm') {
updateFile(
'.npmrc',
'prefer-frozen-lockfile=false\nstrict-peer-dependencies=false\nauto-install-peers=true'
);
}
// TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages. // TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages.
const packages = [ const nxPackages = [
`@nx/angular`, `@nx/angular`,
`@nx/eslint-plugin`, `@nx/eslint-plugin`,
`@nx/express`, `@nx/express`,
@ -93,9 +60,76 @@ export function newProject({
`@nx/webpack`, `@nx/webpack`,
`@nx/react-native`, `@nx/react-native`,
`@nx/expo`, `@nx/expo`,
]; ] as const;
packageInstall(packages.join(` `), projScope);
type NxPackage = typeof nxPackages[number];
/**
* Sets up a new project in the temporary project path
* for the currently selected CLI.
*/
export function newProject({
name = uniq('proj'),
packageManager = getSelectedPackageManager(),
unsetProjectNameAndRootFormat = true,
packages,
}: {
name?: string;
packageManager?: 'npm' | 'yarn' | 'pnpm';
unsetProjectNameAndRootFormat?: boolean;
readonly packages?: Array<NxPackage>;
} = {}): string {
const newProjectStart = performance.mark('new-project:start');
try {
const projScope = 'proj';
let createNxWorkspaceMeasure: PerformanceMeasure;
let packageInstallMeasure: PerformanceMeasure;
if (!directoryExists(tmpBackupProjPath())) {
const createNxWorkspaceStart = performance.mark(
'create-nx-workspace:start'
);
runCreateWorkspace(projScope, {
preset: 'apps',
packageManager,
});
const createNxWorkspaceEnd = performance.mark('create-nx-workspace:end');
createNxWorkspaceMeasure = performance.measure(
'create-nx-workspace',
createNxWorkspaceStart.name,
createNxWorkspaceEnd.name
);
if (unsetProjectNameAndRootFormat) {
console.warn(
'ATTENTION: The workspace generated for this e2e test does not use the new as-provided project name/root format. Please update this test'
);
createFile('apps/.gitkeep');
createFile('libs/.gitkeep');
}
// Temporary hack to prevent installing with `--frozen-lockfile`
if (isCI && packageManager === 'pnpm') {
updateFile(
'.npmrc',
'prefer-frozen-lockfile=false\nstrict-peer-dependencies=false\nauto-install-peers=true'
);
}
if (!packages) {
console.warn(
'ATTENTION: All packages are installed into the new workspace. To make this test faster, please pass the subset of packages that this test needs by passing a packages array in the options'
);
}
const packageInstallStart = performance.mark('packageInstall:start');
packageInstall((packages ?? nxPackages).join(` `), projScope);
const packageInstallEnd = performance.mark('packageInstall:end');
packageInstallMeasure = performance.measure(
'packageInstall',
packageInstallStart.name,
packageInstallEnd.name
);
// stop the daemon // stop the daemon
execSync(`${getPackageManagerCommand().runNx} reset`, { execSync(`${getPackageManagerCommand().runNx} reset`, {
cwd: `${e2eCwd}/proj`, cwd: `${e2eCwd}/proj`,
@ -105,19 +139,55 @@ export function newProject({
moveSync(`${e2eCwd}/proj`, `${tmpBackupProjPath()}`); moveSync(`${e2eCwd}/proj`, `${tmpBackupProjPath()}`);
} }
projName = name; projName = name;
copySync(`${tmpBackupProjPath()}`, `${tmpProjPath()}`);
if (isVerbose()) {
logInfo(`NX`, `E2E created a project: ${tmpProjPath()}`);
}
const projectDirectory = tmpProjPath();
copySync(`${tmpBackupProjPath()}`, `${projectDirectory}`);
// TODO: What is this for?
if (packageManager === 'pnpm') { if (packageManager === 'pnpm') {
execSync(getPackageManagerCommand().install, { execSync(getPackageManagerCommand().install, {
cwd: tmpProjPath(), cwd: projectDirectory,
stdio: 'pipe', stdio: 'pipe',
env: { CI: 'true', ...process.env }, env: { CI: 'true', ...process.env },
encoding: 'utf-8', encoding: 'utf-8',
}); });
} }
const newProjectEnd = performance.mark('new-project:end');
const perfMeasure = performance.measure(
'newProject',
newProjectStart.name,
newProjectEnd.name
);
if (isVerbose()) {
logInfo(
`NX`,
`E2E created a project: ${projectDirectory} in ${
perfMeasure.duration / 1000
} seconds
${
createNxWorkspaceMeasure
? `create-nx-workspace: ${
createNxWorkspaceMeasure.duration / 1000
} seconds\n`
: ''
}${
packageInstallMeasure
? `packageInstall: ${
packageInstallMeasure.duration / 1000
} seconds\n`
: ''
}`
);
}
if (process.env.NX_E2E_EDITOR) {
const editor = process.env.NX_E2E_EDITOR;
execSync(`${editor} ${projectDirectory}`, {
stdio: 'inherit',
});
}
return projScope; return projScope;
} catch (e) { } catch (e) {
logError(`Failed to set up project for e2e tests.`, e.message); logError(`Failed to set up project for e2e tests.`, e.message);