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,99 +583,95 @@ ${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 () => {
const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib}`);
it('should be able to generate a nest library', async () => { const lintResults = runCLI(`lint ${nestlib}`);
const nestlib = uniq('nestlib'); expect(lintResults).toContain('All files pass linting.');
runCLI(`generate @nx/nest:lib ${nestlib}`);
const lintResults = runCLI(`lint ${nestlib}`); const testResults = runCLI(`test ${nestlib}`);
expect(lintResults).toContain('All files pass linting.'); expect(testResults).toContain(
`Successfully ran target test for project ${nestlib}`
);
}, 60000);
const testResults = runCLI(`test ${nestlib}`); it('should be able to generate a nest library w/ service', async () => {
expect(testResults).toContain( const nestlib = uniq('nestlib');
`Successfully ran target test for project ${nestlib}`
);
}, 60000);
it('should be able to generate a nest library w/ service', async () => { runCLI(`generate @nx/nest:lib ${nestlib} --service`);
const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib} --service`); const lintResults = runCLI(`lint ${nestlib}`);
expect(lintResults).toContain('All files pass linting.');
const lintResults = runCLI(`lint ${nestlib}`); const jestResult = await runCLIAsync(`test ${nestlib}`);
expect(lintResults).toContain('All files pass linting.'); expect(jestResult.combinedOutput).toContain(
'Test Suites: 1 passed, 1 total'
);
}, 200000);
const jestResult = await runCLIAsync(`test ${nestlib}`); it('should be able to generate a nest library w/ controller', async () => {
expect(jestResult.combinedOutput).toContain( const nestlib = uniq('nestlib');
'Test Suites: 1 passed, 1 total'
);
}, 200000);
it('should be able to generate a nest library w/ controller', async () => { runCLI(`generate @nx/nest:lib ${nestlib} --controller`);
const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib} --controller`); const lintResults = runCLI(`lint ${nestlib}`);
expect(lintResults).toContain('All files pass linting.');
const lintResults = runCLI(`lint ${nestlib}`); const jestResult = await runCLIAsync(`test ${nestlib}`);
expect(lintResults).toContain('All files pass linting.'); expect(jestResult.combinedOutput).toContain(
'Test Suites: 1 passed, 1 total'
);
}, 200000);
const jestResult = await runCLIAsync(`test ${nestlib}`); it('should be able to generate a nest library w/ controller and service', async () => {
expect(jestResult.combinedOutput).toContain( const nestlib = uniq('nestlib');
'Test Suites: 1 passed, 1 total'
);
}, 200000);
it('should be able to generate a nest library w/ controller and service', async () => { runCLI(`generate @nx/nest:lib ${nestlib} --controller --service`);
const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib} --controller --service`); const lintResults = runCLI(`lint ${nestlib}`);
expect(lintResults).toContain('All files pass linting.');
const lintResults = runCLI(`lint ${nestlib}`); const jestResult = await runCLIAsync(`test ${nestlib}`);
expect(lintResults).toContain('All files pass linting.'); expect(jestResult.combinedOutput).toContain(
'Test Suites: 2 passed, 2 total'
);
}, 200000);
const jestResult = await runCLIAsync(`test ${nestlib}`); it('should have plugin output if specified in `transformers`', async () => {
expect(jestResult.combinedOutput).toContain( const nestlib = uniq('nestlib');
'Test Suites: 2 passed, 2 total' runCLI(`generate @nx/nest:lib ${nestlib} --buildable`);
);
}, 200000);
it('should have plugin output if specified in `transformers`', async () => { packageInstall('@nestjs/swagger', undefined, '^7.0.0');
newProject();
const nestlib = uniq('nestlib');
runCLI(`generate @nx/nest:lib ${nestlib} --buildable`);
packageInstall('@nestjs/swagger', undefined, '^7.0.0'); updateJson(join('libs', nestlib, 'project.json'), (config) => {
config.targets.build.options.transformers = [
updateJson(join('libs', nestlib, 'project.json'), (config) => { {
config.targets.build.options.transformers = [ name: '@nestjs/swagger/plugin',
{ options: {
name: '@nestjs/swagger/plugin', dtoFileNameSuffix: ['.model.ts'],
options: { },
dtoFileNameSuffix: ['.model.ts'],
}, },
}, ];
]; return config;
return config; });
});
updateFile( updateFile(
`libs/${nestlib}/src/lib/foo.model.ts`, `libs/${nestlib}/src/lib/foo.model.ts`,
` `
export class FooModel { export class FooModel {
foo?: string; foo?: string;
bar?: number; bar?: number;
}` }`
); );
await runCLIAsync(`build ${nestlib}`); await runCLIAsync(`build ${nestlib}`);
const fooModelJs = readFile(`dist/libs/${nestlib}/src/lib/foo.model.js`); const fooModelJs = readFile(`dist/libs/${nestlib}/src/lib/foo.model.js`);
expect(stripIndents`${fooModelJs}`).toContain( expect(stripIndents`${fooModelJs}`).toContain(
stripIndents` stripIndents`
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.FooModel = void 0; exports.FooModel = void 0;
@ -681,10 +684,11 @@ class FooModel {
exports.FooModel = FooModel; exports.FooModel = FooModel;
//# sourceMappingURL=foo.model.js.map //# sourceMappingURL=foo.model.js.map
` `
); );
}, 300000); }, 300000);
it('should run default jest tests', async () => { it('should run default jest tests', async () => {
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,13 +29,41 @@ 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;
// TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages.
const nxPackages = [
`@nx/angular`,
`@nx/eslint-plugin`,
`@nx/express`,
`@nx/esbuild`,
`@nx/jest`,
`@nx/js`,
`@nx/eslint`,
`@nx/nest`,
`@nx/next`,
`@nx/node`,
`@nx/nuxt`,
`@nx/plugin`,
`@nx/playwright`,
`@nx/rollup`,
`@nx/react`,
`@nx/storybook`,
`@nx/vue`,
`@nx/vite`,
`@nx/web`,
`@nx/webpack`,
`@nx/react-native`,
`@nx/expo`,
] as const;
type NxPackage = typeof nxPackages[number];
/** /**
* Sets up a new project in the temporary project path * Sets up a new project in the temporary project path
* for the currently selected CLI. * for the currently selected CLI.
@ -43,15 +72,34 @@ export function newProject({
name = uniq('proj'), name = uniq('proj'),
packageManager = getSelectedPackageManager(), packageManager = getSelectedPackageManager(),
unsetProjectNameAndRootFormat = true, unsetProjectNameAndRootFormat = true,
packages,
}: {
name?: string;
packageManager?: 'npm' | 'yarn' | 'pnpm';
unsetProjectNameAndRootFormat?: boolean;
readonly packages?: Array<NxPackage>;
} = {}): string { } = {}): string {
const newProjectStart = performance.mark('new-project:start');
try { try {
const projScope = 'proj'; const projScope = 'proj';
let createNxWorkspaceMeasure: PerformanceMeasure;
let packageInstallMeasure: PerformanceMeasure;
if (!directoryExists(tmpBackupProjPath())) { if (!directoryExists(tmpBackupProjPath())) {
const createNxWorkspaceStart = performance.mark(
'create-nx-workspace:start'
);
runCreateWorkspace(projScope, { runCreateWorkspace(projScope, {
preset: 'apps', preset: 'apps',
packageManager, packageManager,
}); });
const createNxWorkspaceEnd = performance.mark('create-nx-workspace:end');
createNxWorkspaceMeasure = performance.measure(
'create-nx-workspace',
createNxWorkspaceStart.name,
createNxWorkspaceEnd.name
);
if (unsetProjectNameAndRootFormat) { if (unsetProjectNameAndRootFormat) {
console.warn( console.warn(
@ -69,33 +117,19 @@ export function newProject({
); );
} }
// TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages. if (!packages) {
const packages = [ console.warn(
`@nx/angular`, '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'
`@nx/eslint-plugin`, );
`@nx/express`, }
`@nx/esbuild`, const packageInstallStart = performance.mark('packageInstall:start');
`@nx/jest`, packageInstall((packages ?? nxPackages).join(` `), projScope);
`@nx/js`, const packageInstallEnd = performance.mark('packageInstall:end');
`@nx/eslint`, packageInstallMeasure = performance.measure(
`@nx/nest`, 'packageInstall',
`@nx/next`, packageInstallStart.name,
`@nx/node`, packageInstallEnd.name
`@nx/nuxt`, );
`@nx/plugin`,
`@nx/playwright`,
`@nx/rollup`,
`@nx/react`,
`@nx/storybook`,
`@nx/vue`,
`@nx/vite`,
`@nx/web`,
`@nx/webpack`,
`@nx/react-native`,
`@nx/expo`,
];
packageInstall(packages.join(` `), projScope);
// 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);