Что я тестирую : конечные точки экспресс-сервера
Моя цель : автоматизировать тестирование API в одном скрипте
Что я делаю : я запускаю экспресс-сервер в дочернем процессе NodeJS и хотел бы дождаться его запуска до запуска набора тестов (тестирование конечных точек frisby.js)
Что не работает должным образом : тестовый пакет запускается до разрешения Promise
Я полагаюсь на пакет wait-on
, который сервер опрашивает и разрешает, когда ресурс (ы) становится доступным.
const awaitServer = async () => {
await waitOn({
resources: [`http://localhost:${PORT}`],
interval: 1000,
}).then(() => {
console.log('Server is running, launching test suite now!');
});
};
Эта функция используется в функции startServer:
const startServer = async () => {
console.log(`Launching server http://localhost:${PORT} ...`);
// npmRunScripts is a thin wrapper around child_process.exec to easily access node_modules/.bin like in package.json scripts
await npmRunScripts(
`cross-env PORT=${PORT} node -r ts-node/register -r dotenv/config src/index.ts dotenv_config_path=.env-tests`
);
await awaitServer();
}
И, наконец, я использую что-то вроде
describe('Endpoints' () => {
beforeAll(startTestServer);
// describes and tests here ...
});
В любом случае, когда я запускаю jest, 'Server is running, launching test suite now!'
console.log никогда не появляется, и набор тестов не выполняется (так как сервер еще не запущен). Почему Jest начинает тестирование, поскольку awaitServer
, очевидно, еще не разрешен?
Функция npmRunScripts
работает нормально, так как тестовый сервер работает и работает некоторое время после того, как тесты не пройдены. Ради этого вопроса вот как разрешается npmRunScripts:
// From https://humanwhocodes.com/blog/2016/03/mimicking-npm-script-in-node-js/
const { exec } = require('child_process');
const { delimiter, join } = require('path');
const env = { ...process.env };
const binPath = join(__dirname, '../..', 'node_modules', '.bin');
env.PATH = `${binPath}${delimiter}${env.PATH}`;
/**
* Executes a CLI command with `./node_modules/.bin` in the scope like you
* would use in the `scripts` sections of a `package.json`
* @param cmd The actual command
*/
const npmRunScripts = (cmd, resolveProcess = false) =>
new Promise((resolve, reject) => {
if (typeof cmd !== 'string') {
reject(
new TypeError(
`npmRunScripts Error: cmd is a "${typeof cmd}", "string" expected.`
)
);
return;
}
if (cmd === '') {
reject(
new Error(`npmRunScripts Error: No command provided (cmd is empty).`)
);
return;
}
const subProcess = exec(
cmd,
{ cwd: process.cwd(), env }
);
if (resolveProcess) {
resolve(subProcess);
} else {
const cleanUp = () => {
subProcess.stdout.removeAllListeners();
subProcess.stderr.removeAllListeners();
};
subProcess.stdout.on('data', (data) => {
resolve(data);
cleanUp();
});
subProcess.stderr.on('data', (data) => {
reject(data);
cleanUp();
});
}
});
module.exports = npmRunScripts;