From 64839b6bf78973867c186993777122e22b289d0a Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Wed, 11 Jan 2023 13:23:38 -0500 Subject: [PATCH] split unit tests (#16455) Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> --- .circleci/config.yml | 84 ++++++++++--- .depcheckrc.yml | 1 + .eslintrc.js | 4 + codecov.yml | 16 +++ coverage-targets.js | 20 +++ development/jest.config.js | 16 +-- jest.config.js | 42 +++---- nyc.config.js | 6 - package.json | 17 ++- test/merge-coverage.js | 245 +++++++++++++++++++++++++++++++++++++ test/run-unit-tests.js | 192 +++++++++++++++++++++++++++++ test/test-unit-combined.sh | 11 -- test/test-unit-jest.sh | 10 -- yarn.lock | 95 +------------- 14 files changed, 580 insertions(+), 179 deletions(-) create mode 100644 codecov.yml create mode 100644 coverage-targets.js delete mode 100644 nyc.config.js create mode 100644 test/merge-coverage.js create mode 100644 test/run-unit-tests.js delete mode 100755 test/test-unit-combined.sh delete mode 100755 test/test-unit-jest.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 6203b04e0..d6a80404b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,6 +16,7 @@ executors: orbs: gh: circleci/github-cli@2.0 + codecov: codecov/codecov@3.2.2 workflows: test_and_release: @@ -93,9 +94,20 @@ workflows: - test-e2e-chrome-mv3: requires: - prep-build-test-mv3 - - test-unit: + - test-unit-mocha: requires: - prep-deps + - test-unit-jest-main: + requires: + - prep-deps + - test-unit-jest-development: + requires: + - prep-deps + - upload-and-validate-coverage: + requires: + - test-unit-jest-main + - test-unit-jest-development + - test-unit-mocha - test-unit-global: requires: - prep-deps @@ -127,8 +139,11 @@ workflows: - test-lint-shellcheck - test-lint-lockfile - test-lint-changelog - - test-unit + - test-unit-jest-main + - test-unit-jest-development - test-unit-global + - test-unit-mocha + - upload-and-validate-coverage - validate-source-maps - validate-source-maps-beta - validate-source-maps-flask @@ -823,9 +838,6 @@ jobs: - store_artifacts: path: coverage destination: coverage - - store_artifacts: - path: jest-coverage - destination: jest-coverage - store_artifacts: path: test-artifacts destination: test-artifacts @@ -902,8 +914,8 @@ jobs: git config user.email metamaskbot@users.noreply.github.com yarn ts-migration:dashboard:deploy - test-unit: - executor: node-browsers + test-unit-mocha: + executor: node-browsers-medium-plus steps: - checkout - attach_workspace: @@ -911,24 +923,60 @@ jobs: - run: name: test:coverage:mocha command: yarn test:coverage:mocha - - run: - name: test:coverage:jest - command: yarn test:coverage:jest - - run: - name: Validate coverage thresholds - command: | - if ! git diff --exit-code jest.config.js development/jest.config.js; then - echo "Detected changes in coverage thresholds" - exit 1 - fi - persist_to_workspace: root: . paths: - .nyc_output - coverage - - jest-coverage + + test-unit-jest-development: + executor: node-browsers + steps: + - checkout + - attach_workspace: + at: . + - run: + name: jest development unit tests + command: yarn test:coverage:jest:dev + - persist_to_workspace: + root: . + paths: + - coverage - store_test_results: path: test/test-results/junit.xml + + test-unit-jest-main: + executor: node-browsers-medium-plus + parallelism: 12 + steps: + - checkout + - attach_workspace: + at: . + - run: + name: test:coverage:jest + command: yarn test:coverage:jest + - persist_to_workspace: + root: . + paths: + - coverage + - store_test_results: + path: test/test-results/junit.xml + + upload-and-validate-coverage: + executor: node-browsers + steps: + - checkout + - attach_workspace: + at: . + - codecov/upload + - run: + name: test:coverage:validate + command: yarn test:coverage:validate + - persist_to_workspace: + root: . + paths: + - coverage + test-unit-global: executor: node-browsers steps: diff --git a/.depcheckrc.yml b/.depcheckrc.yml index 9ac706b11..ebb12c486 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -36,6 +36,7 @@ ignores: - 'source-map-explorer' # development tool - 'improved-yarn-audit' + - 'nyc' # storybook - '@storybook/core' - '@storybook/addon-essentials' diff --git a/.eslintrc.js b/.eslintrc.js index f2a5be01e..ad7822de4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,6 +42,8 @@ module.exports = { 'test/e2e/**/*.js', 'test/helpers/*.js', 'test/lib/wait-until-called.js', + 'test/run-unit-tests.js', + 'test/merge-coverage.js', ], extends: [ path.resolve(__dirname, '.eslintrc.base.js'), @@ -337,6 +339,8 @@ module.exports = { 'development/**/*.js', 'test/e2e/benchmark.js', 'test/helpers/setup-helper.js', + 'test/run-unit-tests.js', + 'test/merge-coverage.js', ], rules: { 'node/no-process-exit': 'off', diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..6f7608e07 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,16 @@ +codecov: + bot: 'codecov-io' + require_ci_to_pass: yes +coverage: + round: nearest + status: + project: + global: + target: auto + threshold: 0% + base: auto + transforms: + target: 100% + threshold: 0% + paths: + - development/build/transforms/**/*.js diff --git a/coverage-targets.js b/coverage-targets.js new file mode 100644 index 000000000..5b464d2fc --- /dev/null +++ b/coverage-targets.js @@ -0,0 +1,20 @@ +// Codecov uses a yaml file for its configuration and it targets line coverage. +// To keep our policy in place we have thile file separate from our +// codecov.yml file that specifies coverage targets for each project in the +// codecov.yml file. These targets are read by the test/merge-coverage.js +// script, and the paths from the codecov.yml file are used to figure out which +// subset of files to check against these targets. +module.exports = { + global: { + branches: 20, + functions: 30, + lines: 57, + statements: 40, + }, + transforms: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, +}; diff --git a/development/jest.config.js b/development/jest.config.js index fe594713e..d95157a49 100644 --- a/development/jest.config.js +++ b/development/jest.config.js @@ -1,19 +1,11 @@ module.exports = { displayName: '/development', - collectCoverageFrom: ['/**/*.js'], - coverageDirectory: '../jest-coverage/development/', - coverageReporters: ['html', 'text-summary', 'json-summary'], - coverageThreshold: { - './development/build/transforms/**/*.js': { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - }, + collectCoverageFrom: ['/build/transforms/**/*.js'], + coverageDirectory: '../coverage', + coverageReporters: ['json'], resetMocks: true, restoreMocks: true, testEnvironment: 'node', - testMatch: ['/build/**/*.test.js'], + testMatch: ['/build/transforms/**/*.test.js'], testTimeout: 2500, }; diff --git a/jest.config.js b/jest.config.js index 7db4dee25..1df970cb5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,33 +1,19 @@ module.exports = { collectCoverageFrom: [ + '/app/scripts/constants/error-utils.js', + '/app/scripts/controllers/network/**/*.js', '/app/scripts/controllers/permissions/**/*.js', + '/app/scripts/flask/**/*.js', + '/app/scripts/lib/**/*.js', '/app/scripts/lib/createRPCMethodTrackingMiddleware.js', + '/app/scripts/migrations/*.js', + '/app/scripts/platforms/*.js', '/shared/**/*.js', '/ui/**/*.js', ], - coverageDirectory: './jest-coverage/main', + coverageDirectory: './coverage', coveragePathIgnorePatterns: ['.stories.js', '.snap'], - coverageReporters: ['html', 'text-summary', 'json-summary'], - coverageThreshold: { - global: { - branches: 48, - functions: 46, - lines: 52, - statements: 52, - }, - './app/scripts/controllers/permissions/**/*.js': { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - './app/scripts/lib/createRPCMethodTrackingMiddleware.js': { - branches: 95.65, - functions: 100, - lines: 100, - statements: 100, - }, - }, + coverageReporters: ['json'], reporters: [ 'default', [ @@ -48,16 +34,16 @@ module.exports = { ], setupFilesAfterEnv: ['/test/jest/setup.js'], testMatch: [ - '/ui/**/*.test.js', - '/shared/**/*.test.js', - '/app/scripts/lib/**/*.test.js', - '/app/scripts/migrations/*.test.js', - '/app/scripts/platforms/*.test.js', + '/app/scripts/constants/error-utils.test.js', '/app/scripts/controllers/network/**/*.test.js', '/app/scripts/controllers/permissions/**/*.test.js', '/app/scripts/flask/**/*.test.js', + '/app/scripts/lib/**/*.test.js', '/app/scripts/lib/createRPCMethodTrackingMiddleware.test.js', - '/app/scripts/constants/error-utils.test.js', + '/app/scripts/migrations/*.test.js', + '/app/scripts/platforms/*.test.js', + '/shared/**/*.test.js', + '/ui/**/*.test.js', ], testTimeout: 2500, // We have to specify the environment we are running in, which is jsdom. The diff --git a/nyc.config.js b/nyc.config.js deleted file mode 100644 index ee1e2055a..000000000 --- a/nyc.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - branches: 95, - lines: 95, - functions: 95, - statements: 95, -}; diff --git a/package.json b/package.json index d937059bf..f7bd5a657 100644 --- a/package.json +++ b/package.json @@ -26,17 +26,21 @@ "dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'", "forwarder": "node ./development/static-server.js ./node_modules/@metamask/forwarder/dist/ --port 9010", "dapp-forwarder": "concurrently -k -n forwarder,dapp -p '[{time}][{name}]' 'yarn forwarder' 'yarn dapp'", - "test:unit": "./test/test-unit-combined.sh", + "test:unit": "node ./test/run-unit-tests.js --mocha --jestGlobal --jestDev", "test:unit:jest": "./test/test-unit-jest.sh", "test:unit:global": "mocha test/unit-global/*.test.js", - "test:unit:mocha": "mocha './app/**/*.test.js'", + "test:unit:mocha": "node ./test/run-unit-tests.js --mocha", "test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js", "test:e2e:chrome:snaps": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --snaps", "test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js", "test:e2e:firefox:snaps": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js --snaps", "test:e2e:single": "node test/e2e/run-e2e-test.js", - "test:coverage:mocha": "nyc --reporter=text --reporter=html yarn test:unit:mocha", - "test:coverage:jest": "yarn test:unit:jest --coverage --maxWorkers=2 && yarn jest-it-up -m 5", + "test:coverage:mocha": "node ./test/run-unit-tests.js --mocha --coverage", + "test:coverage:jest": "node ./test/run-unit-tests.js --jestGlobal --coverage", + "test:coverage:jest:dev": "node ./test/run-unit-tests.js --jestDev --coverage", + "test:coverage:validate": "node ./test/merge-coverage.js", + "test:coverage": "node ./test/run-unit-tests.js --mocha --jestGlobal --jestDev --coverage && yarn test:coverage:validate", + "test:coverage:html": "yarn test:coverage --html", "ganache:start": "./development/run-ganache.sh", "sentry:publish": "node ./development/sentry-publish.js", "lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:tsc && yarn lint:styles", @@ -447,10 +451,13 @@ "history": "^5.0.0", "improved-yarn-audit": "^3.0.0", "ini": "^3.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.5", "jest": "^29.1.2", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "^29.1.2", - "jest-it-up": "^2.0.2", + "js-yaml": "^4.1.0", "jsdom": "^11.2.0", "koa": "^2.7.0", "lavamoat": "^6.2.0", diff --git a/test/merge-coverage.js b/test/merge-coverage.js new file mode 100644 index 000000000..1059e6d4c --- /dev/null +++ b/test/merge-coverage.js @@ -0,0 +1,245 @@ +const fs = require('fs'); +const path = require('path'); +const libCoverage = require('istanbul-lib-coverage'); +const libReport = require('istanbul-lib-report'); +const reports = require('istanbul-reports'); +const glob = require('fast-glob'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); +const yaml = require('js-yaml'); +const codecovTargets = require('../coverage-targets'); + +const codecovConfig = yaml.load(fs.readFileSync('codecov.yml', 'utf8')); + +const COVERAGE_DIR = './coverage/'; + +/** + * Load .json file at path and parse it into a javascript object + * + * @param {string} filePath - path to the file to load + * @returns {object} the JavaScript object parsed from the file + */ +function loadData(filePath) { + const json = fs.readFileSync(filePath); + return JSON.parse(json); +} + +/** + * Loads an array of json coverage files and merges them into a final coverage + * report. + * + * @param {string[]} files - array of strings that are paths to files + * @returns {libCoverage.CoverageMap} CoverageMap + */ +function mergeCoverageMaps(files) { + const coverageMap = libCoverage.createCoverageMap({}); + + files.forEach((covergeFinalFile) => { + coverageMap.merge(loadData(covergeFinalFile)); + }); + + return coverageMap; +} + +/** + * Given a target directory and a coverageMap generates a finalized coverage + * summary report and saves it to the directory. + * + * @param {string} dir - target directory + * @param {libCoverage.CoverageMap} coverageMap - CoverageMap to report on + * @param reportType + * @param reportOptions + */ +function generateSummaryReport(dir, coverageMap, reportType, reportOptions) { + const context = libReport.createContext({ + dir, + coverageMap, + }); + + reports.create(reportType, reportOptions ?? {}).execute(context); +} + +/** + * Generates a multiline string with coverage data + * + * @param {CoverageTarget} target - Target coverage threshold + * @param {import('istanbul-lib-coverage').CoverageSummaryData} actual - + * istanbul coverage summary detailing actual summary + * @returns {string} multiline report of coverage + */ +function generateConsoleReport(target, actual) { + const { lines, branches, functions, statements } = actual.data; + const breakdown = + `Lines: ${lines.covered}/${lines.total} (${lines.pct}%). Target: ${target.lines}%\n` + + `Branches: ${branches.covered}/${branches.total} (${branches.pct}%). Target: ${target.branches}%\n` + + `Statements: ${statements.covered}/${statements.total} (${statements.pct}%). Target: ${target.statements}%\n` + + `Functions: ${functions.covered}/${functions.total} (${functions.pct}%). Target: ${target.functions}%`; + return breakdown; +} + +/** + * @typedef {object} CoverageTarget + * @property {number} lines - percentage of lines that must be covered + * @property {number} statements - percentage of statements that must be covered + * @property {number} branches - percentage of branches that must be covered + * @property {number} functions - percentage of functions that must be covered + */ + +/** + * Checks if the coverage meets target + * + * @param {CoverageTarget} target + * @param {import('istanbul-lib-coverage').CoverageSummaryData} actual + * @returns {boolean} + */ +function isCoverageInsufficient(target, actual) { + const lineCoverageNotMet = actual.lines.pct < target.lines; + const branchCoverageNotMet = actual.branches.pct < target.branches; + const functionCoverageNotMet = actual.functions.pct < target.functions; + const statementCoverageNotMet = actual.statements.pct < target.statements; + return ( + lineCoverageNotMet || + branchCoverageNotMet || + functionCoverageNotMet || + statementCoverageNotMet + ); +} + +/** + * Checks if the coverage should be bumped up + * + * @param {CoverageTarget} target + * @param {import('istanbul-lib-coverage').CoverageSummaryData} actual + * @returns {boolean} + */ +function shouldCoverageBeBumped(target, actual) { + const lineCoverageNeedsBumped = actual.lines.pct > target.lines + 5; + const branchCoverageNeedsBumped = actual.branches.pct > target.branches + 5; + const functionCoverageNeedsBumped = + actual.functions.pct > target.functions + 5; + const statementCoverageNeedsBumped = + actual.statements.pct > target.statements + 5; + return ( + lineCoverageNeedsBumped || + branchCoverageNeedsBumped || + functionCoverageNeedsBumped || + statementCoverageNeedsBumped + ); +} + +/** + * Creates and returns a combined coverage summary report of every file in the + * provided array. + * + * @param {string[]} files - array of files generated by fast-glob + * @param {libCoverage.CoverageMap} coverageMap + * @returns {import('istanbul-lib-coverage').CoverageSummaryData} + */ +function getFileCoverage(files, coverageMap) { + const subCoverageMap = libCoverage.createCoverageMap({}); + + files.forEach((file) => { + try { + subCoverageMap.merge( + coverageMap.fileCoverageFor(`${process.cwd()}/${file}`), + ); + } catch { + // If the coverage doesn't exist, it means that it was excluded from + // coverage or had no coverage to report, which is fine. Glob is a lot + // wider of a net then what the test file runners match against. + } + }); + + const summary = subCoverageMap.getCoverageSummary(); + return summary; +} + +/** + * Checks coverage and reports to console + * Throws an error if coverage isn't met + * + * @param {string} name - The target's name from coverageThresholds in jest + * config + * @param {CoverageTarget} target - the target coverage threshold + * @param {import('istanbul-lib-coverage').CoverageSummaryData} actual - + * istanbul coverage summary representing actual coverage + */ +function checkCoverage(name, target, actual) { + const breakdown = generateConsoleReport(target, actual); + if (isCoverageInsufficient(target, actual)) { + const errorMsg = `Coverage thresholds for ${name} NOT met\n${breakdown}`; + throw new Error(errorMsg); + } else if (shouldCoverageBeBumped(target, actual)) { + const errorMsg = `Coverage EXCEEDS threshold for ${name} and must be bumped\n${breakdown}`; + throw new Error(errorMsg); + } + console.log(`Coverage thresholds for ${name} met\n${breakdown}\n\n`); +} + +/** + * Primary script function + */ +async function start() { + const { + argv: { html }, + } = yargs(hideBin(process.argv)).usage( + '$0 [options]', + 'Run unit tests on the application code.', + (yargsInstance) => + yargsInstance + .option('html', { + alias: ['h'], + default: false, + description: 'Generate HTML report', + type: 'boolean', + }) + .strict(), + ); + // First get all of the files matching the pattern coverage-final-${n}.json + // from the coverage directory + const files = fs.readdirSync(COVERAGE_DIR); + const filePaths = files + .filter( + (file) => + path.basename(file).startsWith('coverage-final') && + path.extname(file) === '.json', + ) + .map((file) => path.join(COVERAGE_DIR, file)); + + // Next, generate a coverageMap + const coverageMap = mergeCoverageMaps(filePaths, true); + + // Persist this to file, which may eventually be used in more steps + generateSummaryReport(COVERAGE_DIR, coverageMap, 'json-summary'); + if (html) { + generateSummaryReport(COVERAGE_DIR, coverageMap, 'html'); + } + + // Use the keys in coverageThreshold in jest config to determine targets + const coverageTargets = Object.keys(codecovConfig.coverage.status.project); + + // Check coverage totals for each target + coverageTargets.forEach((target) => { + const summary = + target === 'global' + ? coverageMap.getCoverageSummary() + : getFileCoverage( + glob.sync([ + ...codecovConfig.coverage.status.project[target].paths, + // checking test file coverage is redundant. + '!**/*.test.js', + '!**/__mocks__/**/*.js', + '!**/*.stories.js', + ]), + coverageMap, + ); + // Check and validate the coverage + checkCoverage(target, codecovTargets[target], summary); + }); +} + +start().catch((error) => { + // Report the errored coverage check + console.error(error); + process.exit(1); +}); diff --git a/test/run-unit-tests.js b/test/run-unit-tests.js new file mode 100644 index 000000000..a58a858c0 --- /dev/null +++ b/test/run-unit-tests.js @@ -0,0 +1,192 @@ +const { hideBin } = require('yargs/helpers'); +const yargs = require('yargs/yargs'); +const { runCommand, runInShell } = require('../development/lib/run-command'); + +const { CIRCLE_NODE_INDEX, CIRCLE_NODE_TOTAL } = process.env; + +const GLOBAL_JEST_CONFIG = './jest.config.js'; +const DEVELOPMENT_JEST_CONFIG = './development/jest.config.js'; + +start().catch((error) => { + console.error(error); + process.exit(1); +}); + +/** + * @typedef {object} JestParams + * @property {'global' | 'dev'} target - Which configuration to use for Jest. + * @property {boolean} [coverage] - Whether to collect coverage during testing. + * @property {number} [currentShard] - Current process number when using test + * splitting across many processes. + * @property {number} [totalShards] - Total number of processes tests will be + * split across. + * @property {number} [maxWorkers] - Total number of workers to use when + * running tests. + */ + +/** + * Execute jest test runner with given params + * + * @param {JestParams} params - Configuration for jest test runner + */ +async function runJest( + { target, coverage, currentShard, totalShards, maxWorkers } = { + target: 'global', + coverage: false, + currentShard: 1, + totalShards: 1, + maxWorkers: 2, + }, +) { + const options = [ + 'jest', + `--config=${ + target === 'global' ? GLOBAL_JEST_CONFIG : DEVELOPMENT_JEST_CONFIG + }`, + ]; + options.push(`--maxWorkers=${maxWorkers}`); + if (coverage) { + options.push('--coverage'); + } + // We use jest's new 'shard' feature to run tests in parallel across many + // different processes if totalShards > 1 + if (totalShards > 1) { + options.push(`--shard=${currentShard}/${totalShards}`); + } + await runInShell('yarn', options); + if (coverage) { + // Once done we rename the coverage file so that it is unique among test + // runners and job number + await runCommand('mv', [ + './coverage/coverage-final.json', + `./coverage/coverage-final-${target}-${currentShard}.json`, + ]); + } +} + +/** + * Run mocha tests on the app directory. Mocha tests do not yet support + * parallelism / test-splitting. + * + * @param {boolean} coverage - Use nyc to collect coverage + */ +async function runMocha({ coverage }) { + const options = ['mocha', './app/**/*.test.js']; + // If coverage is true, then we need to run nyc as the first command + // and mocha after, so we use unshift to add three options to the beginning + // of the options array. + if (coverage) { + options.unshift('nyc', '--reporter=json', 'yarn'); + } + await runInShell('yarn', options); + if (coverage) { + // Once done we rename the coverage file so that it is unique among test + // runners + await runCommand('mv', [ + './coverage/coverage-final.json', + `./coverage/coverage-final-mocha.json`, + ]); + } +} + +async function start() { + const { + argv: { mocha, jestGlobal, jestDev, coverage, fakeParallelism, maxWorkers }, + } = yargs(hideBin(process.argv)).usage( + '$0 [options]', + 'Run unit tests on the application code.', + (yargsInstance) => + yargsInstance + .option('mocha', { + alias: ['m'], + default: false, + description: 'Run Mocha tests', + type: 'boolean', + }) + .option('jestDev', { + alias: ['d'], + default: false, + description: 'Run Jest tests with development folder config', + type: 'boolean', + }) + .option('jestGlobal', { + alias: ['g'], + default: false, + demandOption: false, + description: 'Run Jest global (primary config) tests', + type: 'boolean', + }) + .option('coverage', { + alias: ['c'], + default: true, + demandOption: false, + description: 'Collect coverage', + type: 'boolean', + }) + .option('fakeParallelism', { + alias: ['f'], + default: 0, + demandOption: false, + description: + 'Pretend to be CircleCI and fake parallelism (use at your own risk)', + type: 'number', + }) + .option('maxWorkers', { + alias: ['mw'], + default: 2, + demandOption: false, + description: + 'The safer way to increase performance locally, sets the number of processes to use internally. Recommended 2', + type: 'number', + }) + .strict(), + ); + + const circleNodeIndex = parseInt(CIRCLE_NODE_INDEX ?? '0', 10); + const circleNodeTotal = parseInt(CIRCLE_NODE_TOTAL ?? '1', 10); + + const maxProcesses = fakeParallelism > 0 ? fakeParallelism : circleNodeTotal; + const currentProcess = circleNodeIndex; + + if (fakeParallelism) { + console.log( + `Using fake parallelism of ${fakeParallelism}. Your machine may become as useful as a brick during this operation.`, + ); + if (jestGlobal && jestDev) { + throw new Error( + 'Do not try to run both jest test configs with fakeParallelism, bad things could happen.', + ); + } else if (mocha) { + throw new Error('Test splitting is not supported for mocha yet.'); + } else { + const processes = []; + for (let x = 0; x < fakeParallelism; x++) { + processes.push( + runJest({ + target: jestGlobal ? 'global' : 'dev', + totalShards: fakeParallelism, + currentShard: x + 1, + maxWorkers: 1, // ignore maxWorker option on purpose + }), + ); + } + await Promise.all(processes); + } + } else { + const options = { + coverage, + currentShard: currentProcess + 1, + totalShards: maxProcesses, + maxWorkers, + }; + if (mocha) { + await runMocha(options); + } + if (jestDev) { + await runJest({ target: 'dev', ...options }); + } + if (jestGlobal) { + await runJest({ target: 'global', ...options }); + } + } +} diff --git a/test/test-unit-combined.sh b/test/test-unit-combined.sh deleted file mode 100755 index d793ba752..000000000 --- a/test/test-unit-combined.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -x -set -e -set -u -set -o pipefail - -concurrently --raw -n mocha,jest,global \ - "yarn test:unit:mocha" \ - "yarn test:unit:jest" \ - "yarn test:unit:global" diff --git a/test/test-unit-jest.sh b/test/test-unit-jest.sh deleted file mode 100755 index 384124920..000000000 --- a/test/test-unit-jest.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -set -x -set -e -set -u -set -o pipefail - -concurrently -n production,development \ - "jest --config=./jest.config.js $*" \ - "jest --config=./development/jest.config.js $*" diff --git a/yarn.lock b/yarn.lock index e1fddd678..f397b4701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2621,44 +2621,6 @@ __metadata: languageName: node linkType: hard -"@inquirer/confirm@npm:^0.0.14-alpha.0": - version: 0.0.14-alpha.0 - resolution: "@inquirer/confirm@npm:0.0.14-alpha.0" - dependencies: - "@inquirer/core": ^0.0.15-alpha.0 - "@inquirer/input": ^0.0.15-alpha.0 - chalk: ^4.1.1 - checksum: 84daf47030318e0adf6eeef85388e341f6f68c0c06c0d1d329cb6185f6d21abf74c3124102bf62f4f42145c8dc9193d719209e3759987a1bdbcfb88139b9936c - languageName: node - linkType: hard - -"@inquirer/core@npm:^0.0.15-alpha.0": - version: 0.0.15-alpha.0 - resolution: "@inquirer/core@npm:0.0.15-alpha.0" - dependencies: - ansi-escapes: ^4.2.1 - chalk: ^4.1.1 - cli-spinners: ^2.6.0 - cli-width: ^3.0.0 - lodash: ^4.17.21 - mute-stream: ^0.0.8 - run-async: ^2.3.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: 26a94a8db80e57f926c889b5730c6c9a0f18d6bf1e6dbfb68eaa6bcda33550db66118ce6afd9e91130ea2e4da42b6d7236b94c16fbab931ed04b4f442b9a5c78 - languageName: node - linkType: hard - -"@inquirer/input@npm:^0.0.15-alpha.0": - version: 0.0.15-alpha.0 - resolution: "@inquirer/input@npm:0.0.15-alpha.0" - dependencies: - "@inquirer/core": ^0.0.15-alpha.0 - chalk: ^4.1.1 - checksum: c294b2fa100e2955e271b1b0590b5f360c65c560d653f39554cd381f145fdf13be9ea2c3c069d39a71f84fc3c8ba20d9d78a0b210f8cb5533d138567029e28ee - languageName: node - linkType: hard - "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.0.0 resolution: "@istanbuljs/load-nyc-config@npm:1.0.0" @@ -8363,13 +8325,6 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:^4.1.0": - version: 4.1.1 - resolution: "ansi-colors@npm:4.1.1" - checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1": version: 4.3.0 resolution: "ansi-escapes@npm:4.3.0" @@ -11227,7 +11182,7 @@ __metadata: languageName: node linkType: hard -"cli-spinners@npm:^2.5.0, cli-spinners@npm:^2.6.0": +"cli-spinners@npm:^2.5.0": version: 2.6.1 resolution: "cli-spinners@npm:2.6.1" checksum: 423409baaa7a58e5104b46ca1745fbfc5888bbd0b0c5a626e052ae1387060839c8efd512fb127e25769b3dc9562db1dc1b5add6e0b93b7ef64f477feb6416a45 @@ -11247,13 +11202,6 @@ __metadata: languageName: node linkType: hard -"cli-width@npm:^3.0.0": - version: 3.0.0 - resolution: "cli-width@npm:3.0.0" - checksum: 4c94af3769367a70e11ed69aa6095f1c600c0ff510f3921ab4045af961820d57c0233acfa8b6396037391f31b4c397e1f614d234294f979ff61430a6c166c3f6 - languageName: node - linkType: hard - "cliui@npm:^3.2.0": version: 3.2.0 resolution: "cliui@npm:3.2.0" @@ -11614,13 +11562,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^9.0.0": - version: 9.2.0 - resolution: "commander@npm:9.2.0" - checksum: 7c82e4cd969712aa6d7c055b8351807a7230f9f31ef7ec7881e11a1147511de85adf5d6ccfd200240a118eecf693b220caf6865b8efbcea558a70d35aa9ed711 - languageName: node - linkType: hard - "comment-parser@npm:1.3.1": version: 1.3.1 resolution: "comment-parser@npm:1.3.1" @@ -20149,7 +20090,7 @@ __metadata: languageName: node linkType: hard -"istanbul-reports@npm:^3.0.0, istanbul-reports@npm:^3.1.3": +"istanbul-reports@npm:^3.0.0, istanbul-reports@npm:^3.1.3, istanbul-reports@npm:^3.1.5": version: 3.1.5 resolution: "istanbul-reports@npm:3.1.5" dependencies: @@ -20437,19 +20378,6 @@ __metadata: languageName: node linkType: hard -"jest-it-up@npm:^2.0.2": - version: 2.0.2 - resolution: "jest-it-up@npm:2.0.2" - dependencies: - "@inquirer/confirm": ^0.0.14-alpha.0 - ansi-colors: ^4.1.0 - commander: ^9.0.0 - bin: - jest-it-up: bin/jest-it-up - checksum: 1482d8b9862331b6cc7d858fd1c05d95fb1bc2adbc37e243c20e6e10f259fecf600d3b1023dadf11acadb158ea171677f8f375969e058dff46002be81a49079b - languageName: node - linkType: hard - "jest-junit@npm:^14.0.1": version: 14.0.1 resolution: "jest-junit@npm:14.0.1" @@ -23126,11 +23054,14 @@ __metadata: improved-yarn-audit: ^3.0.0 ini: ^3.0.0 is-retry-allowed: ^2.2.0 + istanbul-lib-coverage: ^3.2.0 + istanbul-lib-report: ^3.0.0 + istanbul-reports: ^3.1.5 jest: ^29.1.2 jest-canvas-mock: ^2.3.1 jest-environment-jsdom: ^29.1.2 - jest-it-up: ^2.0.2 jest-junit: ^14.0.1 + js-yaml: ^4.1.0 jsdom: ^11.2.0 json-rpc-engine: ^6.1.0 json-rpc-middleware-stream: ^2.1.1 @@ -23985,13 +23916,6 @@ __metadata: languageName: node linkType: hard -"mute-stream@npm:^0.0.8": - version: 0.0.8 - resolution: "mute-stream@npm:0.0.8" - checksum: ff48d251fc3f827e5b1206cda0ffdaec885e56057ee86a3155e1951bc940fd5f33531774b1cc8414d7668c10a8907f863f6561875ee6e8768931a62121a531a1 - languageName: node - linkType: hard - "nan@npm:^2.12.1, nan@npm:^2.13.2, nan@npm:^2.14.0": version: 2.15.0 resolution: "nan@npm:2.15.0" @@ -29048,13 +28972,6 @@ __metadata: languageName: node linkType: hard -"run-async@npm:^2.3.0": - version: 2.4.1 - resolution: "run-async@npm:2.4.1" - checksum: a2c88aa15df176f091a2878eb840e68d0bdee319d8d97bbb89112223259cebecb94bc0defd735662b83c2f7a30bed8cddb7d1674eb48ae7322dc602b22d03797 - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.1.9 resolution: "run-parallel@npm:1.1.9"