diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index b6fb1dba74d9..d966b9cbbdf0 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -17,7 +17,7 @@ "fix:prettier": "prettier --write \"{suites,utils}/**/*.ts\"", "type-check": "tsc", "pretest": "run-s --silent prisma:init", - "test": "jest --forceExit", + "test": "ts-node ./utils/run-tests.ts", "test:watch": "yarn test --watch" }, "dependencies": { @@ -34,8 +34,7 @@ "mongodb-memory-server-global": "^7.6.3", "mysql": "^2.18.1", "nock": "^13.1.0", - "pg": "^8.7.3", - "portfinder": "^1.0.28" + "pg": "^8.7.3" }, "config": { "mongodbMemoryServer": { diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index ad1bb0d4b188..153cbcb794d2 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -5,9 +5,9 @@ import { logger, parseSemver } from '@sentry/utils'; import axios, { AxiosRequestConfig } from 'axios'; import { Express } from 'express'; import * as http from 'http'; +import { AddressInfo } from 'net'; import nock from 'nock'; import * as path from 'path'; -import { getPortPromise } from 'portfinder'; export type TestServerConfig = { url: string; @@ -151,11 +151,9 @@ export class TestEnv { } }); - void getPortPromise().then(port => { - const url = `http://localhost:${port}/test`; - const server = app.listen(port, () => { - resolve([server, url]); - }); + const server = app.listen(0, () => { + const url = `http://localhost:${(server.address() as AddressInfo).port}/test`; + resolve([server, url]); }); }); diff --git a/packages/node-integration-tests/utils/run-tests.ts b/packages/node-integration-tests/utils/run-tests.ts new file mode 100644 index 000000000000..f29aa1a2399b --- /dev/null +++ b/packages/node-integration-tests/utils/run-tests.ts @@ -0,0 +1,63 @@ +/* eslint-disable no-console */ +import childProcess from 'child_process'; +import os from 'os'; + +// This variable will act as a job queue that is consumed by a number of worker threads. Each item represents a test to run. +const testPaths = childProcess.execSync('jest --listTests', { encoding: 'utf8' }).trim().split('\n'); + +const numTests = testPaths.length; +const fails: string[] = []; + +// We're creating a worker for each CPU core. +const workers = os.cpus().map(async (_, i) => { + while (testPaths.length > 0) { + const testPath = testPaths.pop(); + console.log(`(Worker ${i}) Running test "${testPath}"`); + await new Promise(resolve => { + const jestProcess = childProcess.spawn('jest', ['--runTestsByPath', testPath as string, '--forceExit']); + + // We're collecting the output and logging it all at once instead of inheriting stdout and stderr, so that + // test outputs of the individual workers aren't interwoven, in case they print at the same time. + let output = ''; + + jestProcess.stdout.on('data', (data: Buffer) => { + output = output + data.toString(); + }); + + jestProcess.stderr.on('data', (data: Buffer) => { + output = output + data.toString(); + }); + + jestProcess.on('error', error => { + console.log(output); + console.log(`(Worker ${i}) Error in test "${testPath}"`, error); + fails.push(`FAILED: "${testPath}"`); + resolve(); + }); + + jestProcess.on('exit', exitcode => { + console.log(`(Worker ${i}) Finished test "${testPath}"`); + console.log(output); + if (exitcode !== 0) { + fails.push(`FAILED: "${testPath}"`); + } + resolve(); + }); + }); + } +}); + +void Promise.all(workers).then(() => { + console.log('-------------------'); + console.log(`Successfully ran ${numTests} tests.`); + if (fails.length > 0) { + console.log('Not all tests succeeded:\n'); + fails.forEach(fail => { + console.log(`● ${fail}`); + }); + process.exit(1); + } else { + console.log('All tests succeeded.'); + process.exit(0); + } +}); diff --git a/packages/remix/package.json b/packages/remix/package.json index eb92b97feeb7..85b3e3ed6ab7 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -34,7 +34,8 @@ }, "devDependencies": { "@remix-run/node": "^1.4.3", - "@remix-run/react": "^1.4.3" + "@remix-run/react": "^1.4.3", + "portfinder": "^1.0.28" }, "peerDependencies": { "@remix-run/node": "1.x",