355 lines
10 KiB
TypeScript
355 lines
10 KiB
TypeScript
import {
|
|
checkFilesExist,
|
|
cleanupProject,
|
|
getSelectedPackageManager,
|
|
getPackageManagerCommand,
|
|
killPorts,
|
|
newProject,
|
|
promisifiedTreeKill,
|
|
readJson,
|
|
runCLI,
|
|
runCommand,
|
|
runCommandUntil,
|
|
tmpProjPath,
|
|
uniq,
|
|
updateFile,
|
|
updateJson,
|
|
reservePort,
|
|
} from '@nx/e2e-utils';
|
|
import { execSync } from 'child_process';
|
|
import * as http from 'http';
|
|
|
|
let originalEnvPort;
|
|
|
|
describe('Node Applications', () => {
|
|
const pm = getSelectedPackageManager();
|
|
let workspaceName: string;
|
|
|
|
beforeAll(() => {
|
|
originalEnvPort = process.env.PORT;
|
|
workspaceName = newProject({
|
|
packages: [
|
|
'@nx/node',
|
|
'@nx/express',
|
|
'@nx/nest',
|
|
'@nx/webpack',
|
|
'@nx/jest',
|
|
],
|
|
preset: 'ts',
|
|
});
|
|
});
|
|
|
|
afterAll(() => {
|
|
process.env.PORT = originalEnvPort;
|
|
cleanupProject();
|
|
});
|
|
|
|
it('should be able to generate an empty application', async () => {
|
|
const nodeapp = uniq('nodeapp');
|
|
const port = await reservePort();
|
|
process.env.PORT = `${port}`;
|
|
runCLI(
|
|
`generate @nx/node:app apps/${nodeapp} --port=${port} --linter=eslint --unitTestRunner=jest`
|
|
);
|
|
|
|
expect(() => runCLI(`lint ${nodeapp}`)).not.toThrow();
|
|
expect(() => runCLI(`test ${nodeapp}`)).not.toThrow();
|
|
|
|
updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`);
|
|
runCLI(`build ${nodeapp}`);
|
|
|
|
checkFilesExist(`apps/${nodeapp}/dist/main.js`);
|
|
const result = execSync(`node apps/${nodeapp}/dist/main.js`, {
|
|
cwd: tmpProjPath(),
|
|
}).toString();
|
|
expect(result).toContain('Hello World!');
|
|
await killPorts(port);
|
|
}, 300_000);
|
|
|
|
// TODO(jack): fix and re-enable
|
|
xit('should be able to generate an express application', async () => {
|
|
const nodeapp = uniq('nodeapp');
|
|
const nodelib = uniq('nodelib');
|
|
const port = await reservePort();
|
|
process.env.PORT = `${port}`;
|
|
runCLI(
|
|
`generate @nx/express:app apps/${nodeapp} --port=${port} --linter=eslint --unitTestRunner=jest`
|
|
);
|
|
runCLI(
|
|
`generate @nx/node:lib packages/${nodelib} --linter=eslint --unitTestRunner=jest`
|
|
);
|
|
|
|
// No tests are generated by default, add a stub one.
|
|
updateFile(
|
|
`apps/${nodeapp}/src/app/test.spec.ts`,
|
|
`
|
|
describe('test', () => {
|
|
it('should work', () => {
|
|
expect(true).toEqual(true);
|
|
})
|
|
})
|
|
`
|
|
);
|
|
|
|
updateFile(`apps/${nodeapp}/src/assets/file.txt`, `Test`);
|
|
updateFile(`apps/${nodeapp}/src/main.ts`, (content) => {
|
|
return `import { ${nodelib} } from '@${workspaceName}/${nodelib}';\n${content}\nconsole.log(${nodelib}());`;
|
|
});
|
|
// pnpm does not link packages unless they are deps
|
|
// npm, yarn, and bun will link them in the root node_modules regardless
|
|
if (pm === 'pnpm') {
|
|
updateJson(`apps/${nodeapp}/package.json`, (json) => {
|
|
json.dependencies = {
|
|
[`@${workspaceName}/${nodelib}`]: 'workspace:*',
|
|
};
|
|
return json;
|
|
});
|
|
runCommand(`cd apps/${nodeapp} && ${getPackageManagerCommand().install}`);
|
|
}
|
|
runCLI(`sync`);
|
|
|
|
expect(() => runCLI(`lint ${nodeapp}`)).not.toThrow();
|
|
expect(() => runCLI(`test ${nodeapp}`)).not.toThrow();
|
|
expect(() => runCLI(`build ${nodeapp}`)).not.toThrow();
|
|
expect(() => runCLI(`typecheck ${nodeapp}`)).not.toThrow();
|
|
expect(() => runCLI(`lint ${nodelib}`)).not.toThrow();
|
|
expect(() => runCLI(`test ${nodelib}`)).not.toThrow();
|
|
expect(() => runCLI(`build ${nodelib}`)).not.toThrow();
|
|
expect(() => runCLI(`typecheck ${nodelib}`)).not.toThrow();
|
|
|
|
const p = await runCommandUntil(
|
|
`serve ${nodeapp}`,
|
|
(output) => output.includes(`Listening at http://localhost:${port}`),
|
|
|
|
{
|
|
env: {
|
|
NX_DAEMON: 'true',
|
|
},
|
|
}
|
|
);
|
|
|
|
let result = await getData(port);
|
|
expect(result.message).toMatch(`Welcome to ${nodeapp}!`);
|
|
|
|
result = await getData(port, '/assets/file.txt');
|
|
expect(result).toMatch(`Test`);
|
|
|
|
try {
|
|
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
|
expect(await killPorts(port)).toBeTruthy();
|
|
} catch (err) {
|
|
console.log(
|
|
'Error during cleanup (may be expected, especially ECONNRESET):',
|
|
err.message
|
|
);
|
|
}
|
|
}, 300_000);
|
|
|
|
it('should be able to generate a nest application', async () => {
|
|
const nestapp = uniq('nodeapp');
|
|
const port = await reservePort();
|
|
process.env.PORT = `${port}`;
|
|
runCLI(
|
|
`generate @nx/nest:app apps/${nestapp} --linter=eslint --unitTestRunner=jest`
|
|
);
|
|
|
|
expect(() => runCLI(`lint ${nestapp}`)).not.toThrow();
|
|
expect(() => runCLI(`test ${nestapp}`)).not.toThrow();
|
|
|
|
runCLI(`build ${nestapp}`);
|
|
checkFilesExist(`apps/${nestapp}/dist/main.js`);
|
|
|
|
const p = await runCommandUntil(
|
|
`serve ${nestapp}`,
|
|
(output) =>
|
|
output.includes(
|
|
`Application is running on: http://localhost:${port}/api`
|
|
),
|
|
|
|
{
|
|
env: {
|
|
NX_DAEMON: 'true',
|
|
},
|
|
}
|
|
);
|
|
|
|
const result = await getData(port, '/api');
|
|
expect(result.message).toMatch('Hello');
|
|
|
|
try {
|
|
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
|
expect(await killPorts(port)).toBeTruthy();
|
|
} catch (err) {
|
|
console.log(
|
|
'Error during cleanup (may be expected, especially ECONNRESET):',
|
|
err.message
|
|
);
|
|
}
|
|
}, 300_000);
|
|
|
|
// TODO(jack): fix and re-enable
|
|
xit('should be able to import a lib into a nest application', async () => {
|
|
const nestApp = uniq('nestapp');
|
|
const nestLib = uniq('nestlib');
|
|
|
|
const port = await reservePort();
|
|
process.env.PORT = `${port}`;
|
|
runCLI(`generate @nx/nest:app apps/${nestApp} --no-interactive`);
|
|
|
|
runCLI(`generate @nx/nest:lib packages/${nestLib} --no-interactive`);
|
|
|
|
updateFile(`apps/${nestApp}/src/app/app.module.ts`, (content) => {
|
|
return `import '@${workspaceName}/${nestLib}';\n${content}\n`;
|
|
});
|
|
|
|
if (pm === 'pnpm') {
|
|
updateJson(`apps/${nestApp}/package.json`, (json) => {
|
|
json.dependencies = {
|
|
[`@${workspaceName}/${nestLib}`]: 'workspace:*',
|
|
};
|
|
return json;
|
|
});
|
|
runCommand(`${getPackageManagerCommand().install}`);
|
|
}
|
|
runCLI(`sync`);
|
|
|
|
runCLI(`build ${nestApp} --verbose`);
|
|
checkFilesExist(`apps/${nestApp}/dist/main.js`);
|
|
|
|
const p = await runCommandUntil(
|
|
`serve ${nestApp}`,
|
|
(output) =>
|
|
output.includes(
|
|
`Application is running on: http://localhost:${port}/api`
|
|
),
|
|
|
|
{
|
|
env: {
|
|
NX_DAEMON: 'true',
|
|
},
|
|
}
|
|
);
|
|
|
|
const result = await getData(port, '/api');
|
|
expect(result.message).toMatch('Hello');
|
|
|
|
try {
|
|
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
|
expect(await killPorts(port)).toBeTruthy();
|
|
} catch (err) {
|
|
console.log(
|
|
'Error during cleanup (may be expected, especially ECONNRESET):',
|
|
err.message
|
|
);
|
|
}
|
|
}, 300_000);
|
|
|
|
// Dependencies that are not directly imported but are still required for the build to succeed
|
|
// App -> LibA -> LibB
|
|
// LibB is transitive, it's not imported in the app, but is required by LibA
|
|
|
|
it('should be able to work with transitive non-dependencies', async () => {
|
|
const nestApp = uniq('nestApp');
|
|
const nestLibA = uniq('nestliba');
|
|
const nestLibB = uniq('nestlibb');
|
|
const port = await reservePort();
|
|
|
|
process.env.PORT = `${port}`;
|
|
runCLI(`generate @nx/nest:app apps/${nestApp} --no-interactive`);
|
|
|
|
runCLI(`generate @nx/nest:lib packages/${nestLibA} --no-interactive`);
|
|
runCLI(`generate @nx/nest:lib packages/${nestLibB} --no-interactive`);
|
|
|
|
if (pm === 'pnpm') {
|
|
updateJson(`apps/${nestApp}/package.json`, (json) => {
|
|
json.dependencies = {
|
|
[`@${workspaceName}/${nestLibA}`]: 'workspace:*',
|
|
};
|
|
return json;
|
|
});
|
|
|
|
updateJson(`packages/${nestLibA}/package.json`, (json) => {
|
|
json.dependencies = {
|
|
[`@${workspaceName}/${nestLibB}`]: 'workspace:*',
|
|
};
|
|
return json;
|
|
});
|
|
runCommand(`${getPackageManagerCommand().install}`);
|
|
}
|
|
runCLI(`sync`);
|
|
|
|
runCLI(`build ${nestApp} --verbose`);
|
|
checkFilesExist(`apps/${nestApp}/dist/main.js`);
|
|
|
|
const p = await runCommandUntil(
|
|
`serve ${nestApp}`,
|
|
(output) =>
|
|
output.includes(
|
|
`Application is running on: http://localhost:${port}/api`
|
|
),
|
|
|
|
{
|
|
env: {
|
|
NX_DAEMON: 'true',
|
|
},
|
|
}
|
|
);
|
|
|
|
const result = await getData(port, '/api');
|
|
expect(result.message).toMatch('Hello');
|
|
|
|
try {
|
|
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
|
expect(await killPorts(port)).toBeTruthy();
|
|
} catch (err) {
|
|
console.log(
|
|
'Error during cleanup (may be expected, especially ECONNRESET):',
|
|
err.message
|
|
);
|
|
}
|
|
}, 300_000);
|
|
|
|
it('should respect and support generating libraries with a name different than the import path', () => {
|
|
const nodeLib = uniq('node-lib');
|
|
const nestLib = uniq('nest-lib');
|
|
|
|
runCLI(
|
|
`generate @nx/node:lib packages/${nodeLib} --name=${nodeLib} --buildable`
|
|
);
|
|
runCLI(
|
|
`generate @nx/nest:lib packages/${nestLib} --name=${nestLib} --buildable`
|
|
);
|
|
|
|
const packageJson = readJson(`packages/${nodeLib}/package.json`);
|
|
expect(packageJson.nx.name).toBe(nodeLib);
|
|
const nestPackageJson = readJson(`packages/${nestLib}/package.json`);
|
|
expect(nestPackageJson.nx.name).toBe(nestLib);
|
|
|
|
expect(runCLI(`build ${nodeLib}`)).toContain(
|
|
`Successfully ran target build for project ${nodeLib}`
|
|
);
|
|
expect(runCLI(`build ${nestLib}`)).toContain(
|
|
`Successfully ran target build for project ${nestLib}`
|
|
);
|
|
}, 300_000);
|
|
});
|
|
|
|
function getData(port, path = '/api'): Promise<any> {
|
|
return new Promise((resolve) => {
|
|
http.get(`http://localhost:${port}${path}`, (res) => {
|
|
expect(res.statusCode).toEqual(200);
|
|
let data = '';
|
|
res.on('data', (chunk) => {
|
|
data += chunk;
|
|
});
|
|
res.once('end', () => {
|
|
try {
|
|
resolve(JSON.parse(data));
|
|
} catch (e) {
|
|
resolve(data);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|