chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
import {
|
||||
checkFilesDoNotExist,
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
getSelectedPackageManager,
|
||||
killPort,
|
||||
killProcessAndPorts,
|
||||
newProject,
|
||||
readFile,
|
||||
reservePort,
|
||||
runCLI,
|
||||
runCommandUntil,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
} from '@nx/e2e-utils';
|
||||
import { join } from 'path';
|
||||
import { stripVTControlCharacters } from 'node:util';
|
||||
|
||||
describe('Node Applications + webpack', () => {
|
||||
let proj: string;
|
||||
const selectedPm = getSelectedPackageManager();
|
||||
|
||||
// TODO(nicholas): Look into how this can work with npm instead of forcing pnpm.
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/node', '@nx/nest', '@nx/webpack'],
|
||||
// npm has resolution for ajv some packages require ajv6 and some require ajv8 and npm resolves it to ajv6 (Error: Cannot find module 'ajv/dist/compile/codegen')
|
||||
// - ajv@6 (fork-ts-checker-webpack-plugin, terser-webpack-plugin, webpack)
|
||||
// - ajv@8 (babel-loader)
|
||||
// Solution is to use pnpm or run fix it via npm ex.(npm dedupe --force)
|
||||
packageManager: selectedPm === 'npm' ? 'pnpm' : selectedPm,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
function addLibImport(appName: string, libName: string, importPath?: string) {
|
||||
const content = readFile(`apps/${appName}/src/main.ts`);
|
||||
if (importPath) {
|
||||
updateFile(
|
||||
`apps/${appName}/src/main.ts`,
|
||||
`
|
||||
import { ${libName} } from '${importPath}';
|
||||
${content}
|
||||
console.log(${libName}());
|
||||
`
|
||||
);
|
||||
} else {
|
||||
updateFile(
|
||||
`apps/${appName}/src/main.ts`,
|
||||
`
|
||||
import { ${libName} } from '@${proj}/${libName}';
|
||||
${content}
|
||||
console.log(${libName}());
|
||||
`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function runE2eTests(appName: string, port: number) {
|
||||
process.env.PORT = `${port}`;
|
||||
const result = runCLI(`e2e ${appName}-e2e --verbose`);
|
||||
expect(result).toContain('Setting up...');
|
||||
expect(result).toContain('Tearing down..');
|
||||
expect(result).toContain('Successfully ran target e2e');
|
||||
|
||||
await killPort(port);
|
||||
process.env.PORT = '';
|
||||
}
|
||||
|
||||
describe('frameworks', () => {
|
||||
const testLib1 = uniq('test1');
|
||||
const testLib2 = uniq('test2');
|
||||
const expressApp = uniq('expressapp');
|
||||
const fastifyApp = uniq('fastifyapp');
|
||||
const koaApp = uniq('koaapp');
|
||||
const nestApp = uniq('nest');
|
||||
let expressPort: number;
|
||||
let fastifyPort: number;
|
||||
let koaPort: number;
|
||||
let nestPort: number;
|
||||
|
||||
beforeAll(async () => {
|
||||
expressPort = await reservePort();
|
||||
fastifyPort = await reservePort();
|
||||
koaPort = await reservePort();
|
||||
nestPort = await reservePort();
|
||||
runCLI(
|
||||
`generate @nx/node:lib libs/${testLib1} --linter=eslint --unitTestRunner=jest --buildable=false`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:lib libs/${testLib2} --importPath=@acme/test2 --linter=eslint --unitTestRunner=jest --buildable=false`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${expressApp} --framework=express --port=${expressPort} --no-interactive --linter=eslint --unitTestRunner=jest --e2eTestRunner=jest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${fastifyApp} --framework=fastify --port=${fastifyPort} --no-interactive --linter=eslint --unitTestRunner=jest --e2eTestRunner=jest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${koaApp} --framework=koa --port=${koaPort} --no-interactive --linter=eslint --unitTestRunner=jest --e2eTestRunner=jest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${nestApp} --framework=nest --port=${nestPort} --bundler=webpack --no-interactive --linter=eslint --unitTestRunner=jest --e2eTestRunner=jest --verbose`
|
||||
);
|
||||
|
||||
addLibImport(expressApp, testLib1);
|
||||
addLibImport(expressApp, testLib2, '@acme/test2');
|
||||
addLibImport(fastifyApp, testLib1);
|
||||
addLibImport(fastifyApp, testLib2, '@acme/test2');
|
||||
addLibImport(koaApp, testLib1);
|
||||
addLibImport(koaApp, testLib2, '@acme/test2');
|
||||
|
||||
addLibImport(nestApp, testLib1);
|
||||
addLibImport(nestApp, testLib2, '@acme/test2');
|
||||
});
|
||||
|
||||
it('should generate an app defaults using webpack or esbuild', async () => {
|
||||
// Use esbuild by default
|
||||
checkFilesDoNotExist(`apps/${expressApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${fastifyApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${koaApp}/webpack.config.js`);
|
||||
|
||||
// Uses only webpack
|
||||
checkFilesExist(`apps/${nestApp}/webpack.config.js`);
|
||||
|
||||
expect(() => runCLI(`lint ${expressApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}`)).not.toThrow();
|
||||
|
||||
expect(() => runCLI(`lint ${expressApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}-e2e`)).not.toThrow();
|
||||
|
||||
// Only Fastify generates with unit tests since it supports them without additional libraries.
|
||||
expect(() => runCLI(`test ${fastifyApp}`)).not.toThrow();
|
||||
|
||||
// https://github.com/nrwl/nx/issues/16601
|
||||
const nestMainContent = readFile(`apps/${nestApp}/src/main.ts`);
|
||||
updateFile(
|
||||
`apps/${nestApp}/src/main.ts`,
|
||||
`
|
||||
${nestMainContent}
|
||||
// Make sure this is not replaced during build time
|
||||
console.log('env: ' + process.env['NODE_ENV']);
|
||||
`
|
||||
);
|
||||
runCLI(`build ${nestApp}`);
|
||||
expect(readFile(`dist/apps/${nestApp}/main.js`)).toContain(
|
||||
`'env: ' + process.env['NODE_ENV']`
|
||||
);
|
||||
}, 300_000);
|
||||
|
||||
it('should e2e test express app', async () => {
|
||||
await runE2eTests(expressApp, expressPort);
|
||||
});
|
||||
|
||||
it('should e2e test fastify app', async () => {
|
||||
await runE2eTests(fastifyApp, fastifyPort);
|
||||
});
|
||||
|
||||
it('should e2e test koa app', async () => {
|
||||
await runE2eTests(koaApp, koaPort);
|
||||
});
|
||||
|
||||
it('should e2e test nest app', async () => {
|
||||
await runE2eTests(nestApp, nestPort);
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a Dockerfile', async () => {
|
||||
const expressApp = 'docker-express-app'; // needs to be consistent for the Dockerfile snapshot
|
||||
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${expressApp} --framework=express --docker --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
checkFilesExist(`apps/${expressApp}/Dockerfile`);
|
||||
}, 300_000);
|
||||
|
||||
it('should support waitUntilTargets for serve target', async () => {
|
||||
const nodeApp1 = uniq('nodeapp1');
|
||||
const nodeApp2 = uniq('nodeapp2');
|
||||
const nodeApp1Port = await reservePort();
|
||||
const nodeApp2Port = await reservePort();
|
||||
|
||||
// Set ports to avoid conflicts with other tests that might run in parallel
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${nodeApp1} --framework=none --no-interactive --port=${nodeApp1Port} --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${nodeApp2} --framework=none --no-interactive --port=${nodeApp2Port} --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
updateJson(join('apps', nodeApp1, 'project.json'), (config) => {
|
||||
config.targets.serve.options.waitUntilTargets = [`${nodeApp2}:build`];
|
||||
return config;
|
||||
});
|
||||
|
||||
await runCommandUntil(`serve ${nodeApp1} `, (output) =>
|
||||
output.includes('Hello World')
|
||||
);
|
||||
|
||||
checkFilesExist(`dist/apps/${nodeApp1}/main.js`);
|
||||
checkFilesExist(`dist/apps/${nodeApp2}/main.js`);
|
||||
}, 300_000);
|
||||
|
||||
it('should wait for the previous process to exit on watch restart so the port is released', async () => {
|
||||
const app = uniq('slowshutdown');
|
||||
const port = await reservePort();
|
||||
|
||||
runCLI(
|
||||
`generate @nx/node:app apps/${app} --framework=none --no-interactive --port=${port} --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// Server with a slow graceful shutdown that holds the port after SIGTERM.
|
||||
// A watch-mode restart must wait for it to exit, otherwise the new
|
||||
// process fails with EADDRINUSE. The 8s hold exceeds the 5s kill grace
|
||||
// period, so the executor force-kills it rather than waiting forever.
|
||||
updateFile(
|
||||
`apps/${app}/src/main.ts`,
|
||||
`
|
||||
import { createServer } from 'http';
|
||||
const server = createServer((req, res) => res.end('ok'));
|
||||
server.listen(${port}, () => console.log('Listening on ${port} pid ' + process.pid));
|
||||
process.on('SIGTERM', () => {
|
||||
setTimeout(() => process.exit(0), 8000);
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
const p = await runCommandUntil(`serve ${app}`, (output) =>
|
||||
output.includes(`Listening on ${port}`)
|
||||
);
|
||||
|
||||
try {
|
||||
const restarted = new Promise<void>((resolve, reject) => {
|
||||
let output = '';
|
||||
const timeoutId = setTimeout(() => {
|
||||
reject(new Error(`Server did not restart. Output:\n${output}`));
|
||||
}, 120_000);
|
||||
const check = (chunk: Buffer) => {
|
||||
output += stripVTControlCharacters(chunk.toString());
|
||||
if (output.includes('EADDRINUSE')) {
|
||||
clearTimeout(timeoutId);
|
||||
reject(new Error(`Restarted server hit EADDRINUSE:\n${output}`));
|
||||
} else if (output.includes(`Listening on ${port}`)) {
|
||||
clearTimeout(timeoutId);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
p.stdout?.on('data', check);
|
||||
p.stderr?.on('data', check);
|
||||
});
|
||||
|
||||
// Trigger a watch rebuild and restart.
|
||||
updateFile(`apps/${app}/src/main.ts`, (content) =>
|
||||
content.replace(`res.end('ok')`, `res.end('ok v2')`)
|
||||
);
|
||||
|
||||
await restarted;
|
||||
} finally {
|
||||
await killProcessAndPorts(p.pid, port);
|
||||
}
|
||||
}, 300_000);
|
||||
});
|
||||
Reference in New Issue
Block a user