diff --git a/.circleci/config.yml b/.circleci/config.yml index f6614bab3220..b28f93ee3a3c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,5 +54,5 @@ workflows: name: test-node-partial-<< matrix.node-version >> matrix: parameters: - node-version: ['12', '14', '16', '17'] + node-version: ['12', '14', '16', '17', '18'] - test-jest-jasmine diff --git a/.eslintignore b/.eslintignore index 48fa766a507d..36c1940c60f0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,14 +1,19 @@ +!.* **/coverage/** **/node_modules/** bin/ -flow-typed/** packages/*/build/** packages/*/dist/** -packages/jest-diff/src/cleanupSemantic.ts website/.docusaurus website/blog website/build website/node_modules website/i18n/*.js website/static -!.eslintrc.js + +# Third-party script +packages/jest-diff/src/cleanupSemantic.ts + +**/.yarn +**/.pnp.* + diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 94% rename from .eslintrc.js rename to .eslintrc.cjs index 4d69f9235bad..44d9227dfa97 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -5,11 +5,21 @@ * LICENSE file in the root directory of this source tree. */ -const {getPackages} = require('./scripts/buildUtils'); +const fs = require('fs'); +const path = require('path'); +const {sync: readPkg} = require('read-pkg'); -const internalPackages = getPackages() - .map(({pkg}) => pkg.name) - .sort(); +function getPackages() { + const PACKAGES_DIR = path.resolve(__dirname, 'packages'); + const packages = fs + .readdirSync(PACKAGES_DIR) + .map(file => path.resolve(PACKAGES_DIR, file)) + .filter(f => fs.lstatSync(path.resolve(f)).isDirectory()); + return packages.map(packageDir => { + const pkg = readPkg({cwd: packageDir}); + return pkg.name; + }); +} module.exports = { env: { @@ -91,8 +101,6 @@ module.exports = { 'packages/expect/src/print.ts', 'packages/expect/src/toThrowMatchers.ts', 'packages/expect-utils/src/utils.ts', - 'packages/jest-core/src/ReporterDispatcher.ts', - 'packages/jest-core/src/TestScheduler.ts', 'packages/jest-core/src/collectHandles.ts', 'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts', 'packages/jest-haste-map/src/index.ts', @@ -176,12 +184,6 @@ module.exports = { 'import/order': 'off', }, }, - { - files: 'packages/jest-types/**/*', - rules: { - 'import/no-extraneous-dependencies': 'off', - }, - }, { files: 'packages/**/*.ts', rules: { @@ -235,10 +237,12 @@ module.exports = { }, { files: [ + 'e2e/**', 'website/**', + '**/__benchmarks__/**', '**/__tests__/**', - 'e2e/**', - '**/pretty-format/perf/**', + 'packages/jest-types/**/*', + '.eslintplugin/**', ], rules: { 'import/no-extraneous-dependencies': 'off', @@ -254,11 +258,12 @@ module.exports = { }, { env: {node: true}, - files: ['*.js', '*.jsx'], + files: ['*.js', '*.jsx', '*.mjs', '*.cjs'], }, { files: [ 'scripts/*', + 'packages/*/__benchmarks__/test.js', 'packages/jest-cli/src/init/index.ts', 'packages/jest-repl/src/cli/runtime-cli.ts', ], @@ -270,14 +275,10 @@ module.exports = { files: [ 'e2e/**', 'examples/**', - 'scripts/*', 'website/**', '**/__mocks__/**', '**/__tests__/**', '**/__typetests__/**', - '**/__performance_tests__/**', - 'packages/diff-sequences/perf/index.js', - 'packages/pretty-format/perf/test.js', ], rules: { '@typescript-eslint/no-unused-vars': 'off', @@ -325,7 +326,7 @@ module.exports = { 'scripts/**', 'babel.config.js', 'testSetupFile.js', - '.eslintrc.js', + '.eslintrc.cjs', ], }, ], @@ -365,7 +366,6 @@ module.exports = { 'no-bitwise': 'warn', 'no-caller': 'error', 'no-case-declarations': 'off', - 'no-catch-shadow': 'error', 'no-class-assign': 'warn', 'no-cond-assign': 'off', 'no-confusing-arrow': 'off', @@ -467,7 +467,7 @@ module.exports = { 'no-unused-expressions': 'off', 'no-unused-vars': ['error', {argsIgnorePattern: '^_'}], 'no-use-before-define': 'off', - 'no-useless-call': 'warn', + 'no-useless-call': 'error', 'no-useless-computed-key': 'error', 'no-useless-concat': 'error', 'no-var': 'error', @@ -507,7 +507,9 @@ module.exports = { 'import/ignore': ['react-native'], // using `new RegExp` makes sure to escape `/` 'import/internal-regex': new RegExp( - internalPackages.map(pkg => `^${pkg}$`).join('|'), + getPackages() + .map(pkg => `^${pkg}$`) + .join('|'), ).source, 'import/resolver': { typescript: {}, diff --git a/.gitattributes b/.gitattributes index 176a458f94e0..6313b56c5784 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* text=auto +* text=auto eol=lf diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml index f98fa818cc25..2bd97a356e2c 100644 --- a/.github/workflows/close-stale.yml +++ b/.github/workflows/close-stale.yml @@ -8,7 +8,7 @@ jobs: name: 'Close month old issues and PRs' runs-on: ubuntu-latest steps: - - uses: actions/stale@v4 + - uses: actions/stale@v5 with: start-date: '2022-01-01T00:00:00Z' stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.' @@ -27,7 +27,7 @@ jobs: name: 'Close year old issues and PRs' runs-on: ubuntu-latest steps: - - uses: actions/stale@v4 + - uses: actions/stale@v5 with: stale-issue-message: 'This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.' stale-pr-message: 'This PR is stale because it has been open 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.' diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 7fdbb93b150c..125896112f59 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -38,8 +38,8 @@ jobs: YARN_ENABLE_SCRIPTS: false run: yarn --immutable - lint-and-typecheck: - name: Running TypeScript compiler & ESLint + typecheck: + name: Running TypeScript compiler runs-on: ubuntu-latest needs: prepare-yarn-cache @@ -55,8 +55,24 @@ jobs: run: yarn build - name: test typings run: yarn test-types - - name: verify TypeScript@4.2 compatibility + - name: verify TypeScript@4.3 compatibility run: yarn verify-old-ts + + lint: + name: Running Lint + runs-on: ubuntu-latest + needs: prepare-yarn-cache + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: lts/* + cache: yarn + - name: install + run: yarn --immutable + - name: build + run: yarn build:js - name: verify Yarn PnP compatibility run: yarn verify-pnp - name: run eslint @@ -66,12 +82,27 @@ jobs: - name: check copyright headers run: yarn check-copyright-headers + yarn-validate: + name: Validate Yarn dependencies and constraints + runs-on: ubuntu-latest + needs: prepare-yarn-cache + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: lts/* + cache: yarn + - name: 'Check for unmet constraints (fix w/ "yarn constraints --fix")' + run: yarn constraints + - name: 'Check for duplicate dependencies (fix w/ "yarn dedupe")' + run: yarn dedupe --check + test: name: Node v${{ matrix.node-version }} on ${{ matrix.os }} (${{ matrix.shard }}) strategy: fail-fast: false matrix: - node-version: [12.x, 14.x, 16.x, 17.x] + node-version: [12.x, 14.x, 16.x, 17.x, 18.x] os: [ubuntu-latest, macOS-latest, windows-latest] shard: ['1/4', '2/4', '3/4', '4/4'] runs-on: ${{ matrix.os }} @@ -80,9 +111,7 @@ jobs: steps: - name: Set git config shell: bash - run: | - git config --global core.autocrlf false - git config --global core.symlinks true + run: git config --global core.symlinks true if: runner.os == 'Windows' - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} @@ -113,9 +142,7 @@ jobs: steps: - name: Set git config shell: bash - run: | - git config --global core.autocrlf false - git config --global core.symlinks true + run: git config --global core.symlinks true if: runner.os == 'Windows' - uses: actions/checkout@v3 - name: Use Node.js LTS @@ -158,12 +185,12 @@ jobs: uses: SimenB/github-actions-cpu-cores@v1 - name: run tests with coverage run: | - yarn jest-coverage --color --config jest.config.ci.js --max-workers ${{ steps.cpu-cores.outputs.count }} --shard=${{ matrix.shard }} + yarn jest-coverage --color --config jest.config.ci.mjs --max-workers ${{ steps.cpu-cores.outputs.count }} --shard=${{ matrix.shard }} yarn test-leak - name: map coverage - run: node ./scripts/mapCoverage.js + run: node ./scripts/mapCoverage.mjs if: always() - - uses: codecov/codecov-action@v2 + - uses: codecov/codecov-action@v3 if: always() with: directory: ./coverage diff --git a/.prettierignore b/.prettierignore index 9d00ca73631b..0d516049dd16 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,26 @@ -fixtures/failing-jsons/ +.idea +.DS_STORE +.eslintcache +*.swp +*~ +api-extractor.json +coverage + +/packages/*/build +/packages/*/dist +/packages/jest-config/src/__tests__/jest-preset.json +/packages/pretty-format/__benchmarks__/world.geo.json + +# Breaks tests +/e2e/coverage-handlebars/greet.hbs + +# Third-party script packages/jest-diff/src/cleanupSemantic.ts -packages/jest-config/src/__tests__/jest-preset.json -packages/pretty-format/perf/world.geo.json -website/versions.json + +/website/.docusaurus +/website/backers.json +/website/build +/website/versions.json + +**/.yarn +**/.pnp.* diff --git a/.vscode/settings.json b/.vscode/settings.json index bb5f7b2d11d9..3a5ae317697f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.rulers": [80], "files.exclude": { - "**/.git": true, + "**/.git": true }, "javascript.validate.enable": false, "jest.pathToJest": "yarn jest --", diff --git a/.yarn/patches/react-native-npm-0.68.1-8830b7be0d.patch b/.yarn/patches/react-native-npm-0.68.1-8830b7be0d.patch new file mode 100644 index 000000000000..28b1e00527da --- /dev/null +++ b/.yarn/patches/react-native-npm-0.68.1-8830b7be0d.patch @@ -0,0 +1,18 @@ +diff --git a/jest/setup.js b/jest/setup.js +index 5bc65447594091c2da6dadb4be6bc1c6da43c7a0..356e7ff3eb55947e084db5d1df9b2d9f0544a15b 100644 +--- a/jest/setup.js ++++ b/jest/setup.js +@@ -17,13 +17,8 @@ jest.requireActual('@react-native/polyfills/error-guard'); + + global.__DEV__ = true; + +-global.performance = { +- now: jest.fn(Date.now), +-}; +- + global.Promise = jest.requireActual('promise'); + global.regeneratorRuntime = jest.requireActual('regenerator-runtime/runtime'); +-global.window = global; + + global.requestAnimationFrame = function (callback) { + return setTimeout(callback, 0); diff --git a/.yarn/plugins/@yarnpkg/plugin-constraints.cjs b/.yarn/plugins/@yarnpkg/plugin-constraints.cjs new file mode 100644 index 000000000000..f3b0db0c0243 --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-constraints.cjs @@ -0,0 +1,52 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-constraints", +factory: function (require) { +var plugin=(()=>{var Li=Object.create,Je=Object.defineProperty;var Hi=Object.getOwnPropertyDescriptor;var Gi=Object.getOwnPropertyNames;var Yi=Object.getPrototypeOf,Ui=Object.prototype.hasOwnProperty;var Zi=r=>Je(r,"__esModule",{value:!0});var I=(r,u)=>()=>(u||r((u={exports:{}}).exports,u),u.exports),Qi=(r,u)=>{for(var p in u)Je(r,p,{get:u[p],enumerable:!0})},Ji=(r,u,p)=>{if(u&&typeof u=="object"||typeof u=="function")for(let c of Gi(u))!Ui.call(r,c)&&c!=="default"&&Je(r,c,{get:()=>u[c],enumerable:!(p=Hi(u,c))||p.enumerable});return r},G=r=>Ji(Zi(Je(r!=null?Li(Yi(r)):{},"default",r&&r.__esModule&&"default"in r?{get:()=>r.default,enumerable:!0}:{value:r,enumerable:!0})),r);var Xr=I((Nu,_r)=>{var Ki;(function(r){var u=function(){return{"append/2":[new r.type.Rule(new r.type.Term("append",[new r.type.Var("X"),new r.type.Var("L")]),new r.type.Term("foldl",[new r.type.Term("append",[]),new r.type.Var("X"),new r.type.Term("[]",[]),new r.type.Var("L")]))],"append/3":[new r.type.Rule(new r.type.Term("append",[new r.type.Term("[]",[]),new r.type.Var("X"),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("append",[new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("S")])]),new r.type.Term("append",[new r.type.Var("T"),new r.type.Var("X"),new r.type.Var("S")]))],"member/2":[new r.type.Rule(new r.type.Term("member",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("_")])]),null),new r.type.Rule(new r.type.Term("member",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("Xs")])]),new r.type.Term("member",[new r.type.Var("X"),new r.type.Var("Xs")]))],"permutation/2":[new r.type.Rule(new r.type.Term("permutation",[new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("permutation",[new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("permutation",[new r.type.Var("T"),new r.type.Var("P")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("P")]),new r.type.Term("append",[new r.type.Var("X"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("Y")]),new r.type.Var("S")])])]))],"maplist/2":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("X")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("Xs")])]))],"maplist/3":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs")])]))],"maplist/4":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs")])]))],"maplist/5":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds")])]))],"maplist/6":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es")])]))],"maplist/7":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")]),new r.type.Term(".",[new r.type.Var("F"),new r.type.Var("Fs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E"),new r.type.Var("F")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es"),new r.type.Var("Fs")])]))],"maplist/8":[new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("A"),new r.type.Var("As")]),new r.type.Term(".",[new r.type.Var("B"),new r.type.Var("Bs")]),new r.type.Term(".",[new r.type.Var("C"),new r.type.Var("Cs")]),new r.type.Term(".",[new r.type.Var("D"),new r.type.Var("Ds")]),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Es")]),new r.type.Term(".",[new r.type.Var("F"),new r.type.Var("Fs")]),new r.type.Term(".",[new r.type.Var("G"),new r.type.Var("Gs")])]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P"),new r.type.Var("A"),new r.type.Var("B"),new r.type.Var("C"),new r.type.Var("D"),new r.type.Var("E"),new r.type.Var("F"),new r.type.Var("G")]),new r.type.Term("maplist",[new r.type.Var("P"),new r.type.Var("As"),new r.type.Var("Bs"),new r.type.Var("Cs"),new r.type.Var("Ds"),new r.type.Var("Es"),new r.type.Var("Fs"),new r.type.Var("Gs")])]))],"include/3":[new r.type.Rule(new r.type.Term("include",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("include",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("A")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("A"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term("[]",[])]),new r.type.Var("B")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("F"),new r.type.Var("B")]),new r.type.Term(",",[new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("F")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("S")])]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("L"),new r.type.Var("S")])]),new r.type.Term("include",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("S")])])])])]))],"exclude/3":[new r.type.Rule(new r.type.Term("exclude",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Term("[]",[])]),null),new r.type.Rule(new r.type.Term("exclude",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("exclude",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("E")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term("[]",[])]),new r.type.Var("Q")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("R"),new r.type.Var("Q")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("R")]),new r.type.Term(",",[new r.type.Term("!",[]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("E")])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("E")])])])])])])]))],"foldl/4":[new r.type.Rule(new r.type.Term("foldl",[new r.type.Var("_"),new r.type.Term("[]",[]),new r.type.Var("I"),new r.type.Var("I")]),null),new r.type.Rule(new r.type.Term("foldl",[new r.type.Var("P"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Var("T")]),new r.type.Var("I"),new r.type.Var("R")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P"),new r.type.Var("L")]),new r.type.Term(",",[new r.type.Term("append",[new r.type.Var("L"),new r.type.Term(".",[new r.type.Var("I"),new r.type.Term(".",[new r.type.Var("H"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])])])]),new r.type.Var("L2")]),new r.type.Term(",",[new r.type.Term("=..",[new r.type.Var("P2"),new r.type.Var("L2")]),new r.type.Term(",",[new r.type.Term("call",[new r.type.Var("P2")]),new r.type.Term("foldl",[new r.type.Var("P"),new r.type.Var("T"),new r.type.Var("X"),new r.type.Var("R")])])])])]))],"select/3":[new r.type.Rule(new r.type.Term("select",[new r.type.Var("E"),new r.type.Term(".",[new r.type.Var("E"),new r.type.Var("Xs")]),new r.type.Var("Xs")]),null),new r.type.Rule(new r.type.Term("select",[new r.type.Var("E"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Ys")])]),new r.type.Term("select",[new r.type.Var("E"),new r.type.Var("Xs"),new r.type.Var("Ys")]))],"sum_list/2":[new r.type.Rule(new r.type.Term("sum_list",[new r.type.Term("[]",[]),new r.type.Num(0,!1)]),null),new r.type.Rule(new r.type.Term("sum_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("sum_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term("is",[new r.type.Var("S"),new r.type.Term("+",[new r.type.Var("X"),new r.type.Var("Y")])])]))],"max_list/2":[new r.type.Rule(new r.type.Term("max_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("max_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("max_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Var("Y")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("X")]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("Y")])])]))],"min_list/2":[new r.type.Rule(new r.type.Term("min_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("min_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("min_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term(";",[new r.type.Term(",",[new r.type.Term("=<",[new r.type.Var("X"),new r.type.Var("Y")]),new r.type.Term(",",[new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("X")]),new r.type.Term("!",[])])]),new r.type.Term("=",[new r.type.Var("S"),new r.type.Var("Y")])])]))],"prod_list/2":[new r.type.Rule(new r.type.Term("prod_list",[new r.type.Term("[]",[]),new r.type.Num(1,!1)]),null),new r.type.Rule(new r.type.Term("prod_list",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("S")]),new r.type.Term(",",[new r.type.Term("prod_list",[new r.type.Var("Xs"),new r.type.Var("Y")]),new r.type.Term("is",[new r.type.Var("S"),new r.type.Term("*",[new r.type.Var("X"),new r.type.Var("Y")])])]))],"last/2":[new r.type.Rule(new r.type.Term("last",[new r.type.Term(".",[new r.type.Var("X"),new r.type.Term("[]",[])]),new r.type.Var("X")]),null),new r.type.Rule(new r.type.Term("last",[new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("Xs")]),new r.type.Var("X")]),new r.type.Term("last",[new r.type.Var("Xs"),new r.type.Var("X")]))],"prefix/2":[new r.type.Rule(new r.type.Term("prefix",[new r.type.Var("Part"),new r.type.Var("Whole")]),new r.type.Term("append",[new r.type.Var("Part"),new r.type.Var("_"),new r.type.Var("Whole")]))],"nth0/3":[new r.type.Rule(new r.type.Term("nth0",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")])]),new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")]),new r.type.Term("!",[])])])]))],"nth1/3":[new r.type.Rule(new r.type.Term("nth1",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")])]),new r.type.Term(",",[new r.type.Term(">",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("_")]),new r.type.Term("!",[])])])]))],"nth0/4":[new r.type.Rule(new r.type.Term("nth0",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")])]),new r.type.Term(",",[new r.type.Term(">=",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(0,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term("!",[])])])]))],"nth1/4":[new r.type.Rule(new r.type.Term("nth1",[new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term(";",[new r.type.Term("->",[new r.type.Term("var",[new r.type.Var("X")]),new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")])]),new r.type.Term(",",[new r.type.Term(">",[new r.type.Var("X"),new r.type.Num(0,!1)]),new r.type.Term(",",[new r.type.Term("nth",[new r.type.Num(1,!1),new r.type.Var("X"),new r.type.Var("Y"),new r.type.Var("Z"),new r.type.Var("W")]),new r.type.Term("!",[])])])]))],"nth/5":[new r.type.Rule(new r.type.Term("nth",[new r.type.Var("N"),new r.type.Var("N"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("X"),new r.type.Var("Xs")]),null),new r.type.Rule(new r.type.Term("nth",[new r.type.Var("N"),new r.type.Var("O"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Xs")]),new r.type.Var("Y"),new r.type.Term(".",[new r.type.Var("X"),new r.type.Var("Ys")])]),new r.type.Term(",",[new r.type.Term("is",[new r.type.Var("M"),new r.type.Term("+",[new r.type.Var("N"),new r.type.Num(1,!1)])]),new r.type.Term("nth",[new r.type.Var("M"),new r.type.Var("O"),new r.type.Var("Xs"),new r.type.Var("Y"),new r.type.Var("Ys")])]))],"length/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(!r.type.is_variable(g)&&!r.type.is_integer(g))c.throw_error(r.error.type("integer",g,_.indicator));else if(r.type.is_integer(g)&&g.value<0)c.throw_error(r.error.domain("not_less_than_zero",g,_.indicator));else{var h=new r.type.Term("length",[v,new r.type.Num(0,!1),g]);r.type.is_integer(g)&&(h=new r.type.Term(",",[h,new r.type.Term("!",[])])),c.prepend([new r.type.State(w.goal.replace(h),w.substitution,w)])}},"length/3":[new r.type.Rule(new r.type.Term("length",[new r.type.Term("[]",[]),new r.type.Var("N"),new r.type.Var("N")]),null),new r.type.Rule(new r.type.Term("length",[new r.type.Term(".",[new r.type.Var("_"),new r.type.Var("X")]),new r.type.Var("A"),new r.type.Var("N")]),new r.type.Term(",",[new r.type.Term("succ",[new r.type.Var("A"),new r.type.Var("B")]),new r.type.Term("length",[new r.type.Var("X"),new r.type.Var("B"),new r.type.Var("N")])]))],"replicate/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_integer(g))c.throw_error(r.error.type("integer",g,_.indicator));else if(g.value<0)c.throw_error(r.error.domain("not_less_than_zero",g,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=new r.type.Term("[]"),T=0;T0;b--)T[b].equals(T[b-1])&&T.splice(b,1);for(var C=new r.type.Term("[]"),b=T.length-1;b>=0;b--)C=new r.type.Term(".",[T[b],C]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,g])),w.substitution,w)])}}},"msort/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else{for(var h=[],x=v;x.indicator==="./2";)h.push(x.args[0]),x=x.args[1];if(r.type.is_variable(x))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_empty_list(x))c.throw_error(r.error.type("list",v,_.indicator));else{for(var T=h.sort(r.compare),b=new r.type.Term("[]"),C=T.length-1;C>=0;C--)b=new r.type.Term(".",[T[C],b]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,g])),w.substitution,w)])}}},"keysort/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else{for(var h=[],x,T=v;T.indicator==="./2";){if(x=T.args[0],r.type.is_variable(x)){c.throw_error(r.error.instantiation(_.indicator));return}else if(!r.type.is_term(x)||x.indicator!=="-/2"){c.throw_error(r.error.type("pair",x,_.indicator));return}x.args[0].pair=x.args[1],h.push(x.args[0]),T=T.args[1]}if(r.type.is_variable(T))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_empty_list(T))c.throw_error(r.error.type("list",v,_.indicator));else{for(var b=h.sort(r.compare),C=new r.type.Term("[]"),N=b.length-1;N>=0;N--)C=new r.type.Term(".",[new r.type.Term("-",[b[N],b[N].pair]),C]),delete b[N].pair;c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,g])),w.substitution,w)])}}},"take/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g)||r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!r.type.is_integer(v))c.throw_error(r.error.type("integer",v,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=v.value,T=[],b=g;x>0&&b.indicator==="./2";)T.push(b.args[0]),b=b.args[1],x--;if(x===0){for(var C=new r.type.Term("[]"),x=T.length-1;x>=0;x--)C=new r.type.Term(".",[T[x],C]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[C,h])),w.substitution,w)])}}},"drop/3":function(c,w,_){var v=_.args[0],g=_.args[1],h=_.args[2];if(r.type.is_variable(g)||r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!r.type.is_integer(v))c.throw_error(r.error.type("integer",v,_.indicator));else if(!r.type.is_variable(h)&&!r.type.is_list(h))c.throw_error(r.error.type("list",h,_.indicator));else{for(var x=v.value,T=[],b=g;x>0&&b.indicator==="./2";)T.push(b.args[0]),b=b.args[1],x--;x===0&&c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,h])),w.substitution,w)])}},"reverse/2":function(c,w,_){var v=_.args[0],g=_.args[1],h=r.type.is_instantiated_list(v),x=r.type.is_instantiated_list(g);if(r.type.is_variable(v)&&r.type.is_variable(g))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_variable(v)&&!r.type.is_fully_list(v))c.throw_error(r.error.type("list",v,_.indicator));else if(!r.type.is_variable(g)&&!r.type.is_fully_list(g))c.throw_error(r.error.type("list",g,_.indicator));else if(!h&&!x)c.throw_error(r.error.instantiation(_.indicator));else{for(var T=h?v:g,b=new r.type.Term("[]",[]);T.indicator==="./2";)b=new r.type.Term(".",[T.args[0],b]),T=T.args[1];c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[b,h?g:v])),w.substitution,w)])}},"list_to_set/2":function(c,w,_){var v=_.args[0],g=_.args[1];if(r.type.is_variable(v))c.throw_error(r.error.instantiation(_.indicator));else{for(var h=v,x=[];h.indicator==="./2";)x.push(h.args[0]),h=h.args[1];if(r.type.is_variable(h))c.throw_error(r.error.instantiation(_.indicator));else if(!r.type.is_term(h)||h.indicator!=="[]/0")c.throw_error(r.error.type("list",v,_.indicator));else{for(var T=[],b=new r.type.Term("[]",[]),C,N=0;N=0;N--)b=new r.type.Term(".",[T[N],b]);c.prepend([new r.type.State(w.goal.replace(new r.type.Term("=",[g,b])),w.substitution,w)])}}}}},p=["append/2","append/3","member/2","permutation/2","maplist/2","maplist/3","maplist/4","maplist/5","maplist/6","maplist/7","maplist/8","include/3","exclude/3","foldl/4","sum_list/2","max_list/2","min_list/2","prod_list/2","last/2","prefix/2","nth0/3","nth1/3","nth0/4","nth1/4","length/2","replicate/3","select/3","sort/2","msort/2","keysort/2","take/3","drop/3","reverse/2","list_to_set/2"];typeof _r!="undefined"?_r.exports=function(c){r=c,new r.type.Module("lists",u(),p)}:new r.type.Module("lists",u(),p)})(Ki)});var et=I(M=>{"use strict";var Ve=process.platform==="win32",wr="aes-256-cbc",ji="sha256",Br="The current environment doesn't support interactive reading from TTY.",z=require("fs"),Fr=process.binding("tty_wrap").TTY,gr=require("child_process"),_e=require("path"),dr={prompt:"> ",hideEchoBack:!1,mask:"*",limit:[],limitMessage:"Input another, please.$<( [)limit(])>",defaultInput:"",trueValue:[],falseValue:[],caseSensitive:!1,keepWhitespace:!1,encoding:"utf8",bufferSize:1024,print:void 0,history:!0,cd:!1,phContent:void 0,preCheck:void 0},fe="none",oe,Ce,zr=!1,we,Ke,vr,es=0,hr="",Se=[],je,Wr=!1,mr=!1,$e=!1;function Lr(r){function u(p){return p.replace(/[^\w\u0080-\uFFFF]/g,function(c){return"#"+c.charCodeAt(0)+";"})}return Ke.concat(function(p){var c=[];return Object.keys(p).forEach(function(w){p[w]==="boolean"?r[w]&&c.push("--"+w):p[w]==="string"&&r[w]&&c.push("--"+w,u(r[w]))}),c}({display:"string",displayOnly:"boolean",keyIn:"boolean",hideEchoBack:"boolean",mask:"string",limit:"string",caseSensitive:"boolean"}))}function rs(r,u){function p(j){var U,Ue="",Ze;for(vr=vr||require("os").tmpdir();;){U=_e.join(vr,j+Ue);try{Ze=z.openSync(U,"wx")}catch(Qe){if(Qe.code==="EEXIST"){Ue++;continue}else throw Qe}z.closeSync(Ze);break}return U}var c,w,_,v={},g,h,x=p("readline-sync.stdout"),T=p("readline-sync.stderr"),b=p("readline-sync.exit"),C=p("readline-sync.done"),N=require("crypto"),W,ee,te;W=N.createHash(ji),W.update(""+process.pid+es+++Math.random()),te=W.digest("hex"),ee=N.createDecipher(wr,te),c=Lr(r),Ve?(w=process.env.ComSpec||"cmd.exe",process.env.Q='"',_=["/V:ON","/S","/C","(%Q%"+w+"%Q% /V:ON /S /C %Q%%Q%"+we+"%Q%"+c.map(function(j){return" %Q%"+j+"%Q%"}).join("")+" & (echo !ERRORLEVEL!)>%Q%"+b+"%Q%%Q%) 2>%Q%"+T+"%Q% |%Q%"+process.execPath+"%Q% %Q%"+__dirname+"\\encrypt.js%Q% %Q%"+wr+"%Q% %Q%"+te+"%Q% >%Q%"+x+"%Q% & (echo 1)>%Q%"+C+"%Q%"]):(w="/bin/sh",_=["-c",'("'+we+'"'+c.map(function(j){return" '"+j.replace(/'/g,"'\\''")+"'"}).join("")+'; echo $?>"'+b+'") 2>"'+T+'" |"'+process.execPath+'" "'+__dirname+'/encrypt.js" "'+wr+'" "'+te+'" >"'+x+'"; echo 1 >"'+C+'"']),$e&&$e("_execFileSync",c);try{gr.spawn(w,_,u)}catch(j){v.error=new Error(j.message),v.error.method="_execFileSync - spawn",v.error.program=w,v.error.args=_}for(;z.readFileSync(C,{encoding:r.encoding}).trim()!=="1";);return(g=z.readFileSync(b,{encoding:r.encoding}).trim())==="0"?v.input=ee.update(z.readFileSync(x,{encoding:"binary"}),"hex",r.encoding)+ee.final(r.encoding):(h=z.readFileSync(T,{encoding:r.encoding}).trim(),v.error=new Error(Br+(h?` +`+h:"")),v.error.method="_execFileSync",v.error.program=w,v.error.args=_,v.error.extMessage=h,v.error.exitCode=+g),z.unlinkSync(x),z.unlinkSync(T),z.unlinkSync(b),z.unlinkSync(C),v}function ts(r){var u,p={},c,w={env:process.env,encoding:r.encoding};if(we||(Ve?process.env.PSModulePath?(we="powershell.exe",Ke=["-ExecutionPolicy","Bypass","-File",__dirname+"\\read.ps1"]):(we="cscript.exe",Ke=["//nologo",__dirname+"\\read.cs.js"]):(we="/bin/sh",Ke=[__dirname+"/read.sh"])),Ve&&!process.env.PSModulePath&&(w.stdio=[process.stdin]),gr.execFileSync){u=Lr(r),$e&&$e("execFileSync",u);try{p.input=gr.execFileSync(we,u,w)}catch(_){c=_.stderr?(_.stderr+"").trim():"",p.error=new Error(Br+(c?` +`+c:"")),p.error.method="execFileSync",p.error.program=we,p.error.args=u,p.error.extMessage=c,p.error.exitCode=_.status,p.error.code=_.code,p.error.signal=_.signal}}else p=rs(r,w);return p.error||(p.input=p.input.replace(/^\s*'|'\s*$/g,""),r.display=""),p}function br(r){var u="",p=r.display,c=!r.display&&r.keyIn&&r.hideEchoBack&&!r.mask;function w(){var _=ts(r);if(_.error)throw _.error;return _.input}return mr&&mr(r),function(){var _,v,g;function h(){return _||(_=process.binding("fs"),v=process.binding("constants")),_}if(typeof fe=="string")if(fe=null,Ve){if(g=function(x){var T=x.replace(/^\D+/,"").split("."),b=0;return(T[0]=+T[0])&&(b+=T[0]*1e4),(T[1]=+T[1])&&(b+=T[1]*100),(T[2]=+T[2])&&(b+=T[2]),b}(process.version),!(g>=20302&&g<40204||g>=5e4&&g<50100||g>=50600&&g<60200)&&process.stdin.isTTY)process.stdin.pause(),fe=process.stdin.fd,Ce=process.stdin._handle;else try{fe=h().open("CONIN$",v.O_RDWR,parseInt("0666",8)),Ce=new Fr(fe,!0)}catch(x){}if(process.stdout.isTTY)oe=process.stdout.fd;else{try{oe=z.openSync("\\\\.\\CON","w")}catch(x){}if(typeof oe!="number")try{oe=h().open("CONOUT$",v.O_RDWR,parseInt("0666",8))}catch(x){}}}else{if(process.stdin.isTTY){process.stdin.pause();try{fe=z.openSync("/dev/tty","r"),Ce=process.stdin._handle}catch(x){}}else try{fe=z.openSync("/dev/tty","r"),Ce=new Fr(fe,!1)}catch(x){}if(process.stdout.isTTY)oe=process.stdout.fd;else try{oe=z.openSync("/dev/tty","w")}catch(x){}}}(),function(){var _,v,g=!r.hideEchoBack&&!r.keyIn,h,x,T,b,C;je="";function N(W){return W===zr?!0:Ce.setRawMode(W)!==0?!1:(zr=W,!0)}if(Wr||!Ce||typeof oe!="number"&&(r.display||!g)){u=w();return}if(r.display&&(z.writeSync(oe,r.display),r.display=""),!r.displayOnly){if(!N(!g)){u=w();return}for(x=r.keyIn?1:r.bufferSize,h=Buffer.allocUnsafe&&Buffer.alloc?Buffer.alloc(x):new Buffer(x),r.keyIn&&r.limit&&(v=new RegExp("[^"+r.limit+"]","g"+(r.caseSensitive?"":"i")));;){T=0;try{T=z.readSync(fe,h,0,x)}catch(W){if(W.code!=="EOF"){N(!1),u+=w();return}}if(T>0?(b=h.toString(r.encoding,0,T),je+=b):(b=` +`,je+=String.fromCharCode(0)),b&&typeof(C=(b.match(/^(.*?)[\r\n]/)||[])[1])=="string"&&(b=C,_=!0),b&&(b=b.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"")),b&&v&&(b=b.replace(v,"")),b&&(g||(r.hideEchoBack?r.mask&&z.writeSync(oe,new Array(b.length+1).join(r.mask)):z.writeSync(oe,b)),u+=b),!r.keyIn&&_||r.keyIn&&u.length>=x)break}!g&&!c&&z.writeSync(oe,` +`),N(!1)}}(),r.print&&!c&&r.print(p+(r.displayOnly?"":(r.hideEchoBack?new Array(u.length+1).join(r.mask):u)+` +`),r.encoding),r.displayOnly?"":hr=r.keepWhitespace||r.keyIn?u:u.trim()}function ns(r,u){var p=[];function c(w){w!=null&&(Array.isArray(w)?w.forEach(c):(!u||u(w))&&p.push(w))}return c(r),p}function Tr(r){return r.replace(/[\x00-\x7f]/g,function(u){return"\\x"+("00"+u.charCodeAt().toString(16)).substr(-2)})}function Z(){var r=Array.prototype.slice.call(arguments),u,p;return r.length&&typeof r[0]=="boolean"&&(p=r.shift(),p&&(u=Object.keys(dr),r.unshift(dr))),r.reduce(function(c,w){return w==null||(w.hasOwnProperty("noEchoBack")&&!w.hasOwnProperty("hideEchoBack")&&(w.hideEchoBack=w.noEchoBack,delete w.noEchoBack),w.hasOwnProperty("noTrim")&&!w.hasOwnProperty("keepWhitespace")&&(w.keepWhitespace=w.noTrim,delete w.noTrim),p||(u=Object.keys(w)),u.forEach(function(_){var v;if(!!w.hasOwnProperty(_))switch(v=w[_],_){case"mask":case"limitMessage":case"defaultInput":case"encoding":v=v!=null?v+"":"",v&&_!=="limitMessage"&&(v=v.replace(/[\r\n]/g,"")),c[_]=v;break;case"bufferSize":!isNaN(v=parseInt(v,10))&&typeof v=="number"&&(c[_]=v);break;case"displayOnly":case"keyIn":case"hideEchoBack":case"caseSensitive":case"keepWhitespace":case"history":case"cd":c[_]=!!v;break;case"limit":case"trueValue":case"falseValue":c[_]=ns(v,function(g){var h=typeof g;return h==="string"||h==="number"||h==="function"||g instanceof RegExp}).map(function(g){return typeof g=="string"?g.replace(/[\r\n]/g,""):g});break;case"print":case"phContent":case"preCheck":c[_]=typeof v=="function"?v:void 0;break;case"prompt":case"display":c[_]=v!=null?v:"";break}})),c},{})}function xr(r,u,p){return u.some(function(c){var w=typeof c;return w==="string"?p?r===c:r.toLowerCase()===c.toLowerCase():w==="number"?parseFloat(r)===c:w==="function"?c(r):c instanceof RegExp?c.test(r):!1})}function Vr(r,u){var p=_e.normalize(Ve?(process.env.HOMEDRIVE||"")+(process.env.HOMEPATH||""):process.env.HOME||"").replace(/[\/\\]+$/,"");return r=_e.normalize(r),u?r.replace(/^~(?=\/|\\|$)/,p):r.replace(new RegExp("^"+Tr(p)+"(?=\\/|\\\\|$)",Ve?"i":""),"~")}function Oe(r,u){var p="(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?",c=new RegExp("(\\$)?(\\$<"+p+">)","g"),w=new RegExp("(\\$)?(\\$\\{"+p+"\\})","g");function _(v,g,h,x,T,b){var C;return g||typeof(C=u(T))!="string"?h:C?(x||"")+C+(b||""):""}return r.replace(c,_).replace(w,_)}function Hr(r,u,p){var c,w=[],_=-1,v=0,g="",h;function x(T,b){return b.length>3?(T.push(b[0]+"..."+b[b.length-1]),h=!0):b.length&&(T=T.concat(b)),T}return c=r.reduce(function(T,b){return T.concat((b+"").split(""))},[]).reduce(function(T,b){var C,N;return u||(b=b.toLowerCase()),C=/^\d$/.test(b)?1:/^[A-Z]$/.test(b)?2:/^[a-z]$/.test(b)?3:0,p&&C===0?g+=b:(N=b.charCodeAt(0),C&&C===_&&N===v+1?w.push(b):(T=x(T,w),w=[b],_=C),v=N),T},[]),c=x(c,w),g&&(c.push(g),h=!0),{values:c,suppressed:h}}function Gr(r,u){return r.join(r.length>2?", ":u?" / ":"/")}function Yr(r,u){var p,c,w={},_;if(u.phContent&&(p=u.phContent(r,u)),typeof p!="string")switch(r){case"hideEchoBack":case"mask":case"defaultInput":case"caseSensitive":case"keepWhitespace":case"encoding":case"bufferSize":case"history":case"cd":p=u.hasOwnProperty(r)?typeof u[r]=="boolean"?u[r]?"on":"off":u[r]+"":"";break;case"limit":case"trueValue":case"falseValue":c=u[u.hasOwnProperty(r+"Src")?r+"Src":r],u.keyIn?(w=Hr(c,u.caseSensitive),c=w.values):c=c.filter(function(v){var g=typeof v;return g==="string"||g==="number"}),p=Gr(c,w.suppressed);break;case"limitCount":case"limitCountNotZero":p=u[u.hasOwnProperty("limitSrc")?"limitSrc":"limit"].length,p=p||r!=="limitCountNotZero"?p+"":"";break;case"lastInput":p=hr;break;case"cwd":case"CWD":case"cwdHome":p=process.cwd(),r==="CWD"?p=_e.basename(p):r==="cwdHome"&&(p=Vr(p));break;case"date":case"time":case"localeDate":case"localeTime":p=new Date()["to"+r.replace(/^./,function(v){return v.toUpperCase()})+"String"]();break;default:typeof(_=(r.match(/^history_m(\d+)$/)||[])[1])=="string"&&(p=Se[Se.length-_]||"")}return p}function Ur(r){var u=/^(.)-(.)$/.exec(r),p="",c,w,_,v;if(!u)return null;for(c=u[1].charCodeAt(0),w=u[2].charCodeAt(0),v=c +And the length must be: $`,trueValue:null,falseValue:null,caseSensitive:!0},u,{history:!1,cd:!1,phContent:function(N){return N==="charlist"?p.text:N==="length"?c+"..."+w:null}}),v,g,h,x,T,b,C;for(u=u||{},v=Oe(u.charlist?u.charlist+"":"$",Ur),(isNaN(c=parseInt(u.min,10))||typeof c!="number")&&(c=12),(isNaN(w=parseInt(u.max,10))||typeof w!="number")&&(w=24),x=new RegExp("^["+Tr(v)+"]{"+c+","+w+"}$"),p=Hr([v],_.caseSensitive,!0),p.text=Gr(p.values,p.suppressed),g=u.confirmMessage!=null?u.confirmMessage:"Reinput a same one to confirm it: ",h=u.unmatchMessage!=null?u.unmatchMessage:"It differs from first one. Hit only the Enter key if you want to retry from first one.",r==null&&(r="Input new password: "),T=_.limitMessage;!C;)_.limit=x,_.limitMessage=T,b=M.question(r,_),_.limit=[b,""],_.limitMessage=h,C=M.question(g,_);return b};function Jr(r,u,p){var c;function w(_){return c=p(_),!isNaN(c)&&typeof c=="number"}return M.question(r,Z({limitMessage:"Input valid number, please."},u,{limit:w,cd:!1})),c}M.questionInt=function(r,u){return Jr(r,u,function(p){return parseInt(p,10)})};M.questionFloat=function(r,u){return Jr(r,u,parseFloat)};M.questionPath=function(r,u){var p,c="",w=Z({hideEchoBack:!1,limitMessage:`$Input valid path, please.$<( Min:)min>$<( Max:)max>`,history:!0,cd:!0},u,{keepWhitespace:!1,limit:function(_){var v,g,h;_=Vr(_,!0),c="";function x(T){T.split(/\/|\\/).reduce(function(b,C){var N=_e.resolve(b+=C+_e.sep);if(!z.existsSync(N))z.mkdirSync(N);else if(!z.statSync(N).isDirectory())throw new Error("Non directory already exists: "+N);return b},"")}try{if(v=z.existsSync(_),p=v?z.realpathSync(_):_e.resolve(_),!u.hasOwnProperty("exists")&&!v||typeof u.exists=="boolean"&&u.exists!==v)return c=(v?"Already exists":"No such file or directory")+": "+p,!1;if(!v&&u.create&&(u.isDirectory?x(p):(x(_e.dirname(p)),z.closeSync(z.openSync(p,"w"))),p=z.realpathSync(p)),v&&(u.min||u.max||u.isFile||u.isDirectory)){if(g=z.statSync(p),u.isFile&&!g.isFile())return c="Not file: "+p,!1;if(u.isDirectory&&!g.isDirectory())return c="Not directory: "+p,!1;if(u.min&&g.size<+u.min||u.max&&g.size>+u.max)return c="Size "+g.size+" is out of range: "+p,!1}if(typeof u.validate=="function"&&(h=u.validate(p))!==!0)return typeof h=="string"&&(c=h),!1}catch(T){return c=T+"",!1}return!0},phContent:function(_){return _==="error"?c:_!=="min"&&_!=="max"?null:u.hasOwnProperty(_)?u[_]+"":""}});return u=u||{},r==null&&(r='Input path (you can "cd" and "pwd"): '),M.question(r,w),p};function Kr(r,u){var p={},c={};return typeof r=="object"?(Object.keys(r).forEach(function(w){typeof r[w]=="function"&&(c[u.caseSensitive?w:w.toLowerCase()]=r[w])}),p.preCheck=function(w){var _;return p.args=Sr(w),_=p.args[0]||"",u.caseSensitive||(_=_.toLowerCase()),p.hRes=_!=="_"&&c.hasOwnProperty(_)?c[_].apply(w,p.args.slice(1)):c.hasOwnProperty("_")?c._.apply(w,p.args):null,{res:w,forceNext:!1}},c.hasOwnProperty("_")||(p.limit=function(){var w=p.args[0]||"";return u.caseSensitive||(w=w.toLowerCase()),c.hasOwnProperty(w)})):p.preCheck=function(w){return p.args=Sr(w),p.hRes=typeof r=="function"?r.apply(w,p.args):!0,{res:w,forceNext:!1}},p}M.promptCL=function(r,u){var p=Z({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},u),c=Kr(r,p);return p.limit=c.limit,p.preCheck=c.preCheck,M.prompt(p),c.args};M.promptLoop=function(r,u){for(var p=Z({hideEchoBack:!1,trueValue:null,falseValue:null,caseSensitive:!1,history:!0},u);!r(M.prompt(p)););};M.promptCLLoop=function(r,u){var p=Z({hideEchoBack:!1,limitMessage:"Requested command is not available.",caseSensitive:!1,history:!0},u),c=Kr(r,p);for(p.limit=c.limit,p.preCheck=c.preCheck;M.prompt(p),!c.hRes;);};M.promptSimShell=function(r){return M.prompt(Z({hideEchoBack:!1,history:!0},r,{prompt:function(){return Ve?"$>":(process.env.USER||"")+(process.env.HOSTNAME?"@"+process.env.HOSTNAME.replace(/\..*$/,""):"")+":$$ "}()}))};function jr(r,u,p){var c;return r==null&&(r="Are you sure? "),(!u||u.guide!==!1)&&(r+="")&&(r=r.replace(/\s*:?\s*$/,"")+" [y/n]: "),c=M.keyIn(r,Z(u,{hideEchoBack:!1,limit:p,trueValue:"y",falseValue:"n",caseSensitive:!1})),typeof c=="boolean"?c:""}M.keyInYN=function(r,u){return jr(r,u)};M.keyInYNStrict=function(r,u){return jr(r,u,"yn")};M.keyInPause=function(r,u){r==null&&(r="Continue..."),(!u||u.guide!==!1)&&(r+="")&&(r=r.replace(/\s+$/,"")+" (Hit any key)"),M.keyIn(r,Z({limit:null},u,{hideEchoBack:!0,mask:""}))};M.keyInSelect=function(r,u,p){var c=Z({hideEchoBack:!1},p,{trueValue:null,falseValue:null,caseSensitive:!1,phContent:function(h){return h==="itemsCount"?r.length+"":h==="firstItem"?(r[0]+"").trim():h==="lastItem"?(r[r.length-1]+"").trim():null}}),w="",_={},v=49,g=` +`;if(!Array.isArray(r)||!r.length||r.length>35)throw"`items` must be Array (max length: 35).";return r.forEach(function(h,x){var T=String.fromCharCode(v);w+=T,_[T]=x,g+="["+T+"] "+(h+"").trim()+` +`,v=v===57?97:v+1}),(!p||p.cancel!==!1)&&(w+="0",_["0"]=-1,g+="[0] "+(p&&p.cancel!=null&&typeof p.cancel!="boolean"?(p.cancel+"").trim():"CANCEL")+` +`),c.limit=w,g+=` +`,u==null&&(u="Choose one from list: "),(u+="")&&((!p||p.guide!==!1)&&(u=u.replace(/\s*:?\s*$/,"")+" [$]: "),g+=u),_[M.keyIn(g,c).toLowerCase()]};M.getRawInput=function(){return je};function De(r,u){var p;return u.length&&(p={},p[r]=u[0]),M.setDefaultOptions(p)[r]}M.setPrint=function(){return De("print",arguments)};M.setPrompt=function(){return De("prompt",arguments)};M.setEncoding=function(){return De("encoding",arguments)};M.setMask=function(){return De("mask",arguments)};M.setBufferSize=function(){return De("bufferSize",arguments)}});var kr=I((Mu,ie)=>{(function(){var r={major:0,minor:2,patch:66,status:"beta"};tau_file_system={files:{},open:function(e,n,t){var s=tau_file_system.files[e];if(!s){if(t==="read")return null;s={path:e,text:"",type:n,get:function(a,l){return l===this.text.length||l>this.text.length?"end_of_file":this.text.substring(l,l+a)},put:function(a,l){return l==="end_of_file"?(this.text+=a,!0):l==="past_end_of_file"?null:(this.text=this.text.substring(0,l)+a+this.text.substring(l+a.length),!0)},get_byte:function(a){if(a==="end_of_stream")return-1;var l=Math.floor(a/2);if(this.text.length<=l)return-1;var f=_(this.text[Math.floor(a/2)],0);return a%2==0?f&255:f/256>>>0},put_byte:function(a,l){var f=l==="end_of_stream"?this.text.length:Math.floor(l/2);if(this.text.length>>0,y=(y&255)<<8|a&255):(y=y&255,y=(a&255)<<8|y&255),this.text.length===f?this.text+=v(y):this.text=this.text.substring(0,f)+v(y)+this.text.substring(f+1),!0},flush:function(){return!0},close:function(){var a=tau_file_system.files[this.path];return a?!0:null}},tau_file_system.files[e]=s}return t==="write"&&(s.text=""),s}},tau_user_input={buffer:"",get:function(e,n){for(var t;tau_user_input.buffer.length\?\@\^\~\\]+|'(?:[^']*?(?:\\(?:x?\d+)?\\)*(?:'')*(?:\\')*)*')/,number:/^(?:0o[0-7]+|0x[0-9a-fA-F]+|0b[01]+|0'(?:''|\\[abfnrtv\\'"`]|\\x?\d+\\|[^\\])|\d+(?:\.\d+(?:[eE][+-]?\d+)?)?)/,string:/^(?:"([^"]|""|\\")*"|`([^`]|``|\\`)*`)/,l_brace:/^(?:\[)/,r_brace:/^(?:\])/,l_bracket:/^(?:\{)/,r_bracket:/^(?:\})/,bar:/^(?:\|)/,l_paren:/^(?:\()/,r_paren:/^(?:\))/};function te(e,n){return e.get_flag("char_conversion").id==="on"?n.replace(/./g,function(t){return e.get_char_conversion(t)}):n}function j(e){this.thread=e,this.text="",this.tokens=[]}j.prototype.set_last_tokens=function(e){return this.tokens=e},j.prototype.new_text=function(e){this.text=e,this.tokens=[]},j.prototype.get_tokens=function(e){var n,t=0,s=0,a=0,l=[],f=!1;if(e){var y=this.tokens[e-1];t=y.len,n=te(this.thread,this.text.substr(y.len)),s=y.line,a=y.start}else n=this.text;if(/^\s*$/.test(n))return null;for(;n!=="";){var d=[],m=!1;if(/^\n/.exec(n)!==null){s++,a=0,t++,n=n.replace(/\n/,""),f=!0;continue}for(var S in ee)if(ee.hasOwnProperty(S)){var P=ee[S].exec(n);P&&d.push({value:P[0],name:S,matches:P})}if(!d.length)return this.set_last_tokens([{value:n,matches:[],name:"lexical",line:s,start:a}]);var y=p(d,function(B,q){return B.value.length>=q.value.length?B:q});switch(y.start=a,y.line=s,n=n.replace(y.value,""),a+=y.value.length,t+=y.value.length,y.name){case"atom":y.raw=y.value,y.value.charAt(0)==="'"&&(y.value=C(y.value.substr(1,y.value.length-2),"'"),y.value===null&&(y.name="lexical",y.value="unknown escape sequence"));break;case"number":y.float=y.value.substring(0,2)!=="0x"&&y.value.match(/[.eE]/)!==null&&y.value!=="0'.",y.value=W(y.value),y.blank=m;break;case"string":var A=y.value.charAt(0);y.value=C(y.value.substr(1,y.value.length-2),A),y.value===null&&(y.name="lexical",y.value="unknown escape sequence");break;case"whitespace":var R=l[l.length-1];R&&(R.space=!0),m=!0;continue;case"r_bracket":l.length>0&&l[l.length-1].name==="l_bracket"&&(y=l.pop(),y.name="atom",y.value="{}",y.raw="{}",y.space=!1);break;case"r_brace":l.length>0&&l[l.length-1].name==="l_brace"&&(y=l.pop(),y.name="atom",y.value="[]",y.raw="[]",y.space=!1);break}y.len=t,l.push(y),m=!1}var k=this.set_last_tokens(l);return k.length===0?null:k};function U(e,n,t,s,a){if(!n[t])return{type:g,value:i.error.syntax(n[t-1],"expression expected",!0)};var l;if(s==="0"){var f=n[t];switch(f.name){case"number":return{type:h,len:t+1,value:new i.type.Num(f.value,f.float)};case"variable":return{type:h,len:t+1,value:new i.type.Var(f.value)};case"string":var y;switch(e.get_flag("double_quotes").id){case"atom":y=new o(f.value,[]);break;case"codes":y=new o("[]",[]);for(var d=f.value.length-1;d>=0;d--)y=new o(".",[new i.type.Num(_(f.value,d),!1),y]);break;case"chars":y=new o("[]",[]);for(var d=f.value.length-1;d>=0;d--)y=new o(".",[new i.type.Term(f.value.charAt(d),[]),y]);break}return{type:h,len:t+1,value:y};case"l_paren":var k=U(e,n,t+1,e.__get_max_priority(),!0);return k.type!==h?k:n[k.len]&&n[k.len].name==="r_paren"?(k.len++,k):{type:g,derived:!0,value:i.error.syntax(n[k.len]?n[k.len]:n[k.len-1],") or operator expected",!n[k.len])};case"l_bracket":var k=U(e,n,t+1,e.__get_max_priority(),!0);return k.type!==h?k:n[k.len]&&n[k.len].name==="r_bracket"?(k.len++,k.value=new o("{}",[k.value]),k):{type:g,derived:!0,value:i.error.syntax(n[k.len]?n[k.len]:n[k.len-1],"} or operator expected",!n[k.len])}}var m=Ue(e,n,t,a);return m.type===h||m.derived||(m=Ze(e,n,t),m.type===h||m.derived)?m:{type:g,derived:!1,value:i.error.syntax(n[t],"unexpected token")}}var S=e.__get_max_priority(),P=e.__get_next_priority(s),A=t;if(n[t].name==="atom"&&n[t+1]&&(n[t].space||n[t+1].name!=="l_paren")){var f=n[t++],R=e.__lookup_operator_classes(s,f.value);if(R&&R.indexOf("fy")>-1){var k=U(e,n,t,s,a);if(k.type!==g)return f.value==="-"&&!f.space&&i.type.is_number(k.value)?{value:new i.type.Num(-k.value.value,k.value.is_float),len:k.len,type:h}:{value:new i.type.Term(f.value,[k.value]),len:k.len,type:h};l=k}else if(R&&R.indexOf("fx")>-1){var k=U(e,n,t,P,a);if(k.type!==g)return{value:new i.type.Term(f.value,[k.value]),len:k.len,type:h};l=k}}t=A;var k=U(e,n,t,P,a);if(k.type===h){t=k.len;var f=n[t];if(n[t]&&(n[t].name==="atom"&&e.__lookup_operator_classes(s,f.value)||n[t].name==="bar"&&e.__lookup_operator_classes(s,"|"))){var L=P,B=s,R=e.__lookup_operator_classes(s,f.value);if(R.indexOf("xf")>-1)return{value:new i.type.Term(f.value,[k.value]),len:++k.len,type:h};if(R.indexOf("xfx")>-1){var q=U(e,n,t+1,L,a);return q.type===h?{value:new i.type.Term(f.value,[k.value,q.value]),len:q.len,type:h}:(q.derived=!0,q)}else if(R.indexOf("xfy")>-1){var q=U(e,n,t+1,B,a);return q.type===h?{value:new i.type.Term(f.value,[k.value,q.value]),len:q.len,type:h}:(q.derived=!0,q)}else if(k.type!==g)for(;;){t=k.len;var f=n[t];if(f&&f.name==="atom"&&e.__lookup_operator_classes(s,f.value)){var R=e.__lookup_operator_classes(s,f.value);if(R.indexOf("yf")>-1)k={value:new i.type.Term(f.value,[k.value]),len:++t,type:h};else if(R.indexOf("yfx")>-1){var q=U(e,n,++t,L,a);if(q.type===g)return q.derived=!0,q;t=q.len,k={value:new i.type.Term(f.value,[k.value,q.value]),len:t,type:h}}else break}else break}}else l={type:g,value:i.error.syntax(n[k.len-1],"operator expected")};return k}return k}function Ue(e,n,t,s){if(!n[t]||n[t].name==="atom"&&n[t].raw==="."&&!s&&(n[t].space||!n[t+1]||n[t+1].name!=="l_paren"))return{type:g,derived:!1,value:i.error.syntax(n[t-1],"unfounded token")};var a=n[t],l=[];if(n[t].name==="atom"&&n[t].raw!==","){if(t++,n[t-1].space)return{type:h,len:t,value:new i.type.Term(a.value,l)};if(n[t]&&n[t].name==="l_paren"){if(n[t+1]&&n[t+1].name==="r_paren")return{type:g,derived:!0,value:i.error.syntax(n[t+1],"argument expected")};var f=U(e,n,++t,"999",!0);if(f.type===g)return f.derived?f:{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],"argument expected",!n[t])};for(l.push(f.value),t=f.len;n[t]&&n[t].name==="atom"&&n[t].value===",";){if(f=U(e,n,t+1,"999",!0),f.type===g)return f.derived?f:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};l.push(f.value),t=f.len}if(n[t]&&n[t].name==="r_paren")t++;else return{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],", or ) expected",!n[t])}}return{type:h,len:t,value:new i.type.Term(a.value,l)}}return{type:g,derived:!1,value:i.error.syntax(n[t],"term expected")}}function Ze(e,n,t){if(!n[t])return{type:g,derived:!1,value:i.error.syntax(n[t-1],"[ expected")};if(n[t]&&n[t].name==="l_brace"){var s=U(e,n,++t,"999",!0),a=[s.value],l=void 0;if(s.type===g)return n[t]&&n[t].name==="r_brace"?{type:h,len:t+1,value:new i.type.Term("[]",[])}:{type:g,derived:!0,value:i.error.syntax(n[t],"] expected")};for(t=s.len;n[t]&&n[t].name==="atom"&&n[t].value===",";){if(s=U(e,n,t+1,"999",!0),s.type===g)return s.derived?s:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};a.push(s.value),t=s.len}var f=!1;if(n[t]&&n[t].name==="bar"){if(f=!0,s=U(e,n,t+1,"999",!0),s.type===g)return s.derived?s:{type:g,derived:!0,value:i.error.syntax(n[t+1]?n[t+1]:n[t],"argument expected",!n[t+1])};l=s.value,t=s.len}return n[t]&&n[t].name==="r_brace"?{type:h,len:t+1,value:he(a,l)}:{type:g,derived:!0,value:i.error.syntax(n[t]?n[t]:n[t-1],f?"] expected":", or | or ] expected",!n[t])}}return{type:g,derived:!1,value:i.error.syntax(n[t],"list expected")}}function Qe(e,n,t){var s=n[t].line,a=U(e,n,t,e.__get_max_priority(),!1),l=null,f;if(a.type!==g)if(t=a.len,n[t]&&n[t].name==="atom"&&n[t].raw===".")if(t++,i.type.is_term(a.value)){if(a.value.indicator===":-/2"?(l=new i.type.Rule(a.value.args[0],ve(a.value.args[1])),f={value:l,len:t,type:h}):a.value.indicator==="-->/2"?(l=Bi(new i.type.Rule(a.value.args[0],a.value.args[1]),e),l.body=ve(l.body),f={value:l,len:t,type:i.type.is_rule(l)?h:g}):(l=new i.type.Rule(a.value,null),f={value:l,len:t,type:h}),l){var y=l.singleton_variables();y.length>0&&e.throw_warning(i.warning.singleton(y,l.head.indicator,s))}return f}else return{type:g,value:i.error.syntax(n[t],"callable expected")};else return{type:g,value:i.error.syntax(n[t]?n[t]:n[t-1],". or operator expected")};return a}function Di(e,n,t){t=t||{},t.from=t.from?t.from:"$tau-js",t.reconsult=t.reconsult!==void 0?t.reconsult:!0;var s=new j(e),a={},l;s.new_text(n);var f=0,y=s.get_tokens(f);do{if(y===null||!y[f])break;var d=Qe(e,y,f);if(d.type===g)return new o("throw",[d.value]);if(d.value.body===null&&d.value.head.indicator==="?-/1"){var m=new X(e.session);m.add_goal(d.value.head.args[0]),m.answer(function(P){i.type.is_error(P)?e.throw_warning(P.args[0]):(P===!1||P===null)&&e.throw_warning(i.warning.failed_goal(d.value.head.args[0],d.len))}),f=d.len;var S=!0}else if(d.value.body===null&&d.value.head.indicator===":-/1"){var S=e.run_directive(d.value.head.args[0]);f=d.len,d.value.head.args[0].indicator==="char_conversion/2"&&(y=s.get_tokens(f),f=0)}else{l=d.value.head.indicator,t.reconsult!==!1&&a[l]!==!0&&!e.is_multifile_predicate(l)&&(e.session.rules[l]=w(e.session.rules[l]||[],function(A){return A.dynamic}),a[l]=!0);var S=e.add_rule(d.value,t);f=d.len}if(!S)return S}while(!0);return!0}function Xi(e,n){var t=new j(e);t.new_text(n);var s=0;do{var a=t.get_tokens(s);if(a===null)break;var l=U(e,a,0,e.__get_max_priority(),!1);if(l.type!==g){var f=l.len,y=f;if(a[f]&&a[f].name==="atom"&&a[f].raw===".")e.add_goal(ve(l.value));else{var d=a[f];return new o("throw",[i.error.syntax(d||a[f-1],". or operator expected",!d)])}s=l.len+1}else return new o("throw",[l.value])}while(!0);return!0}function Bi(e,n){e=e.rename(n);var t=n.next_free_variable(),s=pr(e.body,t,n);return s.error?s.value:(e.body=s.value,e.head.args=e.head.args.concat([t,s.variable]),e.head=new o(e.head.id,e.head.args),e)}function pr(e,n,t){var s;if(i.type.is_term(e)&&e.indicator==="!/0")return{value:e,variable:n,error:!1};if(i.type.is_term(e)&&e.indicator===",/2"){var a=pr(e.args[0],n,t);if(a.error)return a;var l=pr(e.args[1],a.variable,t);return l.error?l:{value:new o(",",[a.value,l.value]),variable:l.variable,error:!1}}else{if(i.type.is_term(e)&&e.indicator==="{}/1")return{value:e.args[0],variable:n,error:!1};if(i.type.is_empty_list(e))return{value:new o("true",[]),variable:n,error:!1};if(i.type.is_list(e)){s=t.next_free_variable();for(var f=e,y;f.indicator==="./2";)y=f,f=f.args[1];return i.type.is_variable(f)?{value:i.error.instantiation("DCG"),variable:n,error:!0}:i.type.is_empty_list(f)?(y.args[1]=s,{value:new o("=",[n,e]),variable:s,error:!1}):{value:i.error.type("list",e,"DCG"),variable:n,error:!0}}else return i.type.is_callable(e)?(s=t.next_free_variable(),e.args=e.args.concat([n,s]),e=new o(e.id,e.args),{value:e,variable:s,error:!1}):{value:i.error.type("callable",e,"DCG"),variable:n,error:!0}}}function ve(e){return i.type.is_variable(e)?new o("call",[e]):i.type.is_term(e)&&[",/2",";/2","->/2"].indexOf(e.indicator)!==-1?new o(e.id,[ve(e.args[0]),ve(e.args[1])]):e}function he(e,n){for(var t=n||new i.type.Term("[]",[]),s=e.length-1;s>=0;s--)t=new i.type.Term(".",[e[s],t]);return t}function Fi(e,n){for(var t=e.length-1;t>=0;t--)e[t]===n&&e.splice(t,1)}function yr(e){for(var n={},t=[],s=0;s=0;n--)if(e.charAt(n)==="/")return new o("/",[new o(e.substring(0,n)),new E(parseInt(e.substring(n+1)),!1)])}function O(e){this.id=e}function E(e,n){this.is_float=n!==void 0?n:parseInt(e)!==e,this.value=this.is_float?e:parseInt(e)}var $r=0;function o(e,n,t){this.ref=t||++$r,this.id=e,this.args=n||[],this.indicator=e+"/"+this.args.length}var Wi=0;function ne(e,n,t,s,a,l){this.id=Wi++,this.stream=e,this.mode=n,this.alias=t,this.type=s!==void 0?s:"text",this.reposition=a!==void 0?a:!0,this.eof_action=l!==void 0?l:"eof_code",this.position=this.mode==="append"?"end_of_stream":0,this.output=this.mode==="write"||this.mode==="append",this.input=this.mode==="read"}function Y(e){e=e||{},this.links=e}function V(e,n,t){n=n||new Y,t=t||null,this.goal=e,this.substitution=n,this.parent=t}function Q(e,n,t){this.head=e,this.body=n,this.dynamic=t||!1}function D(e){e=e===void 0||e<=0?1e3:e,this.rules={},this.src_predicates={},this.rename=0,this.modules=[],this.thread=new X(this),this.total_threads=1,this.renamed_variables={},this.public_predicates={},this.multifile_predicates={},this.limit=e,this.streams={user_input:new ne(typeof ie!="undefined"&&ie.exports?nodejs_user_input:tau_user_input,"read","user_input","text",!1,"reset"),user_output:new ne(typeof ie!="undefined"&&ie.exports?nodejs_user_output:tau_user_output,"write","user_output","text",!1,"eof_code")},this.file_system=typeof ie!="undefined"&&ie.exports?nodejs_file_system:tau_file_system,this.standard_input=this.streams.user_input,this.standard_output=this.streams.user_output,this.current_input=this.streams.user_input,this.current_output=this.streams.user_output,this.format_success=function(n){return n.substitution},this.format_error=function(n){return n.goal},this.flag={bounded:i.flag.bounded.value,max_integer:i.flag.max_integer.value,min_integer:i.flag.min_integer.value,integer_rounding_function:i.flag.integer_rounding_function.value,char_conversion:i.flag.char_conversion.value,debug:i.flag.debug.value,max_arity:i.flag.max_arity.value,unknown:i.flag.unknown.value,double_quotes:i.flag.double_quotes.value,occurs_check:i.flag.occurs_check.value,dialect:i.flag.dialect.value,version_data:i.flag.version_data.value,nodejs:i.flag.nodejs.value},this.__loaded_modules=[],this.__char_conversion={},this.__operators={1200:{":-":["fx","xfx"],"-->":["xfx"],"?-":["fx"]},1100:{";":["xfy"]},1050:{"->":["xfy"]},1e3:{",":["xfy"]},900:{"\\+":["fy"]},700:{"=":["xfx"],"\\=":["xfx"],"==":["xfx"],"\\==":["xfx"],"@<":["xfx"],"@=<":["xfx"],"@>":["xfx"],"@>=":["xfx"],"=..":["xfx"],is:["xfx"],"=:=":["xfx"],"=\\=":["xfx"],"<":["xfx"],"=<":["xfx"],">":["xfx"],">=":["xfx"]},600:{":":["xfy"]},500:{"+":["yfx"],"-":["yfx"],"/\\":["yfx"],"\\/":["yfx"]},400:{"*":["yfx"],"/":["yfx"],"//":["yfx"],rem:["yfx"],mod:["yfx"],"<<":["yfx"],">>":["yfx"]},200:{"**":["xfx"],"^":["xfy"],"-":["fy"],"+":["fy"],"\\":["fy"]}}}function X(e){this.epoch=Date.now(),this.session=e,this.session.total_threads++,this.total_steps=0,this.cpu_time=0,this.cpu_time_last=0,this.points=[],this.debugger=!1,this.debugger_states=[],this.level="top_level/0",this.__calls=[],this.current_limit=this.session.limit,this.warnings=[]}function Dr(e,n,t){this.id=e,this.rules=n,this.exports=t,i.module[e]=this}Dr.prototype.exports_predicate=function(e){return this.exports.indexOf(e)!==-1},O.prototype.unify=function(e,n){if(n&&u(e.variables(),this.id)!==-1&&!i.type.is_variable(e))return null;var t={};return t[this.id]=e,new Y(t)},E.prototype.unify=function(e,n){return i.type.is_number(e)&&this.value===e.value&&this.is_float===e.is_float?new Y:null},o.prototype.unify=function(e,n){if(i.type.is_term(e)&&this.indicator===e.indicator){for(var t=new Y,s=0;s=0){var s=this.args[0].value,a=Math.floor(s/26),l=s%26;return"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[l]+(a!==0?a:"")}switch(this.indicator){case"[]/0":case"{}/0":case"!/0":return this.id;case"{}/1":return"{"+this.args[0].toString(e)+"}";case"./2":for(var f="["+this.args[0].toString(e),y=this.args[1];y.indicator==="./2";)f+=", "+y.args[0].toString(e),y=y.args[1];return y.indicator!=="[]/0"&&(f+="|"+y.toString(e)),f+="]",f;case",/2":return"("+this.args[0].toString(e)+", "+this.args[1].toString(e)+")";default:var d=this.id,m=e.session?e.session.lookup_operator(this.id,this.args.length):null;if(e.session===void 0||e.ignore_ops||m===null)return e.quoted&&!/^(!|,|;|[a-z][0-9a-zA-Z_]*)$/.test(d)&&d!=="{}"&&d!=="[]"&&(d="'"+N(d)+"'"),d+(this.args.length?"("+c(this.args,function(R){return R.toString(e)}).join(", ")+")":"");var S=m.priority>n.priority||m.priority===n.priority&&(m.class==="xfy"&&this.indicator!==n.indicator||m.class==="yfx"&&this.indicator!==n.indicator||this.indicator===n.indicator&&m.class==="yfx"&&t==="right"||this.indicator===n.indicator&&m.class==="xfy"&&t==="left");m.indicator=this.indicator;var P=S?"(":"",A=S?")":"";return this.args.length===0?"("+this.id+")":["fy","fx"].indexOf(m.class)!==-1?P+d+" "+this.args[0].toString(e,m)+A:["yf","xf"].indexOf(m.class)!==-1?P+this.args[0].toString(e,m)+" "+d+A:P+this.args[0].toString(e,m,"left")+" "+this.id+" "+this.args[1].toString(e,m,"right")+A}},ne.prototype.toString=function(e){return"("+this.id+")"},Y.prototype.toString=function(e){var n="{";for(var t in this.links)!this.links.hasOwnProperty(t)||(n!=="{"&&(n+=", "),n+=t+"/"+this.links[t].toString(e));return n+="}",n},V.prototype.toString=function(e){return this.goal===null?"<"+this.substitution.toString(e)+">":"<"+this.goal.toString(e)+", "+this.substitution.toString(e)+">"},Q.prototype.toString=function(e){return this.body?this.head.toString(e)+" :- "+this.body.toString(e)+".":this.head.toString(e)+"."},D.prototype.toString=function(e){for(var n="",t=0;t=0;a--)s=new o(".",[n[a],s]);return s}return new o(this.id,c(this.args,function(l){return l.apply(e)}),this.ref)},ne.prototype.apply=function(e){return this},Q.prototype.apply=function(e){return new Q(this.head.apply(e),this.body!==null?this.body.apply(e):null)},Y.prototype.apply=function(e){var n,t={};for(n in this.links)!this.links.hasOwnProperty(n)||(t[n]=this.links[n].apply(e));return new Y(t)},o.prototype.select=function(){for(var e=this;e.indicator===",/2";)e=e.args[0];return e},o.prototype.replace=function(e){return this.indicator===",/2"?this.args[0].indicator===",/2"?new o(",",[this.args[0].replace(e),this.args[1]]):e===null?this.args[1]:new o(",",[e,this.args[1]]):e},o.prototype.search=function(e){if(i.type.is_term(e)&&e.ref!==void 0&&this.ref===e.ref)return!0;for(var n=0;nn&&s0&&(n=this.head_point().substitution.domain());u(n,i.format_variable(this.session.rename))!==-1;)this.session.rename++;if(e.id==="_")return new O(i.format_variable(this.session.rename));this.session.renamed_variables[e.id]=i.format_variable(this.session.rename)}return new O(this.session.renamed_variables[e.id])},D.prototype.next_free_variable=function(){return this.thread.next_free_variable()},X.prototype.next_free_variable=function(){this.session.rename++;var e=[];for(this.points.length>0&&(e=this.head_point().substitution.domain());u(e,i.format_variable(this.session.rename))!==-1;)this.session.rename++;return new O(i.format_variable(this.session.rename))},D.prototype.is_public_predicate=function(e){return!this.public_predicates.hasOwnProperty(e)||this.public_predicates[e]===!0},X.prototype.is_public_predicate=function(e){return this.session.is_public_predicate(e)},D.prototype.is_multifile_predicate=function(e){return this.multifile_predicates.hasOwnProperty(e)&&this.multifile_predicates[e]===!0},X.prototype.is_multifile_predicate=function(e){return this.session.is_multifile_predicate(e)},D.prototype.prepend=function(e){return this.thread.prepend(e)},X.prototype.prepend=function(e){for(var n=e.length-1;n>=0;n--)this.points.push(e[n])},D.prototype.success=function(e,n){return this.thread.success(e,n)},X.prototype.success=function(e,n){var n=typeof n=="undefined"?e:n;this.prepend([new V(e.goal.replace(null),e.substitution,n)])},D.prototype.throw_error=function(e){return this.thread.throw_error(e)},X.prototype.throw_error=function(e){this.prepend([new V(new o("throw",[e]),new Y,null,null)])},D.prototype.step_rule=function(e,n){return this.thread.step_rule(e,n)},X.prototype.step_rule=function(e,n){var t=n.indicator;if(e==="user"&&(e=null),e===null&&this.session.rules.hasOwnProperty(t))return this.session.rules[t];for(var s=e===null?this.session.modules:u(this.session.modules,e)===-1?[]:[e],a=0;a1)&&this.again()},D.prototype.answers=function(e,n,t){return this.thread.answers(e,n,t)},X.prototype.answers=function(e,n,t){var s=n||1e3,a=this;if(n<=0){t&&t();return}this.answer(function(l){e(l),l!==!1?setTimeout(function(){a.answers(e,n-1,t)},1):t&&t()})},D.prototype.again=function(e){return this.thread.again(e)},X.prototype.again=function(e){for(var n,t=Date.now();this.__calls.length>0;){for(this.warnings=[],e!==!1&&(this.current_limit=this.session.limit);this.current_limit>0&&this.points.length>0&&this.head_point().goal!==null&&!i.type.is_error(this.head_point().goal);)if(this.current_limit--,this.step()===!0)return;var s=Date.now();this.cpu_time_last=s-t,this.cpu_time+=this.cpu_time_last;var a=this.__calls.shift();this.current_limit<=0?a(null):this.points.length===0?a(!1):i.type.is_error(this.head_point().goal)?(n=this.session.format_error(this.points.pop()),this.points=[],a(n)):(this.debugger&&this.debugger_states.push(this.head_point()),n=this.session.format_success(this.points.pop()),a(n))}},D.prototype.unfold=function(e){if(e.body===null)return!1;var n=e.head,t=e.body,s=t.select(),a=new X(this),l=[];a.add_goal(s),a.step();for(var f=a.points.length-1;f>=0;f--){var y=a.points[f],d=n.apply(y.substitution),m=t.replace(y.goal);m!==null&&(m=m.apply(y.substitution)),l.push(new Q(d,m))}var S=this.rules[n.indicator],P=u(S,e);return l.length>0&&P!==-1?(S.splice.apply(S,[P,1].concat(l)),!0):!1},X.prototype.unfold=function(e){return this.session.unfold(e)},O.prototype.interpret=function(e){return i.error.instantiation(e.level)},E.prototype.interpret=function(e){return this},o.prototype.interpret=function(e){return i.type.is_unitary_list(this)?this.args[0].interpret(e):i.operate(e,this)},O.prototype.compare=function(e){return this.ide.id?1:0},E.prototype.compare=function(e){if(this.value===e.value&&this.is_float===e.is_float)return 0;if(this.valuee.value)return 1},o.prototype.compare=function(e){if(this.args.lengthe.args.length||this.args.length===e.args.length&&this.id>e.id)return 1;for(var n=0;ns)return 1;if(e.constructor===E){if(e.is_float&&n.is_float)return 0;if(e.is_float)return-1;if(n.is_float)return 1}return 0},is_substitution:function(e){return e instanceof Y},is_state:function(e){return e instanceof V},is_rule:function(e){return e instanceof Q},is_variable:function(e){return e instanceof O},is_stream:function(e){return e instanceof ne},is_anonymous_var:function(e){return e instanceof O&&e.id==="_"},is_callable:function(e){return e instanceof o},is_number:function(e){return e instanceof E},is_integer:function(e){return e instanceof E&&!e.is_float},is_float:function(e){return e instanceof E&&e.is_float},is_term:function(e){return e instanceof o},is_atom:function(e){return e instanceof o&&e.args.length===0},is_ground:function(e){if(e instanceof O)return!1;if(e instanceof o){for(var n=0;n0},is_list:function(e){return e instanceof o&&(e.indicator==="[]/0"||e.indicator==="./2")},is_empty_list:function(e){return e instanceof o&&e.indicator==="[]/0"},is_non_empty_list:function(e){return e instanceof o&&e.indicator==="./2"},is_fully_list:function(e){for(;e instanceof o&&e.indicator==="./2";)e=e.args[1];return e instanceof O||e instanceof o&&e.indicator==="[]/0"},is_instantiated_list:function(e){for(;e instanceof o&&e.indicator==="./2";)e=e.args[1];return e instanceof o&&e.indicator==="[]/0"},is_unitary_list:function(e){return e instanceof o&&e.indicator==="./2"&&e.args[1]instanceof o&&e.args[1].indicator==="[]/0"},is_character:function(e){return e instanceof o&&(e.id.length===1||e.id.length>0&&e.id.length<=2&&_(e.id,0)>=65536)},is_character_code:function(e){return e instanceof E&&!e.is_float&&e.value>=0&&e.value<=1114111},is_byte:function(e){return e instanceof E&&!e.is_float&&e.value>=0&&e.value<=255},is_operator:function(e){return e instanceof o&&i.arithmetic.evaluation[e.indicator]},is_directive:function(e){return e instanceof o&&i.directive[e.indicator]!==void 0},is_builtin:function(e){return e instanceof o&&i.predicate[e.indicator]!==void 0},is_error:function(e){return e instanceof o&&e.indicator==="throw/1"},is_predicate_indicator:function(e){return e instanceof o&&e.indicator==="//2"&&e.args[0]instanceof o&&e.args[0].args.length===0&&e.args[1]instanceof E&&e.args[1].is_float===!1},is_flag:function(e){return e instanceof o&&e.args.length===0&&i.flag[e.id]!==void 0},is_value_flag:function(e,n){if(!i.type.is_flag(e))return!1;for(var t in i.flag[e.id].allowed)if(!!i.flag[e.id].allowed.hasOwnProperty(t)&&i.flag[e.id].allowed[t].equals(n))return!0;return!1},is_io_mode:function(e){return i.type.is_atom(e)&&["read","write","append"].indexOf(e.id)!==-1},is_stream_option:function(e){return i.type.is_term(e)&&(e.indicator==="alias/1"&&i.type.is_atom(e.args[0])||e.indicator==="reposition/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="type/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="text"||e.args[0].id==="binary")||e.indicator==="eof_action/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="error"||e.args[0].id==="eof_code"||e.args[0].id==="reset"))},is_stream_position:function(e){return i.type.is_integer(e)&&e.value>=0||i.type.is_atom(e)&&(e.id==="end_of_stream"||e.id==="past_end_of_stream")},is_stream_property:function(e){return i.type.is_term(e)&&(e.indicator==="input/0"||e.indicator==="output/0"||e.indicator==="alias/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0]))||e.indicator==="file_name/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0]))||e.indicator==="position/1"&&(i.type.is_variable(e.args[0])||i.type.is_stream_position(e.args[0]))||e.indicator==="reposition/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false"))||e.indicator==="type/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="text"||e.args[0].id==="binary"))||e.indicator==="mode/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="read"||e.args[0].id==="write"||e.args[0].id==="append"))||e.indicator==="eof_action/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="error"||e.args[0].id==="eof_code"||e.args[0].id==="reset"))||e.indicator==="end_of_stream/1"&&(i.type.is_variable(e.args[0])||i.type.is_atom(e.args[0])&&(e.args[0].id==="at"||e.args[0].id==="past"||e.args[0].id==="not")))},is_streamable:function(e){return e.__proto__.stream!==void 0},is_read_option:function(e){return i.type.is_term(e)&&["variables/1","variable_names/1","singletons/1"].indexOf(e.indicator)!==-1},is_write_option:function(e){return i.type.is_term(e)&&(e.indicator==="quoted/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="ignore_ops/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")||e.indicator==="numbervars/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false"))},is_close_option:function(e){return i.type.is_term(e)&&e.indicator==="force/1"&&i.type.is_atom(e.args[0])&&(e.args[0].id==="true"||e.args[0].id==="false")},is_modifiable_flag:function(e){return i.type.is_flag(e)&&i.flag[e.id].changeable},is_module:function(e){return e instanceof o&&e.indicator==="library/1"&&e.args[0]instanceof o&&e.args[0].args.length===0&&i.module[e.args[0].id]!==void 0}},arithmetic:{evaluation:{"e/0":{type_args:null,type_result:!0,fn:function(e){return Math.E}},"pi/0":{type_args:null,type_result:!0,fn:function(e){return Math.PI}},"tau/0":{type_args:null,type_result:!0,fn:function(e){return 2*Math.PI}},"epsilon/0":{type_args:null,type_result:!0,fn:function(e){return Number.EPSILON}},"+/1":{type_args:null,type_result:null,fn:function(e,n){return e}},"-/1":{type_args:null,type_result:null,fn:function(e,n){return-e}},"\\/1":{type_args:!1,type_result:!1,fn:function(e,n){return~e}},"abs/1":{type_args:null,type_result:null,fn:function(e,n){return Math.abs(e)}},"sign/1":{type_args:null,type_result:null,fn:function(e,n){return Math.sign(e)}},"float_integer_part/1":{type_args:!0,type_result:!1,fn:function(e,n){return parseInt(e)}},"float_fractional_part/1":{type_args:!0,type_result:!0,fn:function(e,n){return e-parseInt(e)}},"float/1":{type_args:null,type_result:!0,fn:function(e,n){return parseFloat(e)}},"floor/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.floor(e)}},"truncate/1":{type_args:!0,type_result:!1,fn:function(e,n){return parseInt(e)}},"round/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.round(e)}},"ceiling/1":{type_args:!0,type_result:!1,fn:function(e,n){return Math.ceil(e)}},"sin/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.sin(e)}},"cos/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.cos(e)}},"tan/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.tan(e)}},"asin/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.asin(e)}},"acos/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.acos(e)}},"atan/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.atan(e)}},"atan2/2":{type_args:null,type_result:!0,fn:function(e,n,t){return Math.atan2(e,n)}},"exp/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.exp(e)}},"sqrt/1":{type_args:null,type_result:!0,fn:function(e,n){return Math.sqrt(e)}},"log/1":{type_args:null,type_result:!0,fn:function(e,n){return e>0?Math.log(e):i.error.evaluation("undefined",n.__call_indicator)}},"+/2":{type_args:null,type_result:null,fn:function(e,n,t){return e+n}},"-/2":{type_args:null,type_result:null,fn:function(e,n,t){return e-n}},"*/2":{type_args:null,type_result:null,fn:function(e,n,t){return e*n}},"//2":{type_args:null,type_result:!0,fn:function(e,n,t){return n?e/n:i.error.evaluation("zero_division",t.__call_indicator)}},"///2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?parseInt(e/n):i.error.evaluation("zero_division",t.__call_indicator)}},"**/2":{type_args:null,type_result:!0,fn:function(e,n,t){return Math.pow(e,n)}},"^/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.pow(e,n)}},"<>/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e>>n}},"/\\/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e&n}},"\\//2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e|n}},"xor/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return e^n}},"rem/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?e%n:i.error.evaluation("zero_division",t.__call_indicator)}},"mod/2":{type_args:!1,type_result:!1,fn:function(e,n,t){return n?e-parseInt(e/n)*n:i.error.evaluation("zero_division",t.__call_indicator)}},"max/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.max(e,n)}},"min/2":{type_args:null,type_result:null,fn:function(e,n,t){return Math.min(e,n)}}}},directive:{"dynamic/1":function(e,n){var t=n.args[0];if(i.type.is_variable(t))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_compound(t)||t.indicator!=="//2")e.throw_error(i.error.type("predicate_indicator",t,n.indicator));else if(i.type.is_variable(t.args[0])||i.type.is_variable(t.args[1]))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_atom(t.args[0]))e.throw_error(i.error.type("atom",t.args[0],n.indicator));else if(!i.type.is_integer(t.args[1]))e.throw_error(i.error.type("integer",t.args[1],n.indicator));else{var s=n.args[0].args[0].id+"/"+n.args[0].args[1].value;e.session.public_predicates[s]=!0,e.session.rules[s]||(e.session.rules[s]=[])}},"multifile/1":function(e,n){var t=n.args[0];i.type.is_variable(t)?e.throw_error(i.error.instantiation(n.indicator)):!i.type.is_compound(t)||t.indicator!=="//2"?e.throw_error(i.error.type("predicate_indicator",t,n.indicator)):i.type.is_variable(t.args[0])||i.type.is_variable(t.args[1])?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_atom(t.args[0])?i.type.is_integer(t.args[1])?e.session.multifile_predicates[n.args[0].args[0].id+"/"+n.args[0].args[1].value]=!0:e.throw_error(i.error.type("integer",t.args[1],n.indicator)):e.throw_error(i.error.type("atom",t.args[0],n.indicator))},"set_prolog_flag/2":function(e,n){var t=n.args[0],s=n.args[1];i.type.is_variable(t)||i.type.is_variable(s)?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_atom(t)?i.type.is_flag(t)?i.type.is_value_flag(t,s)?i.type.is_modifiable_flag(t)?e.session.flag[t.id]=s:e.throw_error(i.error.permission("modify","flag",t)):e.throw_error(i.error.domain("flag_value",new o("+",[t,s]),n.indicator)):e.throw_error(i.error.domain("prolog_flag",t,n.indicator)):e.throw_error(i.error.type("atom",t,n.indicator))},"use_module/1":function(e,n){var t=n.args[0];if(i.type.is_variable(t))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_term(t))e.throw_error(i.error.type("term",t,n.indicator));else if(i.type.is_module(t)){var s=t.args[0].id;u(e.session.modules,s)===-1&&e.session.modules.push(s)}},"char_conversion/2":function(e,n){var t=n.args[0],s=n.args[1];i.type.is_variable(t)||i.type.is_variable(s)?e.throw_error(i.error.instantiation(n.indicator)):i.type.is_character(t)?i.type.is_character(s)?t.id===s.id?delete e.session.__char_conversion[t.id]:e.session.__char_conversion[t.id]=s.id:e.throw_error(i.error.type("character",s,n.indicator)):e.throw_error(i.error.type("character",t,n.indicator))},"op/3":function(e,n){var t=n.args[0],s=n.args[1],a=n.args[2];if(i.type.is_variable(t)||i.type.is_variable(s)||i.type.is_variable(a))e.throw_error(i.error.instantiation(n.indicator));else if(!i.type.is_integer(t))e.throw_error(i.error.type("integer",t,n.indicator));else if(!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,n.indicator));else if(!i.type.is_atom(a))e.throw_error(i.error.type("atom",a,n.indicator));else if(t.value<0||t.value>1200)e.throw_error(i.error.domain("operator_priority",t,n.indicator));else if(a.id===",")e.throw_error(i.error.permission("modify","operator",a,n.indicator));else if(a.id==="|"&&(t.value<1001||s.id.length!==3))e.throw_error(i.error.permission("modify","operator",a,n.indicator));else if(["fy","fx","yf","xf","xfx","yfx","xfy"].indexOf(s.id)===-1)e.throw_error(i.error.domain("operator_specifier",s,n.indicator));else{var l={prefix:null,infix:null,postfix:null};for(var f in e.session.__operators)if(!!e.session.__operators.hasOwnProperty(f)){var y=e.session.__operators[f][a.id];y&&(u(y,"fx")!==-1&&(l.prefix={priority:f,type:"fx"}),u(y,"fy")!==-1&&(l.prefix={priority:f,type:"fy"}),u(y,"xf")!==-1&&(l.postfix={priority:f,type:"xf"}),u(y,"yf")!==-1&&(l.postfix={priority:f,type:"yf"}),u(y,"xfx")!==-1&&(l.infix={priority:f,type:"xfx"}),u(y,"xfy")!==-1&&(l.infix={priority:f,type:"xfy"}),u(y,"yfx")!==-1&&(l.infix={priority:f,type:"yfx"}))}var d;switch(s.id){case"fy":case"fx":d="prefix";break;case"yf":case"xf":d="postfix";break;default:d="infix";break}if(((l.prefix&&d==="prefix"||l.postfix&&d==="postfix"||l.infix&&d==="infix")&&l[d].type!==s.id||l.infix&&d==="postfix"||l.postfix&&d==="infix")&&t.value!==0)e.throw_error(i.error.permission("create","operator",a,n.indicator));else return l[d]&&(Fi(e.session.__operators[l[d].priority][a.id],s.id),e.session.__operators[l[d].priority][a.id].length===0&&delete e.session.__operators[l[d].priority][a.id]),t.value>0&&(e.session.__operators[t.value]||(e.session.__operators[t.value.toString()]={}),e.session.__operators[t.value][a.id]||(e.session.__operators[t.value][a.id]=[]),e.session.__operators[t.value][a.id].push(s.id)),!0}}},predicate:{"op/3":function(e,n,t){i.directive["op/3"](e,t)&&e.success(n)},"current_op/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2],f=[];for(var y in e.session.__operators)for(var d in e.session.__operators[y])for(var m=0;m/2"){var s=e.points,a=e.session.format_success,l=e.session.format_error;e.session.format_success=function(m){return m.substitution},e.session.format_error=function(m){return m.goal},e.points=[new V(t.args[0].args[0],n.substitution,n)];var f=function(m){e.points=s,e.session.format_success=a,e.session.format_error=l,m===!1?e.prepend([new V(n.goal.replace(t.args[1]),n.substitution,n)]):i.type.is_error(m)?e.throw_error(m.args[0]):m===null?(e.prepend([n]),e.__calls.shift()(null)):e.prepend([new V(n.goal.replace(t.args[0].args[1]).apply(m),n.substitution.apply(m),n)])};e.__calls.unshift(f)}else{var y=new V(n.goal.replace(t.args[0]),n.substitution,n),d=new V(n.goal.replace(t.args[1]),n.substitution,n);e.prepend([y,d])}},"!/0":function(e,n,t){var s,a,l=[];for(s=n,a=null;s.parent!==null&&s.parent.goal.search(t);)if(a=s,s=s.parent,s.goal!==null){var f=s.goal.select();if(f&&f.id==="call"&&f.search(t)){s=a;break}}for(var y=e.points.length-1;y>=0;y--){for(var d=e.points[y],m=d.parent;m!==null&&m!==s.parent;)m=m.parent;m===null&&m!==s.parent&&l.push(d)}e.points=l.reverse(),e.success(n)},"\\+/1":function(e,n,t){var s=t.args[0];i.type.is_variable(s)?e.throw_error(i.error.instantiation(e.level)):i.type.is_callable(s)?e.prepend([new V(n.goal.replace(new o(",",[new o(",",[new o("call",[s]),new o("!",[])]),new o("fail",[])])),n.substitution,n),new V(n.goal.replace(null),n.substitution,n)]):e.throw_error(i.error.type("callable",s,e.level))},"->/2":function(e,n,t){var s=n.goal.replace(new o(",",[t.args[0],new o(",",[new o("!"),t.args[1]])]));e.prepend([new V(s,n.substitution,n)])},"fail/0":function(e,n,t){},"false/0":function(e,n,t){},"true/0":function(e,n,t){e.success(n)},"call/1":ye(1),"call/2":ye(2),"call/3":ye(3),"call/4":ye(4),"call/5":ye(5),"call/6":ye(6),"call/7":ye(7),"call/8":ye(8),"once/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("call",[s]),new o("!",[])])),n.substitution,n)])},"forall/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("\\+",[new o(",",[new o("call",[s]),new o("\\+",[new o("call",[a])])])])),n.substitution,n)])},"repeat/0":function(e,n,t){e.prepend([new V(n.goal.replace(null),n.substitution,n),n])},"throw/1":function(e,n,t){i.type.is_variable(t.args[0])?e.throw_error(i.error.instantiation(e.level)):e.throw_error(t.args[0])},"catch/3":function(e,n,t){var s=e.points;e.points=[],e.prepend([new V(t.args[0],n.substitution,n)]);var a=e.session.format_success,l=e.session.format_error;e.session.format_success=function(y){return y.substitution},e.session.format_error=function(y){return y.goal};var f=function(y){var d=e.points;if(e.points=s,e.session.format_success=a,e.session.format_error=l,i.type.is_error(y)){for(var m=[],S=e.points.length-1;S>=0;S--){for(var R=e.points[S],P=R.parent;P!==null&&P!==n.parent;)P=P.parent;P===null&&P!==n.parent&&m.push(R)}e.points=m;var A=e.get_flag("occurs_check").indicator==="true/0",R=new V,k=i.unify(y.args[0],t.args[1],A);k!==null?(R.substitution=n.substitution.apply(k),R.goal=n.goal.replace(t.args[2]).apply(k),R.parent=n,e.prepend([R])):e.throw_error(y.args[0])}else if(y!==!1){for(var L=y===null?[]:[new V(n.goal.apply(y).replace(null),n.substitution.apply(y),n)],B=[],S=d.length-1;S>=0;S--){B.push(d[S]);var q=d[S].goal!==null?d[S].goal.select():null;if(i.type.is_term(q)&&q.indicator==="!/0")break}var F=c(B,function(H){return H.goal===null&&(H.goal=new o("true",[])),H=new V(n.goal.replace(new o("catch",[H.goal,t.args[1],t.args[2]])),n.substitution.apply(H.substitution),H.parent),H.exclude=t.args[0].variables(),H}).reverse();e.prepend(F),e.prepend(L),y===null&&(this.current_limit=0,e.__calls.shift()(null))}};e.__calls.unshift(f)},"=/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=new V,l=i.unify(t.args[0],t.args[1],s);l!==null&&(a.goal=n.goal.apply(l).replace(null),a.substitution=n.substitution.apply(l),a.parent=n,e.prepend([a]))},"unify_with_occurs_check/2":function(e,n,t){var s=new V,a=i.unify(t.args[0],t.args[1],!0);a!==null&&(s.goal=n.goal.apply(a).replace(null),s.substitution=n.substitution.apply(a),s.parent=n,e.prepend([s]))},"\\=/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=i.unify(t.args[0],t.args[1],s);a===null&&e.success(n)},"subsumes_term/2":function(e,n,t){var s=e.get_flag("occurs_check").indicator==="true/0",a=i.unify(t.args[1],t.args[0],s);a!==null&&t.args[1].apply(a).equals(t.args[1])&&e.success(n)},"findall/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(a))e.throw_error(i.error.type("callable",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=e.next_free_variable(),y=new o(",",[a,new o("=",[f,s])]),d=e.points,m=e.session.limit,S=e.session.format_success;e.session.format_success=function(R){return R.substitution},e.add_goal(y,!0,n);var P=[],A=function(R){if(R!==!1&&R!==null&&!i.type.is_error(R))e.__calls.unshift(A),P.push(R.links[f.id]),e.session.limit=e.current_limit;else if(e.points=d,e.session.limit=m,e.session.format_success=S,i.type.is_error(R))e.throw_error(R.args[0]);else if(e.current_limit>0){for(var k=new o("[]"),L=P.length-1;L>=0;L--)k=new o(".",[P[L],k]);e.prepend([new V(n.goal.replace(new o("=",[l,k])),n.substitution,n)])}};e.__calls.unshift(A)}},"bagof/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(l))e.throw_error(i.error.type("callable",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_list(f))e.throw_error(i.error.type("list",f,t.indicator));else{var y=e.next_free_variable(),d;l.indicator==="^/2"?(d=l.args[0].variables(),l=l.args[1]):d=[],d=d.concat(a.variables());for(var m=l.variables().filter(function(F){return u(d,F)===-1}),S=new o("[]"),P=m.length-1;P>=0;P--)S=new o(".",[new O(m[P]),S]);var A=new o(",",[l,new o("=",[y,new o(",",[S,a])])]),R=e.points,k=e.session.limit,L=e.session.format_success;e.session.format_success=function(F){return F.substitution},e.add_goal(A,!0,n);var B=[],q=function(F){if(F!==!1&&F!==null&&!i.type.is_error(F)){e.__calls.unshift(q);var H=!1,J=F.links[y.id].args[0],me=F.links[y.id].args[1];for(var be in B)if(!!B.hasOwnProperty(be)){var Me=B[be];if(Me.variables.equals(J)){Me.answers.push(me),H=!0;break}}H||B.push({variables:J,answers:[me]}),e.session.limit=e.current_limit}else if(e.points=R,e.session.limit=k,e.session.format_success=L,i.type.is_error(F))e.throw_error(F.args[0]);else if(e.current_limit>0){for(var qe=[],ce=0;ce=0;xe--)Te=new o(".",[F[xe],Te]);qe.push(new V(n.goal.replace(new o(",",[new o("=",[S,B[ce].variables]),new o("=",[f,Te])])),n.substitution,n))}e.prepend(qe)}};e.__calls.unshift(q)}},"setof/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(l))e.throw_error(i.error.type("callable",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_list(f))e.throw_error(i.error.type("list",f,t.indicator));else{var y=e.next_free_variable(),d;l.indicator==="^/2"?(d=l.args[0].variables(),l=l.args[1]):d=[],d=d.concat(a.variables());for(var m=l.variables().filter(function(F){return u(d,F)===-1}),S=new o("[]"),P=m.length-1;P>=0;P--)S=new o(".",[new O(m[P]),S]);var A=new o(",",[l,new o("=",[y,new o(",",[S,a])])]),R=e.points,k=e.session.limit,L=e.session.format_success;e.session.format_success=function(F){return F.substitution},e.add_goal(A,!0,n);var B=[],q=function(F){if(F!==!1&&F!==null&&!i.type.is_error(F)){e.__calls.unshift(q);var H=!1,J=F.links[y.id].args[0],me=F.links[y.id].args[1];for(var be in B)if(!!B.hasOwnProperty(be)){var Me=B[be];if(Me.variables.equals(J)){Me.answers.push(me),H=!0;break}}H||B.push({variables:J,answers:[me]}),e.session.limit=e.current_limit}else if(e.points=R,e.session.limit=k,e.session.format_success=L,i.type.is_error(F))e.throw_error(F.args[0]);else if(e.current_limit>0){for(var qe=[],ce=0;ce=0;xe--)Te=new o(".",[F[xe],Te]);qe.push(new V(n.goal.replace(new o(",",[new o("=",[S,B[ce].variables]),new o("=",[f,Te])])),n.substitution,n))}e.prepend(qe)}};e.__calls.unshift(q)}},"functor/3":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2];if(i.type.is_variable(a)&&(i.type.is_variable(l)||i.type.is_variable(f)))e.throw_error(i.error.instantiation("functor/3"));else if(!i.type.is_variable(f)&&!i.type.is_integer(f))e.throw_error(i.error.type("integer",t.args[2],"functor/3"));else if(!i.type.is_variable(l)&&!i.type.is_atomic(l))e.throw_error(i.error.type("atomic",t.args[1],"functor/3"));else if(i.type.is_integer(l)&&i.type.is_integer(f)&&f.value!==0)e.throw_error(i.error.type("atom",t.args[1],"functor/3"));else if(i.type.is_variable(a)){if(t.args[2].value>=0){for(var y=[],d=0;d0&&s<=t.args[1].args.length){var a=new o("=",[t.args[1].args[s-1],t.args[2]]);e.prepend([new V(n.goal.replace(a),n.substitution,n)])}}},"=../2":function(e,n,t){var s;if(i.type.is_variable(t.args[0])&&(i.type.is_variable(t.args[1])||i.type.is_non_empty_list(t.args[1])&&i.type.is_variable(t.args[1].args[0])))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_fully_list(t.args[1]))e.throw_error(i.error.type("list",t.args[1],t.indicator));else if(i.type.is_variable(t.args[0])){if(!i.type.is_variable(t.args[1])){var l=[];for(s=t.args[1].args[1];s.indicator==="./2";)l.push(s.args[0]),s=s.args[1];i.type.is_variable(t.args[0])&&i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):l.length===0&&i.type.is_compound(t.args[1].args[0])?e.throw_error(i.error.type("atomic",t.args[1].args[0],t.indicator)):l.length>0&&(i.type.is_compound(t.args[1].args[0])||i.type.is_number(t.args[1].args[0]))?e.throw_error(i.error.type("atom",t.args[1].args[0],t.indicator)):l.length===0?e.prepend([new V(n.goal.replace(new o("=",[t.args[1].args[0],t.args[0]],n)),n.substitution,n)]):e.prepend([new V(n.goal.replace(new o("=",[new o(t.args[1].args[0].id,l),t.args[0]])),n.substitution,n)])}}else{if(i.type.is_atomic(t.args[0]))s=new o(".",[t.args[0],new o("[]")]);else{s=new o("[]");for(var a=t.args[0].args.length-1;a>=0;a--)s=new o(".",[t.args[0].args[a],s]);s=new o(".",[new o(t.args[0].id),s])}e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n)])}},"copy_term/2":function(e,n,t){var s=t.args[0].rename(e);e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n.parent)])},"term_variables/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(!i.type.is_fully_list(a))e.throw_error(i.error.type("list",a,t.indicator));else{var l=he(c(yr(s.variables()),function(f){return new O(f)}));e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"clause/2":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else if(!i.type.is_variable(t.args[1])&&!i.type.is_callable(t.args[1]))e.throw_error(i.error.type("callable",t.args[1],t.indicator));else if(e.session.rules[t.args[0].indicator]!==void 0)if(e.is_public_predicate(t.args[0].indicator)){var s=[];for(var a in e.session.rules[t.args[0].indicator])if(!!e.session.rules[t.args[0].indicator].hasOwnProperty(a)){var l=e.session.rules[t.args[0].indicator][a];e.session.renamed_variables={},l=l.rename(e),l.body===null&&(l.body=new o("true"));var f=new o(",",[new o("=",[l.head,t.args[0]]),new o("=",[l.body,t.args[1]])]);s.push(new V(n.goal.replace(f),n.substitution,n))}e.prepend(s)}else e.throw_error(i.error.permission("access","private_procedure",t.args[0].indicator,t.indicator))},"current_predicate/1":function(e,n,t){var s=t.args[0];if(!i.type.is_variable(s)&&(!i.type.is_compound(s)||s.indicator!=="//2"))e.throw_error(i.error.type("predicate_indicator",s,t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_variable(s.args[0])&&!i.type.is_atom(s.args[0]))e.throw_error(i.error.type("atom",s.args[0],t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_variable(s.args[1])&&!i.type.is_integer(s.args[1]))e.throw_error(i.error.type("integer",s.args[1],t.indicator));else{var a=[];for(var l in e.session.rules)if(!!e.session.rules.hasOwnProperty(l)){var f=l.lastIndexOf("/"),y=l.substr(0,f),d=parseInt(l.substr(f+1,l.length-(f+1))),m=new o("/",[new o(y),new E(d,!1)]),S=new o("=",[m,s]);a.push(new V(n.goal.replace(S),n.substitution,n))}e.prepend(a)}},"asserta/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=ve(t.args[0].args[1])):(s=t.args[0],a=null),i.type.is_callable(s)?a!==null&&!i.type.is_callable(a)?e.throw_error(i.error.type("callable",a,t.indicator)):e.is_public_predicate(s.indicator)?(e.session.rules[s.indicator]===void 0&&(e.session.rules[s.indicator]=[]),e.session.public_predicates[s.indicator]=!0,e.session.rules[s.indicator]=[new Q(s,a,!0)].concat(e.session.rules[s.indicator]),e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s.indicator,t.indicator)):e.throw_error(i.error.type("callable",s,t.indicator))}},"assertz/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=ve(t.args[0].args[1])):(s=t.args[0],a=null),i.type.is_callable(s)?a!==null&&!i.type.is_callable(a)?e.throw_error(i.error.type("callable",a,t.indicator)):e.is_public_predicate(s.indicator)?(e.session.rules[s.indicator]===void 0&&(e.session.rules[s.indicator]=[]),e.session.public_predicates[s.indicator]=!0,e.session.rules[s.indicator].push(new Q(s,a,!0)),e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s.indicator,t.indicator)):e.throw_error(i.error.type("callable",s,t.indicator))}},"retract/1":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_callable(t.args[0]))e.throw_error(i.error.type("callable",t.args[0],t.indicator));else{var s,a;if(t.args[0].indicator===":-/2"?(s=t.args[0].args[0],a=t.args[0].args[1]):(s=t.args[0],a=new o("true")),typeof n.retract=="undefined")if(e.is_public_predicate(s.indicator)){if(e.session.rules[s.indicator]!==void 0){for(var l=[],f=0;fe.get_flag("max_arity").value)e.throw_error(i.error.representation("max_arity",t.indicator));else{var s=t.args[0].args[0].id+"/"+t.args[0].args[1].value;e.is_public_predicate(s)?(delete e.session.rules[s],e.success(n)):e.throw_error(i.error.permission("modify","static_procedure",s,t.indicator))}},"atom_length/2":function(e,n,t){if(i.type.is_variable(t.args[0]))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_atom(t.args[0]))e.throw_error(i.error.type("atom",t.args[0],t.indicator));else if(!i.type.is_variable(t.args[1])&&!i.type.is_integer(t.args[1]))e.throw_error(i.error.type("integer",t.args[1],t.indicator));else if(i.type.is_integer(t.args[1])&&t.args[1].value<0)e.throw_error(i.error.domain("not_less_than_zero",t.args[1],t.indicator));else{var s=new E(t.args[0].id.length,!1);e.prepend([new V(n.goal.replace(new o("=",[s,t.args[1]])),n.substitution,n)])}},"atom_concat/3":function(e,n,t){var s,a,l=t.args[0],f=t.args[1],y=t.args[2];if(i.type.is_variable(y)&&(i.type.is_variable(l)||i.type.is_variable(f)))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_atom(l))e.throw_error(i.error.type("atom",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_atom(f))e.throw_error(i.error.type("atom",f,t.indicator));else if(!i.type.is_variable(y)&&!i.type.is_atom(y))e.throw_error(i.error.type("atom",y,t.indicator));else{var d=i.type.is_variable(l),m=i.type.is_variable(f);if(!d&&!m)a=new o("=",[y,new o(l.id+f.id)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]);else if(d&&!m)s=y.id.substr(0,y.id.length-f.id.length),s+f.id===y.id&&(a=new o("=",[l,new o(s)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]));else if(m&&!d)s=y.id.substr(l.id.length),l.id+s===y.id&&(a=new o("=",[f,new o(s)]),e.prepend([new V(n.goal.replace(a),n.substitution,n)]));else{for(var S=[],P=0;P<=y.id.length;P++){var A=new o(y.id.substr(0,P)),R=new o(y.id.substr(P));a=new o(",",[new o("=",[A,l]),new o("=",[R,f])]),S.push(new V(n.goal.replace(a),n.substitution,n))}e.prepend(S)}}},"sub_atom/5":function(e,n,t){var s,a=t.args[0],l=t.args[1],f=t.args[2],y=t.args[3],d=t.args[4];if(i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_integer(l))e.throw_error(i.error.type("integer",l,t.indicator));else if(!i.type.is_variable(f)&&!i.type.is_integer(f))e.throw_error(i.error.type("integer",f,t.indicator));else if(!i.type.is_variable(y)&&!i.type.is_integer(y))e.throw_error(i.error.type("integer",y,t.indicator));else if(i.type.is_integer(l)&&l.value<0)e.throw_error(i.error.domain("not_less_than_zero",l,t.indicator));else if(i.type.is_integer(f)&&f.value<0)e.throw_error(i.error.domain("not_less_than_zero",f,t.indicator));else if(i.type.is_integer(y)&&y.value<0)e.throw_error(i.error.domain("not_less_than_zero",y,t.indicator));else{var m=[],S=[],P=[];if(i.type.is_variable(l))for(s=0;s<=a.id.length;s++)m.push(s);else m.push(l.value);if(i.type.is_variable(f))for(s=0;s<=a.id.length;s++)S.push(s);else S.push(f.value);if(i.type.is_variable(y))for(s=0;s<=a.id.length;s++)P.push(s);else P.push(y.value);var A=[];for(var R in m)if(!!m.hasOwnProperty(R)){s=m[R];for(var k in S)if(!!S.hasOwnProperty(k)){var L=S[k],B=a.id.length-s-L;if(u(P,B)!==-1&&s+L+B===a.id.length){var q=a.id.substr(s,L);if(a.id===a.id.substr(0,s)+q+a.id.substr(s+L,B)){var F=new o("=",[new o(q),d]),H=new o("=",[l,new E(s)]),J=new o("=",[f,new E(L)]),me=new o("=",[y,new E(B)]),be=new o(",",[new o(",",[new o(",",[H,J]),me]),F]);A.push(new V(n.goal.replace(be),n.substitution,n))}}}}e.prepend(A)}},"atom_chars/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_variable(s)){for(var y=a,d=i.type.is_variable(s),m="";y.indicator==="./2";){if(i.type.is_character(y.args[0]))m+=y.args[0].id;else if(i.type.is_variable(y.args[0])&&d){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character",y.args[0],t.indicator));return}y=y.args[1]}i.type.is_variable(y)&&d?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_empty_list(y)&&!i.type.is_variable(y)?e.throw_error(i.error.type("list",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[new o(m),s])),n.substitution,n)])}else{for(var l=new o("[]"),f=s.id.length-1;f>=0;f--)l=new o(".",[new o(s.id.charAt(f)),l]);e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"atom_codes/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_variable(s)){for(var y=a,d=i.type.is_variable(s),m="";y.indicator==="./2";){if(i.type.is_character_code(y.args[0]))m+=v(y.args[0].value);else if(i.type.is_variable(y.args[0])&&d){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.representation("character_code",t.indicator));return}y=y.args[1]}i.type.is_variable(y)&&d?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_empty_list(y)&&!i.type.is_variable(y)?e.throw_error(i.error.type("list",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[new o(m),s])),n.substitution,n)])}else{for(var l=new o("[]"),f=s.id.length-1;f>=0;f--)l=new o(".",[new E(_(s.id,f),!1),l]);e.prepend([new V(n.goal.replace(new o("=",[a,l])),n.substitution,n)])}},"char_code/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(i.type.is_variable(s)&&i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_character(s))e.throw_error(i.error.type("character",s,t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_integer(a))e.throw_error(i.error.type("integer",a,t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_character_code(a))e.throw_error(i.error.representation("character_code",t.indicator));else if(i.type.is_variable(a)){var l=new E(_(s.id,0),!1);e.prepend([new V(n.goal.replace(new o("=",[l,a])),n.substitution,n)])}else{var f=new o(v(a.value));e.prepend([new V(n.goal.replace(new o("=",[f,s])),n.substitution,n)])}},"number_chars/2":function(e,n,t){var s,a=t.args[0],l=t.args[1];if(i.type.is_variable(a)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_number(a))e.throw_error(i.error.type("number",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=i.type.is_variable(a);if(!i.type.is_variable(l)){var y=l,d=!0;for(s="";y.indicator==="./2";){if(i.type.is_character(y.args[0]))s+=y.args[0].id;else if(i.type.is_variable(y.args[0]))d=!1;else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character",y.args[0],t.indicator));return}y=y.args[1]}if(d=d&&i.type.is_empty_list(y),!i.type.is_empty_list(y)&&!i.type.is_variable(y)){e.throw_error(i.error.type("list",l,t.indicator));return}if(!d&&f){e.throw_error(i.error.instantiation(t.indicator));return}else if(d)if(i.type.is_variable(y)&&f){e.throw_error(i.error.instantiation(t.indicator));return}else{var m=e.parse(s),S=m.value;!i.type.is_number(S)||m.tokens[m.tokens.length-1].space?e.throw_error(i.error.syntax_by_predicate("parseable_number",t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,S])),n.substitution,n)]);return}}if(!f){s=a.toString();for(var P=new o("[]"),A=s.length-1;A>=0;A--)P=new o(".",[new o(s.charAt(A)),P]);e.prepend([new V(n.goal.replace(new o("=",[l,P])),n.substitution,n)])}}},"number_codes/2":function(e,n,t){var s,a=t.args[0],l=t.args[1];if(i.type.is_variable(a)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(a)&&!i.type.is_number(a))e.throw_error(i.error.type("number",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else{var f=i.type.is_variable(a);if(!i.type.is_variable(l)){var y=l,d=!0;for(s="";y.indicator==="./2";){if(i.type.is_character_code(y.args[0]))s+=v(y.args[0].value);else if(i.type.is_variable(y.args[0]))d=!1;else if(!i.type.is_variable(y.args[0])){e.throw_error(i.error.type("character_code",y.args[0],t.indicator));return}y=y.args[1]}if(d=d&&i.type.is_empty_list(y),!i.type.is_empty_list(y)&&!i.type.is_variable(y)){e.throw_error(i.error.type("list",l,t.indicator));return}if(!d&&f){e.throw_error(i.error.instantiation(t.indicator));return}else if(d)if(i.type.is_variable(y)&&f){e.throw_error(i.error.instantiation(t.indicator));return}else{var m=e.parse(s),S=m.value;!i.type.is_number(S)||m.tokens[m.tokens.length-1].space?e.throw_error(i.error.syntax_by_predicate("parseable_number",t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,S])),n.substitution,n)]);return}}if(!f){s=a.toString();for(var P=new o("[]"),A=s.length-1;A>=0;A--)P=new o(".",[new E(_(s,A),!1),P]);e.prepend([new V(n.goal.replace(new o("=",[l,P])),n.substitution,n)])}}},"upcase_atom/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?!i.type.is_variable(a)&&!i.type.is_atom(a)?e.throw_error(i.error.type("atom",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,new o(s.id.toUpperCase(),[])])),n.substitution,n)]):e.throw_error(i.error.type("atom",s,t.indicator))},"downcase_atom/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?!i.type.is_variable(a)&&!i.type.is_atom(a)?e.throw_error(i.error.type("atom",a,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[a,new o(s.id.toLowerCase(),[])])),n.substitution,n)]):e.throw_error(i.error.type("atom",s,t.indicator))},"atomic_list_concat/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("atomic_list_concat",[s,new o("",[]),a])),n.substitution,n)])},"atomic_list_concat/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(a)||i.type.is_variable(s)&&i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_list(s))e.throw_error(i.error.type("list",s,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_atom(l))e.throw_error(i.error.type("atom",l,t.indicator));else if(i.type.is_variable(l)){for(var y="",d=s;i.type.is_term(d)&&d.indicator==="./2";){if(!i.type.is_atom(d.args[0])&&!i.type.is_number(d.args[0])){e.throw_error(i.error.type("atomic",d.args[0],t.indicator));return}y!==""&&(y+=a.id),i.type.is_atom(d.args[0])?y+=d.args[0].id:y+=""+d.args[0].value,d=d.args[1]}y=new o(y,[]),i.type.is_variable(d)?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_term(d)||d.indicator!=="[]/0"?e.throw_error(i.error.type("list",s,t.indicator)):e.prepend([new V(n.goal.replace(new o("=",[y,l])),n.substitution,n)])}else{var f=he(c(l.id.split(a.id),function(m){return new o(m,[])}));e.prepend([new V(n.goal.replace(new o("=",[f,s])),n.substitution,n)])}},"@=/2":function(e,n,t){i.compare(t.args[0],t.args[1])>0&&e.success(n)},"@>=/2":function(e,n,t){i.compare(t.args[0],t.args[1])>=0&&e.success(n)},"compare/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(i.type.is_atom(s)&&["<",">","="].indexOf(s.id)===-1)e.throw_error(i.type.domain("order",s,t.indicator));else{var f=i.compare(a,l);f=f===0?"=":f===-1?"<":">",e.prepend([new V(n.goal.replace(new o("=",[s,new o(f,[])])),n.substitution,n)])}},"is/2":function(e,n,t){var s=t.args[1].interpret(e);i.type.is_number(s)?e.prepend([new V(n.goal.replace(new o("=",[t.args[0],s],e.level)),n.substitution,n)]):e.throw_error(s)},"between/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2];if(i.type.is_variable(s)||i.type.is_variable(a))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_integer(s))e.throw_error(i.error.type("integer",s,t.indicator));else if(!i.type.is_integer(a))e.throw_error(i.error.type("integer",a,t.indicator));else if(!i.type.is_variable(l)&&!i.type.is_integer(l))e.throw_error(i.error.type("integer",l,t.indicator));else if(i.type.is_variable(l)){var f=[new V(n.goal.replace(new o("=",[l,s])),n.substitution,n)];s.value=l.value&&e.success(n)},"succ/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)&&i.type.is_variable(a)?e.throw_error(i.error.instantiation(t.indicator)):!i.type.is_variable(s)&&!i.type.is_integer(s)?e.throw_error(i.error.type("integer",s,t.indicator)):!i.type.is_variable(a)&&!i.type.is_integer(a)?e.throw_error(i.error.type("integer",a,t.indicator)):!i.type.is_variable(s)&&s.value<0?e.throw_error(i.error.domain("not_less_than_zero",s,t.indicator)):!i.type.is_variable(a)&&a.value<0?e.throw_error(i.error.domain("not_less_than_zero",a,t.indicator)):(i.type.is_variable(a)||a.value>0)&&(i.type.is_variable(s)?e.prepend([new V(n.goal.replace(new o("=",[s,new E(a.value-1,!1)])),n.substitution,n)]):e.prepend([new V(n.goal.replace(new o("=",[a,new E(s.value+1,!1)])),n.substitution,n)]))},"=:=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s===0&&e.success(n)},"=\\=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s!==0&&e.success(n)},"/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s>0&&e.success(n)},">=/2":function(e,n,t){var s=i.arithmetic_compare(e,t.args[0],t.args[1]);i.type.is_term(s)?e.throw_error(s):s>=0&&e.success(n)},"var/1":function(e,n,t){i.type.is_variable(t.args[0])&&e.success(n)},"atom/1":function(e,n,t){i.type.is_atom(t.args[0])&&e.success(n)},"atomic/1":function(e,n,t){i.type.is_atomic(t.args[0])&&e.success(n)},"compound/1":function(e,n,t){i.type.is_compound(t.args[0])&&e.success(n)},"integer/1":function(e,n,t){i.type.is_integer(t.args[0])&&e.success(n)},"float/1":function(e,n,t){i.type.is_float(t.args[0])&&e.success(n)},"number/1":function(e,n,t){i.type.is_number(t.args[0])&&e.success(n)},"nonvar/1":function(e,n,t){i.type.is_variable(t.args[0])||e.success(n)},"ground/1":function(e,n,t){t.variables().length===0&&e.success(n)},"acyclic_term/1":function(e,n,t){for(var s=n.substitution.apply(n.substitution),a=t.args[0].variables(),l=0;l0?k[k.length-1]:null,k!==null&&(A=U(e,k,0,e.__get_max_priority(),!1))}if(A.type===h&&A.len===k.length-1&&L.value==="."){A=A.value.rename(e);var B=new o("=",[a,A]);if(y.variables){var q=he(c(yr(A.variables()),function(F){return new O(F)}));B=new o(",",[B,new o("=",[y.variables,q])])}if(y.variable_names){var q=he(c(yr(A.variables()),function(H){var J;for(J in e.session.renamed_variables)if(e.session.renamed_variables.hasOwnProperty(J)&&e.session.renamed_variables[J]===H)break;return new o("=",[new o(J,[]),new O(H)])}));B=new o(",",[B,new o("=",[y.variable_names,q])])}if(y.singletons){var q=he(c(new Q(A,null).singleton_variables(),function(H){var J;for(J in e.session.renamed_variables)if(e.session.renamed_variables.hasOwnProperty(J)&&e.session.renamed_variables[J]===H)break;return new o("=",[new o(J,[]),new O(H)])}));B=new o(",",[B,new o("=",[y.singletons,q])])}e.prepend([new V(n.goal.replace(B),n.substitution,n)])}else A.type===h?e.throw_error(i.error.syntax(k[A.len],"unexpected token",!1)):e.throw_error(A.value)}}},"write/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write",[new O("S"),s])])),n.substitution,n)])},"write/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("false",[])]),new o(".",[new o("ignore_ops",[new o("false")]),new o(".",[new o("numbervars",[new o("true")]),new o("[]",[])])])])])),n.substitution,n)])},"writeq/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("writeq",[new O("S"),s])])),n.substitution,n)])},"writeq/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("true",[])]),new o(".",[new o("ignore_ops",[new o("false")]),new o(".",[new o("numbervars",[new o("true")]),new o("[]",[])])])])])),n.substitution,n)])},"write_canonical/1":function(e,n,t){var s=t.args[0];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write_canonical",[new O("S"),s])])),n.substitution,n)])},"write_canonical/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o("write_term",[s,a,new o(".",[new o("quoted",[new o("true",[])]),new o(".",[new o("ignore_ops",[new o("true")]),new o(".",[new o("numbervars",[new o("false")]),new o("[]",[])])])])])),n.substitution,n)])},"write_term/2":function(e,n,t){var s=t.args[0],a=t.args[1];e.prepend([new V(n.goal.replace(new o(",",[new o("current_output",[new O("S")]),new o("write_term",[new O("S"),s,a])])),n.substitution,n)])},"write_term/3":function(e,n,t){var s=t.args[0],a=t.args[1],l=t.args[2],f=i.type.is_stream(s)?s:e.get_stream_by_alias(s.id);if(i.type.is_variable(s)||i.type.is_variable(l))e.throw_error(i.error.instantiation(t.indicator));else if(!i.type.is_list(l))e.throw_error(i.error.type("list",l,t.indicator));else if(!i.type.is_stream(s)&&!i.type.is_atom(s))e.throw_error(i.error.domain("stream_or_alias",s,t.indicator));else if(!i.type.is_stream(f)||f.stream===null)e.throw_error(i.error.existence("stream",s,t.indicator));else if(f.input)e.throw_error(i.error.permission("output","stream",s,t.indicator));else if(f.type==="binary")e.throw_error(i.error.permission("output","binary_stream",s,t.indicator));else if(f.position==="past_end_of_stream"&&f.eof_action==="error")e.throw_error(i.error.permission("output","past_end_of_stream",s,t.indicator));else{for(var y={},d=l,m;i.type.is_term(d)&&d.indicator==="./2";){if(m=d.args[0],i.type.is_variable(m)){e.throw_error(i.error.instantiation(t.indicator));return}else if(!i.type.is_write_option(m)){e.throw_error(i.error.domain("write_option",m,t.indicator));return}y[m.id]=m.args[0].id==="true",d=d.args[1]}if(d.indicator!=="[]/0"){i.type.is_variable(d)?e.throw_error(i.error.instantiation(t.indicator)):e.throw_error(i.error.type("list",l,t.indicator));return}else{y.session=e.session;var S=a.toString(y);f.stream.put(S,f.position),typeof f.position=="number"&&(f.position+=S.length),e.success(n)}}},"halt/0":function(e,n,t){e.points=[]},"halt/1":function(e,n,t){var s=t.args[0];i.type.is_variable(s)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_integer(s)?e.points=[]:e.throw_error(i.error.type("integer",s,t.indicator))},"current_prolog_flag/2":function(e,n,t){var s=t.args[0],a=t.args[1];if(!i.type.is_variable(s)&&!i.type.is_atom(s))e.throw_error(i.error.type("atom",s,t.indicator));else if(!i.type.is_variable(s)&&!i.type.is_flag(s))e.throw_error(i.error.domain("prolog_flag",s,t.indicator));else{var l=[];for(var f in i.flag)if(!!i.flag.hasOwnProperty(f)){var y=new o(",",[new o("=",[new o(f),s]),new o("=",[e.get_flag(f),a])]);l.push(new V(n.goal.replace(y),n.substitution,n))}e.prepend(l)}},"set_prolog_flag/2":function(e,n,t){var s=t.args[0],a=t.args[1];i.type.is_variable(s)||i.type.is_variable(a)?e.throw_error(i.error.instantiation(t.indicator)):i.type.is_atom(s)?i.type.is_flag(s)?i.type.is_value_flag(s,a)?i.type.is_modifiable_flag(s)?(e.session.flag[s.id]=a,e.success(n)):e.throw_error(i.error.permission("modify","flag",s)):e.throw_error(i.error.domain("flag_value",new o("+",[s,a]),t.indicator)):e.throw_error(i.error.domain("prolog_flag",s,t.indicator)):e.throw_error(i.error.type("atom",s,t.indicator))}},flag:{bounded:{allowed:[new o("true"),new o("false")],value:new o("true"),changeable:!1},max_integer:{allowed:[new E(Number.MAX_SAFE_INTEGER)],value:new E(Number.MAX_SAFE_INTEGER),changeable:!1},min_integer:{allowed:[new E(Number.MIN_SAFE_INTEGER)],value:new E(Number.MIN_SAFE_INTEGER),changeable:!1},integer_rounding_function:{allowed:[new o("down"),new o("toward_zero")],value:new o("toward_zero"),changeable:!1},char_conversion:{allowed:[new o("on"),new o("off")],value:new o("on"),changeable:!0},debug:{allowed:[new o("on"),new o("off")],value:new o("off"),changeable:!0},max_arity:{allowed:[new o("unbounded")],value:new o("unbounded"),changeable:!1},unknown:{allowed:[new o("error"),new o("fail"),new o("warning")],value:new o("error"),changeable:!0},double_quotes:{allowed:[new o("chars"),new o("codes"),new o("atom")],value:new o("codes"),changeable:!0},occurs_check:{allowed:[new o("false"),new o("true")],value:new o("false"),changeable:!0},dialect:{allowed:[new o("tau")],value:new o("tau"),changeable:!1},version_data:{allowed:[new o("tau",[new E(r.major,!1),new E(r.minor,!1),new E(r.patch,!1),new o(r.status)])],value:new o("tau",[new E(r.major,!1),new E(r.minor,!1),new E(r.patch,!1),new o(r.status)]),changeable:!1},nodejs:{allowed:[new o("yes"),new o("no")],value:new o(typeof ie!="undefined"&&ie.exports?"yes":"no"),changeable:!1}},unify:function(e,n,t){t=t===void 0?!1:t;for(var s=[{left:e,right:n}],a={};s.length!==0;){var l=s.pop();if(e=l.left,n=l.right,i.type.is_term(e)&&i.type.is_term(n)){if(e.indicator!==n.indicator)return null;for(var f=0;fa.value?1:0:a}else return s},operate:function(e,n){if(i.type.is_operator(n)){for(var t=i.type.is_operator(n),s=[],a,l=!1,f=0;fe.get_flag("max_integer").value||a0?e.start+e.matches[0].length:e.start,a=t?new o("token_not_found"):new o("found",[new o(e.value.toString())]),l=new o(".",[new o("line",[new E(e.line+1)]),new o(".",[new o("column",[new E(s+1)]),new o(".",[a,new o("[]",[])])])]);return new o("error",[new o("syntax_error",[new o(n)]),l])},syntax_by_predicate:function(e,n){return new o("error",[new o("syntax_error",[new o(e)]),ae(n)])}},warning:{singleton:function(e,n,t){for(var s=new o("[]"),a=e.length-1;a>=0;a--)s=new o(".",[new O(e[a]),s]);return new o("warning",[new o("singleton_variables",[s,ae(n)]),new o(".",[new o("line",[new E(t,!1)]),new o("[]")])])},failed_goal:function(e,n){return new o("warning",[new o("failed_goal",[e]),new o(".",[new o("line",[new E(n,!1)]),new o("[]")])])}},format_variable:function(e){return"_"+e},format_answer:function(e,n,t){n instanceof D&&(n=n.thread);var t=t||{};if(t.session=n?n.session:void 0,i.type.is_error(e))return"uncaught exception: "+e.args[0].toString();if(e===!1)return"false.";if(e===null)return"limit exceeded ;";var s=0,a="";if(i.type.is_substitution(e)){var l=e.domain(!0);e=e.filter(function(d,m){return!i.type.is_variable(m)||l.indexOf(m.id)!==-1&&d!==m.id})}for(var f in e.links)!e.links.hasOwnProperty(f)||(s++,a!==""&&(a+=", "),a+=f.toString(t)+" = "+e.links[f].toString(t));var y=typeof n=="undefined"||n.points.length>0?" ;":".";return s===0?"true"+y:a+y},flatten_error:function(e){if(!i.type.is_error(e))return null;e=e.args[0];var n={};return n.type=e.args[0].id,n.thrown=n.type==="syntax_error"?null:e.args[1].id,n.expected=null,n.found=null,n.representation=null,n.existence=null,n.existence_type=null,n.line=null,n.column=null,n.permission_operation=null,n.permission_type=null,n.evaluation_type=null,n.type==="type_error"||n.type==="domain_error"?(n.expected=e.args[0].args[0].id,n.found=e.args[0].args[1].toString()):n.type==="syntax_error"?e.args[1].indicator==="./2"?(n.expected=e.args[0].args[0].id,n.found=e.args[1].args[1].args[1].args[0],n.found=n.found.id==="token_not_found"?n.found.id:n.found.args[0].id,n.line=e.args[1].args[0].args[0].value,n.column=e.args[1].args[1].args[0].args[0].value):n.thrown=e.args[1].id:n.type==="permission_error"?(n.found=e.args[0].args[2].toString(),n.permission_operation=e.args[0].args[0].id,n.permission_type=e.args[0].args[1].id):n.type==="evaluation_error"?n.evaluation_type=e.args[0].args[0].id:n.type==="representation_error"?n.representation=e.args[0].args[0].id:n.type==="existence_error"&&(n.existence=e.args[0].args[1].toString(),n.existence_type=e.args[0].args[0].id),n},create:function(e){return new i.type.Session(e)}};typeof ie!="undefined"?ie.exports=i:window.pl=i})()});var er=I((qu,rt)=>{var is=Array.isArray;rt.exports=is});var nt=I(($u,tt)=>{var ss=typeof global=="object"&&global&&global.Object===Object&&global;tt.exports=ss});var rr=I((Du,it)=>{var as=nt(),os=typeof self=="object"&&self&&self.Object===Object&&self,us=as||os||Function("return this")();it.exports=us});var tr=I((Xu,st)=>{var ls=rr(),cs=ls.Symbol;st.exports=cs});var lt=I((Bu,at)=>{var ot=tr(),ut=Object.prototype,fs=ut.hasOwnProperty,ps=ut.toString,Xe=ot?ot.toStringTag:void 0;function ys(r){var u=fs.call(r,Xe),p=r[Xe];try{r[Xe]=void 0;var c=!0}catch(_){}var w=ps.call(r);return c&&(u?r[Xe]=p:delete r[Xe]),w}at.exports=ys});var ft=I((Fu,ct)=>{var _s=Object.prototype,ws=_s.toString;function gs(r){return ws.call(r)}ct.exports=gs});var Pr=I((zu,pt)=>{var yt=tr(),ds=lt(),vs=ft(),hs="[object Null]",ms="[object Undefined]",_t=yt?yt.toStringTag:void 0;function bs(r){return r==null?r===void 0?ms:hs:_t&&_t in Object(r)?ds(r):vs(r)}pt.exports=bs});var gt=I((Wu,wt)=>{function Ts(r){return r!=null&&typeof r=="object"}wt.exports=Ts});var nr=I((Lu,dt)=>{var xs=Pr(),Vs=gt(),Ss="[object Symbol]";function ks(r){return typeof r=="symbol"||Vs(r)&&xs(r)==Ss}dt.exports=ks});var ht=I((Hu,vt)=>{var Ps=er(),Cs=nr(),Os=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Is=/^\w*$/;function Es(r,u){if(Ps(r))return!1;var p=typeof r;return p=="number"||p=="symbol"||p=="boolean"||r==null||Cs(r)?!0:Is.test(r)||!Os.test(r)||u!=null&&r in Object(u)}vt.exports=Es});var ir=I((Gu,mt)=>{function As(r){var u=typeof r;return r!=null&&(u=="object"||u=="function")}mt.exports=As});var Tt=I((Yu,bt)=>{var Ns=Pr(),Rs=ir(),Ms="[object AsyncFunction]",qs="[object Function]",$s="[object GeneratorFunction]",Ds="[object Proxy]";function Xs(r){if(!Rs(r))return!1;var u=Ns(r);return u==qs||u==$s||u==Ms||u==Ds}bt.exports=Xs});var Vt=I((Uu,xt)=>{var Bs=rr(),Fs=Bs["__core-js_shared__"];xt.exports=Fs});var Pt=I((Zu,St)=>{var Cr=Vt(),kt=function(){var r=/[^.]+$/.exec(Cr&&Cr.keys&&Cr.keys.IE_PROTO||"");return r?"Symbol(src)_1."+r:""}();function zs(r){return!!kt&&kt in r}St.exports=zs});var Ot=I((Qu,Ct)=>{var Ws=Function.prototype,Ls=Ws.toString;function Hs(r){if(r!=null){try{return Ls.call(r)}catch(u){}try{return r+""}catch(u){}}return""}Ct.exports=Hs});var Et=I((Ju,It)=>{var Gs=Tt(),Ys=Pt(),Us=ir(),Zs=Ot(),Qs=/[\\^$.*+?()[\]{}|]/g,Js=/^\[object .+?Constructor\]$/,Ks=Function.prototype,js=Object.prototype,ea=Ks.toString,ra=js.hasOwnProperty,ta=RegExp("^"+ea.call(ra).replace(Qs,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function na(r){if(!Us(r)||Ys(r))return!1;var u=Gs(r)?ta:Js;return u.test(Zs(r))}It.exports=na});var Nt=I((Ku,At)=>{function ia(r,u){return r==null?void 0:r[u]}At.exports=ia});var sr=I((ju,Rt)=>{var sa=Et(),aa=Nt();function oa(r,u){var p=aa(r,u);return sa(p)?p:void 0}Rt.exports=oa});var Be=I((el,Mt)=>{var ua=sr(),la=ua(Object,"create");Mt.exports=la});var Dt=I((rl,qt)=>{var $t=Be();function ca(){this.__data__=$t?$t(null):{},this.size=0}qt.exports=ca});var Bt=I((tl,Xt)=>{function fa(r){var u=this.has(r)&&delete this.__data__[r];return this.size-=u?1:0,u}Xt.exports=fa});var zt=I((nl,Ft)=>{var pa=Be(),ya="__lodash_hash_undefined__",_a=Object.prototype,wa=_a.hasOwnProperty;function ga(r){var u=this.__data__;if(pa){var p=u[r];return p===ya?void 0:p}return wa.call(u,r)?u[r]:void 0}Ft.exports=ga});var Lt=I((il,Wt)=>{var da=Be(),va=Object.prototype,ha=va.hasOwnProperty;function ma(r){var u=this.__data__;return da?u[r]!==void 0:ha.call(u,r)}Wt.exports=ma});var Gt=I((sl,Ht)=>{var ba=Be(),Ta="__lodash_hash_undefined__";function xa(r,u){var p=this.__data__;return this.size+=this.has(r)?0:1,p[r]=ba&&u===void 0?Ta:u,this}Ht.exports=xa});var Ut=I((al,Yt)=>{var Va=Dt(),Sa=Bt(),ka=zt(),Pa=Lt(),Ca=Gt();function Ie(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{function Oa(){this.__data__=[],this.size=0}Zt.exports=Oa});var Or=I((ul,Jt)=>{function Ia(r,u){return r===u||r!==r&&u!==u}Jt.exports=Ia});var Fe=I((ll,Kt)=>{var Ea=Or();function Aa(r,u){for(var p=r.length;p--;)if(Ea(r[p][0],u))return p;return-1}Kt.exports=Aa});var en=I((cl,jt)=>{var Na=Fe(),Ra=Array.prototype,Ma=Ra.splice;function qa(r){var u=this.__data__,p=Na(u,r);if(p<0)return!1;var c=u.length-1;return p==c?u.pop():Ma.call(u,p,1),--this.size,!0}jt.exports=qa});var tn=I((fl,rn)=>{var $a=Fe();function Da(r){var u=this.__data__,p=$a(u,r);return p<0?void 0:u[p][1]}rn.exports=Da});var sn=I((pl,nn)=>{var Xa=Fe();function Ba(r){return Xa(this.__data__,r)>-1}nn.exports=Ba});var on=I((yl,an)=>{var Fa=Fe();function za(r,u){var p=this.__data__,c=Fa(p,r);return c<0?(++this.size,p.push([r,u])):p[c][1]=u,this}an.exports=za});var ln=I((_l,un)=>{var Wa=Qt(),La=en(),Ha=tn(),Ga=sn(),Ya=on();function Ee(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{var Ua=sr(),Za=rr(),Qa=Ua(Za,"Map");cn.exports=Qa});var _n=I((gl,pn)=>{var yn=Ut(),Ja=ln(),Ka=fn();function ja(){this.size=0,this.__data__={hash:new yn,map:new(Ka||Ja),string:new yn}}pn.exports=ja});var gn=I((dl,wn)=>{function eo(r){var u=typeof r;return u=="string"||u=="number"||u=="symbol"||u=="boolean"?r!=="__proto__":r===null}wn.exports=eo});var ze=I((vl,dn)=>{var ro=gn();function to(r,u){var p=r.__data__;return ro(u)?p[typeof u=="string"?"string":"hash"]:p.map}dn.exports=to});var hn=I((hl,vn)=>{var no=ze();function io(r){var u=no(this,r).delete(r);return this.size-=u?1:0,u}vn.exports=io});var bn=I((ml,mn)=>{var so=ze();function ao(r){return so(this,r).get(r)}mn.exports=ao});var xn=I((bl,Tn)=>{var oo=ze();function uo(r){return oo(this,r).has(r)}Tn.exports=uo});var Sn=I((Tl,Vn)=>{var lo=ze();function co(r,u){var p=lo(this,r),c=p.size;return p.set(r,u),this.size+=p.size==c?0:1,this}Vn.exports=co});var Pn=I((xl,kn)=>{var fo=_n(),po=hn(),yo=bn(),_o=xn(),wo=Sn();function Ae(r){var u=-1,p=r==null?0:r.length;for(this.clear();++u{var On=Pn(),go="Expected a function";function Ir(r,u){if(typeof r!="function"||u!=null&&typeof u!="function")throw new TypeError(go);var p=function(){var c=arguments,w=u?u.apply(this,c):c[0],_=p.cache;if(_.has(w))return _.get(w);var v=r.apply(this,c);return p.cache=_.set(w,v)||_,v};return p.cache=new(Ir.Cache||On),p}Ir.Cache=On;Cn.exports=Ir});var An=I((Sl,En)=>{var vo=In(),ho=500;function mo(r){var u=vo(r,function(c){return p.size===ho&&p.clear(),c}),p=u.cache;return u}En.exports=mo});var Rn=I((kl,Nn)=>{var bo=An(),To=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,xo=/\\(\\)?/g,Vo=bo(function(r){var u=[];return r.charCodeAt(0)===46&&u.push(""),r.replace(To,function(p,c,w,_){u.push(w?_.replace(xo,"$1"):c||p)}),u});Nn.exports=Vo});var qn=I((Pl,Mn)=>{function So(r,u){for(var p=-1,c=r==null?0:r.length,w=Array(c);++p{var Dn=tr(),ko=qn(),Po=er(),Co=nr(),Oo=1/0,Xn=Dn?Dn.prototype:void 0,Bn=Xn?Xn.toString:void 0;function Fn(r){if(typeof r=="string")return r;if(Po(r))return ko(r,Fn)+"";if(Co(r))return Bn?Bn.call(r):"";var u=r+"";return u=="0"&&1/r==-Oo?"-0":u}$n.exports=Fn});var Ln=I((Ol,Wn)=>{var Io=zn();function Eo(r){return r==null?"":Io(r)}Wn.exports=Eo});var ar=I((Il,Hn)=>{var Ao=er(),No=ht(),Ro=Rn(),Mo=Ln();function qo(r,u){return Ao(r)?r:No(r,u)?[r]:Ro(Mo(r))}Hn.exports=qo});var or=I((El,Gn)=>{var $o=nr(),Do=1/0;function Xo(r){if(typeof r=="string"||$o(r))return r;var u=r+"";return u=="0"&&1/r==-Do?"-0":u}Gn.exports=Xo});var Er=I((Al,Yn)=>{var Bo=ar(),Fo=or();function zo(r,u){u=Bo(u,r);for(var p=0,c=u.length;r!=null&&p{var Wo=Er();function Lo(r,u,p){var c=r==null?void 0:Wo(r,u);return c===void 0?p:c}Un.exports=Lo});var li=I((Ul,ui)=>{var Jo=sr(),Ko=function(){try{var r=Jo(Object,"defineProperty");return r({},"",{}),r}catch(u){}}();ui.exports=Ko});var pi=I((Zl,ci)=>{var fi=li();function jo(r,u,p){u=="__proto__"&&fi?fi(r,u,{configurable:!0,enumerable:!0,value:p,writable:!0}):r[u]=p}ci.exports=jo});var _i=I((Ql,yi)=>{var eu=pi(),ru=Or(),tu=Object.prototype,nu=tu.hasOwnProperty;function iu(r,u,p){var c=r[u];(!(nu.call(r,u)&&ru(c,p))||p===void 0&&!(u in r))&&eu(r,u,p)}yi.exports=iu});var gi=I((Jl,wi)=>{var su=9007199254740991,au=/^(?:0|[1-9]\d*)$/;function ou(r,u){var p=typeof r;return u=u==null?su:u,!!u&&(p=="number"||p!="symbol"&&au.test(r))&&r>-1&&r%1==0&&r{var uu=_i(),lu=ar(),cu=gi(),vi=ir(),fu=or();function pu(r,u,p,c){if(!vi(r))return r;u=lu(u,r);for(var w=-1,_=u.length,v=_-1,g=r;g!=null&&++w<_;){var h=fu(u[w]),x=p;if(h==="__proto__"||h==="constructor"||h==="prototype")return r;if(w!=v){var T=g[h];x=c?c(T,h,g):void 0,x===void 0&&(x=vi(T)?T:cu(u[w+1])?[]:{})}uu(g,h,x),g=g[h]}return r}di.exports=pu});var bi=I((jl,mi)=>{var yu=hi();function _u(r,u,p){return r==null?r:yu(r,u,p)}mi.exports=_u});var xi=I((ec,Ti)=>{function wu(r){var u=r==null?0:r.length;return u?r[u-1]:void 0}Ti.exports=wu});var Si=I((rc,Vi)=>{function gu(r,u,p){var c=-1,w=r.length;u<0&&(u=-u>w?0:w+u),p=p>w?w:p,p<0&&(p+=w),w=u>p?0:p-u>>>0,u>>>=0;for(var _=Array(w);++c{var du=Er(),vu=Si();function hu(r,u){return u.length<2?r:du(r,vu(u,0,-1))}ki.exports=hu});var Oi=I((nc,Ci)=>{var mu=ar(),bu=xi(),Tu=Pi(),xu=or();function Vu(r,u){return u=mu(u,r),r=Tu(r,u),r==null||delete r[xu(bu(u))]}Ci.exports=Vu});var Ei=I((ic,Ii)=>{var Su=Oi();function ku(r,u){return r==null?!0:Su(r,u)}Ii.exports=ku});var Ou={};Qi(Ou,{default:()=>Eu});var $i=G(require("@yarnpkg/core"));var ni=G(require("@yarnpkg/cli")),ur=G(require("@yarnpkg/core")),ii=G(require("@yarnpkg/core")),Le=G(require("clipanion"));var ue=G(require("@yarnpkg/core")),le=G(require("@yarnpkg/core")),Ne=G(require("@yarnpkg/fslib")),jn=G(Xr()),Re=G(kr());var Nr=G(require("@yarnpkg/core")),Rr=G(Ar()),re=G(kr()),Zn=G(require("vm")),{is_atom:ge,is_variable:Ho,is_instantiated_list:Go}=re.default.type;function Qn(r,u,p){r.prepend(p.map(c=>new re.default.type.State(u.goal.replace(c),u.substitution,u)))}var Jn=new WeakMap;function Mr(r){let u=Jn.get(r.session);if(u==null)throw new Error("Assertion failed: A project should have been registered for the active session");return u}var Yo=new re.default.type.Module("constraints",{["project_workspaces_by_descriptor/3"]:(r,u,p)=>{let[c,w,_]=p.args;if(!ge(c)||!ge(w)){r.throw_error(re.default.error.instantiation(p.indicator));return}let v=Nr.structUtils.parseIdent(c.id),g=Nr.structUtils.makeDescriptor(v,w.id),x=Mr(r).tryWorkspaceByDescriptor(g);Ho(_)&&x!==null&&Qn(r,u,[new re.default.type.Term("=",[_,new re.default.type.Term(String(x.relativeCwd))])]),ge(_)&&x!==null&&x.relativeCwd===_.id&&r.success(u)},["workspace_field/3"]:(r,u,p)=>{let[c,w,_]=p.args;if(!ge(c)||!ge(w)){r.throw_error(re.default.error.instantiation(p.indicator));return}let g=Mr(r).tryWorkspaceByCwd(c.id);if(g==null)return;let h=(0,Rr.default)(g.manifest.raw,w.id);typeof h!="undefined"&&Qn(r,u,[new re.default.type.Term("=",[_,new re.default.type.Term(typeof h=="object"?JSON.stringify(h):h)])])},["workspace_field_test/3"]:(r,u,p)=>{let[c,w,_]=p.args;r.prepend([new re.default.type.State(u.goal.replace(new re.default.type.Term("workspace_field_test",[c,w,_,new re.default.type.Term("[]",[])])),u.substitution,u)])},["workspace_field_test/4"]:(r,u,p)=>{let[c,w,_,v]=p.args;if(!ge(c)||!ge(w)||!ge(_)||!Go(v)){r.throw_error(re.default.error.instantiation(p.indicator));return}let h=Mr(r).tryWorkspaceByCwd(c.id);if(h==null)return;let x=(0,Rr.default)(h.manifest.raw,w.id);if(typeof x=="undefined")return;let T={$$:x};for(let[C,N]of v.toJavaScript().entries())T[`$${C}`]=N;Zn.default.runInNewContext(_.id,T)&&r.success(u)}},["project_workspaces_by_descriptor/3","workspace_field/3","workspace_field_test/3","workspace_field_test/4"]);function Kn(r,u){Jn.set(r,u),r.consult(`:- use_module(library(${Yo.id})).`)}(0,jn.default)(Re.default);var We;(function(c){c.Dependencies="dependencies",c.DevDependencies="devDependencies",c.PeerDependencies="peerDependencies"})(We||(We={}));var ei=[We.Dependencies,We.DevDependencies,We.PeerDependencies];function K(r){if(r instanceof Re.default.type.Num)return r.value;if(r instanceof Re.default.type.Term)switch(r.indicator){case"throw/1":return K(r.args[0]);case"error/1":return K(r.args[0]);case"error/2":if(r.args[0]instanceof Re.default.type.Term&&r.args[0].indicator==="syntax_error/1")return Object.assign(K(r.args[0]),...K(r.args[1]));{let u=K(r.args[0]);return u.message+=` (in ${K(r.args[1])})`,u}case"syntax_error/1":return new ue.ReportError(ue.MessageName.PROLOG_SYNTAX_ERROR,`Syntax error: ${K(r.args[0])}`);case"existence_error/2":return new ue.ReportError(ue.MessageName.PROLOG_EXISTENCE_ERROR,`Existence error: ${K(r.args[0])} ${K(r.args[1])} not found`);case"instantiation_error/0":return new ue.ReportError(ue.MessageName.PROLOG_INSTANTIATION_ERROR,"Instantiation error: an argument is variable when an instantiated argument was expected");case"line/1":return{line:K(r.args[0])};case"column/1":return{column:K(r.args[0])};case"found/1":return{found:K(r.args[0])};case"./2":return[K(r.args[0])].concat(K(r.args[1]));case"//2":return`${K(r.args[0])}/${K(r.args[1])}`;default:return r.id}throw`couldn't pretty print because of unsupported node ${r}`}function ri(r){let u;try{u=K(r)}catch(p){throw typeof p=="string"?new ue.ReportError(ue.MessageName.PROLOG_UNKNOWN_ERROR,`Unknown error: ${r} (note: ${p})`):p}return typeof u.line!="undefined"&&typeof u.column!="undefined"&&(u.message+=` at line ${u.line}, column ${u.column}`),u}var ti=class{constructor(u,p){this.session=Re.default.create(),Kn(this.session,u),this.session.consult(":- use_module(library(lists))."),this.session.consult(p)}fetchNextAnswer(){return new Promise(u=>{this.session.answer(p=>{u(p)})})}async*makeQuery(u){let p=this.session.query(u);if(p!==!0)throw ri(p);for(;;){let c=await this.fetchNextAnswer();if(!c)break;if(c.id==="throw")throw ri(c);yield c}}};function ke(r){return r.id==="null"?null:`${r.toJavaScript()}`}function Uo(r){if(r.id==="null")return null;{let u=r.toJavaScript();if(typeof u!="string")return JSON.stringify(u);try{return JSON.stringify(JSON.parse(u))}catch{return JSON.stringify(u)}}}var pe=class{constructor(u){this.source="";this.project=u;let p=u.configuration.get("constraintsPath");Ne.xfs.existsSync(p)&&(this.source=Ne.xfs.readFileSync(p,"utf8"))}static async find(u){return new pe(u)}getProjectDatabase(){let u="";for(let p of ei)u+=`dependency_type(${p}). +`;for(let p of this.project.workspacesByCwd.values()){let c=p.relativeCwd;u+=`workspace(${de(c)}). +`,u+=`workspace_ident(${de(c)}, ${de(le.structUtils.stringifyIdent(p.locator))}). +`,u+=`workspace_version(${de(c)}, ${de(p.manifest.version)}). +`;for(let w of ei)for(let _ of p.manifest[w].values())u+=`workspace_has_dependency(${de(c)}, ${de(le.structUtils.stringifyIdent(_))}, ${de(_.range)}, ${w}). +`}return u+=`workspace(_) :- false. +`,u+=`workspace_ident(_, _) :- false. +`,u+=`workspace_version(_, _) :- false. +`,u+=`workspace_has_dependency(_, _, _, _) :- false. +`,u}getDeclarations(){let u="";return u+=`gen_enforced_dependency(_, _, _, _) :- false. +`,u+=`gen_enforced_field(_, _, _) :- false. +`,u}get fullSource(){return`${this.getProjectDatabase()} +${this.source} +${this.getDeclarations()}`}createSession(){return new ti(this.project,this.fullSource)}async process(){let u=this.createSession();return{enforcedDependencies:await this.genEnforcedDependencies(u),enforcedFields:await this.genEnforcedFields(u)}}async genEnforcedDependencies(u){let p=[];for await(let c of u.makeQuery("workspace(WorkspaceCwd), dependency_type(DependencyType), gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType).")){let w=Ne.ppath.resolve(this.project.cwd,ke(c.links.WorkspaceCwd)),_=ke(c.links.DependencyIdent),v=ke(c.links.DependencyRange),g=ke(c.links.DependencyType);if(w===null||_===null)throw new Error("Invalid rule");let h=this.project.getWorkspaceByCwd(w),x=le.structUtils.parseIdent(_);p.push({workspace:h,dependencyIdent:x,dependencyRange:v,dependencyType:g})}return le.miscUtils.sortMap(p,[({dependencyRange:c})=>c!==null?"0":"1",({workspace:c})=>le.structUtils.stringifyIdent(c.locator),({dependencyIdent:c})=>le.structUtils.stringifyIdent(c)])}async genEnforcedFields(u){let p=[];for await(let c of u.makeQuery("workspace(WorkspaceCwd), gen_enforced_field(WorkspaceCwd, FieldPath, FieldValue).")){let w=Ne.ppath.resolve(this.project.cwd,ke(c.links.WorkspaceCwd)),_=ke(c.links.FieldPath),v=Uo(c.links.FieldValue);if(w===null||_===null)throw new Error("Invalid rule");let g=this.project.getWorkspaceByCwd(w);p.push({workspace:g,fieldPath:_,fieldValue:v})}return le.miscUtils.sortMap(p,[({workspace:c})=>le.structUtils.stringifyIdent(c.locator),({fieldPath:c})=>c])}async*query(u){let p=this.createSession();for await(let c of p.makeQuery(u)){let w={};for(let[_,v]of Object.entries(c.links))_!=="_"&&(w[_]=ke(v));yield w}}};function de(r){return typeof r=="string"?`'${r}'`:"[]"}var He=class extends ni.BaseCommand{constructor(){super(...arguments);this.json=Le.Option.Boolean("--json",!1,{description:"Format the output as an NDJSON stream"});this.query=Le.Option.String()}async execute(){let u=await ur.Configuration.find(this.context.cwd,this.context.plugins),{project:p}=await ur.Project.find(u,this.context.cwd),c=await pe.find(p),w=this.query;return w.endsWith(".")||(w=`${w}.`),(await ii.StreamReport.start({configuration:u,json:this.json,stdout:this.context.stdout},async v=>{for await(let g of c.query(w)){let h=Array.from(Object.entries(g)),x=h.length,T=h.reduce((b,[C])=>Math.max(b,C.length),0);for(let b=0;b{let v=new Set,g=[];for(let h=0,x=this.fix?10:1;h{await h.persistManifest()}));for(let[h,x]of g)_.reportError(h,x)});return w.hasErrors()?w.exitCode():0}};Ye.paths=[["constraints"]],Ye.usage=fr.Command.Usage({category:"Constraints-related commands",description:"check that the project constraints are met",details:` + This command will run constraints on your project and emit errors for each one that is found but isn't met. If any error is emitted the process will exit with a non-zero exit code. + + If the \`--fix\` flag is used, Yarn will attempt to automatically fix the issues the best it can, following a multi-pass process (with a maximum of 10 iterations). Some ambiguous patterns cannot be autofixed, in which case you'll have to manually specify the right resolution. + + For more information as to how to write constraints, please consult our dedicated page on our website: https://yarnpkg.com/features/constraints. + `,examples:[["Check that all constraints are satisfied","yarn constraints"],["Autofix all unmet constraints","yarn constraints --fix"]]});var qi=Ye;async function Pu(r,u,p,{configuration:c,fix:w}){let _=new Map,v=new Map;for(let{workspace:g,dependencyIdent:h,dependencyRange:x,dependencyType:T}of p){let b=v.get(g);typeof b=="undefined"&&v.set(g,b=new Map);let C=b.get(h.identHash);typeof C=="undefined"&&b.set(h.identHash,C=new Map);let N=C.get(T);typeof N=="undefined"&&C.set(T,N=new Set),_.set(h.identHash,h),N.add(x)}for(let[g,h]of v)for(let[x,T]of h){let b=_.get(x);if(typeof b=="undefined")throw new Error("Assertion failed: The ident should have been registered");for(let[C,N]of T){let W=N.has(null)?[null]:[...N];if(W.length>2)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via conflicting ranges ${W.slice(0,-1).map(ee=>$.structUtils.prettyRange(c,String(ee))).join(", ")}, and ${$.structUtils.prettyRange(c,String(W[W.length-1]))} (in ${C})`]);else if(W.length>1)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via conflicting ranges ${$.structUtils.prettyRange(c,String(W[0]))} and ${$.structUtils.prettyRange(c,String(W[1]))} (in ${C})`]);else{let ee=g.manifest[C].get(b.identHash),[te]=W;te!==null?ee?ee.range!==te&&(w?(g.manifest[C].set(b.identHash,$.structUtils.makeDescriptor(b,te)),r.add(g)):u.push([se.MessageName.CONSTRAINTS_INCOMPATIBLE_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} via ${$.structUtils.prettyRange(c,te)}, but uses ${$.structUtils.prettyRange(c,ee.range)} instead (in ${C})`])):w?(g.manifest[C].set(b.identHash,$.structUtils.makeDescriptor(b,te)),r.add(g)):u.push([se.MessageName.CONSTRAINTS_MISSING_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} must depend on ${$.structUtils.prettyIdent(c,b)} (via ${$.structUtils.prettyRange(c,te)}), but doesn't (in ${C})`]):ee&&(w?(g.manifest[C].delete(b.identHash),r.add(g)):u.push([se.MessageName.CONSTRAINTS_EXTRANEOUS_DEPENDENCY,`${$.structUtils.prettyWorkspace(c,g)} has an extraneous dependency on ${$.structUtils.prettyIdent(c,b)} (in ${C})`]))}}}}async function Cu(r,u,p,{configuration:c,fix:w}){let _=new Map;for(let{workspace:v,fieldPath:g,fieldValue:h}of p){let x=Pe.miscUtils.getMapWithDefault(_,v);Pe.miscUtils.getSetWithDefault(x,g).add(h)}for(let[v,g]of _)for(let[h,x]of g){let T=[...x];if(T.length>2)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to conflicting values ${T.slice(0,-1).map(b=>$.formatUtils.pretty(c,String(b),"magenta")).join(", ")}, or ${$.formatUtils.pretty(c,String(T[T.length-1]),"magenta")}`]);else if(T.length>1)u.push([se.MessageName.CONSTRAINTS_AMBIGUITY,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to conflicting values ${$.formatUtils.pretty(c,String(T[0]),"magenta")} or ${$.formatUtils.pretty(c,String(T[1]),"magenta")}`]);else{let b=(0,Ni.default)(v.manifest.raw,h),[C]=T;C!==null?b===void 0?w?(await qr(v,h,C),r.add(v)):u.push([se.MessageName.CONSTRAINTS_MISSING_FIELD,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,String(C),"magenta")}, but doesn't`]):JSON.stringify(b)!==C&&(w?(await qr(v,h,C),r.add(v)):u.push([se.MessageName.CONSTRAINTS_INCOMPATIBLE_FIELD,`${$.structUtils.prettyWorkspace(c,v)} must have a field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,String(C),"magenta")}, but is set to ${$.formatUtils.pretty(c,JSON.stringify(b),"magenta")} instead`])):b!=null&&(w?(await qr(v,h,null),r.add(v)):u.push([se.MessageName.CONSTRAINTS_EXTRANEOUS_FIELD,`${$.structUtils.prettyWorkspace(c,v)} has an extraneous field ${$.formatUtils.pretty(c,h,"cyan")} set to ${$.formatUtils.pretty(c,JSON.stringify(b),"magenta")}`]))}}}async function qr(r,u,p){p===null?(0,Mi.default)(r.manifest.raw,u):(0,Ri.default)(r.manifest.raw,u,JSON.parse(p))}var Iu={configuration:{constraintsPath:{description:"The path of the constraints file.",type:$i.SettingsType.ABSOLUTE_PATH,default:"./constraints.pro"}},commands:[si,oi,qi]},Eu=Iu;return Ou;})(); +return plugin; +} +}; diff --git a/.yarnrc.yml b/.yarnrc.yml index 24f3369457b2..172a2ce7ddc9 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -22,6 +22,8 @@ packageExtensions: "@babel/preset-env": ^7.1.6 plugins: + - path: .yarn/plugins/@yarnpkg/plugin-constraints.cjs + spec: "@yarnpkg/plugin-constraints" - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs spec: "@yarnpkg/plugin-interactive-tools" - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6388a59fd623..882578a296d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,52 @@ ### Features +### Fixes + +### Chore & Maintenance + +### Performance + +## 28.0.1 + +### Features + +- `[jest-resolve]` Expose `ResolverOptions` type ([#12736](https://github.com/facebook/jest/pull/12736)) +- `[jest-circus]` Add `failing` test modifier that inverts the behaviour of tests ([#12484](https://github.com/facebook/jest/pull/12484)) + +### Fixes + +- `[expect]` Add missing dependency `jest-util` ([#12744](https://github.com/facebook/jest/pull/12744)) +- `[jest-circus]` Improve `test.concurrent` ([#12748](https://github.com/facebook/jest/pull/12748)) +- `[jest-resolve]` Correctly throw an error if `jsdom` test environment is used, but not installed ([#12749](https://github.com/facebook/jest/pull/12749)) + +### Chore & Maintenance + +- `[jest-serializer]` Remove deprecated module from source tree ([#12735](https://github.com/facebook/jest/pull/12735)) + +## 28.0.0 + +### Features + - `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399)) - `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376)) +- `[jest-circus]` Support error logging before retry ([#12201](https://github.com/facebook/jest/pull/12201)) - `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#12484](https://github.com/facebook/jest/pull/12484)) - `[jest-cli, jest-config]` [**BREAKING**] Remove `testURL` config, use `testEnvironmentOptions.url` instead ([#10797](https://github.com/facebook/jest/pull/10797)) - `[jest-cli, jest-core]` Add `--shard` parameter for distributed parallel test execution ([#12546](https://github.com/facebook/jest/pull/12546)) +- `[jest-cli]` [**BREAKING**] Remove undocumented `--timers` option ([#12572](https://github.com/facebook/jest/pull/12572)) - `[jest-config]` [**BREAKING**] Stop shipping `jest-environment-jsdom` by default ([#12354](https://github.com/facebook/jest/pull/12354)) - `[jest-config]` [**BREAKING**] Stop shipping `jest-jasmine2` by default ([#12355](https://github.com/facebook/jest/pull/12355)) - `[jest-config, @jest/types]` Add `ci` to `GlobalConfig` ([#12378](https://github.com/facebook/jest/pull/12378)) - `[jest-config]` [**BREAKING**] Rename `moduleLoader` to `runtime` ([#10817](https://github.com/facebook/jest/pull/10817)) - `[jest-config]` [**BREAKING**] Rename `extraGlobals` to `sandboxInjectedGlobals` ([#10817](https://github.com/facebook/jest/pull/10817)) - `[jest-config]` [**BREAKING**] Throw an error instead of showing a warning if multiple configs are used ([#12510](https://github.com/facebook/jest/pull/12510)) +- `[jest-config]` [**BREAKING**] Do not normalize long deprecated configuration options `preprocessorIgnorePatterns`,`scriptPreprocessor`, `setupTestFrameworkScriptFile` and `testPathDirs` ([#1251270110](https://github.com/facebook/jest/pull/12701)) +- `[jest-cli, jest-core]` Add `--ignoreProjects` CLI argument to ignore test suites by project name ([#12620](https://github.com/facebook/jest/pull/12620)) - `[jest-core]` Pass project config to `globalSetup`/`globalTeardown` function as second argument ([#12440](https://github.com/facebook/jest/pull/12440)) +- `[jest-core]` Stabilize test runners with event emitters ([#12641](https://github.com/facebook/jest/pull/12641)) +- `[jest-core, jest-watcher]` [**BREAKING**] Move `TestWatcher` class to `jest-watcher` package ([#12652](https://github.com/facebook/jest/pull/12652)) +- `[jest-core]` Allow using Summary Reporter as stand-alone reporter ([#12687](https://github.com/facebook/jest/pull/12687)) - `[jest-environment-jsdom]` [**BREAKING**] Upgrade jsdom to 19.0.0 ([#12290](https://github.com/facebook/jest/pull/12290)) - `[jest-environment-jsdom]` [**BREAKING**] Add default `browser` condition to `exportConditions` for `jsdom` environment ([#11924](https://github.com/facebook/jest/pull/11924)) - `[jest-environment-jsdom]` [**BREAKING**] Pass global config to Jest environment constructor for `jsdom` environment ([#12461](https://github.com/facebook/jest/pull/12461)) @@ -21,75 +55,109 @@ - `[jest-environment-node]` [**BREAKING**] Add default `node` and `node-addon` conditions to `exportConditions` for `node` environment ([#11924](https://github.com/facebook/jest/pull/11924)) - `[jest-environment-node]` [**BREAKING**] Pass global config to Jest environment constructor for `node` environment ([#12461](https://github.com/facebook/jest/pull/12461)) - `[jest-environment-node]` [**BREAKING**] Second argument `context` to constructor is mandatory ([#12469](https://github.com/facebook/jest/pull/12469)) +- `[jest-environment-node]` Add all available globals to test globals, not just explicit ones ([#12642](https://github.com/facebook/jest/pull/12642), [#12696](https://github.com/facebook/jest/pull/12696)) - `[@jest/expect]` New module which extends `expect` with `jest-snapshot` matchers ([#12404](https://github.com/facebook/jest/pull/12404), [#12410](https://github.com/facebook/jest/pull/12410), [#12418](https://github.com/facebook/jest/pull/12418)) - `[@jest/expect-utils]` New module exporting utils for `expect` ([#12323](https://github.com/facebook/jest/pull/12323)) +- `[@jest/fake-timers]` [**BREAKING**] Rename `timers` configuration option to `fakeTimers` ([#12572](https://github.com/facebook/jest/pull/12572)) +- `[@jest/fake-timers]` [**BREAKING**] Allow `jest.useFakeTimers()` and `projectConfig.fakeTimers` to take an options bag ([#12572](https://github.com/facebook/jest/pull/12572)) - `[jest-haste-map]` [**BREAKING**] `HasteMap.create` now returns a promise ([#12008](https://github.com/facebook/jest/pull/12008)) - `[jest-haste-map]` Add support for `dependencyExtractor` written in ESM ([#12008](https://github.com/facebook/jest/pull/12008)) - `[jest-mock]` [**BREAKING**] Rename exported utility types `ClassLike`, `FunctionLike`, `ConstructorLikeKeys`, `MethodLikeKeys`, `PropertyLikeKeys`; remove exports of utility types `ArgumentsOf`, `ArgsType`, `ConstructorArgumentsOf` - TS builtin utility types `ConstructorParameters` and `Parameters` should be used instead ([#12435](https://github.com/facebook/jest/pull/12435), [#12489](https://github.com/facebook/jest/pull/12489)) - `[jest-mock]` Improve `isMockFunction` to infer types of passed function ([#12442](https://github.com/facebook/jest/pull/12442)) - `[jest-mock]` [**BREAKING**] Improve the usage of `jest.fn` generic type argument ([#12489](https://github.com/facebook/jest/pull/12489)) - `[jest-mock]` Add support for auto-mocking async generator functions ([#11080](https://github.com/facebook/jest/pull/11080)) +- `[jest-mock]` Add `contexts` member to mock functions ([#12601](https://github.com/facebook/jest/pull/12601)) +- `[@jest/reporters]` Add GitHub Actions reporter ([#11320](https://github.com/facebook/jest/pull/11320), [#12658](https://github.com/facebook/jest/pull/12658)) +- `[@jest/reporters]` Pass `reporterContext` to custom reporter constructors as third argument ([#12657](https://github.com/facebook/jest/pull/12657)) - `[jest-resolve]` [**BREAKING**] Add support for `package.json` `exports` ([#11961](https://github.com/facebook/jest/pull/11961), [#12373](https://github.com/facebook/jest/pull/12373)) +- `[jest-resolve]` Support package self-reference ([#12682](https://github.com/facebook/jest/pull/12682)) - `[jest-resolve, jest-runtime]` Add support for `data:` URI import and mock ([#12392](https://github.com/facebook/jest/pull/12392)) - `[jest-resolve, jest-runtime]` Add support for async resolver ([#11540](https://github.com/facebook/jest/pull/11540)) +- `[jest-resolve]` [**BREAKING**] Remove `browser?: boolean` from resolver options, `conditions: ['browser']` should be used instead ([#12707](https://github.com/facebook/jest/pull/12707)) +- `[jest-resolve]` Expose `JestResolver`, `AsyncResolver`, `SyncResolver`, `PackageFilter`, `PathFilter` and `PackageJSON` types ([#12707](https://github.com/facebook/jest/pull/12707), ([#12712](https://github.com/facebook/jest/pull/12712)) - `[jest-runner]` Allow `setupFiles` module to export an async function ([#12042](https://github.com/facebook/jest/pull/12042)) - `[jest-runner]` Allow passing `testEnvironmentOptions` via docblocks ([#12470](https://github.com/facebook/jest/pull/12470)) +- `[jest-runner]` Expose `CallbackTestRunner`, `EmittingTestRunner` abstract classes and `CallbackTestRunnerInterface`, `EmittingTestRunnerInterface` to help typing third party runners ([#12646](https://github.com/facebook/jest/pull/12646), [#12715](https://github.com/facebook/jest/pull/12715)) +- `[jest-runner]` Lock version of `source-map-support` to 0.5.13 ([#12720](https://github.com/facebook/jest/pull/12720)) - `[jest-runtime]` [**BREAKING**] `Runtime.createHasteMap` now returns a promise ([#12008](https://github.com/facebook/jest/pull/12008)) - `[jest-runtime]` Calling `jest.resetModules` function will clear FS and transform cache ([#12531](https://github.com/facebook/jest/pull/12531)) +- `[jest-runtime]` [**BREAKING**] Remove `Context` type export, it must be imported from `@jest/test-result` ([#12685](https://github.com/facebook/jest/pull/12685)) +- `[jest-runtime]` Add `import.meta.jest` ([#12698](https://github.com/facebook/jest/pull/12698)) - `[@jest/schemas]` New module for JSON schemas for Jest's config ([#12384](https://github.com/facebook/jest/pull/12384)) +- `[@jest/source-map]` Migrate from `source-map` to `@jridgewell/trace-mapping` ([#12692](https://github.com/facebook/jest/pull/12692)) +- `[jest-transform]` [**BREAKING**] Make it required for `process()` and `processAsync()` methods to always return structured data ([#12638](https://github.com/facebook/jest/pull/12638)) - `[jest-test-result]` Add duration property to JSON test output ([#12518](https://github.com/facebook/jest/pull/12518)) - `[jest-watcher]` [**BREAKING**] Make `PatternPrompt` class to take `entityName` as third constructor parameter instead of `this._entityName` ([#12591](https://github.com/facebook/jest/pull/12591)) - `[jest-worker]` [**BREAKING**] Allow only absolute `workerPath` ([#12343](https://github.com/facebook/jest/pull/12343)) +- `[jest-worker]` [**BREAKING**] Default to advanced serialization when using child process workers ([#10983](https://github.com/facebook/jest/pull/10983)) - `[pretty-format]` New `maxWidth` parameter ([#12402](https://github.com/facebook/jest/pull/12402)) -- `[jest-circus]` Add `failing` test modifier that inverts the behaviour of tests ([#12484](https://github.com/facebook/jest/pull/12484)) ### Fixes +- `[*]` Use `sha256` instead of `md5` as hashing algortihm for compatibility with FIPS systems ([#12722](https://github.com/facebook/jest/pull/12722)) +- `[babel-jest]` [**BREAKING**] Pass `rootDir` as `root` in Babel's options ([#12689](https://github.com/facebook/jest/pull/12689)) - `[expect]` Move typings of `.not`, `.rejects` and `.resolves` modifiers outside of `Matchers` interface ([#12346](https://github.com/facebook/jest/pull/12346)) - `[expect]` Throw useful error if `expect.extend` is called with invalid matchers ([#12488](https://github.com/facebook/jest/pull/12488)) - `[expect]` Fix `iterableEquality` ignores other properties ([#8359](https://github.com/facebook/jest/pull/8359)) +- `[expect]` Fix print for the `closeTo` matcher ([#12626](https://github.com/facebook/jest/pull/12626)) +- `[jest-changed-files]` Improve `changedFilesWithAncestor` pattern for Mercurial SCM ([#12322](https://github.com/facebook/jest/pull/12322)) - `[jest-circus, @jest/types]` Disallow undefined value in `TestContext` type ([#12507](https://github.com/facebook/jest/pull/12507)) - `[jest-config]` Correctly detect CI environment and update snapshots accordingly ([#12378](https://github.com/facebook/jest/pull/12378)) - `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#12397](https://github.com/facebook/jest/pull/12397)) +- `[jest-config]` [**BREAKING**] Add `mjs` and `cjs` to default `moduleFileExtensions` config ([#12578](https://github.com/facebook/jest/pull/12578)) - `[jest-config, jest-haste-map]` Allow searching for tests in `node_modules` by exposing `retainAllFiles` ([#11084](https://github.com/facebook/jest/pull/11084)) - `[jest-core]` [**BREAKING**] Exit with status `1` if no tests are found with `--findRelatedTests` flag ([#12487](https://github.com/facebook/jest/pull/12487)) +- `[jest-core]` Do not report unref-ed subprocesses as open handles ([#12705](https://github.com/facebook/jest/pull/12705)) - `[jest-each]` `%#` is not replaced with index of the test case ([#12517](https://github.com/facebook/jest/pull/12517)) +- `[jest-each]` Fixes error message with incorrect count of missing arguments ([#12464](https://github.com/facebook/jest/pull/12464)) - `[jest-environment-jsdom]` Make `jsdom` accessible to extending environments again ([#12232](https://github.com/facebook/jest/pull/12232)) - `[jest-environment-jsdom]` Log JSDOM errors more cleanly ([#12386](https://github.com/facebook/jest/pull/12386)) -- `[jest-environment-node]` Add MessageChannel, MessageEvent to globals ([#12553](https://github.com/facebook/jest/pull/12553)) +- `[jest-environment-node]` Add `MessageChannel`, `MessageEvent` to globals ([#12553](https://github.com/facebook/jest/pull/12553)) +- `[jest-environment-node]` Add `structuredClone` to globals ([#12631](https://github.com/facebook/jest/pull/12631)) - `[@jest/expect-utils]` [**BREAKING**] Fix false positives when looking for `undefined` prop ([#8923](https://github.com/facebook/jest/pull/8923)) - `[jest-haste-map]` Don't use partial results if file crawl errors ([#12420](https://github.com/facebook/jest/pull/12420)) +- `[jest-haste-map]` Make watchman existence check lazy+async ([#12675](https://github.com/facebook/jest/pull/12675)) - `[jest-jasmine2, jest-types]` [**BREAKING**] Move all `jasmine` specific types from `@jest/types` to its own package ([#12125](https://github.com/facebook/jest/pull/12125)) - `[jest-jasmine2]` Do not set `duration` to `0` for skipped tests ([#12518](https://github.com/facebook/jest/pull/12518)) - `[jest-matcher-utils]` Pass maxWidth to `pretty-format` to avoid printing every element in arrays by default ([#12402](https://github.com/facebook/jest/pull/12402)) - `[jest-mock]` Fix function overloads for `spyOn` to allow more correct type inference in complex object ([#12442](https://github.com/facebook/jest/pull/12442)) -- `[jest-reporters]` Notifications generated by the `--notify` flag are no longer persistent in GNOME Shell. ([#11733](https://github.com/facebook/jest/pull/11733)) -- `[@jest-reporters]` Move missing icon file which is needed for `NotifyReporter` class. ([#12593](https://github.com/facebook/jest/pull/12593)) +- `[jest-mock]` Handle overridden `Function.name` property ([#12674](https://github.com/facebook/jest/pull/12674)) +- `[@jest/reporters]` Notifications generated by the `--notify` flag are no longer persistent in GNOME Shell. ([#11733](https://github.com/facebook/jest/pull/11733)) +- `[@jest/reporters]` Move missing icon file which is needed for `NotifyReporter` class. ([#12593](https://github.com/facebook/jest/pull/12593)) +- `[@jest/reporters]` Update `v8-to-istanbul` ([#12697](https://github.com/facebook/jest/pull/12697)) +- `[jest-resolver]` Call custom resolver with core node.js modules ([#12654](https://github.com/facebook/jest/pull/12654)) +- `[jest-runner]` Correctly resolve `source-map-support` ([#12706](https://github.com/facebook/jest/pull/12706)) - `[jest-worker]` Fix `Farm` execution results memory leak ([#12497](https://github.com/facebook/jest/pull/12497)) ### Chore & Maintenance - `[*]` [**BREAKING**] Drop support for Node v10 and v15 and target first LTS `16.13.0` ([#12220](https://github.com/facebook/jest/pull/12220)) -- `[*]` [**BREAKING**] Drop support for `typescript@3.8`, minimum version is now `4.2` ([#11142](https://github.com/facebook/jest/pull/11142)) +- `[*]` [**BREAKING**] Drop support for `typescript@3.8`, minimum version is now `4.3` ([#11142](https://github.com/facebook/jest/pull/11142), [#12648](https://github.com/facebook/jest/pull/12648)) - `[*]` Bundle all `.d.ts` files into a single `index.d.ts` per module ([#12345](https://github.com/facebook/jest/pull/12345)) - `[*]` Use `globalThis` instead of `global` ([#12447](https://github.com/facebook/jest/pull/12447)) +- `[babel-jest]` [**BREAKING**] Only export `createTransformer` ([#12407](https://github.com/facebook/jest/pull/12407)) - `[docs]` Add note about not mixing `done()` with Promises ([#11077](https://github.com/facebook/jest/pull/11077)) - `[docs, examples]` Update React examples to match with the new React guidelines for code examples ([#12217](https://github.com/facebook/jest/pull/12217)) - `[docs]` Add clarity for module factory hoisting limitations ([#12453](https://github.com/facebook/jest/pull/12453)) +- `[docs]` Add more information about how code transformers work ([#12407](https://github.com/facebook/jest/pull/12407)) +- `[docs]` Add upgrading guide ([#12633](https://github.com/facebook/jest/pull/12633)) - `[expect]` [**BREAKING**] Remove support for importing `build/utils` ([#12323](https://github.com/facebook/jest/pull/12323)) - `[expect]` [**BREAKING**] Migrate to ESM ([#12344](https://github.com/facebook/jest/pull/12344)) - `[expect]` [**BREAKING**] Snapshot matcher types are moved to `@jest/expect` ([#12404](https://github.com/facebook/jest/pull/12404)) - `[jest-cli]` Update `yargs` to v17 ([#12357](https://github.com/facebook/jest/pull/12357)) - `[jest-config]` [**BREAKING**] Remove `getTestEnvironment` export ([#12353](https://github.com/facebook/jest/pull/12353)) +- `[jest-config]` [**BREAKING**] Rename config option `name` to `id` ([#11981](https://github.com/facebook/jest/pull/11981)) - `[jest-create-cache-key-function]` Added README.md file with basic usage instructions ([#12492](https://github.com/facebook/jest/pull/12492)) - `[@jest/core]` Use `index.ts` instead of `jest.ts` as main export ([#12329](https://github.com/facebook/jest/pull/12329)) - `[jest-environment-jsdom]` [**BREAKING**] Migrate to ESM ([#12340](https://github.com/facebook/jest/pull/12340)) - `[jest-environment-node]` [**BREAKING**] Migrate to ESM ([#12340](https://github.com/facebook/jest/pull/12340)) +- `[jest-haste-map]` Remove legacy `isRegExpSupported` ([#12676](https://github.com/facebook/jest/pull/12676)) - `[@jest/fake-timers]` Update `@sinonjs/fake_timers` to v9 ([#12357](https://github.com/facebook/jest/pull/12357)) - `[jest-jasmine2, jest-runtime]` [**BREAKING**] Use `Symbol` to pass `jest.setTimeout` value instead of `jasmine` specific logic ([#12124](https://github.com/facebook/jest/pull/12124)) - `[jest-phabricator]` [**BREAKING**] Migrate to ESM ([#12341](https://github.com/facebook/jest/pull/12341)) - `[jest-resolve]` [**BREAKING**] Make `requireResolveFunction` argument mandatory ([#12353](https://github.com/facebook/jest/pull/12353)) - `[jest-runner]` [**BREAKING**] Remove some type exports from `@jest/test-result` ([#12353](https://github.com/facebook/jest/pull/12353)) +- `[jest-runner]` [**BREAKING**] Second argument to constructor (`Context`) is not optional ([#12640](https://github.com/facebook/jest/pull/12640)) - `[jest-serializer]` [**BREAKING**] Deprecate package in favour of using `v8` APIs directly ([#12391](https://github.com/facebook/jest/pull/12391)) - `[jest-snapshot]` [**BREAKING**] Migrate to ESM ([#12342](https://github.com/facebook/jest/pull/12342)) - `[jest-transform]` Update `write-file-atomic` to v4 ([#12357](https://github.com/facebook/jest/pull/12357)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 712dee11e9b8..bdb96e0c6bfd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,6 +129,18 @@ Time: 0.232 s, estimated 1 s Ran all test suites. ``` +## Checking Constraints + +We use [Yarn Constraints](https://yarnpkg.com/features/constraints) to enforce various rules across the repository. They are declared inside the [`constraints.pro` file](https://github.com/facebook/jest/blob/main/constraints.pro) and their purposes are documented with comments. + +Constraints can be checked with `yarn constraints`, and fixed with `yarn constraints --fix`. Generally speaking: + +- Workspaces must not depend on conflicting ranges of dependencies. Use the `-i,--interactive` flag and select "Reuse" when installing dependencies and you shouldn't ever have to deal with this rule. + +- A dependency doesn't appear in both `dependencies` and `devDependencies` of the same workspace. + +- Workspaces must point our repository through the `repository` field. + ##### Using jest-circus There may be cases where you want to run jest using `jest-circus` instead of `jest-jasmine2` (which is the default runner) for integration testing. In situations like this, set the environment variable `JEST_CIRCUS` to 1. That will configure jest to use `jest-circus`. So something like this. diff --git a/README.md b/README.md index f2d8c83d9d1c..18868708d026 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Follow on Twitter

+

+ GitHub CI Status + Coverage Status +

Gitpod ready-to-code

diff --git a/constraints.pro b/constraints.pro new file mode 100644 index 000000000000..2d6fb89342c0 --- /dev/null +++ b/constraints.pro @@ -0,0 +1,82 @@ +constraints_min_version(1). + +% This file is written in Prolog +% It contains rules that the project must respect. +% Check with "yarn constraints" (fix w/ "yarn constraints --fix") +% Yarn Constraints https://yarnpkg.com/features/constraints +% Reference for other constraints: +% https://github.com/babel/babel/blob/main/constraints.pro +% https://github.com/yarnpkg/berry/blob/master/constraints.pro + +% This rule will enforce that a workspace MUST depend on the same version of a dependency as the one used by the other workspaces +gen_enforced_dependency(WorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType) :- + % Iterates over all dependencies from all workspaces + workspace_has_dependency(WorkspaceCwd, DependencyIdent, DependencyRange, DependencyType), + % Iterates over similarly-named dependencies from all workspaces (again) + workspace_has_dependency(OtherWorkspaceCwd, DependencyIdent, DependencyRange2, DependencyType2), + % Ignore peer dependencies + DependencyType \= 'peerDependencies', + DependencyType2 \= 'peerDependencies', + % Ignore workspace:*: we use both `workspace:*` and real version such as `^28.0.0-alpha.8` to reference package in monorepo + % TODO: in the future we should make it consistent and remove this ignore + DependencyRange \= 'workspace:*', + DependencyRange2 \= 'workspace:*', + % Get the workspace name + workspace_ident(WorkspaceCwd, WorkspaceIdent), + workspace_ident(OtherWorkspaceCwd, OtherWorkspaceIdent), + % @types/node in the root need to stay on ~12.12.0 + ( + (WorkspaceIdent = '@jest/monorepo'; OtherWorkspaceIdent = '@jest/monorepo') -> + DependencyIdent \= '@types/node' + ; + true + ), + % Allow enzyme example workspace use a older version react and react-dom, because enzyme don't support react 17 + ( + (WorkspaceIdent = 'example-enzyme'; OtherWorkspaceIdent = 'example-enzyme') -> + \+ member(DependencyIdent, ['react', 'react-dom']) + ; + true + ). + +% Enforces that a dependency doesn't appear in both `dependencies` and `devDependencies` +gen_enforced_dependency(WorkspaceCwd, DependencyIdent, null, 'devDependencies') :- + workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, 'devDependencies'), + workspace_has_dependency(WorkspaceCwd, DependencyIdent, _, 'dependencies'). + +% Enforces the license in all public workspaces while removing it from private workspaces +gen_enforced_field(WorkspaceCwd, 'license', 'MIT') :- + \+ workspace_field(WorkspaceCwd, 'private', true). +gen_enforced_field(WorkspaceCwd, 'license', null) :- + workspace_field(WorkspaceCwd, 'private', true). + +% Enforces the repository field for all public workspaces while removing it from private workspaces +gen_enforced_field(WorkspaceCwd, 'repository.type', 'git') :- + \+ workspace_field(WorkspaceCwd, 'private', true). +gen_enforced_field(WorkspaceCwd, 'repository.url', 'https://github.com/facebook/jest.git') :- + \+ workspace_field(WorkspaceCwd, 'private', true). +gen_enforced_field(WorkspaceCwd, 'repository.directory', WorkspaceCwd) :- + \+ workspace_field(WorkspaceCwd, 'private', true). +gen_enforced_field(WorkspaceCwd, 'repository', null) :- + workspace_field(WorkspaceCwd, 'private', true). + +% Enforces 'publishConfig.access' is set to public for public workspaces while removing it from private workspaces +gen_enforced_field(WorkspaceCwd, 'publishConfig.access', 'public') :- + \+ workspace_field(WorkspaceCwd, 'private', true). +gen_enforced_field(WorkspaceCwd, 'publishConfig.access', null) :- + workspace_field(WorkspaceCwd, 'private', true). + +% Enforces the engines.node field for public workspace +gen_enforced_field(WorkspaceCwd, 'engines.node', '^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0') :- + \+ workspace_field(WorkspaceCwd, 'private', true). + +% Enforces the main and types field to start with ./ +gen_enforced_field(WorkspaceCwd, FieldName, ExpectedValue) :- + % Fields the rule applies to + member(FieldName, ['main', 'types']), + % Get current value + workspace_field(WorkspaceCwd, FieldName, CurrentValue), + % Must not start with ./ already + \+ atom_concat('./', _, CurrentValue), + % Store './' + CurrentValue in ExpectedValue + atom_concat('./', CurrentValue, ExpectedValue). diff --git a/docs/Architecture.md b/docs/Architecture.md index 300abd75d9f0..8d9a7c1d5eba 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -3,26 +3,14 @@ id: architecture title: Architecture --- +import LiteYouTubeEmbed from 'react-lite-youtube-embed'; + If you are interested in learning more about how Jest works, understand its architecture, and how Jest is split up into individual reusable packages, check out this video: - + If you'd like to learn how to build a testing framework like Jest from scratch, check out this video: - + There is also a [written guide you can follow](https://cpojer.net/posts/building-a-javascript-testing-framework). It teaches the fundamental concepts of Jest and explains how various parts of Jest can be used to compose a custom testing framework. diff --git a/docs/CLI.md b/docs/CLI.md index 7263a4676883..d32e327f2474 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -158,7 +158,7 @@ Clearing the cache will reduce performance. ### `--clearMocks` -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. +Automatically clear mock calls, instances, contexts and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. ### `--collectCoverageFrom=` @@ -224,6 +224,10 @@ This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, Show the help information, similar to this page. +### `--ignoreProjects ... ` + +Ignore the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. + ### `--init` Generate a basic configuration file. Based on your project, Jest will ask you a few questions that will help to generate a `jest.config.js` file with a short description for each option. @@ -258,7 +262,7 @@ Run all tests affected by file changes in the last commit made. Behaves similarl ### `--listTests` -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. +Lists all test files that Jest will run given the arguments, and exits. ### `--logHeapUsage` @@ -332,7 +336,7 @@ The default regex matching works fine on small runs, but becomes slow if provide ### `--selectProjects ... ` -Run only the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. +Run the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. ### `--setupFilesAfterEnv ... ` diff --git a/docs/CodeTransformation.md b/docs/CodeTransformation.md index 3b1e0f8e3100..b7ceb9aaeb7a 100644 --- a/docs/CodeTransformation.md +++ b/docs/CodeTransformation.md @@ -3,131 +3,153 @@ id: code-transformation title: Code Transformation --- -Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node.js out of the box (such as JSX, types from TypeScript, Vue templates etc.) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers. +Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node out of the box (such as JSX, TypeScript, Vue templates) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers. -Jest supports this via the [`transform` configuration option](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object). +Jest supports this via the [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) configuration option. -A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. +A transformer is a module that provides a method for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in a code preprocessor that would transpile a future version of JavaScript to a current one. Jest will cache the result of a transformation and attempt to invalidate that result based on a number of factors, such as the source of the file being transformed and changing configuration. ## Defaults -Jest ships with one transformer out of the box - `babel-jest`. It will automatically load your project's Babel configuration and transform any file matching the following RegEx: `/\.[jt]sx?$/` meaning any `.js`, `.jsx`, `.ts` and `.tsx` file. In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). +Jest ships with one transformer out of the box – [`babel-jest`](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup). It will load your project's Babel configuration and transform any file matching the `/\.[jt]sx?$/` RegExp (in other words, any `.js`, `.jsx`, `.ts` or `.tsx` file). In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). -If you override the `transform` configuration option `babel-jest` will no longer be active, and you'll need to add it manually if you wish to use Babel. +:::tip + +Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors: + +```json +"transform": { + "\\.[jt]sx?$": "babel-jest", + "\\.css$": "some-css-transformer", +} +``` + +::: ## Writing custom transformers You can write your own transformer. The API of a transformer is as follows: ```ts -interface SyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ +interface TransformOptions { + supportsDynamicImport: boolean; + supportsExportNamespaceFrom: boolean; + supportsStaticESM: boolean; + supportsTopLevelAwait: boolean; + instrument: boolean; + /** Cached file system which is used by `jest-runtime` to improve performance. */ + cacheFS: Map; + /** Jest configuration of currently running project. */ + config: ProjectConfig; + /** Stringified version of the `config` - useful in cache busting. */ + configString: string; + /** Transformer configuration passed through `transform` option by the user. */ + transformerConfig: TransformerConfig; +} + +type TransformedSource = { + code: string; + map?: RawSourceMap | string | null; +}; + +interface SyncTransformer { canInstrument?: boolean; - createTransformer?: (options?: OptionType) => SyncTransformer; getCacheKey?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => string; getCacheKeyAsync?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => Promise; process: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => TransformedSource; processAsync?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => Promise; } -interface AsyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ +interface AsyncTransformer { canInstrument?: boolean; - createTransformer?: (options?: OptionType) => AsyncTransformer; getCacheKey?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => string; getCacheKeyAsync?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => Promise; process?: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => TransformedSource; processAsync: ( sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, + sourcePath: string, + options: TransformOptions, ) => Promise; } -type Transformer = - | SyncTransformer - | AsyncTransformer; +type Transformer = + | SyncTransformer + | AsyncTransformer; -interface TransformOptions { - /** - * If a transformer does module resolution and reads files, it should populate `cacheFS` so that - * Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of - * - */ - cacheFS: Map; - config: Config.ProjectConfig; - /** A stringified version of the configuration - useful in cache busting */ - configString: string; - instrument: boolean; - // names are copied from babel: https://babeljs.io/docs/en/options#caller - supportsDynamicImport: boolean; - supportsExportNamespaceFrom: boolean; - supportsStaticESM: boolean; - supportsTopLevelAwait: boolean; - /** the options passed through Jest's config by the user */ - transformerConfig: OptionType; -} +type TransformerCreator< + X extends Transformer, + TransformerConfig = unknown, +> = (transformerConfig?: TransformerConfig) => X; -type TransformedSource = - | {code: string; map?: RawSourceMap | string | null} - | string; - -// Config.ProjectConfig can be seen in code [here](https://github.com/facebook/jest/blob/v26.6.3/packages/jest-types/src/Config.ts#L323) -// RawSourceMap comes from [`source-map`](https://github.com/mozilla/source-map/blob/0.6.1/source-map.d.ts#L6-L12) +type TransformerFactory = { + createTransformer: TransformerCreator; +}; ``` -As can be seen, only `process` or `processAsync` is mandatory to implement, although we highly recommend implementing `getCacheKey` as well, so we don't waste resources transpiling the same source file when we can read its previous result from disk. You can use [`@jest/create-cache-key-function`](https://www.npmjs.com/package/@jest/create-cache-key-function) to help implement it. +:::note + +The definitions above were trimmed down for brevity. Full code can be found in [Jest repo on GitHub](https://github.com/facebook/jest/blob/main/packages/jest-transform/src/types.ts) (remember to choose the right tag/commit for your version of Jest). + +::: + +There are a couple of ways you can import code into Jest - using Common JS (`require`) or ECMAScript Modules (`import` - which exists in static and dynamic versions). Jest passes files through code transformation on demand (for instance when a `require` or `import` is evaluated). This process, also known as "transpilation", might happen _synchronously_ (in the case of `require`), or _asynchronously_ (in the case of `import` or `import()`, the latter of which also works from Common JS modules). For this reason, the interface exposes both pairs of methods for asynchronous and synchronous processes: `process{Async}` and `getCacheKey{Async}`. The latter is called to figure out if we need to call `process{Async}` at all. Since async transformation can happen synchronously without issue, it's possible for the async case to "fall back" to the sync variant, but not vice versa. + +So if your code base is ESM only implementing the async variants is sufficient. Otherwise, if any code is loaded through `require` (including `createRequire` from within ESM), then you need to implement the synchronous variant. Be aware that `node_modules` is not transpiled with default config. + +Semi-related to this are the supports flags we pass (see `CallerTransformOptions` above), but those should be used within the transform to figure out if it should return ESM or CJS, and has no direct bearing on sync vs async + +Though not required, we _highly recommend_ implementing `getCacheKey` as well, so we do not waste resources transpiling when we could have read its previous result from disk. You can use [`@jest/create-cache-key-function`](https://www.npmjs.com/package/@jest/create-cache-key-function) to help implement it. + +Instead of having your custom transformer implement the `Transformer` interface directly, you can choose to export `createTransformer`, a factory function to dynamically create transformers. This is to allow having a transformer config in your jest config. Note that [ECMAScript module](ECMAScriptModules.md) support is indicated by the passed in `supports*` options. Specifically `supportsDynamicImport: true` means the transformer can return `import()` expressions, which is supported by both ESM and CJS. If `supportsStaticESM: true` it means top level `import` statements are supported and the code will be interpreted as ESM and not CJS. See [Node's docs](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs) for details on the differences. +:::tip + +Make sure `process{Async}` method returns source map alongside with transformed code, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower. + +During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete cache](Troubleshooting.md#caching-issues). + +::: + ### Examples ### TypeScript with type checking @@ -142,8 +164,10 @@ Importing images is a way to include them in your browser bundle, but they are n const path = require('path'); module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; + process(sourceText, sourcePath, options) { + return { + code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`, + }; }, }; ``` diff --git a/docs/Configuration.md b/docs/Configuration.md index 7f9fa35a1b43..a8b148154fb1 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -150,7 +150,7 @@ Jest attempts to scan your dependency tree once (up-front) and cache it in order Default: `false` -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. +Automatically clear mock calls, instances, contexts and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. ### `collectCoverage` \[boolean] @@ -395,6 +395,108 @@ Jest's ESM support is still experimental, see [its docs for more details](ECMASc } ``` +### `fakeTimers` \[object] + +Default: `{}` + +The fake timers may be useful when a piece of code sets a long timeout that we don't want to wait for in a test. For additional details see [Fake Timers guide](TimerMocks.md) and [API documentation](JestObjectAPI.md#fake-timers). + +This option provides the default configuration of fake timers for all tests. Calling `jest.useFakeTimers()` in a test file will use these options or will override them if a configuration object is passed. For example, you can tell Jest to keep the original implementation of `process.nextTick()` and adjust the limit of recursive timers that will be run: + +```json +{ + "fakeTimers": { + "doNotFake": ["nextTick"], + "timerLimit": 1000 + } +} +``` + +```js title="fakeTime.test.js" +// install fake timers for this file using the options from Jest configuration +jest.useFakeTimers(); + +test('increase the limit of recursive timers for this and following tests', () => { + jest.useFakeTimers({timerLimit: 5000}); + // ... +}); +``` + +:::tip + +Instead of including `jest.useFakeTimers()` in each test file, you can enable fake timers globally for all tests: + +```json +{ + "fakeTimers": { + "enableGlobally": true + } +} +``` + +::: + +Configuration options: + +```ts +type FakeableAPI = + | 'Date' + | 'hrtime' + | 'nextTick' + | 'performance' + | 'queueMicrotask' + | 'requestAnimationFrame' + | 'cancelAnimationFrame' + | 'requestIdleCallback' + | 'cancelIdleCallback' + | 'setImmediate' + | 'clearImmediate' + | 'setInterval' + | 'clearInterval' + | 'setTimeout' + | 'clearTimeout'; + +type ModernFakeTimersConfig = { + /** + * If set to `true` all timers will be advanced automatically by 20 milliseconds + * every 20 milliseconds. A custom time delta may be provided by passing a number. + * The default is `false`. + */ + advanceTimers?: boolean | number; + /** + * List of names of APIs that should not be faked. The default is `[]`, meaning + * all APIs are faked. + */ + doNotFake?: Array; + /** Whether fake timers should be enabled for all test files. The default is `false`. */ + enableGlobally?: boolean; + /** + * Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`. + * The default is `false`. + */ + legacyFakeTimers?: boolean; + /** Sets current system time to be used by fake timers. The default is `Date.now()`. */ + now?: number; + /** Maximum number of recursive timers that will be run. The default is `100_000` timers. */ + timerLimit?: number; +}; +``` + +:::info Legacy Fake Timers + +For some reason you might have to use legacy implementation of fake timers. Here is how to enable it globally (additional options are not supported): + +```json +{ + "fakeTimers": { + "enableGlobally": true, + "legacyFakeTimers": true + } +} +``` + +::: + ### `forceCoverageMatch` \[array<string>] Default: `['']` @@ -571,7 +673,7 @@ An array of directory names to be searched recursively up from the requiring mod ### `moduleFileExtensions` \[array<string>] -Default: `["js", "jsx", "ts", "tsx", "json", "node"]` +Default: `["js", "mjs", "cjs", "jsx", "ts", "tsx", "json", "node"]` An array of file extensions your modules use. If you require modules without specifying a file extension, these are the extensions Jest will look for, in left-to-right order. @@ -720,73 +822,86 @@ When using multi-project runner, it's recommended to add a `displayName` for eac Default: `undefined` -Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs. +Use this configuration option to add reporters to Jest. It must be a list of reporter names, additional options can be passed to a reporter using the tuple form: + +```json +{ + "reporters": [ + "default", + ["/custom-reporter.js", {"banana": "yes", "pineapple": "no"}] + ] +} +``` -If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name. +#### Default Reporter -This will override default reporters: +If custom reporters are specified, the default Jest reporter will be overridden. If you wish to keep it, `'default'` must be passed as a reporters name: ```json { - "reporters": ["/my-custom-reporter.js"] + "reporters": [ + "default", + ["jest-junit", {"outputDirectory": "reports", "outputName": "report.xml"}] + ] } ``` -This will use custom reporter in addition to default reporters that Jest provides: +#### GitHub Actions Reporter + +If included in the list, the built-in GitHub Actions Reporter will annotate changed files with test failure messages: ```json { - "reporters": ["default", "/my-custom-reporter.js"] + "reporters": ["default", "github-actions"] } ``` -Additionally, custom reporters can be configured by passing an `options` object as a second argument: +#### Summary Reporter + +Summary reporter prints out summary of all tests. It is a part of default reporter, hence it will be enabled if `'default'` is included in the list. For instance, you might want to use it as stand-alone reporter instead of the default one, or together with [Silent Reporter](https://github.com/rickhanlonii/jest-silent-reporter): ```json { - "reporters": [ - "default", - ["/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}] - ] + "reporters": ["jest-silent-reporter", "summary"] } ``` -Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments: +#### Custom Reporters + +:::tip -Example reporter: +Hungry for reporters? Take a look at long list of [awesome reporters](https://github.com/jest-community/awesome-jest/blob/main/README.md#reporters) from Awesome Jest. -```js title="my-custom-reporter.js" -class MyCustomReporter { - constructor(globalConfig, options) { +::: + +Custom reporter module must export a class that takes `globalConfig`, `reporterOptions` and `reporterContext` as constructor arguments and implements at least `onRunComplete()` method (for the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts)): + +```js title="custom-reporter.js" +class CustomReporter { + constructor(globalConfig, reporterOptions, reporterContext) { this._globalConfig = globalConfig; - this._options = options; + this._options = reporterOptions; + this._context = reporterContext; } - onRunComplete(contexts, results) { + onRunComplete(testContexts, results) { console.log('Custom reporter output:'); - console.log('GlobalConfig: ', this._globalConfig); - console.log('Options: ', this._options); + console.log('global config: ', this._globalConfig); + console.log('options for this reporter from Jest config: ', this._options); + console.log('reporter context passed from test scheduler: ', this._context); } -} - -module.exports = MyCustomReporter; -// or export default MyCustomReporter; -``` - -Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods -```js -class MyCustomReporter { - // ... + // Optionally, reporters can force Jest to exit with non zero code by returning + // an `Error` from `getLastError()` method. getLastError() { if (this._shouldFail) { - return new Error('my-custom-reporter.js reported an error'); + return new Error('Custom error reported!'); } } } -``` -For the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts) +module.exports = CustomReporter; +``` ### `resetMocks` \[boolean] @@ -804,30 +919,39 @@ By default, each test file gets its own independent module registry. Enabling `r Default: `undefined` -This option allows the use of a custom resolver. This resolver must be a node module that exports _either_: +This option allows the use of a custom resolver. This resolver must be a module that exports _either_: 1. a function expecting a string as the first argument for the path to resolve and an options object as the second argument. The function should either return a path to the module that should be resolved or throw an error if the module can't be found. _or_ 2. an object containing `async` and/or `sync` properties. The `sync` property should be a function with the shape explained above, and the `async` property should also be a function that accepts the same arguments, but returns a promise which resolves with the path to the module or rejects with an error. The options object provided to resolvers has the shape: -```json -{ - "basedir": string, - "conditions": [string], - "defaultResolver": "function(request, options) -> string", - "extensions": [string], - "moduleDirectory": [string], - "paths": [string], - "packageFilter": "function(pkg, pkgdir)", - "pathFilter": "function(pkg, path, relativePath)", - "rootDir": [string] -} +```ts +type ResolverOptions = { + /** Directory to begin resolving from. */ + basedir: string; + /** List of export conditions. */ + conditions?: Array; + /** Instance of default resolver. */ + defaultResolver: (path: string, options: ResolverOptions) => string; + /** List of file extensions to search in order. */ + extensions?: Array; + /** List of directory names to be looked up for modules recursively. */ + moduleDirectory?: Array; + /** List of `require.paths` to use if nothing is found in `node_modules`. */ + paths?: Array; + /** Allows transforming parsed `package.json` contents. */ + packageFilter?: (pkg: PackageJSON, file: string, dir: string) => PackageJSON; + /** Allows transforms a path within a package. */ + pathFilter?: (pkg: PackageJSON, path: string, relativePath: string) => string; + /** Current root directory. */ + rootDir?: string; +}; ``` :::tip -The `defaultResolver` passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom synchronous one, e.g. `(request, options)` and returns a string or throws. +The `defaultResolver` passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom synchronous one, e.g. `(path, options)` and returns a string or throws. ::: @@ -835,10 +959,7 @@ For example, if you want to respect Browserify's [`"browser"` field](https://git ```json { - ... - "jest": { - "resolver": "/resolver.js" - } + "resolver": "/resolver.js" } ``` @@ -850,19 +971,8 @@ module.exports = browserResolve.sync; By combining `defaultResolver` and `packageFilter` we can implement a `package.json` "pre-processor" that allows us to change how the default resolver will resolve modules. For example, imagine we want to use the field `"module"` if it is present, otherwise fallback to `"main"`: -```json -{ - ... - "jest": { - "resolver": "my-module-resolve" - } -} -``` - ```js -// my-module-resolve package - -module.exports = (request, options) => { +module.exports = (path, options) => { // Call the defaultResolver, so we leverage its cache, error handling, etc. return options.defaultResolver(request, { ...options, @@ -993,12 +1103,6 @@ If you want a path to be [relative to the root directory of your project](#rootd For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. -:::info - -`setupTestFrameworkScriptFile` is deprecated in favor of `setupFilesAfterEnv`. - -::: - Example `setupFilesAfterEnv` array in a jest.config.js: ```js @@ -1011,6 +1115,20 @@ Example `jest.setup.js` file ```js jest.setTimeout(10000); // in milliseconds + +// you can even use the setup/teardown methods +beforeAll(() => { + // your code +}); +beforeEach(() => { + // your code +}); +afterEach(() => { + // your code +}); +afterAll(() => { + // your code +}); ``` ### `slowTestThreshold` \[number] @@ -1458,39 +1576,30 @@ Default: `5000` Default timeout of a test in milliseconds. -### `timers` \[string] - -Default: `real` - -Setting this value to `fake` or `modern` enables fake timers for all tests by default. Fake timers are useful when a piece of code sets a long timeout that we don't want to wait for in a test. You can learn more about fake timers [here](JestObjectAPI.md#jestusefaketimersimplementation-modern--legacy). - -If the value is `legacy`, the old implementation will be used as implementation instead of one backed by [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - ### `transform` \[object<string, pathToTransformer | \[pathToTransformer, object]>] Default: `{"\\.[jt]sx?$": "babel-jest"}` -A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/main/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md). +A map from regular expressions to paths to transformers. Optionally, a tuple with configuration options can be passed as second argument: `{filePattern: ['path-to-transformer', {options}]}`. For example, here is how you can configure `babel-jest` for non-default behavior: `{'\\.js$': ['babel-jest', {rootMode: 'upward'}]}`. -Examples of such compilers include: +Jest runs the code of your project as JavaScript, hence a transformer is needed if you use some syntax not supported by Node out of the box (such as JSX, TypeScript, Vue templates). By default, Jest will use [`babel-jest`](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup) transformer, which will load your project's Babel configuration and transform any file matching the `/\.[jt]sx?$/` RegExp (in other words, any `.js`, `.jsx`, `.ts` or `.tsx` file). In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). -- [Babel](https://babeljs.io/) -- [TypeScript](http://www.typescriptlang.org/) -- To build your own please visit the [Custom Transformer](CodeTransformation.md#writing-custom-transformers) section - -You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}` +See the [Code Transformation](CodeTransformation.md) section for more details and instructions on building your own transformer. :::tip -A transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues). - -When adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or TypeScript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup). +Keep in mind that a transformer only runs once per file unless the file has changed. -::: +Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors: -A transformer must be an object with at least a `process` function, and it's also recommended to include a `getCacheKey` function. If your transformer is written in ESM you should have a default export with that object. +```json +"transform": { + "\\.[jt]sx?$": "babel-jest", + "\\.css$": "some-css-transformer", +} +``` -If the tests are written using [native ESM](ECMAScriptModules.md) the transformer can export `processAsync` and `getCacheKeyAsync` instead or in addition to the synchronous variants. +::: ### `transformIgnorePatterns` \[array<string>] diff --git a/docs/ECMAScriptModules.md b/docs/ECMAScriptModules.md index b4ad17d411b4..199b7ff61b8c 100644 --- a/docs/ECMAScriptModules.md +++ b/docs/ECMAScriptModules.md @@ -23,7 +23,7 @@ With the warnings out of the way, this is how you activate ESM support in your t ## Differences between ESM and CommonJS -Most of the differences are explained in [Node's documentation](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs), but in addition to the things mentioned there, Jest injects a special variable into all executed files - the [`jest` object](JestObjectAPI.md). To access this object in ESM, you need to import it from the `@jest/globals` module. +Most of the differences are explained in [Node's documentation](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs), but in addition to the things mentioned there, Jest injects a special variable into all executed files - the [`jest` object](JestObjectAPI.md). To access this object in ESM, you need to import it from the `@jest/globals` module or use `import.meta`. ```js import {jest} from '@jest/globals'; @@ -31,6 +31,11 @@ import {jest} from '@jest/globals'; jest.useFakeTimers(); // etc. + +// alternatively +import.meta.jest.useFakeTimers(); + +// jest === import.meta.jest => true ``` Please note that we currently don't support `jest.mock` in a clean way in ESM, but that is something we intend to add proper support for in the future. Follow [this issue](https://github.com/facebook/jest/issues/10025) for updates. diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index da7a47365058..aa25136d861b 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -67,7 +67,9 @@ test('numeric ranges', () => { }); ``` -_Note_: In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this: +:::note + +In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this: ```ts interface CustomMatchers { @@ -83,6 +85,8 @@ declare global { } ``` +::: + #### Async Matchers `expect.extend` also supports async matchers. Async matchers return a Promise so you will need to await the returned value. Let's use an example matcher to illustrate the usage of them. We are going to implement a matcher called `toBeDivisibleByExternalValue`, where the divisible number is going to be pulled from an external source. @@ -800,7 +804,11 @@ test('drinkEach drinks each drink', () => { }); ``` -Note: the nth argument must be positive integer starting from 1. +:::note + +The nth argument must be positive integer starting from 1. + +::: ### `.toHaveReturned()` @@ -899,7 +907,11 @@ test('drink returns expected nth calls', () => { }); ``` -Note: the nth argument must be positive integer starting from 1. +:::note + +The nth argument must be positive integer starting from 1. + +::: ### `.toHaveLength(number)` @@ -1205,7 +1217,11 @@ describe('the La Croix cans on my desk', () => { }); ``` -> Note: `.toEqual` won't perform a _deep equality_ check for two errors. Only the `message` property of an Error is considered for equality. It is recommended to use the `.toThrow` matcher for testing against errors. +:::tip + +`.toEqual` won't perform a _deep equality_ check for two errors. Only the `message` property of an Error is considered for equality. It is recommended to use the `.toThrow` matcher for testing against errors. + +::: If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, use `equals` method of `Buffer` class to assert whether or not buffers contain the same content: @@ -1340,7 +1356,11 @@ test('throws on octopus', () => { }); ``` -> Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. +:::tip + +You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. + +::: You can provide an optional argument to test that a specific error is thrown: diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index b89edbf87579..870942cac091 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -150,6 +150,10 @@ However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transf [ts-jest](https://github.com/kulshekhar/ts-jest) is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript. +```bash npm2yarn +npm install --save-dev ts-jest +``` + #### Type definitions You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. diff --git a/docs/JestObjectAPI.md b/docs/JestObjectAPI.md index ed99e97fc3d0..3b77e532ab35 100644 --- a/docs/JestObjectAPI.md +++ b/docs/JestObjectAPI.md @@ -570,7 +570,7 @@ test('plays audio', () => { ### `jest.clearAllMocks()` -Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. +Clears the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. Returns the `jest` object for chaining. @@ -628,19 +628,118 @@ test('direct', () => { }); ``` -## Mock Timers +## Fake Timers -### `jest.useFakeTimers(implementation?: 'modern' | 'legacy')` +### `jest.useFakeTimers(fakeTimersConfig?)` -Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate` as well as `Date`). +Instructs Jest to use fake versions of the global date, performance, time and timer APIs. Fake timers implementation is backed by [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). -If you pass `'legacy'` as an argument, Jest's legacy implementation will be used rather than one based on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). +Fake timers will swap out `Date`, `performance.now()`, `queueMicrotask()`, `setImmediate()`, `clearImmediate()`, `setInterval()`, `clearInterval()`, `setTimeout()`, `clearTimeout()` with an implementation that gets its time from the fake clock. + +In Node environment `process.hrtime`, `process.nextTick()` and in JSDOM environment `requestAnimationFrame()`, `cancelAnimationFrame()`, `requestIdleCallback()`, `cancelIdleCallback()` will be replaced as well. + +Configuration options: + +```ts +type FakeableAPI = + | 'Date' + | 'hrtime' + | 'nextTick' + | 'performance' + | 'queueMicrotask' + | 'requestAnimationFrame' + | 'cancelAnimationFrame' + | 'requestIdleCallback' + | 'cancelIdleCallback' + | 'setImmediate' + | 'clearImmediate' + | 'setInterval' + | 'clearInterval' + | 'setTimeout' + | 'clearTimeout'; + +type FakeTimersConfig = { + /** + * If set to `true` all timers will be advanced automatically by 20 milliseconds + * every 20 milliseconds. A custom time delta may be provided by passing a number. + * The default is `false`. + */ + advanceTimers?: boolean | number; + /** + * List of names of APIs that should not be faked. The default is `[]`, meaning + * all APIs are faked. + */ + doNotFake?: Array; + /** + * Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`. + * The default is `false`. + */ + legacyFakeTimers?: boolean; + /** Sets current system time to be used by fake timers. The default is `Date.now()`. */ + now?: number | Date; + /** + * The maximum number of recursive timers that will be run when calling `jest.runAllTimers()`. + * The default is `100_000` timers. + */ + timerLimit?: number; +}; +``` + +Calling `jest.useFakeTimers()` will use fake timers for all tests within the file, until original timers are restored with `jest.useRealTimers()`. + +You can call `jest.useFakeTimers()` or `jest.useRealTimers()` from anywhere: top level, inside an `test` block, etc. Keep in mind that this is a **global operation** and will affect other tests within the same file. Calling `jest.useFakeTimers()` once again in the same test file would reset the internal state (e.g. timer count) and reinstall fake timers using the provided options: + +```js +test('advance the timers automatically', () => { + jest.useFakeTimers({advanceTimers: true}); + // ... +}); + +test('do not advance the timers and do not fake `performance`', () => { + jest.useFakeTimers({doNotFake: ['performance']}); + // ... +}); + +test('uninstall fake timers for the rest of tests in the file', () => { + jest.useRealTimers(); + // ... +}); +``` + +:::info Legacy Fake Timers + +For some reason you might have to use legacy implementation of fake timers. It can be enabled like this (additional options are not supported): + +```js +jest.useFakeTimers({ + legacyFakeTimers: true, +}); +``` + +Legacy fake timers will swap out `setImmediate()`, `clearImmediate()`, `setInterval()`, `clearInterval()`, `setTimeout()`, `clearTimeout()` with Jest [mock functions](MockFunctionAPI.md). In Node environment `process.nextTick()` and in JSDOM environment `requestAnimationFrame()`, `cancelAnimationFrame()` will be also replaced. + +::: Returns the `jest` object for chaining. ### `jest.useRealTimers()` -Instructs Jest to use the real versions of the standard timer functions. +Instructs Jest to restore the original implementations of the global date, performance, time and timer APIs. For example, you may call `jest.useRealTimers()` inside `afterEach` hook to restore timers after each test: + +```js +afterEach(() => { + jest.useRealTimers(); +}); + +test('do something with fake timers', () => { + jest.useFakeTimers(); + // ... +}); + +test('do something with real timers', () => { + // ... +}); +``` Returns the `jest` object for chaining. @@ -662,7 +761,11 @@ This is often useful for synchronously executing setTimeouts during a test in or Exhausts all tasks queued by `setImmediate()`. -> Note: This function is not available when using modern fake timers implementation +:::info + +This function is only available when using legacy fake timers implementation. + +::: ### `jest.advanceTimersByTime(msToRun)` @@ -696,13 +799,21 @@ Returns the number of fake timers still left to run. Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`. -> Note: This function is only available when using modern fake timers implementation +:::info + +This function is not available when using legacy fake timers implementation. + +::: ### `jest.getRealSystemTime()` When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function. -> Note: This function is only available when using modern fake timers implementation +:::info + +This function is not available when using legacy fake timers implementation. + +::: ## Misc @@ -720,9 +831,9 @@ Example: jest.setTimeout(1000); // 1 second ``` -### `jest.retryTimes()` +### `jest.retryTimes(numRetries, options)` -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! This must live at the top-level of a test file or in a describe block. Retries _will not_ work if `jest.retryTimes()` is called in a `beforeEach` or a `test` block. +Runs failed tests n-times until they pass or until the max number of retries is exhausted. `options` are optional. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! This must live at the top-level of a test file or in a describe block. Retries _will not_ work if `jest.retryTimes()` is called in a `beforeEach` or a `test` block. Example in a test: @@ -733,4 +844,13 @@ test('will fail', () => { }); ``` +If `logErrorsBeforeRetry` is enabled, Jest will log the error(s) that caused the test to fail to the console, providing visibility on why a retry occurred. + +```js +jest.retryTimes(3, {logErrorsBeforeRetry: true}); +test('will fail', () => { + expect(true).toBe(false); +}); +``` + Returns the `jest` object for chaining. diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 35a28365e986..0ac4de121f65 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -92,6 +92,27 @@ mockFn.mock.instances[0] === a; // true mockFn.mock.instances[1] === b; // true ``` +### `mockFn.mock.contexts` + +An array that contains the contexts for all calls of the mock function. + +A context is the `this` value that a function receives when called. The context can be set using `Function.prototype.bind`, `Function.prototype.call` or `Function.prototype.apply`. + +For example: + +```js +const mockFn = jest.fn(); + +const boundMockFn = mockFn.bind(thisContext0); +boundMockFn('a', 'b'); +mockFn.call(thisContext1, 'a', 'b'); +mockFn.apply(thisContext2, ['a', 'b']); + +mockFn.mock.contexts[0] === thisContext0; // true +mockFn.mock.contexts[1] === thisContext1; // true +mockFn.mock.contexts[2] === thisContext2; // true +``` + ### `mockFn.mock.lastCall` An array containing the call arguments of the last call that was made to this mock function. If the function was not called, it will return `undefined`. @@ -104,9 +125,9 @@ For example: A mock function `f` that has been called twice, with the arguments ### `mockFn.mockClear()` -Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions. +Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances), [`mockFn.mock.contexts`](#mockfnmockcontexts) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions. -Beware that `mockFn.mockClear()` will replace `mockFn.mock`, not just these three properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data. +Beware that `mockFn.mockClear()` will replace `mockFn.mock`, not just reset the values of its properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data. The [`clearMocks`](configuration#clearmocks-boolean) configuration option is available to clear mocks automatically before each tests. diff --git a/docs/MockFunctions.md b/docs/MockFunctions.md index 38870027a515..d1f1752de2d1 100644 --- a/docs/MockFunctions.md +++ b/docs/MockFunctions.md @@ -43,15 +43,17 @@ expect(mockCallback.mock.results[0].value).toBe(42); All mock functions have this special `.mock` property, which is where data about how the function has been called and what the function returned is kept. The `.mock` property also tracks the value of `this` for each call, so it is possible to inspect this as well: ```javascript -const myMock = jest.fn(); +const myMock1 = jest.fn(); +const a = new myMock1(); +console.log(myMock1.mock.instances); +// > [ ] -const a = new myMock(); +const myMock2 = jest.fn(); const b = {}; -const bound = myMock.bind(b); +const bound = myMock2.bind(b); bound(); - -console.log(myMock.mock.instances); -// > [ , ] +console.log(myMock2.mock.contexts); +// > [ ] ``` These mock members are very useful in tests to assert how these functions get called, instantiated, or what they returned: @@ -69,6 +71,9 @@ expect(someMockFunction.mock.calls[0][1]).toBe('second arg'); // The return value of the first call to the function was 'return value' expect(someMockFunction.mock.results[0].value).toBe('return value'); +// The function was called with a certain `this` context: the `element` object. +expect(someMockFunction.mock.contexts[0]).toBe(element); + // This function was instantiated exactly twice expect(someMockFunction.mock.instances.length).toBe(2); diff --git a/docs/TestingAsyncCode.md b/docs/TestingAsyncCode.md index 432db44b3944..ef8762eb7d87 100644 --- a/docs/TestingAsyncCode.md +++ b/docs/TestingAsyncCode.md @@ -5,18 +5,82 @@ title: Testing Asynchronous Code It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this. -## Callbacks +## Promises + +Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will fail. + +For example, let's say that `fetchData` returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: + +```js +test('the data is peanut butter', () => { + return fetchData().then(data => { + expect(data).toBe('peanut butter'); + }); +}); +``` + +## Async/Await + +Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: + +```js +test('the data is peanut butter', async () => { + const data = await fetchData(); + expect(data).toBe('peanut butter'); +}); -The most common asynchronous pattern is callbacks. +test('the fetch fails with an error', async () => { + expect.assertions(1); + try { + await fetchData(); + } catch (e) { + expect(e).toMatch('error'); + } +}); +``` + +You can combine `async` and `await` with `.resolves` or `.rejects`. + +```js +test('the data is peanut butter', async () => { + await expect(fetchData()).resolves.toBe('peanut butter'); +}); + +test('the fetch fails with an error', async () => { + await expect(fetchData()).rejects.toMatch('error'); +}); +``` + +In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. + +:::caution + +Be sure to return (or `await`) the promise - if you omit the `return`/`await` statement, your test will complete before the promise returned from `fetchData` resolves or rejects. + +::: + +If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. + +```js +test('the fetch fails with an error', () => { + expect.assertions(1); + return fetchData().catch(e => expect(e).toMatch('error')); +}); +``` + +## Callbacks -For example, let's say that you have a `fetchData(callback)` function that fetches some data and calls `callback(data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. +If you don't use promises, you can use callbacks. For example, let's say that `fetchData`, instead of returning a promise, expects a callback, i.e. fetches some data and calls `callback(null, data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. By default, Jest tests complete once they reach the end of their execution. That means this test will _not_ work as intended: ```js // Don't do this! test('the data is peanut butter', () => { - function callback(data) { + function callback(error, data) { + if (error) { + throw error; + } expect(data).toBe('peanut butter'); } @@ -30,7 +94,11 @@ There is an alternate form of `test` that fixes this. Instead of putting the tes ```js test('the data is peanut butter', done => { - function callback(data) { + function callback(error, data) { + if (error) { + done(error); + return; + } try { expect(data).toBe('peanut butter'); done(); @@ -49,31 +117,6 @@ If the `expect` statement fails, it throws an error and `done()` is not called. _Note: `done()` should not be mixed with Promises as this tends to lead to memory leaks in your tests._ -## Promises - -If your code uses promises, there is a more straightforward way to handle asynchronous tests. Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -For example, let's say that `fetchData`, instead of using a callback, returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: - -```js -test('the data is peanut butter', () => { - return fetchData().then(data => { - expect(data).toBe('peanut butter'); - }); -}); -``` - -Be sure to return the promise - if you omit this `return` statement, your test will complete before the promise returned from `fetchData` resolves and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. - -```js -test('the fetch fails with an error', () => { - expect.assertions(1); - return fetchData().catch(e => expect(e).toMatch('error')); -}); -``` - ## `.resolves` / `.rejects` You can also use the `.resolves` matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. @@ -94,38 +137,4 @@ test('the fetch fails with an error', () => { }); ``` -## Async/Await - -Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: - -```js -test('the data is peanut butter', async () => { - const data = await fetchData(); - expect(data).toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - expect.assertions(1); - try { - await fetchData(); - } catch (e) { - expect(e).toMatch('error'); - } -}); -``` - -You can combine `async` and `await` with `.resolves` or `.rejects`. - -```js -test('the data is peanut butter', async () => { - await expect(fetchData()).resolves.toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - await expect(fetchData()).rejects.toMatch('error'); -}); -``` - -In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. - None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler. diff --git a/docs/TestingFrameworks.md b/docs/TestingFrameworks.md index 5b085c8658a3..6ae4358fdf7d 100644 --- a/docs/TestingFrameworks.md +++ b/docs/TestingFrameworks.md @@ -42,4 +42,8 @@ Jest is a universal testing platform, with the ability to adapt to any JavaScrip ## Hapi.js -- [Testing Hapi.js With Jest](http://niralar.com/testing-hapi-js-with-jest/) by Niralar ([Sivasankar](http://sivasankar.in/)) +- [Testing Hapi.js With Jest](https://github.com/sivasankars/testing-hapi.js-with-jest) by Niralar + +## Next.js + +- [Jest and React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library) by Next.js docs diff --git a/docs/TimerMocks.md b/docs/TimerMocks.md index 6f811abee6b4..1e0260c7def6 100644 --- a/docs/TimerMocks.md +++ b/docs/TimerMocks.md @@ -3,11 +3,19 @@ id: timer-mocks title: Timer Mocks --- -The native timer functions (i.e., `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`) are less than ideal for a testing environment since they depend on real time to elapse. Jest can swap out timers with functions that allow you to control the passage of time. [Great Scott!](https://www.youtube.com/watch?v=QZoJ2Pt27BY) +The native timer functions (i.e., `setTimeout()`, `setInterval()`, `clearTimeout()`, `clearInterval()`) are less than ideal for a testing environment since they depend on real time to elapse. Jest can swap out timers with functions that allow you to control the passage of time. [Great Scott!](https://www.youtube.com/watch?v=QZoJ2Pt27BY) -```javascript title="timerGame.js" -'use strict'; +:::info + +Also see [Fake Timers API](JestObjectAPI.md#fake-timers) documentation. + +::: + +## Enable Fake Timers + +In the following example we enable fake timers by calling `jest.useFakeTimers()`. This is replacing the original implementation of `setTimeout()` and other timer functions. Timers can be restored to their normal behavior with `jest.useRealTimers()`. +```javascript title="timerGame.js" function timerGame(callback) { console.log('Ready....go!'); setTimeout(() => { @@ -20,8 +28,6 @@ module.exports = timerGame; ``` ```javascript title="__tests__/timerGame-test.js" -'use strict'; - jest.useFakeTimers(); jest.spyOn(global, 'setTimeout'); @@ -34,27 +40,6 @@ test('waits 1 second before ending the game', () => { }); ``` -Here we enable fake timers by calling `jest.useFakeTimers()`. This mocks out `setTimeout` and other timer functions with mock functions. Timers can be restored to their normal behavior with `jest.useRealTimers()`. - -While you can call `jest.useFakeTimers()` or `jest.useRealTimers()` from anywhere (top level, inside an `it` block, etc.), it is a **global operation** and will affect other tests within the same file. Additionally, you need to call `jest.useFakeTimers()` to reset internal counters before each test. If you plan to not use fake timers in all your tests, you will want to clean up manually, as otherwise the faked timers will leak across tests: - -```javascript -afterEach(() => { - jest.useRealTimers(); -}); - -test('do something with fake timers', () => { - jest.useFakeTimers(); - // ... -}); - -test('do something with real timers', () => { - // ... -}); -``` - -Currently, two implementations of the fake timers are included - `modern` and `legacy`, where `modern` is the default one. See [configuration](Configuration.md#timers-string) for how to configure it. - ## Run All Timers Another test we might want to write for this module is one that asserts that the callback is called after 1 second. To do this, we're going to use Jest's timer control APIs to fast-forward time right in the middle of the test: @@ -81,17 +66,11 @@ test('calls the callback after 1 second', () => { ## Run Pending Timers -There are also scenarios where you might have a recursive timer -- that is a timer that sets a new timer in its own callback. For these, running all the timers would be an endless loop, throwing the following error: +There are also scenarios where you might have a recursive timer – that is a timer that sets a new timer in its own callback. For these, running all the timers would be an endless loop, throwing the following error: "Aborting after running 100000 timers, assuming an infinite loop!" -``` -Ran 100000 timers, and there are still more! Assuming we've hit an infinite recursion and bailing out... -``` - -So something like `jest.runAllTimers()` is not desirable. For these cases you might use `jest.runOnlyPendingTimers()`: +If that is your case, using `jest.runOnlyPendingTimers()` will solve the problem: ```javascript title="infiniteTimerGame.js" -'use strict'; - function infiniteTimerGame(callback) { console.log('Ready....go!'); @@ -110,8 +89,6 @@ module.exports = infiniteTimerGame; ``` ```javascript title="__tests__/infiniteTimerGame-test.js" -'use strict'; - jest.useFakeTimers(); jest.spyOn(global, 'setTimeout'); @@ -142,13 +119,21 @@ describe('infiniteTimerGame', () => { }); ``` +:::note + +For debugging or any other reason you can change the limit of timers that will be run before throwing an error: + +```js +jest.useFakeTimers({timerLimit: 100}); +``` + +::: + ## Advance Timers by Time Another possibility is use `jest.advanceTimersByTime(msToRun)`. When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via setTimeout() or setInterval(), and would be executed during this time frame, will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds. ```javascript title="timerGame.js" -'use strict'; - function timerGame(callback) { console.log('Ready....go!'); setTimeout(() => { @@ -182,4 +167,21 @@ it('calls the callback after 1 second via advanceTimersByTime', () => { Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have `jest.clearAllTimers()`. -The code for this example is available at [examples/timer](https://github.com/facebook/jest/tree/main/examples/timer). +## Selective Faking + +Sometimes your code may require to avoid overwriting the original implementation of one or another API. If that is the case, you can use `doNotFake` option. For example, here is how you could provide a custom mock function for `performance.mark()` in jsdom environment: + +```js +/** + * @jest-environment jsdom + */ + +const mockPerformanceMark = jest.fn(); +window.performance.mark = mockPerformanceMark; + +test('allows mocking `performance.mark()`', () => { + jest.useFakeTimers({doNotFake: ['performance']}); + + expect(window.performance.mark).toBe(mockPerformanceMark); +}); +``` diff --git a/docs/UpgradingToJest28.md b/docs/UpgradingToJest28.md new file mode 100644 index 000000000000..774e5329fc44 --- /dev/null +++ b/docs/UpgradingToJest28.md @@ -0,0 +1,208 @@ +--- +id: upgrading-to-jest28 +title: From v27 to v28 +--- + +Upgrading Jest from v27 to v28? This guide aims to help refactoring your configuration and tests. + +:::info + +See [changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md#2800) for the full list of changes. + +::: + +## Compatibility + +The supported Node versions are 12.13, 14.15, 16.13 and above. + +If you plan to use type definitions of Jest (or any of its packages), make sure to install TypeScript version 4.3 or above. + +## Configuration Options + +### `extraGlobals` + +The `extraGlobals` option was renamed to [`sandboxInjectedGlobals`](Configuration.md#sandboxinjectedglobals-arraystring): + +```diff +- extraGlobals: ['Math'] ++ sandboxInjectedGlobals: ['Math'] +``` + +### `timers` + +The `timers` option was renamed to [`fakeTimers`](Configuration.md#faketimers-object). See [Fake Timers](#fake-timers) section below for details. + +### `testURL` + +The `testURL` option is removed. Now you should use [`testEnvironmentOptions`](Configuration.md#testenvironmentoptions-object) to pass `url` option to JSDOM environment: + +```diff +- testURL: 'https://jestjs.io' ++ testEnvironmentOptions: { ++ url: 'https://jestjs.io' ++ } +``` + +### Babel config + +`babel-jest` now passes `root: config.rootDir` to Babel when resolving configuration. This improves compatibility when using `projects` with differing configuration, but it might mean your babel config isn't picked up in the same way anymore. You can override this option by passing options to `babel-jest` in your [configuration](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object). + +## `expect` + +In versions prior to Jest 28, `toHaveProperty` checked for equality instead of existence, which means that e.g. `expect({}).toHaveProperty('a', undefined)` is a passing test. This has been changed in Jest 28 to fail. + +Additionally, if you import `expect` directly, it has been changed from default export to a named export. + +```diff +- import expect from 'expect'; ++ import {expect} from 'expect'; +``` + +```diff +- const expect = require('expect'); ++ const {expect} = require('expect'); +``` + +## Fake Timers + +Fake timers were refactored to allow passing options to the underlying [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). + +### `fakeTimers` + +The `timers` configuration option was renamed to [`fakeTimers`](Configuration.md#faketimers-object) and now takes an object with options: + +```diff +- timers: 'real' ++ fakeTimers: { ++ enableGlobally: false ++ } +``` + +```diff +- timers: 'fake' ++ fakeTimers: { ++ enableGlobally: true ++ } +``` + +```diff +- timers: 'modern' ++ fakeTimers: { ++ enableGlobally: true ++ } +``` + +```diff +- timers: 'legacy' ++ fakeTimers: { ++ enableGlobally: true, ++ legacyFakeTimers: true ++ } +``` + +### `jest.useFakeTimers()` + +An object with options now should be passed to [`jest.useFakeTimers()`](JestObjectAPI.md#jestusefaketimersfaketimersconfig) as well: + +```diff +- jest.useFakeTimers('modern') ++ jest.useFakeTimers() +``` + +```diff +- jest.useFakeTimers('legacy') ++ jest.useFakeTimers({ ++ legacyFakeTimers: true ++ }) +``` + +If legacy fake timers are enabled in Jest config file, but you would like to disable them in a particular test file: + +```diff +- jest.useFakeTimers('modern') ++ jest.useFakeTimers({ ++ legacyFakeTimers: false ++ }) +``` + +## Test Environment + +### Custom Environment + +The constructor of [test environment](Configuration.md#testenvironment-string) class now receives an object with Jest's `globalConfig` and `projectConfig` as its first argument. The second argument is now mandatory. + +```diff + class CustomEnvironment extends NodeEnvironment { +- constructor(config) { +- super(config); ++ constructor({globalConfig, projectConfig}, context) { ++ super({globalConfig, projectConfig}, context); ++ const config = projectConfig; +``` + +### `jsdom` + +If you are using JSDOM [test environment](Configuration.md#testenvironment-string), `jest-environment-jsdom` package now must be installed separately: + +```bash npm2yarn +npm install --save-dev jest-environment-jsdom +``` + +## Test Runner + +If you are using Jasmine [test runner](Configuration.md#testrunner-string), `jest-jasmine2` package now must be installed separately: + +```bash npm2yarn +npm install --save-dev jest-jasmine2 +``` + +## Transformer + +`process()` and `processAsync()` methods of a custom [transformer module](CodeTransformation.md) cannot return a string anymore. They must always return an object: + +```diff + process(sourceText, sourcePath, options) { +- return `module.exports = ${JSON.stringify(path.basename(sourcePath))};`; ++ return { ++ code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`, ++ }; + } +``` + +## `package.json` `exports` + +Jest now includes full support for [package `exports`](https://nodejs.org/api/packages.html#exports), which might mean that files you import are not resolved correctly. Additionally, Jest now supplies more conditions. `jest-environment-node` has `node` and `node-addons`, while `jest-environment-jsdom` has `browser`. As a result, you might e.g. get browser code which assumes ESM, when Jest provides `['require', 'browser']`. You can either report a bug to the library (or Jest, the implementation is new and might have bugs!) or override the conditions Jest passes. + +## TypeScript + +:::info + +The TypeScript examples from this page will only work as document if you import `jest` from `'@jest/globals'`: + +```ts +import {jest} from '@jest/globals'; +``` + +::: + +### `jest.fn()` + +`jest.fn()` now takes only one generic type argument. See [Mock Functions API](MockFunctionAPI.md) page for more usage examples. + +```diff + import add from './add'; +- const mockAdd = jest.fn, Parameters>(); ++ const mockAdd = jest.fn(); +``` + +```diff +- const mock = jest.fn() ++ const mock = jest.fn<() => number>() + .mockReturnValue(42) + .mockReturnValueOnce(12); + +- const asyncMock = jest.fn, []>() ++ const asyncMock = jest.fn<() => Promise>() + .mockResolvedValue('default') + .mockResolvedValueOnce('first call'); +``` diff --git a/docs/Webpack.md b/docs/Webpack.md index e02c1d323db5..533e5b653ac1 100644 --- a/docs/Webpack.md +++ b/docs/Webpack.md @@ -83,16 +83,16 @@ Then all your className lookups on the styles object will be returned as-is (e.g } ``` -> Notice that Proxy is enabled in Node 6 by default. If you are not on Node 6 yet, make sure you invoke Jest using `node --harmony_proxies node_modules/.bin/jest`. - If `moduleNameMapper` cannot fulfill your requirements, you can use Jest's [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) config option to specify how assets are transformed. For example, a transformer that returns the basename of a file (such that `require('logo.jpg');` returns `'logo'`) can be written as: ```js title="fileTransformer.js" const path = require('path'); module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; + process(sourceText, sourcePath, options) { + return { + code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`, + }; }, }; ``` @@ -112,16 +112,19 @@ module.exports = { We've told Jest to ignore files matching a stylesheet or image extension, and instead, require our mock files. You can adjust the regular expression to match the file types your webpack config handles. -_Note: if you are using babel-jest with additional code preprocessors, you have to explicitly define babel-jest as a transformer for your JavaScript code to map `.js` files to the babel-jest module._ +:::tip + +Remember to include the default `babel-jest` transformer explicitly, if you wish to use it alongside with additional code preprocessors: ```json "transform": { - "\\.js$": "babel-jest", - "\\.css$": "custom-transformer", - ... + "\\.[jt]sx?$": "babel-jest", + "\\.css$": "some-css-transformer", } ``` +::: + ### Configuring Jest to find our files Now that Jest knows how to process our files, we need to tell it how to _find_ them. For webpack's `modulesDirectories`, and `extensions` options there are direct analogs in Jest's `moduleDirectories` and `moduleFileExtensions` options. @@ -186,8 +189,7 @@ That's it! webpack is a complex and flexible tool, so you may have to make some webpack 2 offers native support for ES modules. However, Jest runs in Node, and thus requires ES modules to be transpiled to CommonJS modules. As such, if you are using webpack 2, you most likely will want to configure Babel to transpile ES modules to CommonJS modules only in the `test` environment. -```json -// .babelrc +```json title=".babelrc" { "presets": [["env", {"modules": false}]], @@ -203,8 +205,7 @@ webpack 2 offers native support for ES modules. However, Jest runs in Node, and If you use dynamic imports (`import('some-file.js').then(module => ...)`), you need to enable the `dynamic-import-node` plugin. -```json -// .babelrc +```json title=".babelrc" { "presets": [["env", {"modules": false}]], diff --git a/e2e/__tests__/__snapshots__/customReporters.test.ts.snap b/e2e/__tests__/__snapshots__/customReporters.test.ts.snap index 43299913cba8..7506630b07d2 100644 --- a/e2e/__tests__/__snapshots__/customReporters.test.ts.snap +++ b/e2e/__tests__/__snapshots__/customReporters.test.ts.snap @@ -27,7 +27,11 @@ Object { "called": true, "path": false, }, - "options": Object { + "reporterContext": Object { + "firstRun": true, + "previousSuccess": true, + }, + "reporterOptions": Object { "christoph": "pojer", "dmitrii": "abramov", "hello": "world", @@ -55,7 +59,11 @@ Object { "called": true, "path": false, }, - "options": Object { + "reporterContext": Object { + "firstRun": true, + "previousSuccess": true, + }, + "reporterOptions": Object { "christoph": "pojer", "dmitrii": "abramov", "hello": "world", @@ -97,7 +105,11 @@ Object { "called": true, "path": false, }, - "options": Object {}, + "reporterContext": Object { + "firstRun": true, + "previousSuccess": true, + }, + "reporterOptions": Object {}, } `; @@ -146,7 +158,11 @@ exports[`Custom Reporters Integration valid array format for adding reporters 1` "called": true, "path": false }, - "options": { + "reporterContext": { + "firstRun": true, + "previousSuccess": true + }, + "reporterOptions": { "Aaron Abramov": "Awesome" } }" diff --git a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap index 4c7058473159..82e8281b2cad 100644 --- a/e2e/__tests__/__snapshots__/showConfig.test.ts.snap +++ b/e2e/__tests__/__snapshots__/showConfig.test.ts.snap @@ -16,6 +16,9 @@ exports[`--showConfig outputs config info and exits 1`] = ` "detectOpenHandles": false, "errorOnDeprecated": false, "extensionsToTreatAsEsm": [], + "fakeTimers": { + "enableGlobally": false + }, "forceCoverageMatch": [], "globals": {}, "haste": { @@ -24,12 +27,15 @@ exports[`--showConfig outputs config info and exits 1`] = ` "forceNodeFilesystemAPI": true, "throwOnModuleCollision": false }, + "id": "[md5 hash]", "injectGlobals": true, "moduleDirectories": [ "node_modules" ], "moduleFileExtensions": [ "js", + "mjs", + "cjs", "jsx", "ts", "tsx", @@ -38,7 +44,6 @@ exports[`--showConfig outputs config info and exits 1`] = ` ], "moduleNameMapper": [], "modulePathIgnorePatterns": [], - "name": "[md5 hash]", "prettierPath": "prettier", "resetMocks": false, "resetModules": false, @@ -66,7 +71,6 @@ exports[`--showConfig outputs config info and exits 1`] = ` ], "testRegex": [], "testRunner": "<>/jest-circus/runner.js", - "timers": "real", "transform": [ [ "\\\\.[jt]sx?$", diff --git a/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap index 8ea50879d980..ced47e9dad26 100644 --- a/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap +++ b/e2e/__tests__/__snapshots__/stackTraceSourceMapsWithCoverage.test.ts.snap @@ -15,6 +15,6 @@ exports[`processes stack traces and code frames with source maps with coverage 1 15 | } 16 | - at Object.error (lib.ts:14:9) - at Object. (__tests__/fails.ts:10:3)" + at error (lib.ts:14:9) + at Object. (__tests__/fails.ts:10:8)" `; diff --git a/e2e/__tests__/__snapshots__/testMatch.test.ts.snap b/e2e/__tests__/__snapshots__/testMatch.test.ts.snap new file mode 100644 index 000000000000..ed8312fe8a59 --- /dev/null +++ b/e2e/__tests__/__snapshots__/testMatch.test.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`testMatch should able to match file with cjs and mjs extension 1`] = ` +"Test Suites: 2 passed, 2 total +Tests: 2 passed, 2 total +Snapshots: 0 total +Time: <> +Ran all test suites." +`; diff --git a/e2e/__tests__/__snapshots__/testRetries.test.ts.snap b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap new file mode 100644 index 000000000000..09c1f807a028 --- /dev/null +++ b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test Retries logs error(s) before retry 1`] = ` +"LOGGING RETRY ERRORS retryTimes set + RETRY 1 + + expect(received).toBeFalsy() + + Received: true + + 14 | expect(true).toBeTruthy(); + 15 | } else { + > 16 | expect(true).toBeFalsy(); + | ^ + 17 | } + 18 | }); + 19 | + + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + + RETRY 2 + + expect(received).toBeFalsy() + + Received: true + + 14 | expect(true).toBeTruthy(); + 15 | } else { + > 16 | expect(true).toBeFalsy(); + | ^ + 17 | } + 18 | }); + 19 | + + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + +PASS __tests__/logErrorsBeforeRetries.test.js + ✓ retryTimes set" +`; diff --git a/e2e/__tests__/customReporters.test.ts b/e2e/__tests__/customReporters.test.ts index 8ae42e478d2a..90aca200027b 100644 --- a/e2e/__tests__/customReporters.test.ts +++ b/e2e/__tests__/customReporters.test.ts @@ -142,7 +142,6 @@ describe('Custom Reporters Integration', () => { 'package.json': JSON.stringify({ jest: { reporters: ['default', '/reporter.js'], - testEnvironment: 'node', }, }), 'reporter.js': ` @@ -167,7 +166,6 @@ describe('Custom Reporters Integration', () => { 'package.json': JSON.stringify({ jest: { reporters: ['default', '/reporter.mjs'], - testEnvironment: 'node', }, }), 'reporter.mjs': ` diff --git a/e2e/__tests__/dependencyClash.test.ts b/e2e/__tests__/dependencyClash.test.ts index d6ea667631a1..98fc1267e453 100644 --- a/e2e/__tests__/dependencyClash.test.ts +++ b/e2e/__tests__/dependencyClash.test.ts @@ -64,10 +64,12 @@ test('does not require project modules from inside node_modules', () => { if (!threw) { throw new Error('It used the wrong invariant module!'); } - return script.replace( - 'INVALID CODE FRAGMENT THAT WILL BE REMOVED BY THE TRANSFORMER', - '' - ); + return { + code: script.replace( + 'INVALID CODE FRAGMENT THAT WILL BE REMOVED BY THE TRANSFORMER', + '', + ), + }; }, }; `, diff --git a/e2e/__tests__/detectOpenHandles.ts b/e2e/__tests__/detectOpenHandles.ts index 08c6b8c96b1d..97586bbbbf16 100644 --- a/e2e/__tests__/detectOpenHandles.ts +++ b/e2e/__tests__/detectOpenHandles.ts @@ -11,6 +11,10 @@ function getTextAfterTest(stderr: string) { return (stderr.split(/Ran all test suites(.*)\n/)[2] || '').trim(); } +beforeAll(() => { + jest.retryTimes(3); +}); + it('prints message about flag on slow tests', async () => { const run = runContinuous('detect-open-handles', ['outside']); await run.waitUntil(({stderr}) => @@ -104,6 +108,17 @@ it('does not report timeouts using unref', () => { expect(textAfterTest).toBe(''); }); +it('does not report child_process using unref', () => { + // The test here is basically that it exits cleanly without reporting anything (does not need `until`) + const {stderr} = runJest('detect-open-handles', [ + 'child_process', + '--detectOpenHandles', + ]); + const textAfterTest = getTextAfterTest(stderr); + + expect(textAfterTest).toBe(''); +}); + it('prints out info about open handlers from inside tests', async () => { const run = runContinuous('detect-open-handles', [ 'inside', diff --git a/e2e/__tests__/fakeTimers.test.ts b/e2e/__tests__/fakeTimers.test.ts index ce1afb178804..4adefe0f056c 100644 --- a/e2e/__tests__/fakeTimers.test.ts +++ b/e2e/__tests__/fakeTimers.test.ts @@ -19,13 +19,24 @@ describe('useFakeTimers', () => { const result = runJest('fake-timers/use-fake-timers'); expect(result.exitCode).toBe(0); }); -}); -describe('requestAnimationFrame', () => { - test('fakes requestAnimationFrame', () => { - const result = runJest('fake-timers/request-animation-frame'); + test('allows to pass advanceTimers option', () => { + const result = runJest('fake-timers/advance-timers'); + expect(result.exitCode).toBe(0); + }); - expect(result.stderr).toMatch('requestAnimationFrame test'); + test('allows to pass doNotFake option', () => { + const result = runJest('fake-timers/do-not-fake'); + expect(result.exitCode).toBe(0); + }); + + test('allows to pass timerLimit option', () => { + const result = runJest('fake-timers/timer-limit'); + expect(result.exitCode).toBe(0); + }); + + test('allows clearing not faked timers', () => { + const result = runJest('fake-timers/clear-real-timers'); expect(result.exitCode).toBe(0); }); }); @@ -39,10 +50,19 @@ describe('setImmediate', () => { }); }); +describe('requestAnimationFrame', () => { + test('fakes requestAnimationFrame', () => { + const result = runJest('fake-timers/request-animation-frame'); + + expect(result.stderr).toMatch('requestAnimationFrame test'); + expect(result.exitCode).toBe(0); + }); +}); + describe('useRealTimers', () => { test('restores timers to the native implementation', () => { const result = runJest('fake-timers/use-real-timers'); - expect(result.stdout).toMatch('API is not mocked with fake timers.'); + expect(result.stdout).toMatch('APIs are not replaced with fake timers.'); expect(result.exitCode).toBe(0); }); }); diff --git a/e2e/__tests__/fakeTimersLegacy.test.ts b/e2e/__tests__/fakeTimersLegacy.test.ts index dbc612382ba3..52571da9d4d0 100644 --- a/e2e/__tests__/fakeTimersLegacy.test.ts +++ b/e2e/__tests__/fakeTimersLegacy.test.ts @@ -8,15 +8,22 @@ import runJest from '../runJest'; describe('enableGlobally', () => { - test('enables fake timers from Jest config', () => { + test('enables legacy fake timers from Jest config', () => { const result = runJest('fake-timers-legacy/enable-globally'); expect(result.exitCode).toBe(0); }); }); +describe('legacyFakeTimers', () => { + test('toggles legacy fake timers from Jest config', () => { + const result = runJest('fake-timers-legacy/enable-legacy-fake-timers'); + expect(result.exitCode).toBe(0); + }); +}); + describe('useFakeTimers', () => { - test('enables fake timers from Jest object', () => { - const result = runJest('fake-timers-legacy/use-fake-timers'); + test('enables legacy fake timers from Jest object', () => { + const result = runJest('fake-timers-legacy/use-legacy-fake-timers'); expect(result.exitCode).toBe(0); }); }); @@ -42,7 +49,7 @@ describe('setImmediate', () => { describe('useRealTimers', () => { test('restores timers to the native implementation', () => { const result = runJest('fake-timers-legacy/use-real-timers'); - expect(result.stdout).toMatch('API is not mocked with fake timers.'); + expect(result.stdout).toMatch('APIs are not mocked with fake timers.'); expect(result.exitCode).toBe(0); }); }); diff --git a/e2e/__tests__/hasteMapMockChanged.test.ts b/e2e/__tests__/hasteMapMockChanged.test.ts index 435225064e27..266e24e0536c 100644 --- a/e2e/__tests__/hasteMapMockChanged.test.ts +++ b/e2e/__tests__/hasteMapMockChanged.test.ts @@ -20,10 +20,10 @@ test('should not warn when a mock file changes', async () => { computeSha1: false, extensions: ['js', 'json', 'png'], forceNodeFilesystemAPI: false, + id: `tmp_${Date.now()}`, ignorePattern: / ^/, maxWorkers: 2, mocksPattern: '__mocks__', - name: `tmp_${Date.now()}`, platforms: [], retainAllFiles: false, rootDir: DIR, diff --git a/e2e/__tests__/hasteMapSha1.test.ts b/e2e/__tests__/hasteMapSha1.test.ts index bba942943671..a11e71672e3d 100644 --- a/e2e/__tests__/hasteMapSha1.test.ts +++ b/e2e/__tests__/hasteMapSha1.test.ts @@ -30,10 +30,10 @@ test('exits the process after test are done but before timers complete', async ( computeSha1: true, extensions: ['js', 'json', 'png'], forceNodeFilesystemAPI: true, + id: 'tmp', ignorePattern: / ^/, maxWorkers: 2, mocksPattern: '', - name: 'tmp', platforms: ['ios', 'android'], retainAllFiles: true, rootDir: DIR, diff --git a/e2e/__tests__/hasteMapSize.test.ts b/e2e/__tests__/hasteMapSize.test.ts index e21246b55ad9..e88d6269000c 100644 --- a/e2e/__tests__/hasteMapSize.test.ts +++ b/e2e/__tests__/hasteMapSize.test.ts @@ -24,10 +24,10 @@ afterEach(() => cleanup(DIR)); const options = { extensions: ['js'], forceNodeFilesystemAPI: true, + id: 'tmp', ignorePattern: / ^/, maxWorkers: 2, mocksPattern: '', - name: 'tmp', platforms: [], retainAllFiles: true, rootDir: DIR, diff --git a/e2e/__tests__/jasmineAsync.test.ts b/e2e/__tests__/jasmineAsync.test.ts index 8b6cfb93ec1c..3d716e3171fb 100644 --- a/e2e/__tests__/jasmineAsync.test.ts +++ b/e2e/__tests__/jasmineAsync.test.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {isJestJasmineRun} from '@jest/test-utils'; import runJest, {json as runWithJson} from '../runJest'; describe('async jasmine', () => { @@ -107,16 +108,25 @@ describe('async jasmine', () => { }); it('works with concurrent', () => { - const {json} = runWithJson('jasmine-async', ['concurrent.test.js']); + const {json, stderr} = runWithJson('jasmine-async', ['concurrent.test.js']); expect(json.numTotalTests).toBe(4); expect(json.numPassedTests).toBe(2); expect(json.numFailedTests).toBe(1); expect(json.numPendingTests).toBe(1); expect(json.testResults[0].message).toMatch(/concurrent test fails/); + if (!isJestJasmineRun()) { + expect(stderr.match(/\[\[\w+\]\]/g)).toEqual([ + '[[beforeAll]]', + '[[test]]', + '[[test]]', + '[[test]]', + '[[afterAll]]', + ]); + } }); it('works with concurrent within a describe block when invoked with testNamePattern', () => { - const {json} = runWithJson('jasmine-async', [ + const {json, stderr} = runWithJson('jasmine-async', [ '--testNamePattern', 'one concurrent test fails', 'concurrentWithinDescribe.test.js', @@ -126,6 +136,8 @@ describe('async jasmine', () => { expect(json.numFailedTests).toBe(1); expect(json.numPendingTests).toBe(1); expect(json.testResults[0].message).toMatch(/concurrent test fails/); + expect(stderr).toMatch(/this is logged \d/); + expect(stderr).not.toMatch(/this is not logged \d/); }); it('works with concurrent.each', () => { diff --git a/e2e/__tests__/jestChangedFiles.test.ts b/e2e/__tests__/jestChangedFiles.test.ts index 44251db4acf9..c97f1dd53bbf 100644 --- a/e2e/__tests__/jestChangedFiles.test.ts +++ b/e2e/__tests__/jestChangedFiles.test.ts @@ -47,6 +47,10 @@ function gitCreateBranch(branchName: string, dir: string) { run(`git branch ${branchName}`, dir); } +beforeAll(() => { + jest.retryTimes(3); +}); + beforeEach(() => cleanup(DIR)); afterEach(() => cleanup(DIR)); @@ -358,14 +362,6 @@ test('handles a bad revision for "changedSince", for git', async () => { }); testIfHg('gets changed files for hg', async () => { - if (process.env.CI) { - // Circle and Travis have very old version of hg (v2, and current - // version is v4.2) and its API changed since then and not compatible - // any more. Changing the SCM version on CIs is not trivial, so we'll just - // skip this test and run it only locally. - return; - } - // file1.txt is used to make a multi-line commit message // with `hg commit -l file1.txt`. // This is done to ensure that `changedFiles` only returns files diff --git a/e2e/__tests__/multiProjectRunner.test.ts b/e2e/__tests__/multiProjectRunner.test.ts index b4b42588ccf8..50dd3d04af21 100644 --- a/e2e/__tests__/multiProjectRunner.test.ts +++ b/e2e/__tests__/multiProjectRunner.test.ts @@ -346,7 +346,7 @@ test('resolves projects and their properly', () => { }, }), 'project1.conf.json': JSON.stringify({ - name: 'project1', + id: 'project1', rootDir: './project1', // root dir should be this project's directory setupFiles: ['/project1_setup.js'], @@ -358,7 +358,7 @@ test('resolves projects and their properly', () => { 'project2/__tests__/test.test.js': "test('project2', () => expect(globalThis.project2).toBe(true))", 'project2/project2.conf.json': JSON.stringify({ - name: 'project2', + id: 'project2', rootDir: '../', // root dir is set to the top level setupFiles: ['/project2/project2_setup.js'], // rootDir shold be of the testEnvironment: 'node', @@ -450,7 +450,7 @@ test('Does transform files with the corresponding project transformer', () => { };`, 'project1/transformer.js': ` module.exports = { - process: () => 'module.exports = "PROJECT1";', + process: () => ({code: 'module.exports = "PROJECT1";'}), getCacheKey: () => 'PROJECT1_CACHE_KEY', } `, @@ -465,7 +465,7 @@ test('Does transform files with the corresponding project transformer', () => { };`, 'project2/transformer.js': ` module.exports = { - process: () => 'module.exports = "PROJECT2";', + process: () => ({code: 'module.exports = "PROJECT2";'}), getCacheKey: () => 'PROJECT2_CACHE_KEY', } `, @@ -514,13 +514,13 @@ describe("doesn't bleed module file extensions resolution with multiple workers" expect(configs).toHaveLength(2); - const [{name: name1}, {name: name2}] = configs; + const [{id: id1}, {id: id2}] = configs; - expect(name1).toEqual(expect.any(String)); - expect(name2).toEqual(expect.any(String)); - expect(name1).toHaveLength(32); - expect(name2).toHaveLength(32); - expect(name1).not.toEqual(name2); + expect(id1).toEqual(expect.any(String)); + expect(id2).toEqual(expect.any(String)); + expect(id1).toHaveLength(32); + expect(id2).toHaveLength(32); + expect(id1).not.toEqual(id2); const {stderr} = runJest(DIR, [ '--no-watchman', @@ -557,13 +557,13 @@ describe("doesn't bleed module file extensions resolution with multiple workers" expect(configs).toHaveLength(2); - const [{name: name1}, {name: name2}] = configs; + const [{id: id1}, {id: id2}] = configs; - expect(name1).toEqual(expect.any(String)); - expect(name2).toEqual(expect.any(String)); - expect(name1).toHaveLength(32); - expect(name2).toHaveLength(32); - expect(name1).not.toEqual(name2); + expect(id1).toEqual(expect.any(String)); + expect(id2).toEqual(expect.any(String)); + expect(id1).toHaveLength(32); + expect(id2).toHaveLength(32); + expect(id1).not.toEqual(id2); const {stderr} = runJest(DIR, ['--no-watchman', '-w=2']); @@ -572,3 +572,41 @@ describe("doesn't bleed module file extensions resolution with multiple workers" expect(stderr).toMatch('PASS project2/__tests__/project2.test.js'); }); }); + +describe('Babel config in individual project works in multi-project', () => { + it('Prj-1 works individually', () => { + const result = runJest('multi-project-babel/prj-1'); + expect(result.stderr).toMatch('PASS ./index.test.js'); + expect(result.exitCode).toBe(0); + }); + it('Prj-2 works individually', () => { + const result = runJest('multi-project-babel/prj-2'); + expect(result.stderr).toMatch('PASS ./index.test.js'); + expect(result.exitCode).toBe(0); + }); + it('Prj-3 works individually', () => { + const result = runJest('multi-project-babel/prj-3'); + expect(result.stderr).toMatch('PASS src/index.test.js'); + expect(result.exitCode).toBe(0); + }); + it('Prj-4 works individually', () => { + const result = runJest('multi-project-babel/prj-4'); + expect(result.stderr).toMatch('PASS src/index.test.js'); + expect(result.exitCode).toBe(0); + }); + it('Prj-5 works individually', () => { + const result = runJest('multi-project-babel/prj-5'); + expect(result.stderr).toMatch('PASS src/index.test.js'); + expect(result.exitCode).toBe(0); + }); + it('All project work when running from multiproject', () => { + const result = runJest('multi-project-babel'); + expect(result.stderr).toMatch('PASS prj-1/index.test.js'); + expect(result.stderr).toMatch('PASS prj-2/index.test.js'); + expect(result.stderr).toMatch('PASS prj-3/src/index.test.js'); + expect(result.stderr).toMatch('PASS prj-4/src/index.test.js'); + expect(result.stderr).toMatch('PASS prj-5/src/index.test.js'); + expect(result.stderr).toMatch('PASS prj-3/src/index.test.js'); + expect(result.exitCode).toBe(0); + }); +}); diff --git a/e2e/__tests__/onlyChanged.test.ts b/e2e/__tests__/onlyChanged.test.ts index 34eeee93f793..3cbbb282f039 100644 --- a/e2e/__tests__/onlyChanged.test.ts +++ b/e2e/__tests__/onlyChanged.test.ts @@ -312,13 +312,6 @@ test('onlyChanged in config is overwritten by --all or testPathPattern', () => { }); testIfHg('gets changed files for hg', async () => { - if (process.env.CI) { - // Circle and Travis have very old version of hg (v2, and current - // version is v4.2) and its API changed since then and not compatible - // any more. Changing the SCM version on CIs is not trivial, so we'll just - // skip this test and run it only locally. - return; - } writeFiles(DIR, { '.watchmanconfig': '', '__tests__/file1.test.js': "require('../file1'); test('file1', () => {});", diff --git a/e2e/__tests__/selectProjects.test.ts b/e2e/__tests__/selectProjects.test.ts index 2e48085b6cf1..2928fc8c2b18 100644 --- a/e2e/__tests__/selectProjects.test.ts +++ b/e2e/__tests__/selectProjects.test.ts @@ -111,6 +111,131 @@ describe('Given a config with two named projects, first-project and second-proje ); }); }); + + describe('when Jest is started with `--ignoreProjects first-project', () => { + let result: RunJestJsonResult; + beforeAll(() => { + result = runWithJson('select-projects', [ + '--ignoreProjects', + 'first-project', + ]); + }); + it('runs the tests in the second project only', () => { + expect(result.json).toHaveProperty('success', true); + expect(result.json).toHaveProperty('numTotalTests', 1); + expect(result.json.testResults.map(({name}) => name)).toEqual([ + resolve(dir, '__tests__/second-project.test.js'), + ]); + }); + it('prints that only second-project will run', () => { + expect(result.stderr).toMatch(/^Running one project: second-project/); + }); + }); + + describe('when Jest is started with `--ignoreProjects second-project', () => { + let result: RunJestJsonResult; + beforeAll(() => { + result = runWithJson('select-projects', [ + '--ignoreProjects', + 'second-project', + ]); + }); + it('runs the tests in the first project only', () => { + expect(result.json).toHaveProperty('success', true); + expect(result.json).toHaveProperty('numTotalTests', 1); + expect(result.json.testResults.map(({name}) => name)).toEqual([ + resolve(dir, '__tests__/first-project.test.js'), + ]); + }); + it('prints that only first-project will run', () => { + expect(result.stderr).toMatch(/^Running one project: first-project/); + }); + }); + + describe('when Jest is started with `--ignoreProjects third-project`', () => { + let result: RunJestJsonResult; + beforeAll(() => { + result = runWithJson('select-projects', [ + '--ignoreProjects', + 'third-project', + ]); + }); + it('runs the tests in the first and second projects', () => { + expect(result.json).toHaveProperty('success', true); + expect(result.json).toHaveProperty('numTotalTests', 2); + expect(result.json.testResults.map(({name}) => name).sort()).toEqual([ + resolve(dir, '__tests__/first-project.test.js'), + resolve(dir, '__tests__/second-project.test.js'), + ]); + }); + it('prints that both first-project and second-project will run', () => { + expect(result.stderr).toMatch( + /^Running 2 projects:\n- first-project\n- second-project/, + ); + }); + }); + + describe('when Jest is started with `--ignoreProjects first-project second-project`', () => { + let result: RunJestResult; + beforeAll(() => { + result = run('select-projects', [ + '--ignoreProjects', + 'first-project', + 'second-project', + ]); + }); + it('fails', () => { + expect(result).toHaveProperty('failed', true); + }); + it.skip('prints that no project was found', () => { + expect(result.stdout).toMatch( + /^You provided values for --ignoreProjects, but no projects were found matching the selection/, + ); + }); + }); + + describe('when Jest is started with `--selectProjects first-project second-project --ignoreProjects first-project` ', () => { + let result: RunJestJsonResult; + beforeAll(() => { + result = runWithJson('select-projects', [ + '--selectProjects', + 'first-project', + 'second-project', + '--ignoreProjects', + 'first-project', + ]); + }); + it('runs the tests in the second project only', () => { + expect(result.json).toHaveProperty('success', true); + expect(result.json).toHaveProperty('numTotalTests', 1); + expect(result.json.testResults.map(({name}) => name)).toEqual([ + resolve(dir, '__tests__/second-project.test.js'), + ]); + }); + it('prints that only second-project will run', () => { + expect(result.stderr).toMatch(/^Running one project: second-project/); + }); + }); + + describe('when Jest is started with `--selectProjects first-project --ignoreProjects first-project` ', () => { + let result: RunJestResult; + beforeAll(() => { + result = run('select-projects', [ + '--selectProjects', + 'first-project', + '--ignoreProjects', + 'first-project', + ]); + }); + it('fails', () => { + expect(result).toHaveProperty('failed', true); + }); + it.skip('prints that no project was found', () => { + expect(result.stdout).toMatch( + /^You provided values for --selectProjects and --ignoreProjects, but no projects were found matching the selection./, + ); + }); + }); }); describe('Given a config with two projects, first-project and an unnamed project', () => { @@ -185,4 +310,32 @@ describe('Given a config with two projects, first-project and an unnamed project ); }); }); + + describe('when Jest is started with `--ignoreProjects first-project`', () => { + let result: RunJestJsonResult; + beforeAll(() => { + result = runWithJson('select-projects-missing-name', [ + '--ignoreProjects', + 'first-project', + ]); + }); + it('runs the tests in the second project only', () => { + expect(result.json.success).toBe(true); + expect(result.json.numTotalTests).toBe(1); + expect(result.json.testResults.map(({name}) => name)).toEqual([ + resolve(dir, '__tests__/second-project.test.js'), + ]); + }); + it('prints that a project does not have a name', () => { + expect(result.stderr).toMatch( + /^You provided values for --ignoreProjects but a project does not have a name/, + ); + }); + it('prints that only second-project will run', () => { + const stderrThirdLine = result.stderr.split('\n')[2]; + expect(stderrThirdLine).toMatch( + /^Running one project: /, + ); + }); + }); }); diff --git a/e2e/__tests__/showConfig.test.ts b/e2e/__tests__/showConfig.test.ts index f0c545d28c19..41570e07e1da 100644 --- a/e2e/__tests__/showConfig.test.ts +++ b/e2e/__tests__/showConfig.test.ts @@ -33,7 +33,7 @@ test('--showConfig outputs config info and exits', () => { .replace(/\\\\\.pnp\\\\\.\[\^[/\\]+\]\+\$/g, '<>') .replace(/\\\\(?:([^.]+?)|$)/g, '/$1') .replace(/"cacheDirectory": "(.+)"/g, '"cacheDirectory": "/tmp/jest"') - .replace(/"name": "(.+)"/g, '"name": "[md5 hash]"') + .replace(/"id": "(.+)"/g, '"id": "[md5 hash]"') .replace(/"version": "(.+)"/g, '"version": "[version]"') .replace(/"maxWorkers": (\d+)/g, '"maxWorkers": "[maxWorkers]"') .replace(/"\S*show-config-test/gm, '"<>') diff --git a/e2e/__tests__/testMatch.test.ts b/e2e/__tests__/testMatch.test.ts new file mode 100644 index 000000000000..60b10ca627af --- /dev/null +++ b/e2e/__tests__/testMatch.test.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {extractSummary} from '../Utils'; +import runJest from '../runJest'; + +it('testMatch should able to match file with cjs and mjs extension', () => { + const result = runJest('test-match'); + expect(result.exitCode).toBe(0); + const {summary} = extractSummary(result.stderr); + expect(summary).toMatchSnapshot(); +}); diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index d29faeee59fb..ec938b9231e2 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import * as fs from 'graceful-fs'; import {skipSuiteOnJasmine} from '@jest/test-utils'; +import {extractSummary} from '../Utils'; import runJest from '../runJest'; skipSuiteOnJasmine(); @@ -19,6 +20,7 @@ describe('Test Retries', () => { 'e2e/test-retries/', outputFileName, ); + const logErrorsBeforeRetryErrorMessage = 'LOGGING RETRY ERRORS'; afterAll(() => { fs.unlinkSync(outputFilePath); @@ -29,6 +31,15 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); + expect(result.stderr).not.toContain(logErrorsBeforeRetryErrorMessage); + }); + + it('logs error(s) before retry', () => { + const result = runJest('test-retries', ['logErrorsBeforeRetries.test.js']); + expect(result.exitCode).toEqual(0); + expect(result.failed).toBe(false); + expect(result.stderr).toContain(logErrorsBeforeRetryErrorMessage); + expect(extractSummary(result.stderr).rest).toMatchSnapshot(); }); it('reporter shows more than 1 invocation if test is retried', () => { diff --git a/e2e/babel-plugin-jest-hoist/package.json b/e2e/babel-plugin-jest-hoist/package.json index f8c92b090b70..b55189deec84 100644 --- a/e2e/babel-plugin-jest-hoist/package.json +++ b/e2e/babel-plugin-jest-hoist/package.json @@ -3,7 +3,7 @@ "@babel/preset-env": "^7.0.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-typescript": "^7.0.0", - "react": "*" + "react": "17.0.2" }, "jest": { "automock": true, diff --git a/e2e/babel-plugin-jest-hoist/yarn.lock b/e2e/babel-plugin-jest-hoist/yarn.lock index 8e19431564b8..27bdf2e3a8d7 100644 --- a/e2e/babel-plugin-jest-hoist/yarn.lock +++ b/e2e/babel-plugin-jest-hoist/yarn.lock @@ -1596,7 +1596,7 @@ __metadata: languageName: node linkType: hard -"react@npm:*": +"react@npm:17.0.2": version: 17.0.2 resolution: "react@npm:17.0.2" dependencies: @@ -1703,7 +1703,7 @@ __metadata: "@babel/preset-env": ^7.0.0 "@babel/preset-flow": ^7.0.0 "@babel/preset-typescript": ^7.0.0 - react: "*" + react: 17.0.2 languageName: unknown linkType: soft diff --git a/e2e/coverage-provider-v8/cjs-native-without-sourcemap/package.json b/e2e/coverage-provider-v8/cjs-native-without-sourcemap/package.json index 70ac892f2653..80fef6b071ba 100644 --- a/e2e/coverage-provider-v8/cjs-native-without-sourcemap/package.json +++ b/e2e/coverage-provider-v8/cjs-native-without-sourcemap/package.json @@ -3,7 +3,6 @@ "collectCoverageFrom": [ "/*.js" ], - "transform": { - } + "transform": {} } } diff --git a/e2e/coverage-provider-v8/cjs-with-babel-transformer/babel.config.js b/e2e/coverage-provider-v8/cjs-with-babel-transformer/babel.config.js new file mode 100644 index 000000000000..19dd8862b516 --- /dev/null +++ b/e2e/coverage-provider-v8/cjs-with-babel-transformer/babel.config.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-typescript', + ], +}; diff --git a/e2e/coverage-provider-v8/cjs-with-babel-transformer/package.json b/e2e/coverage-provider-v8/cjs-with-babel-transformer/package.json index cc9f8cec07ef..ef60481c0092 100644 --- a/e2e/coverage-provider-v8/cjs-with-babel-transformer/package.json +++ b/e2e/coverage-provider-v8/cjs-with-babel-transformer/package.json @@ -1,10 +1,4 @@ { - "babel": { - "presets": [ - ["@babel/preset-env", {"targets": {"node": "current"}}], - "@babel/preset-typescript" - ] - }, "jest": { "collectCoverageFrom": [ "/*.ts" diff --git a/e2e/coverage-provider-v8/esm-native-without-sourcemap/package.json b/e2e/coverage-provider-v8/esm-native-without-sourcemap/package.json index aba08ce1d284..f0745e4dcac1 100644 --- a/e2e/coverage-provider-v8/esm-native-without-sourcemap/package.json +++ b/e2e/coverage-provider-v8/esm-native-without-sourcemap/package.json @@ -4,7 +4,6 @@ "collectCoverageFrom": [ "/*.js" ], - "transform": { - } + "transform": {} } } diff --git a/e2e/coverage-provider-v8/esm-with-custom-transformer/typescriptPreprocessor.js b/e2e/coverage-provider-v8/esm-with-custom-transformer/typescriptPreprocessor.js index 3fec16a6891b..1fdcb803c166 100644 --- a/e2e/coverage-provider-v8/esm-with-custom-transformer/typescriptPreprocessor.js +++ b/e2e/coverage-provider-v8/esm-with-custom-transformer/typescriptPreprocessor.js @@ -21,6 +21,6 @@ export default { return {code: outputText, map: sourceMapText}; } - return sourceText; + return {code: sourceText}; }, }; diff --git a/e2e/coverage-provider-v8/no-sourcemap/cssTransform.js b/e2e/coverage-provider-v8/no-sourcemap/cssTransform.js index c973ad34fe44..4772932d79dd 100644 --- a/e2e/coverage-provider-v8/no-sourcemap/cssTransform.js +++ b/e2e/coverage-provider-v8/no-sourcemap/cssTransform.js @@ -7,5 +7,5 @@ module.exports = { getCacheKey: () => 'cssTransform', - process: () => 'module.exports = {};', + process: () => ({code: 'module.exports = {};'}), }; diff --git a/e2e/coverage-remapping/package.json b/e2e/coverage-remapping/package.json index ff2715e71f98..b7b9342d8f56 100644 --- a/e2e/coverage-remapping/package.json +++ b/e2e/coverage-remapping/package.json @@ -7,6 +7,6 @@ "testEnvironment": "node" }, "dependencies": { - "typescript": "^3.7.4" + "typescript": "^4.6.2" } } diff --git a/e2e/coverage-remapping/typescriptPreprocessor.js b/e2e/coverage-remapping/typescriptPreprocessor.js index dd8575ce8cba..25b9a60dab28 100644 --- a/e2e/coverage-remapping/typescriptPreprocessor.js +++ b/e2e/coverage-remapping/typescriptPreprocessor.js @@ -22,6 +22,6 @@ module.exports = { map: JSON.parse(result.sourceMapText), }; } - return src; + return {code: src}; }, }; diff --git a/e2e/coverage-remapping/yarn.lock b/e2e/coverage-remapping/yarn.lock index 98dbf8ec233b..68121efe22b0 100644 --- a/e2e/coverage-remapping/yarn.lock +++ b/e2e/coverage-remapping/yarn.lock @@ -9,26 +9,26 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - typescript: ^3.7.4 + typescript: ^4.6.2 languageName: unknown linkType: soft -"typescript@npm:^3.7.4": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" +"typescript@npm:^4.6.2": + version: 4.6.3 + resolution: "typescript@npm:4.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + checksum: 255bb26c8cb846ca689dd1c3a56587af4f69055907aa2c154796ea28ee0dea871535b1c78f85a6212c77f2657843a269c3a742d09d81495b97b914bf7920415b languageName: node linkType: hard -"typescript@patch:typescript@^3.7.4#~builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=bda367" +"typescript@patch:typescript@^4.6.2#~builtin": + version: 4.6.3 + resolution: "typescript@patch:typescript@npm%3A4.6.3#~builtin::version=4.6.3&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + checksum: 6bf45caf847062420592e711bc9c28bf5f9a9a7fa8245343b81493e4ededae33f1774009d1234d911422d1646a2c839f44e1a23ecb111b40a60ac2ea4c1482a8 languageName: node linkType: hard diff --git a/e2e/custom-haste-map/hasteMap.js b/e2e/custom-haste-map/hasteMap.js index a7a5bcfb99e4..d132db0ea181 100644 --- a/e2e/custom-haste-map/hasteMap.js +++ b/e2e/custom-haste-map/hasteMap.js @@ -109,7 +109,7 @@ class HasteMap { constructor(options) { this._cachePath = HasteMap.getCacheFilePath( options.cacheDirectory, - options.name, + options.id, ); } @@ -120,8 +120,8 @@ class HasteMap { }; } - static getCacheFilePath(tmpdir, name) { - return path.join(tmpdir, name); + static getCacheFilePath(tmpdir, id) { + return path.join(tmpdir, id); } getCacheFilePath() { diff --git a/e2e/custom-reporters/reporters/IncompleteReporter.js b/e2e/custom-reporters/reporters/IncompleteReporter.js index 760e101ac74c..0cfba83192a1 100644 --- a/e2e/custom-reporters/reporters/IncompleteReporter.js +++ b/e2e/custom-reporters/reporters/IncompleteReporter.js @@ -15,7 +15,7 @@ * This only implements one method onRunComplete which should be called */ class IncompleteReporter { - onRunComplete(contexts, results) { + onRunComplete(testContexts, results) { console.log('onRunComplete is called'); console.log(`Passed Tests: ${results.numPassedTests}`); console.log(`Failed Tests: ${results.numFailedTests}`); diff --git a/e2e/custom-reporters/reporters/TestReporter.js b/e2e/custom-reporters/reporters/TestReporter.js index 5cd404b766b2..300fb5478bd3 100644 --- a/e2e/custom-reporters/reporters/TestReporter.js +++ b/e2e/custom-reporters/reporters/TestReporter.js @@ -15,8 +15,9 @@ * to get the output. */ class TestReporter { - constructor(globalConfig, options) { - this._options = options; + constructor(globalConfig, reporterOptions, reporterContext) { + this._context = reporterContext; + this._options = reporterOptions; /** * statsCollected property @@ -30,7 +31,8 @@ class TestReporter { onRunStart: {}, onTestResult: {times: 0}, onTestStart: {}, - options, + reporterContext, + reporterOptions, }; } @@ -66,7 +68,7 @@ class TestReporter { onRunStart.options = typeof options; } - onRunComplete(contexts, results) { + onRunComplete(testContexts, results) { const onRunComplete = this._statsCollected.onRunComplete; onRunComplete.called = true; @@ -75,9 +77,9 @@ class TestReporter { onRunComplete.numFailedTests = results.numFailedTests; onRunComplete.numTotalTests = results.numTotalTests; - if (this._statsCollected.options.maxWorkers) { + if (this._statsCollected.reporterOptions.maxWorkers) { // Since it's a different number on different machines. - this._statsCollected.options.maxWorkers = '<>'; + this._statsCollected.reporterOptions.maxWorkers = '<>'; } // The Final Call process.stdout.write(JSON.stringify(this._statsCollected, null, 4)); diff --git a/e2e/detect-open-handles/__tests__/child_process.js b/e2e/detect-open-handles/__tests__/child_process.js new file mode 100644 index 000000000000..3440138ba757 --- /dev/null +++ b/e2e/detect-open-handles/__tests__/child_process.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {spawn} = require('child_process'); + +test('something', () => { + const subprocess = spawn( + process.argv[0], + [require.resolve('../interval-code')], + { + detached: true, + stdio: 'ignore', + }, + ); + subprocess.unref(); + expect(true).toBe(true); +}); diff --git a/testSetupFile.js b/e2e/detect-open-handles/interval-code.js similarity index 66% rename from testSetupFile.js rename to e2e/detect-open-handles/interval-code.js index ef9ed18680f6..3e1309e032cf 100644 --- a/testSetupFile.js +++ b/e2e/detect-open-handles/interval-code.js @@ -5,6 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -// Some of the `jest-runtime` tests are very slow and cause -// timeouts on travis -jest.setTimeout(70000); +setInterval(() => { + console.log('XX'); +}, 1000); diff --git a/e2e/fake-promises/asap/package.json b/e2e/fake-promises/asap/package.json index 0f50640514e1..b873db8ccbb8 100644 --- a/e2e/fake-promises/asap/package.json +++ b/e2e/fake-promises/asap/package.json @@ -1,9 +1,10 @@ { "jest": { - "timers": "fake", + "fakeTimers": { + "enableGlobally": true + }, "setupFiles": [ "/fake-promises" - ], - "testEnvironment": "node" + ] } } diff --git a/e2e/fake-promises/immediate/package.json b/e2e/fake-promises/immediate/package.json index 0f50640514e1..b873db8ccbb8 100644 --- a/e2e/fake-promises/immediate/package.json +++ b/e2e/fake-promises/immediate/package.json @@ -1,9 +1,10 @@ { "jest": { - "timers": "fake", + "fakeTimers": { + "enableGlobally": true + }, "setupFiles": [ "/fake-promises" - ], - "testEnvironment": "node" + ] } } diff --git a/e2e/fake-timers-legacy/enable-globally/__tests__/enableGlobally.test.js b/e2e/fake-timers-legacy/enable-globally/__tests__/enableGlobally.test.js index c02789c9bd43..674b8cb3810c 100644 --- a/e2e/fake-timers-legacy/enable-globally/__tests__/enableGlobally.test.js +++ b/e2e/fake-timers-legacy/enable-globally/__tests__/enableGlobally.test.js @@ -9,12 +9,6 @@ test('getRealSystemTime', () => { expect(() => jest.getRealSystemTime()).toThrow( - 'getRealSystemTime is not available when not using modern timers', - ); -}); - -test('setSystemTime', () => { - expect(() => jest.setSystemTime(0)).toThrow( - 'setSystemTime is not available when not using modern timers', + '`jest.getRealSystemTime()` is not available when using legacy fake timers.', ); }); diff --git a/e2e/fake-timers-legacy/enable-globally/package.json b/e2e/fake-timers-legacy/enable-globally/package.json index ad49f336e7a7..3cd8baba5623 100644 --- a/e2e/fake-timers-legacy/enable-globally/package.json +++ b/e2e/fake-timers-legacy/enable-globally/package.json @@ -1,6 +1,9 @@ { "name": "enable-globally-legacy", "jest": { - "timers": "legacy" + "fakeTimers": { + "enableGlobally": true, + "legacyFakeTimers": true + } } } diff --git a/e2e/fake-timers-legacy/enable-legacy-fake-timers/__tests__/legacyFakeTimers.test.js b/e2e/fake-timers-legacy/enable-legacy-fake-timers/__tests__/legacyFakeTimers.test.js new file mode 100644 index 000000000000..c781899e02dc --- /dev/null +++ b/e2e/fake-timers-legacy/enable-legacy-fake-timers/__tests__/legacyFakeTimers.test.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +test('fake timers', () => { + jest.useFakeTimers(); + const f = jest.fn(); + setTimeout(f, 0); + jest.runAllTimers(); + expect(f).toHaveBeenCalledTimes(1); +}); + +test('getRealSystemTime', () => { + expect(() => jest.getRealSystemTime()).toThrow( + '`jest.getRealSystemTime()` is not available when using legacy fake timers.', + ); +}); + +test('setSystemTime', () => { + expect(() => jest.setSystemTime(0)).toThrow( + '`jest.setSystemTime()` is not available when using legacy fake timers.', + ); +}); diff --git a/e2e/fake-timers-legacy/enable-legacy-fake-timers/package.json b/e2e/fake-timers-legacy/enable-legacy-fake-timers/package.json new file mode 100644 index 000000000000..9935c2240d23 --- /dev/null +++ b/e2e/fake-timers-legacy/enable-legacy-fake-timers/package.json @@ -0,0 +1,8 @@ +{ + "name": "enable-legacy-fake-timers", + "jest": { + "fakeTimers": { + "legacyFakeTimers": true + } + } +} diff --git a/e2e/fake-timers-legacy/request-animation-frame/__tests__/requestAnimationFrame.test.js b/e2e/fake-timers-legacy/request-animation-frame/__tests__/requestAnimationFrame.test.js index 502ce365a3c6..0dd9668303ea 100644 --- a/e2e/fake-timers-legacy/request-animation-frame/__tests__/requestAnimationFrame.test.js +++ b/e2e/fake-timers-legacy/request-animation-frame/__tests__/requestAnimationFrame.test.js @@ -10,7 +10,10 @@ 'use strict'; test('requestAnimationFrame test', () => { - jest.useFakeTimers('legacy'); + jest.useFakeTimers({ + legacyFakeTimers: true, + }); + let frameTimestamp = -1; requestAnimationFrame(timestamp => { frameTimestamp = timestamp; diff --git a/e2e/fake-timers-legacy/reset-all-mocks/package.json b/e2e/fake-timers-legacy/reset-all-mocks/package.json index 3d15b9a19778..9859a3b1aacb 100644 --- a/e2e/fake-timers-legacy/reset-all-mocks/package.json +++ b/e2e/fake-timers-legacy/reset-all-mocks/package.json @@ -1,7 +1,9 @@ { "name": "reset-all-mocks", "jest": { - "resetMocks": false, - "timers": "legacy" + "fakeTimers": { + "legacyFakeTimers": true + }, + "resetMocks": false } } diff --git a/e2e/fake-timers-legacy/reset-mocks/__tests__/resetMock.test.js b/e2e/fake-timers-legacy/reset-mocks/__tests__/resetMocks.test.js similarity index 94% rename from e2e/fake-timers-legacy/reset-mocks/__tests__/resetMock.test.js rename to e2e/fake-timers-legacy/reset-mocks/__tests__/resetMocks.test.js index 2710469e093e..05d594015029 100644 --- a/e2e/fake-timers-legacy/reset-mocks/__tests__/resetMock.test.js +++ b/e2e/fake-timers-legacy/reset-mocks/__tests__/resetMocks.test.js @@ -8,7 +8,6 @@ 'use strict'; test('works when resetMocks is set in Jest config', () => { - jest.useFakeTimers(); const f = jest.fn(); setTimeout(f, 0); jest.runAllTimers(); diff --git a/e2e/fake-timers-legacy/reset-mocks/package.json b/e2e/fake-timers-legacy/reset-mocks/package.json index 2a41b1e6782d..d999103bf1d1 100644 --- a/e2e/fake-timers-legacy/reset-mocks/package.json +++ b/e2e/fake-timers-legacy/reset-mocks/package.json @@ -1,7 +1,10 @@ { "name": "reset-mocks", "jest": { - "resetMocks": true, - "timers": "legacy" + "fakeTimers": { + "enableGlobally": true, + "legacyFakeTimers": true + }, + "resetMocks": true } } diff --git a/e2e/fake-timers-legacy/set-immediate/package.json b/e2e/fake-timers-legacy/set-immediate/package.json index 88cb67703874..d9c70d1fd4e5 100644 --- a/e2e/fake-timers-legacy/set-immediate/package.json +++ b/e2e/fake-timers-legacy/set-immediate/package.json @@ -1,6 +1,9 @@ { "name": "set-immediate-legacy", "jest": { - "timers": "legacy" + "fakeTimers": { + "enableGlobally": true, + "legacyFakeTimers": true + } } } diff --git a/e2e/fake-timers-legacy/use-fake-timers/__tests__/useFakeTimers.test.js b/e2e/fake-timers-legacy/use-fake-timers/__tests__/useFakeTimers.test.js index 982b23cbf809..843dace7b7af 100644 --- a/e2e/fake-timers-legacy/use-fake-timers/__tests__/useFakeTimers.test.js +++ b/e2e/fake-timers-legacy/use-fake-timers/__tests__/useFakeTimers.test.js @@ -8,9 +8,11 @@ 'use strict'; test('fake timers', () => { - jest.useFakeTimers('legacy'); + jest.useFakeTimers({ + legacyFakeTimers: true, + }); expect(() => jest.setSystemTime(0)).toThrow( - 'setSystemTime is not available when not using modern timers', + '`jest.setSystemTime()` is not available when using legacy fake timers.', ); }); diff --git a/e2e/fake-timers-legacy/use-legacy-fake-timers/__tests__/useFakeTimers.test.js b/e2e/fake-timers-legacy/use-legacy-fake-timers/__tests__/useFakeTimers.test.js new file mode 100644 index 000000000000..843dace7b7af --- /dev/null +++ b/e2e/fake-timers-legacy/use-legacy-fake-timers/__tests__/useFakeTimers.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +test('fake timers', () => { + jest.useFakeTimers({ + legacyFakeTimers: true, + }); + + expect(() => jest.setSystemTime(0)).toThrow( + '`jest.setSystemTime()` is not available when using legacy fake timers.', + ); +}); diff --git a/e2e/fake-timers-legacy/use-legacy-fake-timers/package.json b/e2e/fake-timers-legacy/use-legacy-fake-timers/package.json new file mode 100644 index 000000000000..2223ef489425 --- /dev/null +++ b/e2e/fake-timers-legacy/use-legacy-fake-timers/package.json @@ -0,0 +1,3 @@ +{ + "name": "use-legacy-fake-timers" +} diff --git a/e2e/fake-timers-legacy/use-real-timers/package.json b/e2e/fake-timers-legacy/use-real-timers/package.json index e0052f4b506f..9f83a4f4086f 100644 --- a/e2e/fake-timers-legacy/use-real-timers/package.json +++ b/e2e/fake-timers-legacy/use-real-timers/package.json @@ -1,6 +1,9 @@ { "name": "use-real-timers-legacy", "jest": { - "timers": "legacy" + "fakeTimers": { + "enableGlobally": true, + "legacyFakeTimers": true + } } } diff --git a/e2e/fake-timers/advance-timers/__tests__/advanceTimers.test.js b/e2e/fake-timers/advance-timers/__tests__/advanceTimers.test.js new file mode 100644 index 000000000000..c62b7cda7260 --- /dev/null +++ b/e2e/fake-timers/advance-timers/__tests__/advanceTimers.test.js @@ -0,0 +1,43 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +test('advances timers if true is passed', done => { + jest.useFakeTimers({advanceTimers: true}); + + const start = Date.now(); + + setTimeout(() => { + done(); + expect(Date.now() - start).toEqual(45); + }, 45); +}); + +test('advances timers if a number is passed', done => { + jest.useFakeTimers({advanceTimers: 40}); + + const start = Date.now(); + + setTimeout(() => { + done(); + expect(Date.now() - start).toEqual(35); + }, 35); +}); + +test('works with `now` option', done => { + jest.useFakeTimers({advanceTimers: 30, now: new Date('2015-09-25')}); + + expect(Date.now()).toEqual(1443139200000); + + const start = Date.now(); + + setTimeout(() => { + done(); + expect(Date.now() - start).toEqual(25); + }, 25); +}); diff --git a/e2e/fake-timers/advance-timers/package.json b/e2e/fake-timers/advance-timers/package.json new file mode 100644 index 000000000000..19979034fc88 --- /dev/null +++ b/e2e/fake-timers/advance-timers/package.json @@ -0,0 +1,3 @@ +{ + "name": "advance-timers" +} diff --git a/e2e/fake-timers/clear-real-timers/__tests__/clearRealTimers.test.js b/e2e/fake-timers/clear-real-timers/__tests__/clearRealTimers.test.js new file mode 100644 index 000000000000..0e5ebdc640a0 --- /dev/null +++ b/e2e/fake-timers/clear-real-timers/__tests__/clearRealTimers.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +test('allows clearing not faked timers', () => { + const timer = setTimeout(() => { + throw new Error('Should not throw'); + }, 1000); + + jest.useFakeTimers(); + + clearTimeout(timer); +}); diff --git a/e2e/fake-timers/clear-real-timers/package.json b/e2e/fake-timers/clear-real-timers/package.json new file mode 100644 index 000000000000..7c7d151e8229 --- /dev/null +++ b/e2e/fake-timers/clear-real-timers/package.json @@ -0,0 +1,3 @@ +{ + "name": "clear-real-timers" +} diff --git a/e2e/fake-timers/do-not-fake/__tests__/doNotFake.test.js b/e2e/fake-timers/do-not-fake/__tests__/doNotFake.test.js new file mode 100644 index 000000000000..8d4f5f149a78 --- /dev/null +++ b/e2e/fake-timers/do-not-fake/__tests__/doNotFake.test.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* global window */ + +'use strict'; + +const mockPerformanceMark = jest.fn(); +window.performance.mark = mockPerformanceMark; + +test('fakes all APIs', () => { + jest.useFakeTimers(); + + expect(window.performance.mark).toBeUndefined(); +}); + +test('does not fake `performance` instance', () => { + jest.useFakeTimers({doNotFake: ['performance']}); + + expect(window.performance.mark).toBe(mockPerformanceMark); +}); diff --git a/e2e/fake-timers/do-not-fake/package.json b/e2e/fake-timers/do-not-fake/package.json new file mode 100644 index 000000000000..eed48bf98af2 --- /dev/null +++ b/e2e/fake-timers/do-not-fake/package.json @@ -0,0 +1,6 @@ +{ + "name": "do-not-fake", + "jest": { + "testEnvironment": "jsdom" + } +} diff --git a/e2e/fake-timers/enable-globally/__tests__/enableGlobally.test.js b/e2e/fake-timers/enable-globally/__tests__/enableGlobally.test.js index 1d488f487a17..d5dbdc8878bd 100644 --- a/e2e/fake-timers/enable-globally/__tests__/enableGlobally.test.js +++ b/e2e/fake-timers/enable-globally/__tests__/enableGlobally.test.js @@ -29,6 +29,6 @@ test('fake timers with Date argument', () => { test('runAllImmediates', () => { expect(() => jest.runAllImmediates()).toThrow( - 'runAllImmediates is not available when using modern timers', + '`jest.runAllImmediates()` is only available when using legacy fake timers.', ); }); diff --git a/e2e/fake-timers/enable-globally/package.json b/e2e/fake-timers/enable-globally/package.json index 6bc480418b0d..8d75224d03c3 100644 --- a/e2e/fake-timers/enable-globally/package.json +++ b/e2e/fake-timers/enable-globally/package.json @@ -1,6 +1,8 @@ { "name": "enable-globally", "jest": { - "timers": "modern" + "fakeTimers": { + "enableGlobally": true + } } } diff --git a/e2e/fake-timers/request-animation-frame/__tests__/requestAnimationFrame.test.js b/e2e/fake-timers/request-animation-frame/__tests__/requestAnimationFrame.test.js index 6f4316230a0b..5548e95b0005 100644 --- a/e2e/fake-timers/request-animation-frame/__tests__/requestAnimationFrame.test.js +++ b/e2e/fake-timers/request-animation-frame/__tests__/requestAnimationFrame.test.js @@ -10,7 +10,8 @@ 'use strict'; test('requestAnimationFrame test', () => { - jest.useFakeTimers('modern'); + jest.useFakeTimers(); + let frameTimestamp = -1; requestAnimationFrame(timestamp => { frameTimestamp = timestamp; diff --git a/e2e/fake-timers/set-immediate/package.json b/e2e/fake-timers/set-immediate/package.json index b0c2eeea2314..2826715e9705 100644 --- a/e2e/fake-timers/set-immediate/package.json +++ b/e2e/fake-timers/set-immediate/package.json @@ -1,6 +1,8 @@ { "name": "set-immediate", "jest": { - "timers": "fake" + "fakeTimers": { + "enableGlobally": true + } } } diff --git a/e2e/fake-timers/timer-limit/__tests__/timerLimit.test.js b/e2e/fake-timers/timer-limit/__tests__/timerLimit.test.js new file mode 100644 index 000000000000..7f326e93f54c --- /dev/null +++ b/e2e/fake-timers/timer-limit/__tests__/timerLimit.test.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +test('reads timerLimit from Jest config', () => { + jest.useFakeTimers(); + + setTimeout(function infinitelyRecursingCallback() { + setTimeout(infinitelyRecursingCallback, 0); + }, 0); + + expect(() => { + jest.runAllTimers(); + }).toThrow( + new Error('Aborting after running 10 timers, assuming an infinite loop!'), + ); +}); + +test('allows to override timerLimit set via Jest config', () => { + jest.useFakeTimers({timerLimit: 100}); + + setTimeout(function infinitelyRecursingCallback() { + setTimeout(infinitelyRecursingCallback, 0); + }, 0); + + expect(() => { + jest.runAllTimers(); + }).toThrow( + new Error('Aborting after running 100 timers, assuming an infinite loop!'), + ); +}); + +test('allows to override timerLimit set via Jest object', () => { + jest.useFakeTimers({timerLimit: 1000}); + + setTimeout(function infinitelyRecursingCallback() { + setTimeout(infinitelyRecursingCallback, 0); + }, 0); + + expect(() => { + jest.runAllTimers(); + }).toThrow( + new Error('Aborting after running 1000 timers, assuming an infinite loop!'), + ); +}); diff --git a/e2e/fake-timers/timer-limit/package.json b/e2e/fake-timers/timer-limit/package.json new file mode 100644 index 000000000000..81b4c8e4fb99 --- /dev/null +++ b/e2e/fake-timers/timer-limit/package.json @@ -0,0 +1,9 @@ +{ + "name": "timer-limit", + "jest": { + "fakeTimers": { + "enableGlobally": true, + "timerLimit": 10 + } + } +} diff --git a/e2e/fake-timers/use-fake-timers/__tests__/useFakeTimers.test.js b/e2e/fake-timers/use-fake-timers/__tests__/useFakeTimers.test.js index 6fd28535c2bd..fd294d2323e3 100644 --- a/e2e/fake-timers/use-fake-timers/__tests__/useFakeTimers.test.js +++ b/e2e/fake-timers/use-fake-timers/__tests__/useFakeTimers.test.js @@ -8,7 +8,7 @@ 'use strict'; test('fake timers with number argument', () => { - jest.useFakeTimers('modern'); + jest.useFakeTimers(); jest.setSystemTime(0); @@ -20,7 +20,7 @@ test('fake timers with number argument', () => { }); test('fake timers with Date argument', () => { - jest.useFakeTimers('modern'); + jest.useFakeTimers(); jest.setSystemTime(new Date(0)); diff --git a/e2e/fake-timers/use-real-timers/package.json b/e2e/fake-timers/use-real-timers/package.json index 56af25025716..9e31cf9ea744 100644 --- a/e2e/fake-timers/use-real-timers/package.json +++ b/e2e/fake-timers/use-real-timers/package.json @@ -1,6 +1,8 @@ { "name": "use-real-timers", "jest": { - "timers": "fake" + "fakeTimers": { + "enableGlobally": true + } } } diff --git a/e2e/global-setup-custom-transform/transformer.js b/e2e/global-setup-custom-transform/transformer.js index 178565a491e2..77db60a2742d 100644 --- a/e2e/global-setup-custom-transform/transformer.js +++ b/e2e/global-setup-custom-transform/transformer.js @@ -12,9 +12,9 @@ const fileToTransform = require.resolve('./index.js'); module.exports = { process(src, filename) { if (filename === fileToTransform) { - return src.replace('hello', 'hello, world'); + return {code: src.replace('hello', 'hello, world')}; } - return src; + return {code: src}; }, }; diff --git a/e2e/global-setup/projects.jest.config.js b/e2e/global-setup/projects.jest.config.js index 9d008ac5cd06..08da804c2003 100644 --- a/e2e/global-setup/projects.jest.config.js +++ b/e2e/global-setup/projects.jest.config.js @@ -15,6 +15,9 @@ module.exports = { globalSetup: '/setup.js', rootDir: path.resolve(__dirname, './project-1'), testMatch: ['/**/*.test.js'], + transform: { + '\\.[jt]sx?$': [require.resolve('babel-jest'), {root: __dirname}], + }, transformIgnorePatterns: ['/node_modules/', '/packages/'], }, { @@ -22,6 +25,9 @@ module.exports = { globalSetup: '/setup.js', rootDir: path.resolve(__dirname, './project-2'), testMatch: ['/**/*.test.js'], + transform: { + '\\.[jt]sx?$': [require.resolve('babel-jest'), {root: __dirname}], + }, transformIgnorePatterns: ['/node_modules/', '/packages/'], }, ], diff --git a/e2e/jasmine-async/__tests__/concurrent.test.js b/e2e/jasmine-async/__tests__/concurrent.test.js index ef0cc1ba7b10..2b852c2f141e 100644 --- a/e2e/jasmine-async/__tests__/concurrent.test.js +++ b/e2e/jasmine-async/__tests__/concurrent.test.js @@ -7,7 +7,27 @@ 'use strict'; -it.concurrent('one', () => Promise.resolve()); -it.concurrent.skip('two', () => Promise.resolve()); -it.concurrent('three', () => Promise.resolve()); -it.concurrent('concurrent test fails', () => Promise.reject()); +const marker = s => console.log(`[[${s}]]`); + +beforeAll(() => marker('beforeAll')); +afterAll(() => marker('afterAll')); + +beforeEach(() => marker('beforeEach')); +afterEach(() => marker('afterEach')); + +it.concurrent('one', () => { + marker('test'); + return Promise.resolve(); +}); +it.concurrent.skip('two', () => { + marker('test'); + return Promise.resolve(); +}); +it.concurrent('three', () => { + marker('test'); + return Promise.resolve(); +}); +it.concurrent('concurrent test fails', () => { + marker('test'); + return Promise.reject(); +}); diff --git a/e2e/jasmine-async/__tests__/concurrentWithinDescribe.test.js b/e2e/jasmine-async/__tests__/concurrentWithinDescribe.test.js index 1e674e10d2e1..56c389d3a089 100644 --- a/e2e/jasmine-async/__tests__/concurrentWithinDescribe.test.js +++ b/e2e/jasmine-async/__tests__/concurrentWithinDescribe.test.js @@ -8,6 +8,12 @@ 'use strict'; describe('one', () => { - it.concurrent('concurrent test gets skipped', () => Promise.resolve()); - it.concurrent('concurrent test fails', () => Promise.reject()); + it.concurrent('concurrent test gets skipped', () => { + console.log(`this is not logged ${Math.random()}`); + return Promise.resolve(); + }); + it.concurrent('concurrent test fails', () => { + console.log(`this is logged ${Math.random()}`); + return Promise.reject(new Error()); + }); }); diff --git a/e2e/multi-project-babel/package.json b/e2e/multi-project-babel/package.json new file mode 100644 index 000000000000..c51f8db5ccfa --- /dev/null +++ b/e2e/multi-project-babel/package.json @@ -0,0 +1,15 @@ +{ + "jest": { + "projects": [ + { + "rootDir": "/prj-1" + }, + { + "rootDir": "/prj-2" + }, + "/prj-3", + "/prj-4", + "/prj-5" + ] + } +} diff --git a/e2e/multi-project-babel/prj-1/babel.config.js b/e2e/multi-project-babel/prj-1/babel.config.js new file mode 100644 index 000000000000..186d686670e3 --- /dev/null +++ b/e2e/multi-project-babel/prj-1/babel.config.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: ['@babel/preset-flow'], +}; diff --git a/e2e/multi-project-babel/prj-1/index.js b/e2e/multi-project-babel/prj-1/index.js new file mode 100644 index 000000000000..881ff4073178 --- /dev/null +++ b/e2e/multi-project-babel/prj-1/index.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = (text: string) => text; diff --git a/e2e/multi-project-babel/prj-1/index.test.js b/e2e/multi-project-babel/prj-1/index.test.js new file mode 100644 index 000000000000..f0f19c2e9ab9 --- /dev/null +++ b/e2e/multi-project-babel/prj-1/index.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const f = require('./'); + +it('Transpiles', () => { + expect(f('test')).toBe('test'); +}); diff --git a/e2e/multi-project-babel/prj-1/package.json b/e2e/multi-project-babel/prj-1/package.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/e2e/multi-project-babel/prj-1/package.json @@ -0,0 +1 @@ +{} diff --git a/e2e/multi-project-babel/prj-2/.babelrc.js b/e2e/multi-project-babel/prj-2/.babelrc.js new file mode 100644 index 000000000000..186d686670e3 --- /dev/null +++ b/e2e/multi-project-babel/prj-2/.babelrc.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: ['@babel/preset-flow'], +}; diff --git a/e2e/multi-project-babel/prj-2/index.js b/e2e/multi-project-babel/prj-2/index.js new file mode 100644 index 000000000000..881ff4073178 --- /dev/null +++ b/e2e/multi-project-babel/prj-2/index.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = (text: string) => text; diff --git a/e2e/multi-project-babel/prj-2/index.test.js b/e2e/multi-project-babel/prj-2/index.test.js new file mode 100644 index 000000000000..f0f19c2e9ab9 --- /dev/null +++ b/e2e/multi-project-babel/prj-2/index.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const f = require('./'); + +it('Transpiles', () => { + expect(f('test')).toBe('test'); +}); diff --git a/e2e/multi-project-babel/prj-2/package.json b/e2e/multi-project-babel/prj-2/package.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/e2e/multi-project-babel/prj-2/package.json @@ -0,0 +1 @@ +{} diff --git a/e2e/multi-project-babel/prj-3/package.json b/e2e/multi-project-babel/prj-3/package.json new file mode 100644 index 000000000000..2b9231f5dcc3 --- /dev/null +++ b/e2e/multi-project-babel/prj-3/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "rootDir": "src" + } +} diff --git a/e2e/multi-project-babel/prj-3/src/babel.config.js b/e2e/multi-project-babel/prj-3/src/babel.config.js new file mode 100644 index 000000000000..186d686670e3 --- /dev/null +++ b/e2e/multi-project-babel/prj-3/src/babel.config.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: ['@babel/preset-flow'], +}; diff --git a/e2e/multi-project-babel/prj-3/src/index.js b/e2e/multi-project-babel/prj-3/src/index.js new file mode 100644 index 000000000000..881ff4073178 --- /dev/null +++ b/e2e/multi-project-babel/prj-3/src/index.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = (text: string) => text; diff --git a/e2e/multi-project-babel/prj-3/src/index.test.js b/e2e/multi-project-babel/prj-3/src/index.test.js new file mode 100644 index 000000000000..f0f19c2e9ab9 --- /dev/null +++ b/e2e/multi-project-babel/prj-3/src/index.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const f = require('./'); + +it('Transpiles', () => { + expect(f('test')).toBe('test'); +}); diff --git a/e2e/multi-project-babel/prj-4/.babelrc.js b/e2e/multi-project-babel/prj-4/.babelrc.js new file mode 100644 index 000000000000..186d686670e3 --- /dev/null +++ b/e2e/multi-project-babel/prj-4/.babelrc.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: ['@babel/preset-flow'], +}; diff --git a/e2e/multi-project-babel/prj-4/package.json b/e2e/multi-project-babel/prj-4/package.json new file mode 100644 index 000000000000..2b9231f5dcc3 --- /dev/null +++ b/e2e/multi-project-babel/prj-4/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "rootDir": "src" + } +} diff --git a/e2e/multi-project-babel/prj-4/src/index.js b/e2e/multi-project-babel/prj-4/src/index.js new file mode 100644 index 000000000000..881ff4073178 --- /dev/null +++ b/e2e/multi-project-babel/prj-4/src/index.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = (text: string) => text; diff --git a/e2e/multi-project-babel/prj-4/src/index.test.js b/e2e/multi-project-babel/prj-4/src/index.test.js new file mode 100644 index 000000000000..f0f19c2e9ab9 --- /dev/null +++ b/e2e/multi-project-babel/prj-4/src/index.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const f = require('./'); + +it('Transpiles', () => { + expect(f('test')).toBe('test'); +}); diff --git a/e2e/multi-project-babel/prj-5/.babelrc.js b/e2e/multi-project-babel/prj-5/.babelrc.js new file mode 100644 index 000000000000..186d686670e3 --- /dev/null +++ b/e2e/multi-project-babel/prj-5/.babelrc.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + presets: ['@babel/preset-flow'], +}; diff --git a/e2e/multi-project-babel/prj-5/package.json b/e2e/multi-project-babel/prj-5/package.json new file mode 100644 index 000000000000..2b9231f5dcc3 --- /dev/null +++ b/e2e/multi-project-babel/prj-5/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "rootDir": "src" + } +} diff --git a/e2e/multi-project-babel/prj-5/src/index.js b/e2e/multi-project-babel/prj-5/src/index.js new file mode 100644 index 000000000000..881ff4073178 --- /dev/null +++ b/e2e/multi-project-babel/prj-5/src/index.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = (text: string) => text; diff --git a/e2e/multi-project-babel/prj-5/src/index.test.js b/e2e/multi-project-babel/prj-5/src/index.test.js new file mode 100644 index 000000000000..f0f19c2e9ab9 --- /dev/null +++ b/e2e/multi-project-babel/prj-5/src/index.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const f = require('./'); + +it('Transpiles', () => { + expect(f('test')).toBe('test'); +}); diff --git a/e2e/native-esm/__tests__/native-esm.test.js b/e2e/native-esm/__tests__/native-esm.test.js index 73a652bdac5e..4a4088a7c2c2 100644 --- a/e2e/native-esm/__tests__/native-esm.test.js +++ b/e2e/native-esm/__tests__/native-esm.test.js @@ -28,8 +28,10 @@ test('should have correct import.meta', () => { expect(typeof require).toBe('undefined'); expect(typeof jest).toBe('undefined'); expect(import.meta).toEqual({ + jest: expect.anything(), url: expect.any(String), }); + expect(import.meta.jest).toBe(jestObject); expect( import.meta.url.endsWith('/e2e/native-esm/__tests__/native-esm.test.js'), ).toBe(true); diff --git a/e2e/resolve-async/package.json b/e2e/resolve-async/package.json index 50e870688cbf..ea366d4b6bde 100644 --- a/e2e/resolve-async/package.json +++ b/e2e/resolve-async/package.json @@ -2,7 +2,6 @@ "type": "module", "jest": { "resolver": "/resolver.cjs", - "transform": { - } + "transform": {} } } diff --git a/e2e/snapshot-serializers/transformer.js b/e2e/snapshot-serializers/transformer.js index 5606ac36381f..44d68384a734 100644 --- a/e2e/snapshot-serializers/transformer.js +++ b/e2e/snapshot-serializers/transformer.js @@ -10,8 +10,8 @@ module.exports = { process(src, filename) { if (/bar.js$/.test(filename)) { - return `${src};\nmodule.exports = createPlugin('bar');`; + return {code: `${src};\nmodule.exports = createPlugin('bar');`}; } - return src; + return {code: src}; }, }; diff --git a/e2e/stack-trace-source-maps-with-coverage/package.json b/e2e/stack-trace-source-maps-with-coverage/package.json index 56d2defe8c95..60a023465a34 100644 --- a/e2e/stack-trace-source-maps-with-coverage/package.json +++ b/e2e/stack-trace-source-maps-with-coverage/package.json @@ -8,6 +8,6 @@ "testRegex": "fails" }, "dependencies": { - "typescript": "^3.7.4" + "typescript": "^4.6.2" } } diff --git a/e2e/stack-trace-source-maps-with-coverage/preprocessor.js b/e2e/stack-trace-source-maps-with-coverage/preprocessor.js index 133d42ec44a2..ded4db6d8929 100644 --- a/e2e/stack-trace-source-maps-with-coverage/preprocessor.js +++ b/e2e/stack-trace-source-maps-with-coverage/preprocessor.js @@ -8,14 +8,19 @@ const tsc = require('typescript'); module.exports = { - process(src, path) { - return tsc.transpileModule(src, { - compilerOptions: { - inlineSourceMap: true, - module: tsc.ModuleKind.CommonJS, - target: 'es5', - }, - fileName: path, - }).outputText; + process(sourceText, fileName) { + if (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) { + const {outputText, sourceMapText} = tsc.transpileModule(sourceText, { + compilerOptions: { + inlineSourceMap: true, + module: tsc.ModuleKind.CommonJS, + target: 'es5', + }, + fileName, + }); + + return {code: outputText, map: sourceMapText}; + } + return {code: sourceText}; }, }; diff --git a/e2e/stack-trace-source-maps-with-coverage/yarn.lock b/e2e/stack-trace-source-maps-with-coverage/yarn.lock index 98dbf8ec233b..68121efe22b0 100644 --- a/e2e/stack-trace-source-maps-with-coverage/yarn.lock +++ b/e2e/stack-trace-source-maps-with-coverage/yarn.lock @@ -9,26 +9,26 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - typescript: ^3.7.4 + typescript: ^4.6.2 languageName: unknown linkType: soft -"typescript@npm:^3.7.4": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" +"typescript@npm:^4.6.2": + version: 4.6.3 + resolution: "typescript@npm:4.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + checksum: 255bb26c8cb846ca689dd1c3a56587af4f69055907aa2c154796ea28ee0dea871535b1c78f85a6212c77f2657843a269c3a742d09d81495b97b914bf7920415b languageName: node linkType: hard -"typescript@patch:typescript@^3.7.4#~builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=bda367" +"typescript@patch:typescript@^4.6.2#~builtin": + version: 4.6.3 + resolution: "typescript@patch:typescript@npm%3A4.6.3#~builtin::version=4.6.3&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + checksum: 6bf45caf847062420592e711bc9c28bf5f9a9a7fa8245343b81493e4ededae33f1774009d1234d911422d1646a2c839f44e1a23ecb111b40a60ac2ea4c1482a8 languageName: node linkType: hard diff --git a/e2e/stack-trace-source-maps/package.json b/e2e/stack-trace-source-maps/package.json index 56d2defe8c95..60a023465a34 100644 --- a/e2e/stack-trace-source-maps/package.json +++ b/e2e/stack-trace-source-maps/package.json @@ -8,6 +8,6 @@ "testRegex": "fails" }, "dependencies": { - "typescript": "^3.7.4" + "typescript": "^4.6.2" } } diff --git a/e2e/stack-trace-source-maps/preprocessor.js b/e2e/stack-trace-source-maps/preprocessor.js index 133d42ec44a2..ded4db6d8929 100644 --- a/e2e/stack-trace-source-maps/preprocessor.js +++ b/e2e/stack-trace-source-maps/preprocessor.js @@ -8,14 +8,19 @@ const tsc = require('typescript'); module.exports = { - process(src, path) { - return tsc.transpileModule(src, { - compilerOptions: { - inlineSourceMap: true, - module: tsc.ModuleKind.CommonJS, - target: 'es5', - }, - fileName: path, - }).outputText; + process(sourceText, fileName) { + if (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) { + const {outputText, sourceMapText} = tsc.transpileModule(sourceText, { + compilerOptions: { + inlineSourceMap: true, + module: tsc.ModuleKind.CommonJS, + target: 'es5', + }, + fileName, + }); + + return {code: outputText, map: sourceMapText}; + } + return {code: sourceText}; }, }; diff --git a/e2e/stack-trace-source-maps/yarn.lock b/e2e/stack-trace-source-maps/yarn.lock index 98dbf8ec233b..68121efe22b0 100644 --- a/e2e/stack-trace-source-maps/yarn.lock +++ b/e2e/stack-trace-source-maps/yarn.lock @@ -9,26 +9,26 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - typescript: ^3.7.4 + typescript: ^4.6.2 languageName: unknown linkType: soft -"typescript@npm:^3.7.4": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" +"typescript@npm:^4.6.2": + version: 4.6.3 + resolution: "typescript@npm:4.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + checksum: 255bb26c8cb846ca689dd1c3a56587af4f69055907aa2c154796ea28ee0dea871535b1c78f85a6212c77f2657843a269c3a742d09d81495b97b914bf7920415b languageName: node linkType: hard -"typescript@patch:typescript@^3.7.4#~builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=bda367" +"typescript@patch:typescript@^4.6.2#~builtin": + version: 4.6.3 + resolution: "typescript@patch:typescript@npm%3A4.6.3#~builtin::version=4.6.3&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + checksum: 6bf45caf847062420592e711bc9c28bf5f9a9a7fa8245343b81493e4ededae33f1774009d1234d911422d1646a2c839f44e1a23ecb111b40a60ac2ea4c1482a8 languageName: node linkType: hard diff --git a/e2e/test-match/package.json b/e2e/test-match/package.json new file mode 100644 index 000000000000..3d6779c71dd7 --- /dev/null +++ b/e2e/test-match/package.json @@ -0,0 +1,7 @@ +{ + "jest": { + "testMatch": [ + "**/test-suites/*.?js" + ] + } +} diff --git a/e2e/test-match/test-suites/sample-suite.mjs b/e2e/test-match/test-suites/sample-suite.mjs new file mode 100644 index 000000000000..0d0703ef8e57 --- /dev/null +++ b/e2e/test-match/test-suites/sample-suite.mjs @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +test('mjs extension', () => { + expect(1).toBe(1); +}); diff --git a/e2e/test-match/test-suites/sample-suite2.cjs b/e2e/test-match/test-suites/sample-suite2.cjs new file mode 100644 index 000000000000..bb80acb6f879 --- /dev/null +++ b/e2e/test-match/test-suites/sample-suite2.cjs @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +test('cjs extension', () => { + expect(1).toBe(1); +}); diff --git a/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js new file mode 100644 index 000000000000..46d53526ba6e --- /dev/null +++ b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +let i = 0; +jest.retryTimes(3, {logErrorsBeforeRetry: true}); +it('retryTimes set', () => { + i++; + if (i === 3) { + expect(true).toBeTruthy(); + } else { + expect(true).toBeFalsy(); + } +}); diff --git a/e2e/transform-linked-modules/preprocessor.js b/e2e/transform-linked-modules/preprocessor.js index acf4723313d1..67c48b478bc3 100644 --- a/e2e/transform-linked-modules/preprocessor.js +++ b/e2e/transform-linked-modules/preprocessor.js @@ -7,6 +7,6 @@ module.exports = { process() { - return 'module.exports = "transformed"'; + return {code: 'module.exports = "transformed"'}; }, }; diff --git a/e2e/transform/async-transformer/my-transform.cjs b/e2e/transform/async-transformer/my-transform.cjs index 9f9b4a91398f..e1c13f1171a3 100644 --- a/e2e/transform/async-transformer/my-transform.cjs +++ b/e2e/transform/async-transformer/my-transform.cjs @@ -24,12 +24,14 @@ module.exports = { // we want to wait to ensure the module cache is populated with the correct module await wait(100); - return src; + return {code: src}; } - return src.replace( - "export default 'It was not transformed!!'", - 'export default 42', - ); + return { + code: src.replace( + "export default 'It was not transformed!!'", + 'export default 42', + ), + }; }, }; diff --git a/e2e/transform/babel-jest-async/transformer.js b/e2e/transform/babel-jest-async/transformer.js index 9bc35a2a9ad1..41a96de04751 100644 --- a/e2e/transform/babel-jest-async/transformer.js +++ b/e2e/transform/babel-jest-async/transformer.js @@ -6,10 +6,10 @@ */ import {fileURLToPath} from 'url'; -import babelJest from 'babel-jest'; +import {createTransformer} from 'babel-jest'; export default { - ...babelJest.default.createTransformer({ + ...createTransformer({ presets: ['@babel/preset-flow'], root: fileURLToPath(import.meta.url), }), diff --git a/e2e/transform/cache/transformer.js b/e2e/transform/cache/transformer.js index 10d02e637f80..84b368ab9b57 100644 --- a/e2e/transform/cache/transformer.js +++ b/e2e/transform/cache/transformer.js @@ -6,11 +6,11 @@ */ module.exports = { - process(src, path) { + process(code, path) { if (path.includes('common')) { console.log(path); } - return src; + return {code}; }, }; diff --git a/e2e/transform/custom-instrumenting-preprocessor/preprocessor.js b/e2e/transform/custom-instrumenting-preprocessor/preprocessor.js index 99e12ff008e4..bb1dbf38c648 100644 --- a/e2e/transform/custom-instrumenting-preprocessor/preprocessor.js +++ b/e2e/transform/custom-instrumenting-preprocessor/preprocessor.js @@ -8,12 +8,12 @@ module.exports = { canInstrument: true, process(src, filename, options) { - src = `${src};\nglobalThis.__PREPROCESSED__ = true;`; + let code = `${src};\nglobalThis.__PREPROCESSED__ = true;`; if (options.instrument) { - src = `${src};\nglobalThis.__INSTRUMENTED__ = true;`; + code = `${src};\nglobalThis.__INSTRUMENTED__ = true;`; } - return src; + return {code}; }, }; diff --git a/e2e/transform/esm-transformer/my-transform.mjs b/e2e/transform/esm-transformer/my-transform.mjs index 241e27ee29db..96d8c23058f3 100644 --- a/e2e/transform/esm-transformer/my-transform.mjs +++ b/e2e/transform/esm-transformer/my-transform.mjs @@ -14,9 +14,9 @@ const fileToTransform = require.resolve('./module'); export default { process(src, filepath) { if (filepath === fileToTransform) { - return 'module.exports = 42;'; + return {code: 'module.exports = 42;'}; } - return src; + return {code: src}; }, }; diff --git a/e2e/transform/multiple-transformers/cssPreprocessor.js b/e2e/transform/multiple-transformers/cssPreprocessor.js index 5ca52dd30e54..6e5da5fce882 100644 --- a/e2e/transform/multiple-transformers/cssPreprocessor.js +++ b/e2e/transform/multiple-transformers/cssPreprocessor.js @@ -7,13 +7,15 @@ module.exports = { process() { - return ` - module.exports = { - root: 'App-root', - header: 'App-header', - logo: 'App-logo', - intro: 'App-intro', - }; - `; + const code = ` + module.exports = { + root: 'App-root', + header: 'App-header', + logo: 'App-logo', + intro: 'App-intro', + }; + `; + + return {code}; }, }; diff --git a/e2e/transform/multiple-transformers/filePreprocessor.js b/e2e/transform/multiple-transformers/filePreprocessor.js index c49b641e62d2..b419a52779c8 100644 --- a/e2e/transform/multiple-transformers/filePreprocessor.js +++ b/e2e/transform/multiple-transformers/filePreprocessor.js @@ -9,8 +9,10 @@ const path = require('path'); module.exports = { process(src, filename) { - return ` - module.exports = '${path.basename(filename)}'; - `; + const code = ` + module.exports = '${path.basename(filename)}'; + `; + + return {code}; }, }; diff --git a/e2e/transform/multiple-transformers/package.json b/e2e/transform/multiple-transformers/package.json index fb6fec0c6fff..f0f60dd9cba5 100644 --- a/e2e/transform/multiple-transformers/package.json +++ b/e2e/transform/multiple-transformers/package.json @@ -17,8 +17,8 @@ "@babel/core": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", - "react": "*", - "react-dom": "*", - "react-test-renderer": "*" + "react": "17.0.2", + "react-dom": "^17.0.1", + "react-test-renderer": "17.0.2" } } diff --git a/e2e/transform/multiple-transformers/yarn.lock b/e2e/transform/multiple-transformers/yarn.lock index 045c4016ff28..c5d158a23f24 100644 --- a/e2e/transform/multiple-transformers/yarn.lock +++ b/e2e/transform/multiple-transformers/yarn.lock @@ -1700,7 +1700,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:*": +"react-dom@npm:^17.0.1": version: 17.0.2 resolution: "react-dom@npm:17.0.2" dependencies: @@ -1732,7 +1732,7 @@ __metadata: languageName: node linkType: hard -"react-test-renderer@npm:*": +"react-test-renderer@npm:17.0.2": version: 17.0.2 resolution: "react-test-renderer@npm:17.0.2" dependencies: @@ -1746,7 +1746,7 @@ __metadata: languageName: node linkType: hard -"react@npm:*": +"react@npm:17.0.2": version: 17.0.2 resolution: "react@npm:17.0.2" dependencies: @@ -1853,9 +1853,9 @@ __metadata: "@babel/core": ^7.0.0 "@babel/preset-env": ^7.0.0 "@babel/preset-react": ^7.0.0 - react: "*" - react-dom: "*" - react-test-renderer: "*" + react: 17.0.2 + react-dom: ^17.0.1 + react-test-renderer: 17.0.2 languageName: unknown linkType: soft diff --git a/e2e/transform/transform-runner/runner.ts b/e2e/transform/transform-runner/runner.ts index fb45dbf4be69..325956eae108 100644 --- a/e2e/transform/transform-runner/runner.ts +++ b/e2e/transform/transform-runner/runner.ts @@ -13,8 +13,8 @@ import type { OnTestStart, OnTestSuccess, TestRunnerContext, - TestWatcher, } from 'jest-runner'; +import type {TestWatcher} from 'jest-watcher'; export default class BaseTestRunner { private _globalConfig: Config.GlobalConfig; diff --git a/e2e/typescript-coverage/package.json b/e2e/typescript-coverage/package.json index 24255a467dd7..b7b9342d8f56 100644 --- a/e2e/typescript-coverage/package.json +++ b/e2e/typescript-coverage/package.json @@ -7,6 +7,6 @@ "testEnvironment": "node" }, "dependencies": { - "typescript": "^3.3.1" + "typescript": "^4.6.2" } } diff --git a/e2e/typescript-coverage/typescriptPreprocessor.js b/e2e/typescript-coverage/typescriptPreprocessor.js index 8633bdab5a95..251c68f5e38f 100644 --- a/e2e/typescript-coverage/typescriptPreprocessor.js +++ b/e2e/typescript-coverage/typescriptPreprocessor.js @@ -8,18 +8,19 @@ const tsc = require('typescript'); module.exports = { - process(src, path) { - if (path.endsWith('.ts') || path.endsWith('.tsx')) { - return tsc.transpile( - src, - { + process(sourceText, fileName) { + if (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) { + const {outputText, sourceMapText} = tsc.transpileModule(sourceText, { + compilerOptions: { jsx: tsc.JsxEmit.React, module: tsc.ModuleKind.CommonJS, + sourceMap: true, // if code is transformed, source map is necessary for coverage }, - path, - [], - ); + fileName, + }); + + return {code: outputText, map: sourceMapText}; } - return src; + return {code: sourceText}; }, }; diff --git a/e2e/typescript-coverage/yarn.lock b/e2e/typescript-coverage/yarn.lock index 077ddea218e3..68121efe22b0 100644 --- a/e2e/typescript-coverage/yarn.lock +++ b/e2e/typescript-coverage/yarn.lock @@ -9,26 +9,26 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - typescript: ^3.3.1 + typescript: ^4.6.2 languageName: unknown linkType: soft -"typescript@npm:^3.3.1": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" +"typescript@npm:^4.6.2": + version: 4.6.3 + resolution: "typescript@npm:4.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 46c842e2cd4797b88b66ef06c9c41dd21da48b95787072ccf39d5f2aa3124361bc4c966aa1c7f709fae0509614d76751455b5231b12dbb72eb97a31369e1ff92 + checksum: 255bb26c8cb846ca689dd1c3a56587af4f69055907aa2c154796ea28ee0dea871535b1c78f85a6212c77f2657843a269c3a742d09d81495b97b914bf7920415b languageName: node linkType: hard -"typescript@patch:typescript@^3.3.1#~builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#~builtin::version=3.9.10&hash=bda367" +"typescript@patch:typescript@^4.6.2#~builtin": + version: 4.6.3 + resolution: "typescript@patch:typescript@npm%3A4.6.3#~builtin::version=4.6.3&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: dc7141ab555b23a8650a6787f98845fc11692063d02b75ff49433091b3af2fe3d773650dea18389d7c21f47d620fb3b110ea363dab4ab039417a6ccbbaf96fc2 + checksum: 6bf45caf847062420592e711bc9c28bf5f9a9a7fa8245343b81493e4ededae33f1774009d1234d911422d1646a2c839f44e1a23ecb111b40a60ac2ea4c1482a8 languageName: node linkType: hard diff --git a/e2e/watch-plugins/cjs/my-watch-plugin.cjs b/e2e/watch-plugins/cjs/my-watch-plugin.cjs index 6fa3d51d68d4..b68701c9893c 100644 --- a/e2e/watch-plugins/cjs/my-watch-plugin.cjs +++ b/e2e/watch-plugins/cjs/my-watch-plugin.cjs @@ -6,8 +6,7 @@ */ class MyWatchPlugin { // Add hooks to Jest lifecycle events - apply(jestHooks) { - } + apply(jestHooks) {} // Get the prompt information for interactive plugins getUsageInfo(globalConfig) { @@ -15,8 +14,7 @@ class MyWatchPlugin { } // Executed when the key from `getUsageInfo` is input - run(globalConfig, updateConfigAndRun) { - } + run(globalConfig, updateConfigAndRun) {} } module.exports = MyWatchPlugin; diff --git a/e2e/watch-plugins/mjs/my-watch-plugin.mjs b/e2e/watch-plugins/mjs/my-watch-plugin.mjs index 873aaed19d58..f313583a7bb4 100644 --- a/e2e/watch-plugins/mjs/my-watch-plugin.mjs +++ b/e2e/watch-plugins/mjs/my-watch-plugin.mjs @@ -6,8 +6,7 @@ */ class MyWatchPlugin { // Add hooks to Jest lifecycle events - apply(jestHooks) { - } + apply(jestHooks) {} // Get the prompt information for interactive plugins getUsageInfo(globalConfig) { @@ -15,8 +14,7 @@ class MyWatchPlugin { } // Executed when the key from `getUsageInfo` is input - run(globalConfig, updateConfigAndRun) { - } + run(globalConfig, updateConfigAndRun) {} } export default MyWatchPlugin; diff --git a/examples/angular/.babelrc.js b/examples/angular/.babelrc.js index 7a1d6c383636..118538499498 100644 --- a/examples/angular/.babelrc.js +++ b/examples/angular/.babelrc.js @@ -19,5 +19,5 @@ module.exports = { }, ], '@babel/preset-typescript', - ] + ], }; diff --git a/examples/angular/package.json b/examples/angular/package.json index 077edb2aae2e..08a4887857c3 100644 --- a/examples/angular/package.json +++ b/examples/angular/package.json @@ -15,18 +15,18 @@ "core-js": "^3.2.1", "rxjs": "^7.5.5", "tslib": "^2.0.0", - "typescript": "*", + "typescript": "^4.6.2", "zone.js": "~0.11.3" }, "devDependencies": { - "@babel/core": "*", + "@babel/core": "^7.11.6", "@babel/plugin-proposal-decorators": "*", - "@babel/preset-env": "*", - "@babel/preset-typescript": "*", - "@types/jest": "*", - "babel-jest": "*", + "@babel/preset-env": "^7.1.0", + "@babel/preset-typescript": "^7.0.0", + "@types/jest": "^27.4.0", + "babel-jest": "workspace:*", "babel-plugin-transform-typescript-metadata": "*", - "jest": "*", + "jest": "workspace:*", "jest-zone-patch": "*" } } diff --git a/examples/async/package.json b/examples/async/package.json index 9a4169870dea..0aad9aa5f6a5 100644 --- a/examples/async/package.json +++ b/examples/async/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-async", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/automatic-mocks/package.json b/examples/automatic-mocks/package.json index aefcce3021ea..4289a81e4597 100644 --- a/examples/automatic-mocks/package.json +++ b/examples/automatic-mocks/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-automatic-mocks", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/enzyme/package.json b/examples/enzyme/package.json index 4474451b81a8..41ba3928b558 100644 --- a/examples/enzyme/package.json +++ b/examples/enzyme/package.json @@ -7,13 +7,13 @@ "react-dom": "^16.14.0" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-react": "*", - "babel-jest": "*", + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.12.1", + "babel-jest": "workspace:*", "enzyme": "*", "enzyme-adapter-react-16": "*", - "jest": "*" + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/expect-extend/package.json b/examples/expect-extend/package.json index 7ffa1f92b773..161c3c2b5b3b 100644 --- a/examples/expect-extend/package.json +++ b/examples/expect-extend/package.json @@ -3,9 +3,9 @@ "version": "0.0.0", "name": "example-expect-extend", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-typescript": "*", + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-typescript": "^7.0.0", "@jest/globals": "workspace:*", "babel-jest": "workspace:*", "expect": "workspace:*", diff --git a/examples/getting-started/package.json b/examples/getting-started/package.json index 2c430deeb0d3..ede124a1f08e 100644 --- a/examples/getting-started/package.json +++ b/examples/getting-started/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-getting-started", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/jquery/package.json b/examples/jquery/package.json index 87a6963ee511..6b125a32579e 100644 --- a/examples/jquery/package.json +++ b/examples/jquery/package.json @@ -3,13 +3,13 @@ "version": "0.0.0", "name": "example-jquery", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "dependencies": { - "jquery": "*" + "jquery": "^3.2.1" }, "scripts": { "test": "jest" diff --git a/examples/manual-mocks/package.json b/examples/manual-mocks/package.json index 2f256f4d34d9..5c55909d7389 100644 --- a/examples/manual-mocks/package.json +++ b/examples/manual-mocks/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-manual-mocks", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/module-mock/package.json b/examples/module-mock/package.json index fa5b980256af..d0eb838c5c88 100644 --- a/examples/module-mock/package.json +++ b/examples/module-mock/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-manual-mock", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/mongodb/package.json b/examples/mongodb/package.json index d7bca6c768f3..6a552bdb71c6 100644 --- a/examples/mongodb/package.json +++ b/examples/mongodb/package.json @@ -1,19 +1,18 @@ { "name": "example-mongodb", "version": "0.0.0", - "main": "index.js", - "license": "MIT", + "main": "./index.js", "private": true, "dependencies": { - "jest-environment-node": "*", + "jest-environment-node": "workspace:*", "mongodb": "^4.3.1", "mongodb-memory-server": "^8.3.0" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/react-native/.watchmanconfig b/examples/react-native/.watchmanconfig index 9e26dfeeb6e6..0967ef424bce 100644 --- a/examples/react-native/.watchmanconfig +++ b/examples/react-native/.watchmanconfig @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/examples/react-native/.babelrc.js b/examples/react-native/babel.config.js similarity index 100% rename from examples/react-native/.babelrc.js rename to examples/react-native/babel.config.js diff --git a/examples/react-native/jest.config.js b/examples/react-native/jest.config.js index 4ea28b54a72f..5e3d4871ef83 100644 --- a/examples/react-native/jest.config.js +++ b/examples/react-native/jest.config.js @@ -3,9 +3,6 @@ const {resolve} = require('path'); module.exports = { preset: 'react-native', testEnvironment: 'jsdom', - transform: { - '\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'), - }, // this is specific to the Jest repo, not generally needed (the files we ignore will be in node_modules which is ignored by default) transformIgnorePatterns: [resolve(__dirname, '../../packages')], }; diff --git a/examples/react-native/package.json b/examples/react-native/package.json index b0dcd1dfd028..3f19aae39f42 100644 --- a/examples/react-native/package.json +++ b/examples/react-native/package.json @@ -8,14 +8,14 @@ }, "dependencies": { "react": "17.0.2", - "react-native": "0.67.2" + "react-native": "0.68.1" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*", - "metro-react-native-babel-preset": "0.66.2", + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*", + "metro-react-native-babel-preset": "0.67.0", "react-test-renderer": "17.0.2" } } diff --git a/examples/react-testing-library/package.json b/examples/react-testing-library/package.json index f18927368449..e6aa06186295 100644 --- a/examples/react-testing-library/package.json +++ b/examples/react-testing-library/package.json @@ -3,16 +3,16 @@ "version": "0.0.0", "name": "example-react-testing-library", "dependencies": { - "react": "*", - "react-dom": "*" + "react": "17.0.2", + "react-dom": "^17.0.1" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-react": "*", + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.12.1", "@testing-library/react": "*", - "babel-jest": "*", - "jest": "*" + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/react/package.json b/examples/react/package.json index 61bcaa0188ff..5d24a3b06a8d 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -3,15 +3,15 @@ "version": "0.0.0", "name": "example-react", "dependencies": { - "react": "*", - "react-dom": "*" + "react": "17.0.2", + "react-dom": "^17.0.1" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-react": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.12.1", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/snapshot/package.json b/examples/snapshot/package.json index 9d11dbcce92b..cb49aba118ea 100644 --- a/examples/snapshot/package.json +++ b/examples/snapshot/package.json @@ -3,15 +3,15 @@ "version": "0.0.0", "name": "example-snapshot", "dependencies": { - "react": "*" + "react": "17.0.2" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-react": "*", - "babel-jest": "*", - "jest": "*", - "react-test-renderer": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.12.1", + "babel-jest": "workspace:*", + "jest": "workspace:*", + "react-test-renderer": "17.0.2" }, "scripts": { "test": "jest" diff --git a/examples/timer/package.json b/examples/timer/package.json index cf1b13617e94..b6540587b1f2 100644 --- a/examples/timer/package.json +++ b/examples/timer/package.json @@ -3,10 +3,10 @@ "version": "0.0.0", "name": "example-timer", "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 8a2c828a89de..7effcd539ec6 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -3,18 +3,18 @@ "version": "0.0.0", "name": "example-typescript", "dependencies": { - "react": "*", - "react-dom": "*", - "typescript": "*" + "react": "17.0.2", + "react-dom": "^17.0.1", + "typescript": "^4.6.2" }, "devDependencies": { - "@babel/core": "*", - "@babel/preset-env": "*", - "@babel/preset-react": "*", - "@babel/preset-typescript": "*", - "@types/jest": "*", - "babel-jest": "*", - "jest": "*" + "@babel/core": "^7.11.6", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.12.1", + "@babel/preset-typescript": "^7.0.0", + "@types/jest": "^27.4.0", + "babel-jest": "workspace:*", + "jest": "workspace:*" }, "scripts": { "test": "jest" diff --git a/jest.config.ci.js b/jest.config.ci.mjs similarity index 79% rename from jest.config.ci.js rename to jest.config.ci.mjs index 68e16b4300ac..ffba96d753c6 100644 --- a/jest.config.ci.js +++ b/jest.config.ci.mjs @@ -5,12 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; +import jestConfigBase from './jest.config.mjs'; -module.exports = { - ...require('./jest.config'), +export default { + ...jestConfigBase, coverageReporters: ['json'], reporters: [ + 'github-actions', [ 'jest-junit', {outputDirectory: 'reports/junit', outputName: 'js-test-results.xml'}, @@ -19,5 +20,6 @@ module.exports = { 'jest-silent-reporter', {showPaths: true, showWarnings: true, useDots: true}, ], + 'summary', ], }; diff --git a/jest.config.js b/jest.config.mjs similarity index 82% rename from jest.config.js rename to jest.config.mjs index f6eb19eb57c9..3d5bb0fcddcd 100644 --- a/jest.config.js +++ b/jest.config.mjs @@ -5,16 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; +import {createRequire} from 'module'; +const require = createRequire(import.meta.url); /** @type import('@jest/types').Config.InitialOptions */ -module.exports = { +export default { collectCoverageFrom: [ '**/packages/*/**/*.js', '**/packages/*/**/*.ts', '!**/bin/**', '!**/cli/**', - '!**/perf/**', + '!**/__benchmarks__/**', '!**/__mocks__/**', '!**/__tests__/**', '!**/__typetests__/**', @@ -31,19 +32,23 @@ module.exports = { 'e2e/runtime-internal-module-registry/__mocks__', ], projects: ['', '/examples/*/'], - setupFilesAfterEnv: ['/testSetupFile.js'], snapshotFormat: { escapeString: false, }, snapshotSerializers: [require.resolve('pretty-format/ConvertAnsi')], testPathIgnorePatterns: [ '/__arbitraries__/', + '/__benchmarks__/', '/__typetests__/', '/node_modules/', '/examples/', '/e2e/.*/__tests__', '/e2e/global-setup', '/e2e/global-teardown', + '/e2e/custom-*', + '/e2e/test-in-root', + '/e2e/run-programmatically-multiple-projects', + '/e2e/multi-project-babel', '\\.snap$', '/packages/.*/build', '/packages/.*/src/__tests__/setPrettyPrint.ts', @@ -61,16 +66,15 @@ module.exports = { '/packages/jest-snapshot/src/__tests__/plugins', '/packages/jest-snapshot/src/__tests__/fixtures/', '/packages/jest-validate/src/__tests__/fixtures/', - '/packages/jest-worker/src/__performance_tests__', - '/packages/pretty-format/perf/test.js', '/e2e/__tests__/iterator-to-null-test.ts', ], + testTimeout: 70000, transform: { - '\\.[jt]sx?$': '/packages/babel-jest', + '\\.[jt]sx?$': require.resolve('babel-jest'), }, watchPathIgnorePatterns: ['coverage'], watchPlugins: [ - 'jest-watch-typeahead/filename', - 'jest-watch-typeahead/testname', + require.resolve('jest-watch-typeahead/filename'), + require.resolve('jest-watch-typeahead/testname'), ], }; diff --git a/jest.config.tsd.js b/jest.config.tsd.mjs similarity index 68% rename from jest.config.tsd.js rename to jest.config.tsd.mjs index 2ac85943e857..47edf683be0a 100644 --- a/jest.config.tsd.js +++ b/jest.config.tsd.mjs @@ -5,16 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; +import jestConfigBase from './jest.config.mjs'; -const {modulePathIgnorePatterns} = require('./jest.config'); - -module.exports = { +export default { displayName: { color: 'blue', name: 'types', }, - modulePathIgnorePatterns, + modulePathIgnorePatterns: jestConfigBase.modulePathIgnorePatterns, + reporters: ['default', 'github-actions'], roots: ['/packages'], runner: 'jest-runner-tsd', testMatch: ['**/__typetests__/**/*.ts'], diff --git a/lerna.json b/lerna.json index 6d8afb117b1f..f494750106bd 100644 --- a/lerna.json +++ b/lerna.json @@ -3,5 +3,5 @@ "packages/*" ], "npmClient": "yarn", - "version": "28.0.0-alpha.7" + "version": "28.0.1" } diff --git a/netlify.toml b/netlify.toml index 993bd38b2657..ddb2c3fdd54e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,4 +1,3 @@ - # Note: this file's config override the Netlify UI admin config # production build @@ -10,6 +9,9 @@ [build.environment] NODE_VERSION = "lts/*" NODE_OPTIONS = "--max_old_space_size=4096" + # default cache + YARN_ENABLE_GLOBAL_CACHE = "true" + YARN_GLOBAL_FOLDER = "/opt/buildhome/.yarn_cache" [context.production] # Do not build the site if there's no site-related changes @@ -19,7 +21,6 @@ [context.deploy-preview] command = "yarn workspace jest-website netlify:ci:deployPreview" - [[plugins]] package = "netlify-plugin-cache" [plugins.inputs] diff --git a/package.json b/package.json index 01ecdac5d036..8377a7b57f0e 100644 --- a/package.json +++ b/package.json @@ -3,22 +3,22 @@ "private": true, "version": "0.0.0", "devDependencies": { - "@babel/core": "^7.3.4", + "@babel/core": "^7.11.6", "@babel/plugin-transform-modules-commonjs": "^7.1.0", "@babel/preset-env": "^7.1.0", - "@babel/preset-react": "^7.0.0", + "@babel/preset-react": "^7.12.1", "@babel/preset-typescript": "^7.0.0", "@babel/register": "^7.0.0", "@crowdin/cli": "^3.5.2", "@jest/globals": "workspace:*", "@jest/test-utils": "workspace:*", - "@microsoft/api-extractor": "^7.19.4", + "@microsoft/api-extractor": "^7.23.0", "@tsconfig/node12": "^1.0.9", "@tsd/typescript": "~4.6.2", - "@types/babel__core": "^7.0.0", + "@types/babel__core": "^7.1.14", "@types/babel__generator": "^7.0.0", - "@types/babel__template": "^7.0.0", - "@types/dedent": "0.7.0", + "@types/babel__template": "^7.0.2", + "@types/dedent": "^0.7.0", "@types/jest": "^27.4.0", "@types/node": "~12.12.0", "@types/which": "^2.0.0", @@ -43,10 +43,10 @@ "execa": "^5.0.0", "fast-check": "^2.0.0", "find-process": "^1.4.1", - "glob": "^7.1.1", - "globby": "^11.0.0", + "glob": "^7.1.3", + "globby": "^11.0.1", "graceful-fs": "^4.2.9", - "isbinaryfile": "^4.0.0", + "isbinaryfile": "^5.0.0", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-reports": "^3.1.3", @@ -57,7 +57,7 @@ "jest-runner-tsd": "^3.0.0", "jest-silent-reporter": "^0.5.0", "jest-snapshot": "workspace:*", - "jest-watch-typeahead": "^1.0.0", + "jest-watch-typeahead": "^1.1.0", "jquery": "^3.2.1", "js-yaml": "^4.1.0", "lerna": "^4.0.0", @@ -70,9 +70,9 @@ "progress": "^2.0.0", "promise": "^8.0.2", "read-pkg": "^5.2.0", - "resolve": "^1.15.0", + "resolve": "^1.20.0", "rimraf": "^3.0.0", - "semver": "^7.3.2", + "semver": "^7.3.5", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -81,37 +81,36 @@ "throat": "^6.0.1", "ts-node": "^10.5.0", "type-fest": "^2.11.2", - "typescript": "^4.2.4", + "typescript": "^4.6.2", "which": "^2.0.1" }, "scripts": { "build-clean": "rimraf './packages/*/build' './packages/*/dist' './packages/*/tsconfig.tsbuildinfo' './packages/*/api-extractor.json' './api-extractor.json'", "build": "yarn build:js && yarn build:ts && yarn bundle:ts", - "build:js": "node ./scripts/build.js", - "build:ts": "node ./scripts/buildTs.js", - "bundle:ts": "node ./scripts/bundleTs.js", - "check-copyright-headers": "node ./scripts/checkCopyrightHeaders.js", + "build:js": "node ./scripts/build.mjs", + "build:ts": "node ./scripts/buildTs.mjs", + "bundle:ts": "node ./scripts/bundleTs.mjs", + "check-copyright-headers": "node ./scripts/checkCopyrightHeaders.mjs", "clean-all": "yarn clean-e2e && yarn build-clean && rimraf './packages/*/node_modules' && rimraf './node_modules'", - "clean-e2e": "node ./scripts/cleanE2e.js", + "clean-e2e": "node ./scripts/cleanE2e.mjs", "crowdin:upload": "echo 'Uploading sources to Crowdin' && crowdin upload sources --config ./crowdin.yaml", "crowdin:download": "echo 'Downloading translations from Crowdin' && crowdin download --config ./crowdin.yaml", "jest": "node ./packages/jest-cli/bin/jest.js", "jest-jasmine": "JEST_JASMINE=1 yarn jest", - "jest-jasmine-ci": "yarn jest-jasmine --color --config jest.config.ci.js", + "jest-jasmine-ci": "yarn jest-jasmine --color --config jest.config.ci.mjs", "jest-coverage": "yarn jest --coverage", - "lint": "eslint . --cache --ext js,jsx,ts,tsx,md", - "lint:prettier": "prettier '**/*.{md,yml,yaml}' 'website/**/*.{css,js}' --write --ignore-path .gitignore", - "lint:prettier:ci": "prettier '**/*.{md,yml,yaml}' 'website/**/*.{css,js}' --check --ignore-path .gitignore", - "remove-examples": "node ./scripts/remove-examples.js", - "test-types": "yarn jest --config jest.config.tsd.js", + "lint": "eslint . --cache --ext js,jsx,cjs,mjs,ts,tsx,md", + "lint:prettier": "prettier . \"!**/*.{js,jsx,cjs,mjs,ts,tsx}\" --write", + "lint:prettier:ci": "prettier . \"!**/*.{js,jsx,cjs,mjs,ts,tsx}\" --check", + "remove-examples": "node ./scripts/remove-examples.mjs", + "test-types": "yarn jest --config jest.config.tsd.mjs", "test-ci-partial": "yarn test-ci-partial:parallel -i", - "test-ci-partial:parallel": "yarn jest --color --config jest.config.ci.js", - "test-pretty-format-perf": "node packages/pretty-format/perf/test.js", + "test-ci-partial:parallel": "yarn jest --color --config jest.config.ci.mjs", "test-leak": "yarn jest -i --detectLeaks --color jest-mock jest-diff jest-repl pretty-format", "test": "yarn lint && yarn jest", - "verify-old-ts": "node ./scripts/verifyOldTs.js", - "verify-pnp": "node ./scripts/verifyPnP.js", - "watch": "yarn build:js && node ./scripts/watch.js", + "verify-old-ts": "node ./scripts/verifyOldTs.mjs", + "verify-pnp": "node ./scripts/verifyPnP.mjs", + "watch": "yarn build:js && node ./scripts/watch.mjs", "watch:ts": "yarn build:ts --watch" }, "workspaces": [ @@ -132,6 +131,16 @@ "trailingComma": "es5" } }, + { + "files": [ + "lerna.json", + "website/sidebars.json", + "website/versioned_sidebars/*.json" + ], + "options": { + "parser": "json-stringify" + } + }, { "files": ".yarnrc.yml", "options": { @@ -158,12 +167,11 @@ "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" }, "resolutions": { - "@jest/create-cache-key-function": "workspace:*", - "@testing-library/dom/pretty-format": "26.6.1", + "@types/node": "~12.12.0", "babel-jest": "workspace:*", "jest": "workspace:*", "jest-environment-node": "workspace:*", - "react-native": "patch:react-native@0.67.2#./patches/react-native.patch" + "react-native@0.68.1": "patch:react-native@npm:0.68.1#.yarn/patches/react-native-npm-0.68.1-8830b7be0d.patch" }, "packageManager": "yarn@3.2.0" } diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 61fbbffad96b..a985c1a52b08 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -1,7 +1,7 @@ { "name": "babel-jest", "description": "Jest plugin to use babel for transformation.", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -18,17 +18,17 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/transform": "^28.0.0-alpha.7", + "@jest/transform": "^28.0.1", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.0.0-alpha.6", + "babel-preset-jest": "^28.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "devDependencies": { - "@babel/core": "^7.8.0", - "@jest/test-utils": "^28.0.0-alpha.7", + "@babel/core": "^7.11.6", + "@jest/test-utils": "^28.0.1", "@types/graceful-fs": "^4.1.3" }, "peerDependencies": { diff --git a/packages/babel-jest/src/__tests__/getCacheKey.test.ts b/packages/babel-jest/src/__tests__/getCacheKey.test.ts index 63e38cff3cb3..d6eaa5d9295b 100644 --- a/packages/babel-jest/src/__tests__/getCacheKey.test.ts +++ b/packages/babel-jest/src/__tests__/getCacheKey.test.ts @@ -9,6 +9,8 @@ import type {TransformOptions as BabelTransformOptions} from '@babel/core'; import type {TransformOptions as JestTransformOptions} from '@jest/transform'; import babelJest from '../index'; +const {getCacheKey} = babelJest.createTransformer(); + const processVersion = process.version; const nodeEnv = process.env.NODE_ENV; const babelEnv = process.env.BABEL_ENV; @@ -39,11 +41,7 @@ describe('getCacheKey', () => { instrument: true, } as JestTransformOptions; - const oldCacheKey = babelJest.getCacheKey( - sourceText, - sourcePath, - transformOptions, - ); + const oldCacheKey = getCacheKey(sourceText, sourcePath, transformOptions); test('returns cache key hash', () => { expect(oldCacheKey.length).toEqual(32); @@ -54,9 +52,9 @@ describe('getCacheKey', () => { readFileSync: () => 'new this file', })); - const {default: babelJest}: typeof import('../index') = require('../index'); + const {createTransformer}: typeof import('../index') = require('../index'); - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = createTransformer().getCacheKey( sourceText, sourcePath, transformOptions, @@ -77,9 +75,9 @@ describe('getCacheKey', () => { }; }); - const {default: babelJest}: typeof import('../index') = require('../index'); + const {createTransformer}: typeof import('../index') = require('../index'); - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = createTransformer().getCacheKey( sourceText, sourcePath, transformOptions, @@ -89,7 +87,7 @@ describe('getCacheKey', () => { }); test('if `sourceText` value is changing', () => { - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = getCacheKey( 'new source text', sourcePath, transformOptions, @@ -99,7 +97,7 @@ describe('getCacheKey', () => { }); test('if `sourcePath` value is changing', () => { - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = getCacheKey( sourceText, 'new-source-path.js', transformOptions, @@ -109,7 +107,7 @@ describe('getCacheKey', () => { }); test('if `configString` value is changing', () => { - const newCacheKey = babelJest.getCacheKey(sourceText, sourcePath, { + const newCacheKey = getCacheKey(sourceText, sourcePath, { ...transformOptions, configString: 'new-config-string', }); @@ -129,9 +127,9 @@ describe('getCacheKey', () => { }; }); - const {default: babelJest}: typeof import('../index') = require('../index'); + const {createTransformer}: typeof import('../index') = require('../index'); - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = createTransformer().getCacheKey( sourceText, sourcePath, transformOptions, @@ -152,9 +150,9 @@ describe('getCacheKey', () => { }; }); - const {default: babelJest}: typeof import('../index') = require('../index'); + const {createTransformer}: typeof import('../index') = require('../index'); - const newCacheKey = babelJest.getCacheKey( + const newCacheKey = createTransformer().getCacheKey( sourceText, sourcePath, transformOptions, @@ -164,7 +162,7 @@ describe('getCacheKey', () => { }); test('if `instrument` value is changing', () => { - const newCacheKey = babelJest.getCacheKey(sourceText, sourcePath, { + const newCacheKey = getCacheKey(sourceText, sourcePath, { ...transformOptions, instrument: false, }); @@ -175,11 +173,7 @@ describe('getCacheKey', () => { test('if `process.env.NODE_ENV` value is changing', () => { process.env.NODE_ENV = 'NEW_NODE_ENV'; - const newCacheKey = babelJest.getCacheKey( - sourceText, - sourcePath, - transformOptions, - ); + const newCacheKey = getCacheKey(sourceText, sourcePath, transformOptions); expect(oldCacheKey).not.toEqual(newCacheKey); }); @@ -187,11 +181,7 @@ describe('getCacheKey', () => { test('if `process.env.BABEL_ENV` value is changing', () => { process.env.BABEL_ENV = 'NEW_BABEL_ENV'; - const newCacheKey = babelJest.getCacheKey( - sourceText, - sourcePath, - transformOptions, - ); + const newCacheKey = getCacheKey(sourceText, sourcePath, transformOptions); expect(oldCacheKey).not.toEqual(newCacheKey); }); @@ -200,11 +190,7 @@ describe('getCacheKey', () => { delete process.version; process.version = 'new-node-version'; - const newCacheKey = babelJest.getCacheKey( - sourceText, - sourcePath, - transformOptions, - ); + const newCacheKey = getCacheKey(sourceText, sourcePath, transformOptions); expect(oldCacheKey).not.toEqual(newCacheKey); }); diff --git a/packages/babel-jest/src/__tests__/index.ts b/packages/babel-jest/src/__tests__/index.ts index 7d3db19bdc7a..46dd4677eee3 100644 --- a/packages/babel-jest/src/__tests__/index.ts +++ b/packages/babel-jest/src/__tests__/index.ts @@ -6,7 +6,7 @@ */ import {makeProjectConfig} from '@jest/test-utils'; -import babelJest from '../index'; +import babelJest, {createTransformer} from '../index'; import {loadPartialConfig} from '../loadBabelConfig'; jest.mock('../loadBabelConfig', () => { @@ -20,6 +20,8 @@ jest.mock('../loadBabelConfig', () => { }; }); +const defaultBabelJestTransformer = babelJest.createTransformer(null); + //Mock data for all the tests const sourceString = ` const sum = (a, b) => a+b; @@ -38,11 +40,17 @@ beforeEach(() => { }); test('Returns source string with inline maps when no transformOptions is passed', () => { - const result = babelJest.process(sourceString, 'dummy_path.js', { - config: makeProjectConfig(), - configString: JSON.stringify(makeProjectConfig()), - instrument: false, - }) as any; + const result = defaultBabelJestTransformer.process( + sourceString, + 'dummy_path.js', + { + cacheFS: new Map(), + config: makeProjectConfig(), + configString: JSON.stringify(makeProjectConfig()), + instrument: false, + transformerConfig: {}, + }, + ) as any; expect(typeof result).toBe('object'); expect(result.code).toBeDefined(); expect(result.map).toBeDefined(); @@ -53,13 +61,15 @@ test('Returns source string with inline maps when no transformOptions is passed' }); test('Returns source string with inline maps when no transformOptions is passed async', async () => { - const result: any = await babelJest.processAsync!( + const result: any = await defaultBabelJestTransformer.processAsync!( sourceString, 'dummy_path.js', { + cacheFS: new Map(), config: makeProjectConfig(), configString: JSON.stringify(makeProjectConfig()), instrument: false, + transformerConfig: {}, }, ); expect(typeof result).toBe('object'); @@ -108,10 +118,12 @@ describe('caller option correctly merges from defaults and options', () => { }, ], ])('%j -> %j', (input, output) => { - babelJest.process(sourceString, 'dummy_path.js', { + defaultBabelJestTransformer.process(sourceString, 'dummy_path.js', { + cacheFS: new Map(), config: makeProjectConfig(), configString: JSON.stringify(makeProjectConfig()), instrument: false, + transformerConfig: {}, ...input, }); @@ -130,11 +142,13 @@ describe('caller option correctly merges from defaults and options', () => { }); test('can pass null to createTransformer', () => { - const transformer = babelJest.createTransformer(null); + const transformer = createTransformer(null); transformer.process(sourceString, 'dummy_path.js', { + cacheFS: new Map(), config: makeProjectConfig(), configString: JSON.stringify(makeProjectConfig()), instrument: false, + transformerConfig: {}, }); expect(loadPartialConfig).toHaveBeenCalledTimes(1); diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index 522d5cc58390..da93a3c4cecf 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -19,6 +19,7 @@ import slash = require('slash'); import type { TransformOptions as JestTransformOptions, SyncTransformer, + TransformerCreator, } from '@jest/transform'; import {loadPartialConfig, loadPartialConfigAsync} from './loadBabelConfig'; @@ -26,8 +27,6 @@ const THIS_FILE = fs.readFileSync(__filename); const jestPresetPath = require.resolve('babel-preset-jest'); const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); -type CreateTransformer = SyncTransformer['createTransformer']; - function assertLoadedBabelConfig( babelConfig: Readonly | null, cwd: string, @@ -79,7 +78,7 @@ function getCacheKeyFromConfig( const configPath = [babelOptions.config || '', babelOptions.babelrc || '']; - return createHash('md5') + return createHash('sha256') .update(THIS_FILE) .update('\0', 'utf8') .update(JSON.stringify(babelOptions.options)) @@ -99,7 +98,8 @@ function getCacheKeyFromConfig( .update(process.env.BABEL_ENV || '') .update('\0', 'utf8') .update(process.version) - .digest('hex'); + .digest('hex') + .substring(0, 32); } function loadBabelConfig( @@ -148,7 +148,10 @@ async function loadBabelOptionsAsync( return addIstanbulInstrumentation(options, jestTransformOptions); } -export const createTransformer: CreateTransformer = userOptions => { +export const createTransformer: TransformerCreator< + SyncTransformer, + TransformOptions +> = userOptions => { const inputOptions = userOptions ?? {}; const options = { @@ -171,10 +174,11 @@ export const createTransformer: CreateTransformer = userOptions => { filename: string, transformOptions: JestTransformOptions, ): TransformOptions { - const {cwd} = transformOptions.config; - // `cwd` first to allow incoming options to override it + const {cwd, rootDir} = transformOptions.config; + // `cwd` and `root` first to allow incoming options to override it return { cwd, + root: rootDir, ...options, caller: { ...options.caller, @@ -242,7 +246,7 @@ export const createTransformer: CreateTransformer = userOptions => { } } - return sourceText; + return {code: sourceText}; }, async processAsync(sourceText, sourcePath, transformOptions) { const babelOptions = await loadBabelOptionsAsync( @@ -264,16 +268,15 @@ export const createTransformer: CreateTransformer = userOptions => { } } - return sourceText; + return {code: sourceText}; }, }; }; -const transformer: SyncTransformer = { - ...createTransformer(), - // Assigned here so only the exported transformer has `createTransformer`, - // instead of all created transformers by the function +const transformerFactory = { + // Assigned here, instead of as a separate export, due to limitations in Jest's + // requireOrImportModule, requiring all exports to be on the `default` export createTransformer, }; -export default transformer; +export default transformerFactory; diff --git a/packages/babel-plugin-jest-hoist/package.json b/packages/babel-plugin-jest-hoist/package.json index ff4e6113733d..cf8f9259a48c 100644 --- a/packages/babel-plugin-jest-hoist/package.json +++ b/packages/babel-plugin-jest-hoist/package.json @@ -1,6 +1,6 @@ { "name": "babel-plugin-jest-hoist", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -22,7 +22,7 @@ "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", + "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" }, "devDependencies": { @@ -30,7 +30,7 @@ "@babel/preset-react": "^7.12.1", "@types/babel__template": "^7.0.2", "@types/node": "*", - "@types/prettier": "^2.0.0", + "@types/prettier": "^2.1.5", "babel-plugin-tester": "^10.0.0", "prettier": "^2.1.1" }, diff --git a/packages/babel-preset-jest/package.json b/packages/babel-preset-jest/package.json index 93db3751fa04..d6a9a6b281ef 100644 --- a/packages/babel-preset-jest/package.json +++ b/packages/babel-preset-jest/package.json @@ -1,6 +1,6 @@ { "name": "babel-preset-jest", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -13,7 +13,7 @@ "./package.json": "./package.json" }, "dependencies": { - "babel-plugin-jest-hoist": "^28.0.0-alpha.6", + "babel-plugin-jest-hoist": "^28.0.0", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { diff --git a/packages/diff-sequences/.npmignore b/packages/diff-sequences/.npmignore index 80bf61eb922c..8dda7de8f4fe 100644 --- a/packages/diff-sequences/.npmignore +++ b/packages/diff-sequences/.npmignore @@ -1,5 +1,6 @@ **/__mocks__/** **/__tests__/** +__benchmarks__ __typetests__ src tsconfig.json diff --git a/packages/diff-sequences/perf/example.md b/packages/diff-sequences/__benchmarks__/example.md similarity index 100% rename from packages/diff-sequences/perf/example.md rename to packages/diff-sequences/__benchmarks__/example.md diff --git a/packages/diff-sequences/perf/index.js b/packages/diff-sequences/__benchmarks__/test.js similarity index 91% rename from packages/diff-sequences/perf/index.js rename to packages/diff-sequences/__benchmarks__/test.js index f537b61f14e2..04da41dc9900 100644 --- a/packages/diff-sequences/perf/index.js +++ b/packages/diff-sequences/__benchmarks__/test.js @@ -5,19 +5,23 @@ * LICENSE file in the root directory of this source tree. */ -// Make sure to run node with --expose-gc option! - -// The times are reliable if about 1% relative mean error if you run it: +/** + * To start the test, build the repo and run: + * node --expose-gc test.js + */ -// * immediately after restart -// * with 100% battery charge -// * not connected to network +/** + * The times are reliable if about 1% relative mean error if you run it: + * - immediately after restart + * - with 100% battery charge + * - not connected to network + */ -/* eslint import/no-extraneous-dependencies: "off" */ +'use strict'; const Benchmark = require('benchmark'); const diffBaseline = require('diff').diffLines; -const diffImproved = require('../build/index.js').default; +const diffImproved = require('../').default; const testBaseline = (a, b) => { const benchmark = new Benchmark({ @@ -163,6 +167,10 @@ const testLength = n => { ); // simulate TDD }; +if (!globalThis.gc) { + throw new Error('GC not present, start with: node --expose-gc test.js'); +} + writeHeading2(); testLength(20); diff --git a/packages/diff-sequences/package.json b/packages/diff-sequences/package.json index 2f82c6e97cbe..ee094222d390 100644 --- a/packages/diff-sequences/package.json +++ b/packages/diff-sequences/package.json @@ -1,6 +1,6 @@ { "name": "diff-sequences", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -27,9 +27,6 @@ }, "./package.json": "./package.json" }, - "scripts": { - "perf": "node --expose-gc perf/index.js" - }, "devDependencies": { "benchmark": "^2.1.4", "diff": "^5.0.0", diff --git a/packages/expect-utils/package.json b/packages/expect-utils/package.json index 86fe0fd74870..6a07e400fd61 100644 --- a/packages/expect-utils/package.json +++ b/packages/expect-utils/package.json @@ -1,6 +1,6 @@ { "name": "@jest/expect-utils", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,10 +17,10 @@ "./package.json": "./package.json" }, "dependencies": { - "jest-get-type": "^28.0.0-alpha.3" + "jest-get-type": "^28.0.0" }, "devDependencies": { - "jest-matcher-utils": "^28.0.0-alpha.7" + "jest-matcher-utils": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/expect/__typetests__/tsconfig.json b/packages/expect/__typetests__/tsconfig.json index fe8eab794254..165ba1343021 100644 --- a/packages/expect/__typetests__/tsconfig.json +++ b/packages/expect/__typetests__/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { + "composite": false, "noUnusedLocals": false, "noUnusedParameters": false, "skipLibCheck": true, diff --git a/packages/expect/package.json b/packages/expect/package.json index ec2734c77251..4383ff193374 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -1,6 +1,6 @@ { "name": "expect", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -18,13 +18,14 @@ "./build/matchers": "./build/matchers.js" }, "dependencies": { - "@jest/expect-utils": "^28.0.0-alpha.7", - "jest-get-type": "^28.0.0-alpha.3", - "jest-matcher-utils": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7" + "@jest/expect-utils": "^28.0.1", + "jest-get-type": "^28.0.0", + "jest-matcher-utils": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-util": "^28.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@tsd/typescript": "~4.6.2", "chalk": "^4.0.0", "fast-check": "^2.0.0", diff --git a/packages/expect/src/asymmetricMatchers.ts b/packages/expect/src/asymmetricMatchers.ts index 9af6ce60c2a7..e9aff27f9a75 100644 --- a/packages/expect/src/asymmetricMatchers.ts +++ b/packages/expect/src/asymmetricMatchers.ts @@ -13,6 +13,7 @@ import { subsetEquality, } from '@jest/expect-utils'; import * as matcherUtils from 'jest-matcher-utils'; +import {pluralize} from 'jest-util'; import {getState} from './jestMatchersObject'; import type { AsymmetricMatcher as AsymmetricMatcherInterface, @@ -131,7 +132,7 @@ class Any extends AsymmetricMatcher { return 'Any'; } - getExpectedType() { + override getExpectedType() { if (this.sample == String) { return 'string'; } @@ -155,7 +156,7 @@ class Any extends AsymmetricMatcher { return fnNameFor(this.sample); } - toAsymmetricMatcher() { + override toAsymmetricMatcher() { return `Any<${fnNameFor(this.sample)}>`; } } @@ -171,7 +172,7 @@ class Anything extends AsymmetricMatcher { // No getExpectedType method, because it matches either null or undefined. - toAsymmetricMatcher() { + override toAsymmetricMatcher() { return 'Anything'; } } @@ -203,7 +204,7 @@ class ArrayContaining extends AsymmetricMatcher> { return `Array${this.inverse ? 'Not' : ''}Containing`; } - getExpectedType() { + override getExpectedType() { return 'array'; } } @@ -240,7 +241,7 @@ class ObjectContaining extends AsymmetricMatcher> { return `Object${this.inverse ? 'Not' : ''}Containing`; } - getExpectedType() { + override getExpectedType() { return 'object'; } } @@ -263,7 +264,7 @@ class StringContaining extends AsymmetricMatcher { return `String${this.inverse ? 'Not' : ''}Containing`; } - getExpectedType() { + override getExpectedType() { return 'string'; } } @@ -286,7 +287,7 @@ class StringMatching extends AsymmetricMatcher { return `String${this.inverse ? 'Not' : ''}Matching`; } - getExpectedType() { + override getExpectedType() { return 'string'; } } @@ -326,9 +327,17 @@ class CloseTo extends AsymmetricMatcher { return `Number${this.inverse ? 'Not' : ''}CloseTo`; } - getExpectedType() { + override getExpectedType() { return 'number'; } + + override toAsymmetricMatcher(): string { + return [ + this.toString(), + this.sample, + `(${pluralize('digit', this.precision)})`, + ].join(' '); + } } export const any = (expectedObject: unknown): Any => new Any(expectedObject); diff --git a/packages/expect/src/index.ts b/packages/expect/src/index.ts index ee6179aa8402..4511b9e0b8bb 100644 --- a/packages/expect/src/index.ts +++ b/packages/expect/src/index.ts @@ -49,6 +49,7 @@ import type { ThrowingMatcherFn, } from './types'; +export {AsymmetricMatcher} from './asymmetricMatchers'; export type { AsymmetricMatchers, BaseExpect, diff --git a/packages/expect/src/jestMatchersObject.ts b/packages/expect/src/jestMatchersObject.ts index 121a3116f26d..090434d66f60 100644 --- a/packages/expect/src/jestMatchersObject.ts +++ b/packages/expect/src/jestMatchersObject.ts @@ -94,11 +94,11 @@ export const setMatchers = ( return `${this.inverse ? 'not.' : ''}${key}`; } - getExpectedType() { + override getExpectedType() { return 'any'; } - toAsymmetricMatcher() { + override toAsymmetricMatcher() { return `${this.toString()}<${this.sample.map(String).join(', ')}>`; } } diff --git a/packages/expect/tsconfig.json b/packages/expect/tsconfig.json index 9c5b8e7c5117..5c132ec1e784 100644 --- a/packages/expect/tsconfig.json +++ b/packages/expect/tsconfig.json @@ -11,6 +11,7 @@ {"path": "../jest-get-type"}, {"path": "../jest-matcher-utils"}, {"path": "../jest-message-util"}, + {"path": "../jest-util"}, {"path": "../test-utils"} ] } diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index 84df83148d09..bede8739651e 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -1,6 +1,6 @@ { "name": "jest-changed-files", - "version": "28.0.0-alpha.3", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", diff --git a/packages/jest-changed-files/src/hg.ts b/packages/jest-changed-files/src/hg.ts index 02f1ea6099cd..c9fb11b5558a 100644 --- a/packages/jest-changed-files/src/hg.ts +++ b/packages/jest-changed-files/src/hg.ts @@ -18,7 +18,7 @@ const adapter: SCMAdapter = { const args = ['status', '-amnu']; if (options.withAncestor) { - args.push('--rev', 'min((!public() & ::.)+.)^'); + args.push('--rev', 'first(min(!public() & ::.)^+.^)'); } else if (options.changedSince) { args.push('--rev', `ancestor(., ${options.changedSince})`); } else if (options.lastCommit === true) { diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index b0585d5e670e..abc49183171f 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -1,6 +1,6 @@ { "name": "jest-circus", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -18,30 +18,30 @@ "./runner": "./runner.js" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/expect": "^28.0.0-alpha.7", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/environment": "^28.0.1", + "@jest/expect": "^28.0.1", + "@jest/test-result": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^0.7.0", "is-generator-fn": "^2.0.0", - "jest-each": "^28.0.0-alpha.7", - "jest-matcher-utils": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", - "jest-snapshot": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "pretty-format": "^28.0.0-alpha.7", + "jest-each": "^28.0.1", + "jest-matcher-utils": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-runtime": "^28.0.1", + "jest-snapshot": "^28.0.1", + "jest-util": "^28.0.1", + "pretty-format": "^28.0.1", "slash": "^3.0.0", "stack-utils": "^2.0.3", "throat": "^6.0.1" }, "devDependencies": { - "@babel/core": "^7.1.0", + "@babel/core": "^7.11.6", "@babel/register": "^7.0.0", - "@types/co": "^4.6.0", + "@types/co": "^4.6.2", "@types/dedent": "^0.7.0", "@types/graceful-fs": "^4.1.3", "@types/stack-utils": "^2.0.0", diff --git a/packages/jest-circus/src/__mocks__/testUtils.ts b/packages/jest-circus/src/__mocks__/testUtils.ts index 52557f72fcb8..300159fd3e71 100644 --- a/packages/jest-circus/src/__mocks__/testUtils.ts +++ b/packages/jest-circus/src/__mocks__/testUtils.ts @@ -31,7 +31,10 @@ interface Result extends ExecaSyncReturnValue { } export const runTest = (source: string) => { - const filename = createHash('md5').update(source).digest('hex'); + const filename = createHash('sha256') + .update(source) + .digest('hex') + .substring(0, 32); const tmpFilename = path.join(tmpdir(), filename); const content = ` diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index 02d02e3131f7..38aee07a563e 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -10,7 +10,7 @@ import { injectGlobalErrorHandlers, restoreGlobalErrorHandlers, } from './globalErrorHandlers'; -import {TEST_TIMEOUT_SYMBOL} from './types'; +import {LOG_ERRORS_BEFORE_RETRY, TEST_TIMEOUT_SYMBOL} from './types'; import { addErrorToEachTestUnderDescribe, describeBlockHasTests, @@ -122,7 +122,7 @@ const eventHandler: Circus.EventHandler = (event, state) => { } case 'add_test': { const {currentDescribeBlock, currentlyRunningTest, hasStarted} = state; - const {asyncError, fn, mode, testName: name, timeout, failing} = event; + const {asyncError, fn, mode, testName: name, timeout, concurrent, failing} = event; if (currentlyRunningTest) { currentlyRunningTest.errors.push( @@ -143,6 +143,7 @@ const eventHandler: Circus.EventHandler = (event, state) => { const test = makeTest( fn, mode, + concurrent, name, currentDescribeBlock, timeout, @@ -206,6 +207,12 @@ const eventHandler: Circus.EventHandler = (event, state) => { break; } case 'test_retry': { + const logErrorsBeforeRetry: boolean = + // eslint-disable-next-line no-restricted-globals + global[LOG_ERRORS_BEFORE_RETRY] || false; + if (logErrorsBeforeRetry) { + event.test.retryReasons.push(...event.test.errors); + } event.test.errors = []; break; } diff --git a/packages/jest-circus/src/index.ts b/packages/jest-circus/src/index.ts index 7d1c0c01552c..89a2b8b7fec7 100644 --- a/packages/jest-circus/src/index.ts +++ b/packages/jest-circus/src/index.ts @@ -117,24 +117,34 @@ const test: Global.It = (() => { testName: Circus.TestNameLike, fn: Circus.TestFn, timeout?: number, - ): void => _addTest(testName, undefined, fn, test, timeout); + ): void => _addTest(testName, undefined, false, fn, test, timeout); const skip = ( testName: Circus.TestNameLike, fn?: Circus.TestFn, timeout?: number, - ): void => _addTest(testName, 'skip', fn, skip, timeout); + ): void => _addTest(testName, 'skip', false, fn, skip, timeout); const only = ( testName: Circus.TestNameLike, fn: Circus.TestFn, timeout?: number, - ): void => _addTest(testName, 'only', fn, test.only, timeout); + ): void => _addTest(testName, 'only', false, fn, test.only, timeout); + const concurrentTest = ( + testName: Circus.TestNameLike, + fn: Circus.TestFn, + timeout?: number, + ): void => _addTest(testName, undefined, true, fn, concurrentTest, timeout); + const concurrentOnly = ( + testName: Circus.TestNameLike, + fn: Circus.TestFn, + timeout?: number, + ): void => _addTest(testName, 'only', true, fn, concurrentOnly, timeout); - const bindFailing = (mode: Circus.TestMode) => { + const bindFailing = (concurrent: boolean, mode: Circus.TestMode) => { const failing = ( testName: Circus.TestNameLike, fn?: Circus.TestFn, timeout?: number, - ): void => _addTest(testName, mode, fn, failing, timeout, true); + ): void => _addTest(testName, mode, concurrent, fn, failing, timeout, true); return failing; }; @@ -145,12 +155,13 @@ const test: Global.It = (() => { test.todo, ); } - return _addTest(testName, 'todo', () => {}, test.todo); + return _addTest(testName, 'todo', false, () => {}, test.todo); }; const _addTest = ( testName: Circus.TestNameLike, mode: Circus.TestMode, + concurrent: boolean, fn: Circus.TestFn | undefined, testFn: ( testName: Circus.TestNameLike, @@ -183,6 +194,7 @@ const test: Global.It = (() => { return dispatchSync({ asyncError, + concurrent, failing: failing === undefined ? false : failing, fn, mode, @@ -196,12 +208,18 @@ const test: Global.It = (() => { only.each = bindEach(only); skip.each = bindEach(skip); - only.failing = bindFailing('only'); - skip.failing = bindFailing('skip'); + concurrentTest.each = bindEach(concurrentTest, false); + concurrentOnly.each = bindEach(concurrentOnly, false); + + only.failing = bindFailing(false, 'only'); + skip.failing = bindFailing(false, 'skip'); - test.failing = bindFailing(); + test.failing = bindFailing(false); test.only = only; test.skip = skip; + test.concurrent = concurrentTest; + concurrentTest.only = concurrentOnly; + concurrentTest.skip = skip; return test; })(); diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index dc8059b39acf..9df215a6d5b3 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -38,11 +38,13 @@ const jestAdapter = async ( testPath, }); - if (config.timers === 'fake' || config.timers === 'modern') { - // during setup, this cannot be null (and it's fine to explode if it is) - environment.fakeTimersModern!.useFakeTimers(); - } else if (config.timers === 'legacy') { - environment.fakeTimers!.useFakeTimers(); + if (config.fakeTimers.enableGlobally) { + if (config.fakeTimers.legacyFakeTimers) { + // during setup, this cannot be null (and it's fine to explode if it is) + environment.fakeTimers!.useFakeTimers(); + } else { + environment.fakeTimersModern!.useFakeTimers(); + } } globals.beforeEach(() => { @@ -57,7 +59,10 @@ const jestAdapter = async ( if (config.resetMocks) { runtime.resetAllMocks(); - if (config.timers === 'legacy') { + if ( + config.fakeTimers.enableGlobally && + config.fakeTimers.legacyFakeTimers + ) { // during setup, this cannot be null (and it's fine to explode if it is) environment.fakeTimers!.useFakeTimers(); } diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts index cd2781a1af1f..6e00f54eed5f 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import throat from 'throat'; import type {JestEnvironment} from '@jest/environment'; import {JestExpect, jestExpect} from '@jest/expect'; import { @@ -16,7 +15,6 @@ import { createEmptyTestResult, } from '@jest/test-result'; import type {Circus, Config, Global} from '@jest/types'; -import {bind} from 'jest-each'; import {formatExecError, formatResultsErrors} from 'jest-message-util'; import { SnapshotState, @@ -63,8 +61,7 @@ export const initialize = async ({ if (globalConfig.testTimeout) { getRunnerState().testTimeout = globalConfig.testTimeout; } - - const mutex = throat(globalConfig.maxConcurrency); + getRunnerState().maxConcurrency = globalConfig.maxConcurrency; // @ts-expect-error const globalsObject: Global.TestFrameworkGlobals = { @@ -76,45 +73,6 @@ export const initialize = async ({ xtest: globals.it.skip, }; - globalsObject.test.concurrent = (test => { - const concurrent = ( - testName: Global.TestNameLike, - testFn: Global.ConcurrentTestFn, - timeout?: number, - ) => { - // For concurrent tests we first run the function that returns promise, and then register a - // normal test that will be waiting on the returned promise (when we start the test, the promise - // will already be in the process of execution). - // Unfortunately at this stage there's no way to know if there are any `.only` tests in the suite - // that will result in this test to be skipped, so we'll be executing the promise function anyway, - // even if it ends up being skipped. - const promise = mutex(() => testFn()); - // Avoid triggering the uncaught promise rejection handler in case the test errors before - // being awaited on. - promise.catch(() => {}); - globalsObject.test(testName, () => promise, timeout); - }; - - const only = ( - testName: Global.TestNameLike, - testFn: Global.ConcurrentTestFn, - timeout?: number, - ) => { - const promise = mutex(() => testFn()); - // eslint-disable-next-line jest/no-focused-tests - test.only(testName, () => promise, timeout); - }; - - concurrent.only = only; - concurrent.skip = test.skip; - - concurrent.each = bind(test, false); - concurrent.skip.each = bind(test.skip, false); - only.each = bind(test.only, false); - - return concurrent; - })(globalsObject.test); - addEventHandler(eventHandler); if (environment.handleTestEvent) { @@ -220,6 +178,7 @@ export const runAndTransformResultsToJestFormat = async ({ invocations: testResult.invocations, location: testResult.location, numPassingAsserts: 0, + retryReasons: testResult.retryReasons, status, title: testResult.testPath[testResult.testPath.length - 1], }; diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index f4255bedd79e..9fde8e0451d5 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import throat from 'throat'; import type {Circus} from '@jest/types'; import {ErrorWithStack} from 'jest-util'; import {dispatch, getState} from './state'; @@ -21,7 +22,7 @@ import { const run = async (): Promise => { const {rootDescribeBlock} = getState(); await dispatch({name: 'run_start'}); - await _runTestsForDescribeBlock(rootDescribeBlock); + await _runTestsForDescribeBlock(rootDescribeBlock, true); await dispatch({name: 'run_finish'}); return makeRunResult( getState().rootDescribeBlock, @@ -31,6 +32,7 @@ const run = async (): Promise => { const _runTestsForDescribeBlock = async ( describeBlock: Circus.DescribeBlock, + isRootBlock = false, ) => { await dispatch({describeBlock, name: 'run_describe_start'}); const {beforeAll, afterAll} = getAllHooksForDescribe(describeBlock); @@ -43,6 +45,24 @@ const _runTestsForDescribeBlock = async ( } } + if (isRootBlock) { + const concurrentTests = collectConcurrentTests(describeBlock); + const mutex = throat(getState().maxConcurrency); + for (const test of concurrentTests) { + try { + const promise = mutex(test.fn); + // Avoid triggering the uncaught promise rejection handler in case the + // test errors before being awaited on. + promise.catch(() => {}); + test.fn = () => promise; + } catch (err) { + test.fn = () => { + throw err; + }; + } + } + } + // Tests that fail and are retried we run after other tests // eslint-disable-next-line no-restricted-globals const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; @@ -92,6 +112,30 @@ const _runTestsForDescribeBlock = async ( await dispatch({describeBlock, name: 'run_describe_finish'}); }; +function collectConcurrentTests( + describeBlock: Circus.DescribeBlock, +): Array & {fn: Circus.ConcurrentTestFn}> { + if (describeBlock.mode === 'skip') { + return []; + } + const {hasFocusedTests, testNamePattern} = getState(); + return describeBlock.children.flatMap(child => { + switch (child.type) { + case 'describeBlock': + return collectConcurrentTests(child); + case 'test': + const skip = + !child.concurrent || + child.mode === 'skip' || + (hasFocusedTests && child.mode !== 'only') || + (testNamePattern && !testNamePattern.test(getTestID(child))); + return skip + ? [] + : [child as Circus.TestEntry & {fn: Circus.ConcurrentTestFn}]; + } + }); +} + const _runTest = async ( test: Circus.TestEntry, parentSkipped: boolean, diff --git a/packages/jest-circus/src/state.ts b/packages/jest-circus/src/state.ts index 264e0d160746..e9f0321e979f 100644 --- a/packages/jest-circus/src/state.ts +++ b/packages/jest-circus/src/state.ts @@ -27,6 +27,7 @@ const createState = (): Circus.State => { hasFocusedTests: false, hasStarted: false, includeTestLocationInResult: false, + maxConcurrency: 5, parentProcess: null, rootDescribeBlock: ROOT_DESCRIBE_BLOCK, testNamePattern: null, diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index ff81f685234e..9192e2412b2d 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -11,6 +11,7 @@ export const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); export const RETRY_TIMES = Symbol.for('RETRY_TIMES'); // To pass this value from Runtime object to state we need to use global[sym] export const TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL'); +export const LOG_ERRORS_BEFORE_RETRY = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); declare global { namespace NodeJS { @@ -18,6 +19,7 @@ declare global { [STATE_SYM]: Circus.State; [RETRY_TIMES]: string; [TEST_TIMEOUT_SYMBOL]: number; + [LOG_ERRORS_BEFORE_RETRY]: boolean; } } } diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 61ded789218c..4d787f83fa90 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -56,6 +56,7 @@ export const makeDescribe = ( export const makeTest = ( fn: Circus.TestFn, mode: Circus.TestMode, + concurrent: boolean, name: Circus.TestName, parent: Circus.DescribeBlock, timeout: number | undefined, @@ -64,6 +65,7 @@ export const makeTest = ( ): Circus.TestEntry => ({ type: 'test', // eslint-disable-next-line sort-keys asyncError, + concurrent, duration: null, errors: [], failing, @@ -72,6 +74,7 @@ export const makeTest = ( mode, name: convertDescriptorToString(name), parent, + retryReasons: [], seenDone: false, startedAt: null, status: null, @@ -129,6 +132,11 @@ type TestHooks = { export const getEachHooksForTest = (test: Circus.TestEntry): TestHooks => { const result: TestHooks = {afterEach: [], beforeEach: []}; + if (test.concurrent) { + // *Each hooks are not run for concurrent tests + return result; + } + let block: Circus.DescribeBlock | undefined | null = test.parent; do { @@ -356,6 +364,7 @@ export const makeSingleTestResult = ( errorsDetailed, invocations: test.invocations, location, + retryReasons: test.retryReasons.map(_getError).map(getErrorStack), status, testPath: Array.from(testPath), }; @@ -477,6 +486,7 @@ export const parseSingleTestResult = ( invocations: testResult.invocations, location: testResult.location, numPassingAsserts: 0, + retryReasons: Array.from(testResult.retryReasons), status, title: testResult.testPath[testResult.testPath.length - 1], }; diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 15dad3d10dba..3a23347738b8 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -1,7 +1,7 @@ { "name": "jest-cli", "description": "Delightful JavaScript Testing.", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -13,16 +13,16 @@ "./bin/jest": "./bin/jest.js" }, "dependencies": { - "@jest/core": "^28.0.0-alpha.7", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/core": "^28.0.1", + "@jest/test-result": "^28.0.1", + "@jest/types": "^28.0.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-validate": "^28.0.0-alpha.7", + "jest-config": "^28.0.1", + "jest-util": "^28.0.1", + "jest-validate": "^28.0.1", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -48,7 +48,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/facebook/jest", + "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-cli" }, "bugs": { diff --git a/packages/jest-cli/src/__tests__/cli/args.test.ts b/packages/jest-cli/src/__tests__/cli/args.test.ts index f40f37aeb873..89593f889ebd 100644 --- a/packages/jest-cli/src/__tests__/cli/args.test.ts +++ b/packages/jest-cli/src/__tests__/cli/args.test.ts @@ -81,6 +81,12 @@ describe('check', () => { ); }); + it('raises an exception if ignoreProjects is not provided any project names', () => { + expect(() => check(argv({ignoreProjects: []}))).toThrow( + 'The --ignoreProjects option requires the name of at least one project to be specified.\n', + ); + }); + it('raises an exception if config is not a valid JSON string', () => { expect(() => check(argv({config: 'x:1'}))).toThrow( 'The --config option requires a JSON string literal, or a file path with one of these extensions: .js, .ts, .mjs, .cjs, .json', diff --git a/packages/jest-cli/src/cli/args.ts b/packages/jest-cli/src/cli/args.ts index 57c55a59f129..da80102fc9c9 100644 --- a/packages/jest-cli/src/cli/args.ts +++ b/packages/jest-cli/src/cli/args.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import type {Options} from 'yargs'; import type {Config} from '@jest/types'; import {constants, isJSONString} from 'jest-config'; @@ -68,6 +69,13 @@ export function check(argv: Config.Argv): true { ); } + if (argv.ignoreProjects && argv.ignoreProjects.length === 0) { + throw new Error( + 'The --ignoreProjects option requires the name of at least one project to be specified.\n' + + 'Example usage: jest --ignoreProjects my-first-project my-second-project', + ); + } + if ( argv.config && !isJSONString(argv.config) && @@ -95,7 +103,7 @@ export const usage = export const docs = 'Documentation: https://jestjs.io/'; // The default values are all set in jest-config -export const options = { +export const options: {[key: string]: Options} = { all: { description: 'The opposite of `onlyChanged`. If `onlyChanged` is set by ' + @@ -154,7 +162,7 @@ export const options = { }, clearMocks: { description: - 'Automatically clear mock calls, instances and results before every test. ' + + 'Automatically clear mock calls, instances, contexts and results before every test. ' + 'Equivalent to calling jest.clearAllMocks() before each test.', type: 'boolean', }, @@ -301,6 +309,13 @@ export const options = { 'A JSON string with map of variables for the haste module system', type: 'string', }, + ignoreProjects: { + description: + 'Ignore the tests of the specified projects.' + + 'Jest uses the attribute `displayName` in the configuration to identify each project.', + string: true, + type: 'array', + }, init: { description: 'Generate a basic configuration file', type: 'boolean', @@ -502,7 +517,7 @@ export const options = { }, selectProjects: { description: - 'Run only the tests of the specified projects.' + + 'Run the tests of the specified projects.' + 'Jest uses the attribute `displayName` in the configuration to identify each project.', string: true, type: 'array', @@ -622,12 +637,6 @@ export const options = { description: 'This option sets the default timeouts of test cases.', type: 'number', }, - timers: { - description: - 'Setting this value to fake allows the use of fake timers ' + - 'for functions such as setTimeout.', - type: 'string', - }, transform: { description: 'A JSON string which maps from regular expressions to paths ' + @@ -695,4 +704,4 @@ export const options = { '--no-watchman.', type: 'boolean', }, -} as const; +}; diff --git a/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap b/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap index 157419444c3d..31cca289857e 100644 --- a/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap +++ b/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap @@ -108,7 +108,7 @@ Array [ }, Object { "initial": false, - "message": "Automatically clear mock calls, instances and results before every test?", + "message": "Automatically clear mock calls, instances, contexts and results before every test?", "name": "clearMocks", "type": "confirm", }, @@ -131,7 +131,7 @@ module.exports = { // The directory where Jest should store its cached dependency information // cacheDirectory: "/tmp/jest", - // Automatically clear mock calls, instances and results before every test + // Automatically clear mock calls, instances, contexts and results before every test // clearMocks: false, // Indicates whether the coverage information should be collected while executing the test @@ -168,6 +168,11 @@ module.exports = { // Make calling deprecated APIs throw helpful error messages // errorOnDeprecated: false, + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + // Force coverage collection from ignored files using an array of glob patterns // forceCoverageMatch: [], @@ -191,6 +196,8 @@ module.exports = { // An array of file extensions your modules use // moduleFileExtensions: [ // "js", + // "mjs", + // "cjs", // "jsx", // "ts", // "tsx", @@ -283,9 +290,6 @@ module.exports = { // This option allows use of a custom test runner // testRunner: "jest-circus/runner", - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - // A map from regular expressions to paths to transformers // transform: undefined, diff --git a/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dependencies/package.json b/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dependencies/package.json index 100738a1126c..45d694a634cc 100644 --- a/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dependencies/package.json +++ b/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dependencies/package.json @@ -1,6 +1,6 @@ { "name": "typescript_in_dev_dependencies", "devDependencies": { - "typescript": "*" + "typescript": "^4.6.2" } } diff --git a/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dev-dependencies/package.json b/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dev-dependencies/package.json index c6578ac6d5e9..90688f161ebf 100644 --- a/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dev-dependencies/package.json +++ b/packages/jest-cli/src/init/__tests__/fixtures/typescript-in-dev-dependencies/package.json @@ -1,6 +1,6 @@ { "name": "only-package-json", "dependencies": { - "typescript": "*" + "typescript": "^4.6.2" } } diff --git a/packages/jest-cli/src/init/questions.ts b/packages/jest-cli/src/init/questions.ts index fd4e9325ff7f..9d86faa6ccc2 100644 --- a/packages/jest-cli/src/init/questions.ts +++ b/packages/jest-cli/src/init/questions.ts @@ -43,7 +43,7 @@ const defaultQuestions: Array = [ { initial: false, message: - 'Automatically clear mock calls, instances and results before every test?', + 'Automatically clear mock calls, instances, contexts and results before every test?', name: 'clearMocks', type: 'confirm', }, diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index fad9b7cb360a..38654fc95752 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -1,6 +1,6 @@ { "name": "jest-config", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -29,26 +29,26 @@ } }, "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", - "babel-jest": "^28.0.0-alpha.7", + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.0.1", + "@jest/types": "^28.0.1", + "babel-jest": "^28.0.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", - "glob": "^7.1.1", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^28.0.0-alpha.7", - "jest-environment-node": "^28.0.0-alpha.7", - "jest-get-type": "^28.0.0-alpha.3", - "jest-regex-util": "^28.0.0-alpha.6", - "jest-resolve": "^28.0.0-alpha.7", - "jest-runner": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-validate": "^28.0.0-alpha.7", + "jest-circus": "^28.0.1", + "jest-environment-node": "^28.0.1", + "jest-get-type": "^28.0.0", + "jest-regex-util": "^28.0.0", + "jest-resolve": "^28.0.1", + "jest-runner": "^28.0.1", + "jest-util": "^28.0.1", + "jest-validate": "^28.0.1", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^28.0.0-alpha.7", + "pretty-format": "^28.0.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -58,7 +58,7 @@ "@types/micromatch": "^4.0.1", "semver": "^7.3.5", "ts-node": "^10.5.0", - "typescript": "^4.2.4" + "typescript": "^4.6.2" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index 9cc30e2d09e3..cb85437b3593 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -31,6 +31,7 @@ const defaultOptions: Config.DefaultOptions = { errorOnDeprecated: false, expand: false, extensionsToTreatAsEsm: [], + fakeTimers: {enableGlobally: false}, forceCoverageMatch: [], globals: {}, haste: { @@ -44,7 +45,16 @@ const defaultOptions: Config.DefaultOptions = { maxConcurrency: 5, maxWorkers: '50%', moduleDirectories: ['node_modules'], - moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'], + moduleFileExtensions: [ + 'js', + 'mjs', + 'cjs', + 'jsx', + 'ts', + 'tsx', + 'json', + 'node', + ], moduleNameMapper: {}, modulePathIgnorePatterns: [], noStackTrace: false, @@ -72,7 +82,6 @@ const defaultOptions: Config.DefaultOptions = { testRegex: [], testRunner: 'jest-circus/runner', testSequencer: '@jest/test-sequencer', - timers: 'real', transformIgnorePatterns: [NODE_MODULES_REGEXP, `\\.pnp\\.[^\\${sep}]+$`], useStderr: false, watch: false, diff --git a/packages/jest-config/src/Deprecated.ts b/packages/jest-config/src/Deprecated.ts index 4568d7fed3c0..647dd29691d3 100644 --- a/packages/jest-config/src/Deprecated.ts +++ b/packages/jest-config/src/Deprecated.ts @@ -7,9 +7,6 @@ import chalk = require('chalk'); import type {DeprecatedOptions} from 'jest-validate'; -import {format as prettyFormat} from 'pretty-format'; - -const format = (value: unknown) => prettyFormat(value, {min: true}); const deprecatedOptions: DeprecatedOptions = { browser: () => @@ -29,7 +26,7 @@ const deprecatedOptions: DeprecatedOptions = { Please update your configuration.`, - preprocessorIgnorePatterns: (options: { + preprocessorIgnorePatterns: (_options: { preprocessorIgnorePatterns?: Array; }) => ` Option ${chalk.bold( '"preprocessorIgnorePatterns"', @@ -37,16 +34,9 @@ const deprecatedOptions: DeprecatedOptions = { '"transformIgnorePatterns"', )}, which support multiple preprocessors. - Jest now treats your current configuration as: - { - ${chalk.bold('"transformIgnorePatterns"')}: ${chalk.bold( - format(options.preprocessorIgnorePatterns), - )} - } - Please update your configuration.`, - scriptPreprocessor: (options: { + scriptPreprocessor: (_options: { scriptPreprocessor?: string; }) => ` Option ${chalk.bold( '"scriptPreprocessor"', @@ -54,13 +44,6 @@ const deprecatedOptions: DeprecatedOptions = { '"transform"', )}, which support multiple preprocessors. - Jest now treats your current configuration as: - { - ${chalk.bold('"transform"')}: ${chalk.bold( - `{".*": ${format(options.scriptPreprocessor)}}`, - )} - } - Please update your configuration.`, setupTestFrameworkScriptFile: (_options: { @@ -73,17 +56,12 @@ const deprecatedOptions: DeprecatedOptions = { Please update your configuration.`, - testPathDirs: (options: { + testPathDirs: (_options: { testPathDirs?: Array; }) => ` Option ${chalk.bold('"testPathDirs"')} was replaced by ${chalk.bold( '"roots"', )}. - Jest now treats your current configuration as: - { - ${chalk.bold('"roots"')}: ${chalk.bold(format(options.testPathDirs))} - } - Please update your configuration. `, @@ -94,6 +72,12 @@ const deprecatedOptions: DeprecatedOptions = { )}. Please update your configuration.`, + + timers: (_options: {timers?: string}) => ` Option ${chalk.bold( + '"timers"', + )} was replaced by ${chalk.bold('"fakeTimers"')}. + + Please update your configuration.`, }; export default deprecatedOptions; diff --git a/packages/jest-config/src/Descriptions.ts b/packages/jest-config/src/Descriptions.ts index 795637460a1d..cd5ecb00cef1 100644 --- a/packages/jest-config/src/Descriptions.ts +++ b/packages/jest-config/src/Descriptions.ts @@ -13,7 +13,7 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = { cacheDirectory: 'The directory where Jest should store its cached dependency information', clearMocks: - 'Automatically clear mock calls, instances and results before every test', + 'Automatically clear mock calls, instances, contexts and results before every test', collectCoverage: 'Indicates whether the coverage information should be collected while executing the test', collectCoverageFrom: @@ -31,6 +31,7 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = { dependencyExtractor: 'A path to a custom dependency extractor', errorOnDeprecated: 'Make calling deprecated APIs throw helpful error messages', + fakeTimers: 'The default configuration for fake timers', forceCoverageMatch: 'Force coverage collection from ignored files using an array of glob patterns', globalSetup: @@ -84,8 +85,6 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = { testResultsProcessor: 'This option allows the use of a custom results processor', testRunner: 'This option allows use of a custom test runner', - timers: - 'Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"', transform: 'A map from regular expressions to paths to transformers', transformIgnorePatterns: 'An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation', diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 5757431123ed..18b083bbb8bf 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -49,6 +49,30 @@ const initialOptions: Config.InitialOptions = { errorOnDeprecated: false, expand: false, extensionsToTreatAsEsm: [], + fakeTimers: { + advanceTimers: multipleValidOptions(40, true), + doNotFake: [ + 'Date', + 'hrtime', + 'nextTick', + 'performance', + 'queueMicrotask', + 'requestAnimationFrame', + 'cancelAnimationFrame', + 'requestIdleCallback', + 'cancelIdleCallback', + 'setImmediate', + 'clearImmediate', + 'setInterval', + 'clearInterval', + 'setTimeout', + 'clearTimeout', + ], + enableGlobally: true, + legacyFakeTimers: false, + now: 1483228800000, + timerLimit: 1000, + }, filter: '/filter.js', forceCoverageMatch: ['**/*.t.js'], forceExit: false, @@ -66,6 +90,7 @@ const initialOptions: Config.InitialOptions = { retainAllFiles: false, throwOnModuleCollision: false, }, + id: 'string', injectGlobals: true, json: false, lastCommit: false, @@ -74,13 +99,21 @@ const initialOptions: Config.InitialOptions = { maxConcurrency: 5, maxWorkers: '50%', moduleDirectories: ['node_modules'], - moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'], + moduleFileExtensions: [ + 'js', + 'mjs', + 'cjs', + 'json', + 'jsx', + 'ts', + 'tsx', + 'node', + ], moduleNameMapper: { '^React$': '/node_modules/react', }, modulePathIgnorePatterns: ['/build/'], modulePaths: ['/shared/vendor/modules'], - name: 'string', noStackTrace: false, notify: false, notifyMode: 'failure-change', @@ -132,7 +165,6 @@ const initialOptions: Config.InitialOptions = { testRunner: 'circus', testSequencer: '@jest/test-sequencer', testTimeout: 5000, - timers: 'real', transform: { '\\.js$': '/preprocessor.js', }, diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.ts.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.ts.snap index 46538f039b17..00386d08b28c 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.ts.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.ts.snap @@ -1,22 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Upgrade help logs a warning when \`scriptPreprocessor\` and/or \`preprocessorIgnorePatterns\` are used 1`] = ` -" Deprecation Warning: - - Option "preprocessorIgnorePatterns" was replaced by "transformIgnorePatterns", which support multiple preprocessors. - - Jest now treats your current configuration as: - { - "transformIgnorePatterns": ["bar/baz", "qux/quux"] - } - - Please update your configuration. - - Configuration Documentation: - https://jestjs.io/docs/configuration -" -`; - exports[`displayName generates a default color for the runner jest-runner 1`] = `"yellow"`; exports[`displayName generates a default color for the runner jest-runner-eslint 1`] = `"magenta"`; @@ -132,7 +115,29 @@ exports[`extensionsToTreatAsEsm throws on .mjs 1`] = ` " `; -exports[`extraGlobals logs a deprecation warning when \`extraGlobals\` is used 1`] = ` +exports[`logs a deprecation warning when 'browser' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "browser" has been deprecated. Please install "browser-resolve" and use the "resolver" option in Jest configuration as shown in the documentation: https://jestjs.io/docs/configuration#resolver-string + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'extraGlobals' option is passed 1`] = ` [MockFunction] { "calls": Array [ Array [ @@ -156,7 +161,7 @@ exports[`extraGlobals logs a deprecation warning when \`extraGlobals\` is used 1 } `; -exports[`moduleLoader logs a deprecation warning when \`moduleLoader\` is used 1`] = ` +exports[`logs a deprecation warning when 'moduleLoader' option is passed 1`] = ` [MockFunction] { "calls": Array [ Array [ @@ -180,6 +185,151 @@ exports[`moduleLoader logs a deprecation warning when \`moduleLoader\` is used 1 } `; +exports[`logs a deprecation warning when 'preprocessorIgnorePatterns' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "preprocessorIgnorePatterns" was replaced by "transformIgnorePatterns", which support multiple preprocessors. + + Please update your configuration. + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'scriptPreprocessor' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "scriptPreprocessor" was replaced by "transform", which support multiple preprocessors. + + Please update your configuration. + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'setupTestFrameworkScriptFile' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "setupTestFrameworkScriptFile" was replaced by configuration "setupFilesAfterEnv", which supports multiple paths. + + Please update your configuration. + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'testPathDirs' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "testPathDirs" was replaced by "roots". + + Please update your configuration. + + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'testURL' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "testURL" was replaced by passing the URL via "testEnvironmentOptions.url". + + Please update your configuration. + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`logs a deprecation warning when 'timers' option is passed 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + " Deprecation Warning: + + Option "timers" was replaced by "fakeTimers". + + Please update your configuration. + + Configuration Documentation: + https://jestjs.io/docs/configuration +", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + exports[`preset throws when module was found but no "jest-preset.js" or "jest-preset.json" files 1`] = ` "Validation Error: @@ -200,43 +350,88 @@ exports[`preset throws when preset not found 1`] = ` " `; -exports[`rootDir throws if the options is missing a rootDir property 1`] = ` -"Validation Error: +exports[`reporters throws an error if first value in the tuple is not a string 1`] = ` +"Reporter Validation Error: + + Unexpected value for Path at index 0 of reporter at index 0 + Expected: + string + Got: + number + Reporter configuration: + [ + 123 + ] - Configuration option rootDir must be specified. + Configuration Documentation: + https://jestjs.io/docs/configuration +" +`; + +exports[`reporters throws an error if second value in the tuple is not an object 1`] = ` +"Reporter Validation Error: + + Unexpected value for Reporter Configuration at index 1 of reporter at index 0 + Expected: + object + Got: + boolean + Reporter configuration: + [ + "some-reporter", + true + ] Configuration Documentation: https://jestjs.io/docs/configuration " `; -exports[`runner throw error when a runner is not found 1`] = ` -"Validation Error: +exports[`reporters throws an error if second value is missing in the tuple 1`] = ` +"Reporter Validation Error: + + Unexpected value for Reporter Configuration at index 1 of reporter at index 0 + Expected: + object + Got: + undefined + Reporter configuration: + [ + "some-reporter" + ] - Jest Runner missing-runner cannot be found. Make sure the runner configuration option points to an existing node module. + Configuration Documentation: + https://jestjs.io/docs/configuration +" +`; + +exports[`reporters throws an error if value is neither string nor array 1`] = ` +"Reporter Validation Error: + + Reporter at index 0 must be of type: + array or string + but instead received: + number Configuration Documentation: https://jestjs.io/docs/configuration " `; -exports[`setupTestFrameworkScriptFile logs a deprecation warning when \`setupTestFrameworkScriptFile\` is used 1`] = ` -" Deprecation Warning: - - Option "setupTestFrameworkScriptFile" was replaced by configuration "setupFilesAfterEnv", which supports multiple paths. - - Please update your configuration. - - Configuration Documentation: - https://jestjs.io/docs/configuration -" +exports[`rootDir throws if the options is missing a rootDir property 1`] = ` +"Validation Error: + + Configuration option rootDir must be specified. + + Configuration Documentation: + https://jestjs.io/docs/configuration +" `; -exports[`setupTestFrameworkScriptFile logs an error when \`setupTestFrameworkScriptFile\` and \`setupFilesAfterEnv\` are used 1`] = ` +exports[`runner throw error when a runner is not found 1`] = ` "Validation Error: - Options: setupTestFrameworkScriptFile and setupFilesAfterEnv cannot be used together. - Please change your configuration to only use setupFilesAfterEnv. + Jest Runner missing-runner cannot be found. Make sure the runner configuration option points to an existing node module. Configuration Documentation: https://jestjs.io/docs/configuration @@ -277,30 +472,6 @@ exports[`testTimeout should throw an error if timeout is a negative number 1`] = " `; -exports[`testURL logs a deprecation warning when \`testURL\` is used 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - " Deprecation Warning: - - Option "testURL" was replaced by passing the URL via "testEnvironmentOptions.url". - - Please update your configuration. - - Configuration Documentation: - https://jestjs.io/docs/configuration -", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], -} -`; - exports[`watchPlugins throw error when a watch plugin is not found 1`] = ` "Validation Error: diff --git a/packages/jest-config/src/__tests__/normalize.test.ts b/packages/jest-config/src/__tests__/normalize.test.ts index ccca146c1c54..a480b4b4229e 100644 --- a/packages/jest-config/src/__tests__/normalize.test.ts +++ b/packages/jest-config/src/__tests__/normalize.test.ts @@ -66,44 +66,45 @@ afterEach(() => { (console.warn as unknown as jest.SpyInstance).mockRestore(); }); -it('picks a name based on the rootDir', async () => { +it('picks an id based on the rootDir', async () => { const rootDir = '/root/path/foo'; - const expected = createHash('md5') + const expected = createHash('sha256') .update('/root/path/foo') .update(String(Infinity)) - .digest('hex'); + .digest('hex') + .substring(0, 32); const {options} = await normalize( { rootDir, }, {} as Config.Argv, ); - expect(options.name).toBe(expected); + expect(options.id).toBe(expected); }); -it('keeps custom project name based on the projects rootDir', async () => { - const name = 'test'; +it('keeps custom project id based on the projects rootDir', async () => { + const id = 'test'; const {options} = await normalize( { - projects: [{name, rootDir: '/path/to/foo'}], + projects: [{id, rootDir: '/path/to/foo'}], rootDir: '/root/path/baz', }, {} as Config.Argv, ); - expect(options.projects[0].name).toBe(name); + expect(options.projects[0].id).toBe(id); }); -it('keeps custom names based on the rootDir', async () => { +it('keeps custom ids based on the rootDir', async () => { const {options} = await normalize( { - name: 'custom-name', + id: 'custom-id', rootDir: '/root/path/foo', }, {} as Config.Argv, ); - expect(options.name).toBe('custom-name'); + expect(options.id).toBe('custom-id'); }); it('minimal config is stable across runs', async () => { @@ -309,6 +310,102 @@ describe('roots', () => { testPathArray('roots'); }); +describe('reporters', () => { + let Resolver; + beforeEach(() => { + Resolver = require('jest-resolve').default; + Resolver.findNodeModule = jest.fn(name => name); + }); + + it('allows empty list', async () => { + const {options} = await normalize( + { + reporters: [], + rootDir: '/root/', + }, + {} as Config.Argv, + ); + + expect(options.reporters).toEqual([]); + }); + + it('normalizes the path and options object', async () => { + const {options} = await normalize( + { + reporters: [ + 'default', + 'github-actions', + '/custom-reporter.js', + ['/custom-reporter.js', {banana: 'yes', pineapple: 'no'}], + ['jest-junit', {outputName: 'report.xml'}], + ], + rootDir: '/root/', + }, + {} as Config.Argv, + ); + + expect(options.reporters).toEqual([ + ['default', {}], + ['github-actions', {}], + ['/root/custom-reporter.js', {}], + ['/root/custom-reporter.js', {banana: 'yes', pineapple: 'no'}], + ['jest-junit', {outputName: 'report.xml'}], + ]); + }); + + it('throws an error if value is neither string nor array', async () => { + expect.assertions(1); + await expect( + normalize( + { + reporters: [123], + rootDir: '/root/', + }, + {} as Config.Argv, + ), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + it('throws an error if first value in the tuple is not a string', async () => { + expect.assertions(1); + await expect( + normalize( + { + reporters: [[123]], + rootDir: '/root/', + }, + {} as Config.Argv, + ), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + it('throws an error if second value is missing in the tuple', async () => { + expect.assertions(1); + await expect( + normalize( + { + reporters: [['some-reporter']], + rootDir: '/root/', + }, + {} as Config.Argv, + ), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + it('throws an error if second value in the tuple is not an object', async () => { + expect.assertions(1); + await expect( + normalize( + { + reporters: [['some-reporter', true]], + rootDir: '/root/', + }, + {} as Config.Argv, + ), + ).rejects.toThrowErrorMatchingSnapshot(); + }); +}); + describe('transform', () => { let Resolver; beforeEach(() => { @@ -425,45 +522,6 @@ describe('setupFilesAfterEnv', () => { }); }); -describe('setupTestFrameworkScriptFile', () => { - let Resolver; - - beforeEach(() => { - (console.warn as unknown as jest.SpyInstance).mockImplementation(() => {}); - Resolver = require('jest-resolve').default; - Resolver.findNodeModule = jest.fn(name => - name.startsWith('/') ? name : `/root/path/foo${path.sep}${name}`, - ); - }); - - it('logs a deprecation warning when `setupTestFrameworkScriptFile` is used', async () => { - await normalize( - { - rootDir: '/root/path/foo', - setupTestFrameworkScriptFile: 'bar/baz', - }, - {} as Config.Argv, - ); - - expect( - (console.warn as unknown as jest.SpyInstance).mock.calls[0][0], - ).toMatchSnapshot(); - }); - - it('logs an error when `setupTestFrameworkScriptFile` and `setupFilesAfterEnv` are used', async () => { - await expect( - normalize( - { - rootDir: '/root/path/foo', - setupFilesAfterEnv: ['bar/baz'], - setupTestFrameworkScriptFile: 'bar/baz', - }, - {} as Config.Argv, - ), - ).rejects.toThrowErrorMatchingSnapshot(); - }); -}); - describe('coveragePathIgnorePatterns', () => { it('does not normalize paths relative to rootDir', async () => { // This is a list of patterns, so we can't assume any of them are @@ -831,45 +889,6 @@ describe('babel-jest', () => { }); }); -describe('Upgrade help', () => { - beforeEach(() => { - (console.warn as unknown as jest.SpyInstance).mockImplementation(() => {}); - - const Resolver = require('jest-resolve').default; - Resolver.findNodeModule = jest.fn(name => { - if (name == 'bar/baz') { - return '/node_modules/bar/baz'; - } - return findNodeModule(name); - }); - }); - - it('logs a warning when `scriptPreprocessor` and/or `preprocessorIgnorePatterns` are used', async () => { - const {options: options, hasDeprecationWarnings} = await normalize( - { - preprocessorIgnorePatterns: ['bar/baz', 'qux/quux'], - rootDir: '/root/path/foo', - scriptPreprocessor: 'bar/baz', - }, - {} as Config.Argv, - ); - - expect(options.transform).toEqual([['.*', '/node_modules/bar/baz', {}]]); - expect(options.transformIgnorePatterns).toEqual([ - joinForPattern('bar', 'baz'), - joinForPattern('qux', 'quux'), - ]); - - expect(options).not.toHaveProperty('scriptPreprocessor'); - expect(options).not.toHaveProperty('preprocessorIgnorePatterns'); - expect(hasDeprecationWarnings).toBeTruthy(); - - expect( - (console.warn as unknown as jest.SpyInstance).mock.calls[0][0], - ).toMatchSnapshot(); - }); -}); - describe('testRegex', () => { it('testRegex empty string is mapped to empty array', async () => { const {options} = await normalize( @@ -1668,6 +1687,8 @@ describe('moduleFileExtensions', () => { expect(options.moduleFileExtensions).toEqual([ 'js', + 'mjs', + 'cjs', 'jsx', 'ts', 'tsx', @@ -1919,33 +1940,49 @@ describe('updateSnapshot', () => { }); }); -describe('testURL', () => { +describe('shards', () => { + it('should be object if defined', async () => { + const {options} = await normalize({rootDir: '/root/'}, { + shard: '1/2', + } as Config.Argv); + + expect(options.shard).toEqual({shardCount: 2, shardIndex: 1}); + }); +}); + +describe('logs a deprecation warning', () => { beforeEach(() => { - jest.mocked(console.warn).mockImplementation(() => {}); + (console.warn as unknown as jest.SpyInstance).mockImplementation(() => {}); }); - it('logs a deprecation warning when `testURL` is used', async () => { + test("when 'browser' option is passed", async () => { await normalize( { + browser: true, rootDir: '/root/', - testURL: 'https://jestjs.io/', }, {} as Config.Argv, ); expect(console.warn).toMatchSnapshot(); }); -}); -describe('extraGlobals', () => { - beforeEach(() => { - jest.mocked(console.warn).mockImplementation(() => {}); + test("when 'extraGlobals' option is passed", async () => { + await normalize( + { + extraGlobals: ['Math'], + rootDir: '/root/', + }, + {} as Config.Argv, + ); + + expect(console.warn).toMatchSnapshot(); }); - it('logs a deprecation warning when `extraGlobals` is used', async () => { + test("when 'moduleLoader' option is passed", async () => { await normalize( { - extraGlobals: ['Math'], + moduleLoader: '/runtime.js', rootDir: '/root/', }, {} as Config.Argv, @@ -1953,32 +1990,76 @@ describe('extraGlobals', () => { expect(console.warn).toMatchSnapshot(); }); -}); -describe('moduleLoader', () => { - beforeEach(() => { - jest.mocked(console.warn).mockImplementation(() => {}); + test("when 'preprocessorIgnorePatterns' option is passed", async () => { + await normalize( + { + preprocessorIgnorePatterns: ['/node_modules/'], + rootDir: '/root/', + }, + {} as Config.Argv, + ); + + expect(console.warn).toMatchSnapshot(); }); - it('logs a deprecation warning when `moduleLoader` is used', async () => { + test("when 'scriptPreprocessor' option is passed", async () => { await normalize( { - moduleLoader: '/runtime.js', rootDir: '/root/', + scriptPreprocessor: 'preprocessor.js', }, {} as Config.Argv, ); expect(console.warn).toMatchSnapshot(); }); -}); -describe('shards', () => { - it('should be object if defined', async () => { - const {options} = await normalize({rootDir: '/root/'}, { - shard: '1/2', - } as Config.Argv); + test("when 'setupTestFrameworkScriptFile' option is passed", async () => { + await normalize( + { + rootDir: '/root/', + setupTestFrameworkScriptFile: 'setup.js', + }, + {} as Config.Argv, + ); - expect(options.shard).toEqual({shardCount: 2, shardIndex: 1}); + expect(console.warn).toMatchSnapshot(); + }); + + test("when 'testPathDirs' option is passed", async () => { + await normalize( + { + rootDir: '/root/', + testPathDirs: [''], + }, + {} as Config.Argv, + ); + + expect(console.warn).toMatchSnapshot(); + }); + + test("when 'testURL' option is passed", async () => { + await normalize( + { + rootDir: '/root/', + testURL: 'https://jestjs.io', + }, + {} as Config.Argv, + ); + + expect(console.warn).toMatchSnapshot(); + }); + + test("when 'timers' option is passed", async () => { + await normalize( + { + rootDir: '/root/', + timers: 'real', + }, + {} as Config.Argv, + ); + + expect(console.warn).toMatchSnapshot(); }); }); diff --git a/packages/jest-config/src/constants.ts b/packages/jest-config/src/constants.ts index 35e13c6f0a46..c24a77233c00 100644 --- a/packages/jest-config/src/constants.ts +++ b/packages/jest-config/src/constants.ts @@ -9,7 +9,6 @@ import * as path from 'path'; export const NODE_MODULES = `${path.sep}node_modules${path.sep}`; export const DEFAULT_JS_PATTERN = '\\.[jt]sx?$'; -export const DEFAULT_REPORTER_LABEL = 'default'; export const PACKAGE_JSON = 'package.json'; export const JEST_CONFIG_BASE_NAME = 'jest.config'; export const JEST_CONFIG_EXT_CJS = '.cjs'; diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 229bdf750921..5728f473e5cc 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -185,19 +185,20 @@ const groupOptions = ( displayName: options.displayName, errorOnDeprecated: options.errorOnDeprecated, extensionsToTreatAsEsm: options.extensionsToTreatAsEsm, + fakeTimers: options.fakeTimers, filter: options.filter, forceCoverageMatch: options.forceCoverageMatch, globalSetup: options.globalSetup, globalTeardown: options.globalTeardown, globals: options.globals, haste: options.haste, + id: options.id, injectGlobals: options.injectGlobals, moduleDirectories: options.moduleDirectories, moduleFileExtensions: options.moduleFileExtensions, moduleNameMapper: options.moduleNameMapper, modulePathIgnorePatterns: options.modulePathIgnorePatterns, modulePaths: options.modulePaths, - name: options.name, prettierPath: options.prettierPath, resetMocks: options.resetMocks, resetModules: options.resetModules, @@ -223,7 +224,6 @@ const groupOptions = ( testPathIgnorePatterns: options.testPathIgnorePatterns, testRegex: options.testRegex, testRunner: options.testRunner, - timers: options.timers, transform: options.transform, transformIgnorePatterns: options.transformIgnorePatterns, unmockedModulePathPatterns: options.unmockedModulePathPatterns, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 6a09c510435f..43d82bfb8c64 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -32,7 +32,7 @@ import DEPRECATED_CONFIG from './Deprecated'; import {validateReporters} from './ReporterValidationErrors'; import VALID_CONFIG from './ValidConfig'; import {getDisplayNameColor} from './color'; -import {DEFAULT_JS_PATTERN, DEFAULT_REPORTER_LABEL} from './constants'; +import {DEFAULT_JS_PATTERN} from './constants'; import getMaxWorkers from './getMaxWorkers'; import {parseShardPair} from './parseShardPair'; import setFromArgv from './setFromArgv'; @@ -327,56 +327,19 @@ const normalizeUnmockedModulePathPatterns = ( replacePathSepForRegex(pattern.replace(//g, options.rootDir)), ); -const normalizePreprocessor = ( - options: Config.InitialOptionsWithRootDir, -): Config.InitialOptionsWithRootDir => { - if (options.scriptPreprocessor && options.transform) { - throw createConfigError( - ` Options: ${chalk.bold('scriptPreprocessor')} and ${chalk.bold( - 'transform', - )} cannot be used together. - Please change your configuration to only use ${chalk.bold('transform')}.`, - ); - } - - if (options.preprocessorIgnorePatterns && options.transformIgnorePatterns) { - throw createConfigError( - ` Options ${chalk.bold('preprocessorIgnorePatterns')} and ${chalk.bold( - 'transformIgnorePatterns', - )} cannot be used together. - Please change your configuration to only use ${chalk.bold( - 'transformIgnorePatterns', - )}.`, - ); - } - - if (options.scriptPreprocessor) { - options.transform = { - '.*': options.scriptPreprocessor, - }; - } - - if (options.preprocessorIgnorePatterns) { - options.transformIgnorePatterns = options.preprocessorIgnorePatterns; - } - - delete options.scriptPreprocessor; - delete options.preprocessorIgnorePatterns; - return options; -}; - const normalizeMissingOptions = ( options: Config.InitialOptionsWithRootDir, configPath: string | null | undefined, projectIndex: number, ): Config.InitialOptionsWithRootDir => { - if (!options.name) { - options.name = createHash('md5') + if (!options.id) { + options.id = createHash('sha256') .update(options.rootDir) // In case we load config from some path that has the same root dir .update(configPath || '') .update(String(projectIndex)) - .digest('hex'); + .digest('hex') + .substring(0, 32); } if (!options.setupFiles) { @@ -433,7 +396,7 @@ const normalizeReporters = (options: Config.InitialOptionsWithRootDir) => { normalizedReporterConfig[0], ); - if (reporterPath !== DEFAULT_REPORTER_LABEL) { + if (!['default', 'github-actions', 'summary'].includes(reporterPath)) { const reporter = Resolver.findNodeModule(reporterPath, { basedir: options.rootDir, }); @@ -573,13 +536,11 @@ export default async function normalize( ], }); - let options = normalizePreprocessor( - normalizeReporters( - normalizeMissingOptions( - normalizeRootDir(setFromArgv(initialOptions, argv)), - configPath, - projectIndex, - ), + let options = normalizeReporters( + normalizeMissingOptions( + normalizeRootDir(setFromArgv(initialOptions, argv)), + configPath, + projectIndex, ), ); @@ -591,22 +552,6 @@ export default async function normalize( options.setupFilesAfterEnv = []; } - if ( - options.setupTestFrameworkScriptFile && - options.setupFilesAfterEnv.length > 0 - ) { - throw createConfigError(` Options: ${chalk.bold( - 'setupTestFrameworkScriptFile', - )} and ${chalk.bold('setupFilesAfterEnv')} cannot be used together. - Please change your configuration to only use ${chalk.bold( - 'setupFilesAfterEnv', - )}.`); - } - - if (options.setupTestFrameworkScriptFile) { - options.setupFilesAfterEnv.push(options.setupTestFrameworkScriptFile); - } - options.testEnvironment = resolveTestEnvironment({ requireResolveFunction: require.resolve, rootDir: options.rootDir, @@ -615,11 +560,6 @@ export default async function normalize( require.resolve(DEFAULT_CONFIG.testEnvironment), }); - if (!options.roots && options.testPathDirs) { - options.roots = options.testPathDirs; - delete options.testPathDirs; - } - if (!options.roots) { options.roots = [options.rootDir]; } @@ -636,7 +576,7 @@ export default async function normalize( options.testRunner = require.resolve('jest-jasmine2'); } catch (error: any) { if (error.code === 'MODULE_NOT_FOUND') { - createConfigError( + throw createConfigError( 'jest-jasmine is no longer shipped by default with Jest, you need to install it explicitly or provide an absolute path to Jest', ); } @@ -980,6 +920,7 @@ export default async function normalize( case 'expand': case 'extensionsToTreatAsEsm': case 'globals': + case 'fakeTimers': case 'findRelatedTests': case 'forceCoverageMatch': case 'forceExit': @@ -988,7 +929,7 @@ export default async function normalize( case 'listTests': case 'logHeapUsage': case 'maxConcurrency': - case 'name': + case 'id': case 'noStackTrace': case 'notify': case 'notifyMode': @@ -1014,7 +955,6 @@ export default async function normalize( case 'testFailureExitCode': case 'testLocationInResults': case 'testNamePattern': - case 'timers': case 'useStderr': case 'verbose': case 'watch': diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index fd3ea09ad91d..96e2bd87bc50 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@jest/console", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,16 +17,15 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", + "jest-message-util": "^28.0.1", + "jest-util": "^28.0.1", "slash": "^3.0.0" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", - "@types/node": "*" + "@jest/test-utils": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-console/src/BufferedConsole.ts b/packages/jest-console/src/BufferedConsole.ts index f65874209f8c..f30f5829a623 100644 --- a/packages/jest-console/src/BufferedConsole.ts +++ b/packages/jest-console/src/BufferedConsole.ts @@ -24,7 +24,7 @@ export default class BufferedConsole extends Console { private _timers: LogTimers = {}; private _groupDepth = 0; - Console: typeof Console = Console; + override Console: typeof Console = Console; constructor() { super({ @@ -71,7 +71,7 @@ export default class BufferedConsole extends Console { ); } - assert(value: unknown, message?: string | Error): void { + override assert(value: unknown, message?: string | Error): void { try { assert(value, message); } catch (error: any) { @@ -79,7 +79,7 @@ export default class BufferedConsole extends Console { } } - count(label: string = 'default'): void { + override count(label: string = 'default'): void { if (!this._counters[label]) { this._counters[label] = 0; } @@ -87,28 +87,28 @@ export default class BufferedConsole extends Console { this._log('count', format(`${label}: ${++this._counters[label]}`)); } - countReset(label: string = 'default'): void { + override countReset(label: string = 'default'): void { this._counters[label] = 0; } - debug(firstArg: unknown, ...rest: Array): void { + override debug(firstArg: unknown, ...rest: Array): void { this._log('debug', format(firstArg, ...rest)); } - dir(firstArg: unknown, options: InspectOptions = {}): void { + override dir(firstArg: unknown, options: InspectOptions = {}): void { const representation = inspect(firstArg, options); this._log('dir', formatWithOptions(options, representation)); } - dirxml(firstArg: unknown, ...rest: Array): void { + override dirxml(firstArg: unknown, ...rest: Array): void { this._log('dirxml', format(firstArg, ...rest)); } - error(firstArg: unknown, ...rest: Array): void { + override error(firstArg: unknown, ...rest: Array): void { this._log('error', format(firstArg, ...rest)); } - group(title?: string, ...rest: Array): void { + override group(title?: string, ...rest: Array): void { this._groupDepth++; if (title || rest.length > 0) { @@ -116,7 +116,7 @@ export default class BufferedConsole extends Console { } } - groupCollapsed(title?: string, ...rest: Array): void { + override groupCollapsed(title?: string, ...rest: Array): void { this._groupDepth++; if (title || rest.length > 0) { @@ -124,21 +124,21 @@ export default class BufferedConsole extends Console { } } - groupEnd(): void { + override groupEnd(): void { if (this._groupDepth > 0) { this._groupDepth--; } } - info(firstArg: unknown, ...rest: Array): void { + override info(firstArg: unknown, ...rest: Array): void { this._log('info', format(firstArg, ...rest)); } - log(firstArg: unknown, ...rest: Array): void { + override log(firstArg: unknown, ...rest: Array): void { this._log('log', format(firstArg, ...rest)); } - time(label: string = 'default'): void { + override time(label: string = 'default'): void { if (this._timers[label]) { return; } @@ -146,7 +146,7 @@ export default class BufferedConsole extends Console { this._timers[label] = new Date(); } - timeEnd(label: string = 'default'): void { + override timeEnd(label: string = 'default'): void { const startTime = this._timers[label]; if (startTime) { @@ -157,7 +157,7 @@ export default class BufferedConsole extends Console { } } - timeLog(label = 'default', ...data: Array): void { + override timeLog(label = 'default', ...data: Array): void { const startTime = this._timers[label]; if (startTime) { @@ -167,7 +167,7 @@ export default class BufferedConsole extends Console { } } - warn(firstArg: unknown, ...rest: Array): void { + override warn(firstArg: unknown, ...rest: Array): void { this._log('warn', format(firstArg, ...rest)); } diff --git a/packages/jest-console/src/CustomConsole.ts b/packages/jest-console/src/CustomConsole.ts index a02850252d31..a363ad939c3b 100644 --- a/packages/jest-console/src/CustomConsole.ts +++ b/packages/jest-console/src/CustomConsole.ts @@ -22,7 +22,7 @@ export default class CustomConsole extends Console { private _timers: LogTimers = {}; private _groupDepth = 0; - Console: typeof Console = Console; + override Console: typeof Console = Console; constructor( stdout: NodeJS.WriteStream, @@ -49,7 +49,7 @@ export default class CustomConsole extends Console { ); } - assert(value: unknown, message?: string | Error): asserts value { + override assert(value: unknown, message?: string | Error): asserts value { try { assert(value, message); } catch (error: any) { @@ -57,7 +57,7 @@ export default class CustomConsole extends Console { } } - count(label: string = 'default'): void { + override count(label: string = 'default'): void { if (!this._counters[label]) { this._counters[label] = 0; } @@ -65,28 +65,28 @@ export default class CustomConsole extends Console { this._log('count', format(`${label}: ${++this._counters[label]}`)); } - countReset(label: string = 'default'): void { + override countReset(label: string = 'default'): void { this._counters[label] = 0; } - debug(firstArg: unknown, ...args: Array): void { + override debug(firstArg: unknown, ...args: Array): void { this._log('debug', format(firstArg, ...args)); } - dir(firstArg: unknown, options: InspectOptions = {}): void { + override dir(firstArg: unknown, options: InspectOptions = {}): void { const representation = inspect(firstArg, options); this._log('dir', formatWithOptions(options, representation)); } - dirxml(firstArg: unknown, ...args: Array): void { + override dirxml(firstArg: unknown, ...args: Array): void { this._log('dirxml', format(firstArg, ...args)); } - error(firstArg: unknown, ...args: Array): void { + override error(firstArg: unknown, ...args: Array): void { this._logError('error', format(firstArg, ...args)); } - group(title?: string, ...args: Array): void { + override group(title?: string, ...args: Array): void { this._groupDepth++; if (title || args.length > 0) { @@ -94,7 +94,7 @@ export default class CustomConsole extends Console { } } - groupCollapsed(title?: string, ...args: Array): void { + override groupCollapsed(title?: string, ...args: Array): void { this._groupDepth++; if (title || args.length > 0) { @@ -102,21 +102,21 @@ export default class CustomConsole extends Console { } } - groupEnd(): void { + override groupEnd(): void { if (this._groupDepth > 0) { this._groupDepth--; } } - info(firstArg: unknown, ...args: Array): void { + override info(firstArg: unknown, ...args: Array): void { this._log('info', format(firstArg, ...args)); } - log(firstArg: unknown, ...args: Array): void { + override log(firstArg: unknown, ...args: Array): void { this._log('log', format(firstArg, ...args)); } - time(label: string = 'default'): void { + override time(label: string = 'default'): void { if (this._timers[label]) { return; } @@ -124,7 +124,7 @@ export default class CustomConsole extends Console { this._timers[label] = new Date(); } - timeEnd(label: string = 'default'): void { + override timeEnd(label: string = 'default'): void { const startTime = this._timers[label]; if (startTime) { @@ -135,7 +135,7 @@ export default class CustomConsole extends Console { } } - timeLog(label = 'default', ...data: Array): void { + override timeLog(label = 'default', ...data: Array): void { const startTime = this._timers[label]; if (startTime) { @@ -145,7 +145,7 @@ export default class CustomConsole extends Console { } } - warn(firstArg: unknown, ...args: Array): void { + override warn(firstArg: unknown, ...args: Array): void { this._logError('warn', format(firstArg, ...args)); } diff --git a/packages/jest-console/src/NullConsole.ts b/packages/jest-console/src/NullConsole.ts index 002e3a9738ff..de8d3e2ba5e2 100644 --- a/packages/jest-console/src/NullConsole.ts +++ b/packages/jest-console/src/NullConsole.ts @@ -8,18 +8,18 @@ import CustomConsole from './CustomConsole'; export default class NullConsole extends CustomConsole { - assert(): void {} - debug(): void {} - dir(): void {} - error(): void {} - info(): void {} - log(): void {} - time(): void {} - timeEnd(): void {} - timeLog(): void {} - trace(): void {} - warn(): void {} - group(): void {} - groupCollapsed(): void {} - groupEnd(): void {} + override assert(): void {} + override debug(): void {} + override dir(): void {} + override error(): void {} + override info(): void {} + override log(): void {} + override time(): void {} + override timeEnd(): void {} + override timeLog(): void {} + override trace(): void {} + override warn(): void {} + override group(): void {} + override groupCollapsed(): void {} + override groupEnd(): void {} } diff --git a/packages/jest-core/package.json b/packages/jest-core/package.json index 71af163c66be..9307f9899ec3 100644 --- a/packages/jest-core/package.json +++ b/packages/jest-core/package.json @@ -1,7 +1,7 @@ { "name": "@jest/core", "description": "Delightful JavaScript Testing.", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -12,41 +12,41 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/console": "^28.0.0-alpha.7", - "@jest/reporters": "^28.0.0-alpha.7", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/console": "^28.0.1", + "@jest/reporters": "^28.0.1", + "@jest/test-result": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.8.1", + "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.0.0-alpha.3", - "jest-config": "^28.0.0-alpha.7", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-regex-util": "^28.0.0-alpha.6", - "jest-resolve": "^28.0.0-alpha.7", - "jest-resolve-dependencies": "^28.0.0-alpha.7", - "jest-runner": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", - "jest-snapshot": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-validate": "^28.0.0-alpha.7", - "jest-watcher": "^28.0.0-alpha.7", + "jest-changed-files": "^28.0.0", + "jest-config": "^28.0.1", + "jest-haste-map": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-regex-util": "^28.0.0", + "jest-resolve": "^28.0.1", + "jest-resolve-dependencies": "^28.0.1", + "jest-runner": "^28.0.1", + "jest-runtime": "^28.0.1", + "jest-snapshot": "^28.0.1", + "jest-util": "^28.0.1", + "jest-validate": "^28.0.1", + "jest-watcher": "^28.0.1", "micromatch": "^4.0.4", - "pretty-format": "^28.0.0-alpha.7", + "pretty-format": "^28.0.1", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "devDependencies": { - "@jest/test-sequencer": "^28.0.0-alpha.7", - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-sequencer": "^28.0.1", + "@jest/test-utils": "^28.0.1", "@types/exit": "^0.1.30", - "@types/graceful-fs": "^4.1.2", + "@types/graceful-fs": "^4.1.3", "@types/micromatch": "^4.0.1", "@types/rimraf": "^3.0.0" }, @@ -63,7 +63,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/facebook/jest", + "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-core" }, "bugs": { diff --git a/packages/jest-core/src/ReporterDispatcher.ts b/packages/jest-core/src/ReporterDispatcher.ts index 992925cdd65b..a70691f5ddcf 100644 --- a/packages/jest-core/src/ReporterDispatcher.ts +++ b/packages/jest-core/src/ReporterDispatcher.ts @@ -5,16 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -/* eslint-disable local/ban-types-eventually */ - import type {Reporter, ReporterOnStartOptions} from '@jest/reporters'; import type { AggregatedResult, Test, TestCaseResult, + TestContext, TestResult, } from '@jest/test-result'; -import type {Context} from 'jest-runtime'; +import type {ReporterConstructor} from './TestScheduler'; export default class ReporterDispatcher { private _reporters: Array; @@ -27,9 +26,9 @@ export default class ReporterDispatcher { this._reporters.push(reporter); } - unregister(ReporterClass: Function): void { + unregister(reporterConstructor: ReporterConstructor): void { this._reporters = this._reporters.filter( - reporter => !(reporter instanceof ReporterClass), + reporter => !(reporter instanceof reporterConstructor), ); } @@ -82,12 +81,12 @@ export default class ReporterDispatcher { } async onRunComplete( - contexts: Set, + testContexts: Set, results: AggregatedResult, ): Promise { for (const reporter of this._reporters) { if (reporter.onRunComplete) { - await reporter.onRunComplete(contexts, results); + await reporter.onRunComplete(testContexts, results); } } } diff --git a/packages/jest-core/src/SearchSource.ts b/packages/jest-core/src/SearchSource.ts index fd8d254204b6..b1c8d31125fd 100644 --- a/packages/jest-core/src/SearchSource.ts +++ b/packages/jest-core/src/SearchSource.ts @@ -8,13 +8,12 @@ import * as os from 'os'; import * as path from 'path'; import micromatch = require('micromatch'); -import type {Test} from '@jest/test-result'; +import type {Test, TestContext} from '@jest/test-result'; import type {Config} from '@jest/types'; import type {ChangedFiles} from 'jest-changed-files'; import {replaceRootDirInPath} from 'jest-config'; import {escapePathForRegex} from 'jest-regex-util'; import {DependencyResolver} from 'jest-resolve-dependencies'; -import type {Context} from 'jest-runtime'; import {buildSnapshotResolver} from 'jest-snapshot'; import {globsToMatcher, testPathPatternToRegExp} from 'jest-util'; import type {Filter, Stats, TestPathCases} from './types'; @@ -41,7 +40,7 @@ const regexToMatcher = (testRegex: Config.ProjectConfig['testRegex']) => { }); }; -const toTests = (context: Context, tests: Array) => +const toTests = (context: TestContext, tests: Array) => tests.map(path => ({ context, duration: undefined, @@ -56,11 +55,11 @@ const hasSCM = (changedFilesInfo: ChangedFiles) => { }; export default class SearchSource { - private _context: Context; + private _context: TestContext; private _dependencyResolver: DependencyResolver | null; private _testPathCases: TestPathCases = []; - constructor(context: Context) { + constructor(context: TestContext) { const {config} = context; this._context = context; this._dependencyResolver = null; diff --git a/packages/jest-core/src/TestNamePatternPrompt.ts b/packages/jest-core/src/TestNamePatternPrompt.ts index 426a8bc45956..0449eaa8a653 100644 --- a/packages/jest-core/src/TestNamePatternPrompt.ts +++ b/packages/jest-core/src/TestNamePatternPrompt.ts @@ -18,7 +18,7 @@ export default class TestNamePatternPrompt extends PatternPrompt { super(pipe, prompt, 'tests'); } - protected _onChange(pattern: string, options: ScrollOptions): void { + protected override _onChange(pattern: string, options: ScrollOptions): void { super._onChange(pattern, options); this._printPrompt(pattern); } diff --git a/packages/jest-core/src/TestPathPatternPrompt.ts b/packages/jest-core/src/TestPathPatternPrompt.ts index 6c9d948da346..86875cc1b3d4 100644 --- a/packages/jest-core/src/TestPathPatternPrompt.ts +++ b/packages/jest-core/src/TestPathPatternPrompt.ts @@ -18,7 +18,7 @@ export default class TestPathPatternPrompt extends PatternPrompt { super(pipe, prompt, 'filenames'); } - protected _onChange(pattern: string, options: ScrollOptions): void { + protected override _onChange(pattern: string, options: ScrollOptions): void { super._onChange(pattern, options); this._printPrompt(pattern); } diff --git a/packages/jest-core/src/TestScheduler.ts b/packages/jest-core/src/TestScheduler.ts index 54955ed39623..7c088051334b 100644 --- a/packages/jest-core/src/TestScheduler.ts +++ b/packages/jest-core/src/TestScheduler.ts @@ -5,15 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -/* eslint-disable local/ban-types-eventually */ - import chalk = require('chalk'); +import {GITHUB_ACTIONS} from 'ci-info'; import exit = require('exit'); import { CoverageReporter, DefaultReporter, + GitHubActionsReporter, + BaseReporter as JestReporter, NotifyReporter, Reporter, + ReporterContext, SummaryReporter, VerboseReporter, } from '@jest/reporters'; @@ -21,6 +23,7 @@ import { AggregatedResult, SerializableError, Test, + TestContext, TestResult, addResult, buildFailureTestResult, @@ -29,33 +32,34 @@ import { import {createScriptTransformer} from '@jest/transform'; import type {Config} from '@jest/types'; import {formatExecError} from 'jest-message-util'; -import type TestRunner from 'jest-runner'; -import type {Context} from 'jest-runtime'; +import type {JestTestRunner, TestRunnerContext} from 'jest-runner'; import { buildSnapshotResolver, cleanup as cleanupSnapshots, } from 'jest-snapshot'; import {requireOrImportModule} from 'jest-util'; +import type {TestWatcher} from 'jest-watcher'; import ReporterDispatcher from './ReporterDispatcher'; -import type TestWatcher from './TestWatcher'; import {shouldRunInBand} from './testSchedulerHelper'; -export type TestSchedulerOptions = { - startRun: (globalConfig: Config.GlobalConfig) => void; -}; -export type TestSchedulerContext = { - firstRun: boolean; - previousSuccess: boolean; - changedFiles?: Set; - sourcesRelatedToTestsInChangedFiles?: Set; -}; +export type ReporterConstructor = new ( + globalConfig: Config.GlobalConfig, + reporterConfig: Record, + reporterContext: ReporterContext, +) => JestReporter; + +type TestRunnerConstructor = new ( + globalConfig: Config.GlobalConfig, + testRunnerContext: TestRunnerContext, +) => JestTestRunner; + +export type TestSchedulerContext = ReporterContext & TestRunnerContext; export async function createTestScheduler( globalConfig: Config.GlobalConfig, - options: TestSchedulerOptions, context: TestSchedulerContext, ): Promise { - const scheduler = new TestScheduler(globalConfig, options, context); + const scheduler = new TestScheduler(globalConfig, context); await scheduler._setupReporters(); @@ -63,28 +67,25 @@ export async function createTestScheduler( } class TestScheduler { + private readonly _context: TestSchedulerContext; private readonly _dispatcher: ReporterDispatcher; private readonly _globalConfig: Config.GlobalConfig; - private readonly _options: TestSchedulerOptions; - private readonly _context: TestSchedulerContext; constructor( globalConfig: Config.GlobalConfig, - options: TestSchedulerOptions, context: TestSchedulerContext, ) { + this._context = context; this._dispatcher = new ReporterDispatcher(); this._globalConfig = globalConfig; - this._options = options; - this._context = context; } addReporter(reporter: Reporter): void { this._dispatcher.register(reporter); } - removeReporter(ReporterClass: Function): void { - this._dispatcher.unregister(ReporterClass); + removeReporter(reporterConstructor: ReporterConstructor): void { + this._dispatcher.unregister(reporterConstructor); } async scheduleTests( @@ -95,9 +96,9 @@ class TestScheduler { this._dispatcher, ); const timings: Array = []; - const contexts = new Set(); + const testContexts = new Set(); tests.forEach(test => { - contexts.add(test.context); + testContexts.add(test.context); if (test.duration) { timings.push(test.duration); } @@ -148,7 +149,7 @@ class TestScheduler { testResult, aggregatedResults, ); - return this._bailIfNeeded(contexts, aggregatedResults, watcher); + return this._bailIfNeeded(testContexts, aggregatedResults, watcher); }; const onFailure = async (test: Test, error: SerializableError) => { @@ -172,7 +173,7 @@ class TestScheduler { const updateSnapshotState = async () => { const contextsWithSnapshotResolvers = await Promise.all( - Array.from(contexts).map( + Array.from(testContexts).map( async context => [context, await buildSnapshotResolver(context.config)] as const, ), @@ -206,19 +207,19 @@ class TestScheduler { showStatus: !runInBand, }); - const testRunners: {[key: string]: TestRunner} = Object.create(null); - const contextsByTestRunner = new WeakMap(); + const testRunners: Record = Object.create(null); + const contextsByTestRunner = new WeakMap(); await Promise.all( - Array.from(contexts).map(async context => { + Array.from(testContexts).map(async context => { const {config} = context; if (!testRunners[config.runner]) { const transformer = await createScriptTransformer(config); - const Runner: typeof TestRunner = + const Runner: TestRunnerConstructor = await transformer.requireAndTranspileModule(config.runner); const runner = new Runner(this._globalConfig, { - changedFiles: this._context?.changedFiles, + changedFiles: this._context.changedFiles, sourcesRelatedToTestsInChangedFiles: - this._context?.sourcesRelatedToTestsInChangedFiles, + this._context.sourcesRelatedToTestsInChangedFiles, }); testRunners[config.runner] = runner; contextsByTestRunner.set(runner, context); @@ -242,11 +243,7 @@ class TestScheduler { serial: runInBand || Boolean(testRunner.isSerial), }; - /** - * Test runners with event emitters are still not supported - * for third party test runners. - */ - if (testRunner.__PRIVATE_UNSTABLE_API_supportsEventEmitters__) { + if (testRunner.supportsEventEmitters) { const unsubscribes = [ testRunner.on('test-file-start', ([test]) => onTestFileStart(test), @@ -266,14 +263,7 @@ class TestScheduler { ), ]; - await testRunner.runTests( - tests, - watcher, - undefined, - undefined, - undefined, - testRunnerOptions, - ); + await testRunner.runTests(tests, watcher, testRunnerOptions); unsubscribes.forEach(sub => sub()); } else { @@ -296,7 +286,7 @@ class TestScheduler { await updateSnapshotState(); aggregatedResults.wasInterrupted = watcher.isInterrupted(); - await this._dispatcher.onRunComplete(contexts, aggregatedResults); + await this._dispatcher.onRunComplete(testContexts, aggregatedResults); const anyTestFailures = !( aggregatedResults.numFailedTests === 0 && @@ -314,7 +304,7 @@ class TestScheduler { } private _partitionTests( - testRunners: Record, + testRunners: Record, tests: Array, ): Record> | null { if (Object.keys(testRunners).length > 1) { @@ -336,110 +326,65 @@ class TestScheduler { } } - private _shouldAddDefaultReporters( - reporters?: Array, - ): boolean { - return ( - !reporters || - !!reporters.find( - reporter => this._getReporterProps(reporter).path === 'default', - ) - ); - } - async _setupReporters() { - const {collectCoverage, notify, reporters} = this._globalConfig; - const isDefault = this._shouldAddDefaultReporters(reporters); - - if (isDefault) { - this._setupDefaultReporters(collectCoverage); - } - - if (!isDefault && collectCoverage) { - this.addReporter( - new CoverageReporter(this._globalConfig, { - changedFiles: this._context?.changedFiles, - sourcesRelatedToTestsInChangedFiles: - this._context?.sourcesRelatedToTestsInChangedFiles, - }), - ); + const {collectCoverage: coverage, notify, verbose} = this._globalConfig; + const reporters = this._globalConfig.reporters || [['default', {}]]; + let summary = false; + + for (const [reporter, options] of reporters) { + switch (reporter) { + case 'default': + summary = true; + verbose + ? this.addReporter(new VerboseReporter(this._globalConfig)) + : this.addReporter(new DefaultReporter(this._globalConfig)); + break; + case 'github-actions': + GITHUB_ACTIONS && this.addReporter(new GitHubActionsReporter()); + break; + case 'summary': + summary = true; + break; + default: + await this._addCustomReporter(reporter, options); + } } if (notify) { - this.addReporter( - new NotifyReporter( - this._globalConfig, - this._options.startRun, - this._context, - ), - ); + this.addReporter(new NotifyReporter(this._globalConfig, this._context)); } - if (reporters && Array.isArray(reporters)) { - await this._addCustomReporters(reporters); + if (coverage) { + this.addReporter(new CoverageReporter(this._globalConfig, this._context)); } - } - - private _setupDefaultReporters(collectCoverage: boolean) { - this.addReporter( - this._globalConfig.verbose - ? new VerboseReporter(this._globalConfig) - : new DefaultReporter(this._globalConfig), - ); - if (collectCoverage) { - this.addReporter( - new CoverageReporter(this._globalConfig, { - changedFiles: this._context?.changedFiles, - sourcesRelatedToTestsInChangedFiles: - this._context?.sourcesRelatedToTestsInChangedFiles, - }), - ); + if (summary) { + this.addReporter(new SummaryReporter(this._globalConfig)); } - - this.addReporter(new SummaryReporter(this._globalConfig)); } - private async _addCustomReporters( - reporters: Array, + private async _addCustomReporter( + reporter: string, + options: Record, ) { - for (const reporter of reporters) { - const {options, path} = this._getReporterProps(reporter); - - if (path === 'default') continue; - - try { - const Reporter = await requireOrImportModule(path, true); - this.addReporter(new Reporter(this._globalConfig, options)); - } catch (error: any) { - error.message = `An error occurred while adding the reporter at path "${chalk.bold( - path, - )}".${error.message}`; - throw error; - } - } - } + try { + const Reporter: ReporterConstructor = await requireOrImportModule( + reporter, + ); - /** - * Get properties of a reporter in an object - * to make dealing with them less painful. - */ - private _getReporterProps(reporter: string | Config.ReporterConfig): { - path: string; - options: Record; - } { - if (typeof reporter === 'string') { - return {options: this._options, path: reporter}; - } else if (Array.isArray(reporter)) { - const [path, options] = reporter; - return {options, path}; + this.addReporter( + new Reporter(this._globalConfig, options, this._context), + ); + } catch (error: any) { + error.message = `An error occurred while adding the reporter at path "${chalk.bold( + reporter, + )}".\n${error instanceof Error ? error.message : ''}`; + throw error; } - - throw new Error('Reporter should be either a string or an array'); } private async _bailIfNeeded( - contexts: Set, + testContexts: Set, aggregatedResults: AggregatedResult, watcher: TestWatcher, ): Promise { @@ -453,7 +398,7 @@ class TestScheduler { } try { - await this._dispatcher.onRunComplete(contexts, aggregatedResults); + await this._dispatcher.onRunComplete(testContexts, aggregatedResults); } finally { const exitCode = this._globalConfig.testFailureExitCode; exit(exitCode); diff --git a/packages/jest-core/src/__tests__/SearchSource.test.ts b/packages/jest-core/src/__tests__/SearchSource.test.ts index 25e53955a382..7e4af257a870 100644 --- a/packages/jest-core/src/__tests__/SearchSource.test.ts +++ b/packages/jest-core/src/__tests__/SearchSource.test.ts @@ -57,13 +57,13 @@ const initSearchSource = async ( }; describe('SearchSource', () => { - const name = 'SearchSource'; + const id = 'SearchSource'; let searchSource: SearchSource; describe('isTestFilePath', () => { beforeEach(async () => { searchSource = await initSearchSource({ - name, + id, rootDir: '.', roots: [], }); @@ -76,7 +76,7 @@ describe('SearchSource', () => { return; } const searchSource = await initSearchSource({ - name, + id, rootDir: '.', roots: [], testMatch: undefined, @@ -113,8 +113,8 @@ describe('SearchSource', () => { it('finds tests matching a pattern via testRegex', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx', 'txt'], - name, rootDir, testMatch: undefined, testRegex: 'not-really-a-test', @@ -127,8 +127,8 @@ describe('SearchSource', () => { it('finds tests matching a pattern via testMatch', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx', 'txt'], - name, rootDir, testMatch: ['**/not-really-a-test.txt', '!**/do-not-match-me.txt'], testRegex: '', @@ -141,8 +141,8 @@ describe('SearchSource', () => { it('finds tests matching a JS regex pattern', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx'], - name, rootDir, testMatch: undefined, testRegex: 'test.jsx?', @@ -155,8 +155,8 @@ describe('SearchSource', () => { it('finds tests matching a JS glob pattern', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx'], - name, rootDir, testMatch: ['**/test.js?(x)'], testRegex: '', @@ -169,8 +169,8 @@ describe('SearchSource', () => { it('finds tests matching a JS with overriding glob patterns', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx'], - name, rootDir, testMatch: [ '**/*.js?(x)', @@ -188,7 +188,7 @@ describe('SearchSource', () => { it('finds tests with default file extensions using testRegex', async () => { const paths = await getTestPaths({ - name, + id, rootDir, testMatch: undefined, testRegex, @@ -201,7 +201,7 @@ describe('SearchSource', () => { it('finds tests with default file extensions using testMatch', async () => { const paths = await getTestPaths({ - name, + id, rootDir, testMatch, testRegex: '', @@ -214,7 +214,7 @@ describe('SearchSource', () => { it('finds tests with parentheses in their rootDir when using testMatch', async () => { const paths = await getTestPaths({ - name, + id, rootDir: path.resolve(__dirname, 'test_root_with_(parentheses)'), testMatch: ['**/__testtests__/**/*'], testRegex: undefined, @@ -226,8 +226,8 @@ describe('SearchSource', () => { it('finds tests with similar but custom file extensions', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx'], - name, rootDir, testMatch, }); @@ -239,8 +239,8 @@ describe('SearchSource', () => { it('finds tests with totally custom foobar file extensions', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'foobar'], - name, rootDir, testMatch, }); @@ -252,8 +252,8 @@ describe('SearchSource', () => { it('finds tests with many kinds of file extensions', async () => { const paths = await getTestPaths({ + id, moduleFileExtensions: ['js', 'jsx'], - name, rootDir, testMatch, }); @@ -265,7 +265,7 @@ describe('SearchSource', () => { it('finds tests using a regex only', async () => { const paths = await getTestPaths({ - name, + id, rootDir, testMatch: undefined, testRegex, @@ -278,7 +278,7 @@ describe('SearchSource', () => { it('finds tests using a glob only', async () => { const paths = await getTestPaths({ - name, + id, rootDir, testMatch, testRegex: '', @@ -294,7 +294,7 @@ describe('SearchSource', () => { beforeEach(async () => { searchSource = await initSearchSource( { - name, + id, rootDir: '.', roots: [], }, @@ -385,7 +385,7 @@ describe('SearchSource', () => { 'haste_impl.js', ), }, - name: 'SearchSource-findRelatedTests-tests', + id: 'SearchSource-findRelatedTests-tests', rootDir, }); }); @@ -432,8 +432,8 @@ describe('SearchSource', () => { describe('findRelatedTestsFromPattern', () => { beforeEach(async () => { searchSource = await initSearchSource({ + id, moduleFileExtensions: ['js', 'jsx', 'foobar'], - name, rootDir, testMatch, }); @@ -484,7 +484,7 @@ describe('SearchSource', () => { return; } searchSource = await initSearchSource({ - name, + id, rootDir: '.', roots: ['/foo/bar/prefix'], }); @@ -509,7 +509,7 @@ describe('SearchSource', () => { '../../../jest-haste-map/src/__tests__/haste_impl.js', ), }, - name: 'SearchSource-findRelatedSourcesFromTestsInChangedFiles-tests', + id: 'SearchSource-findRelatedSourcesFromTestsInChangedFiles-tests', rootDir, }); }); diff --git a/packages/jest-core/src/__tests__/TestScheduler.test.js b/packages/jest-core/src/__tests__/TestScheduler.test.js index b707ef3289c9..66aba2262bb0 100644 --- a/packages/jest-core/src/__tests__/TestScheduler.test.js +++ b/packages/jest-core/src/__tests__/TestScheduler.test.js @@ -6,12 +6,30 @@ * */ -import {SummaryReporter} from '@jest/reporters'; -import {makeProjectConfig} from '@jest/test-utils'; +import { + CoverageReporter, + DefaultReporter, + GitHubActionsReporter, + NotifyReporter, + SummaryReporter, + VerboseReporter, +} from '@jest/reporters'; +import {makeGlobalConfig, makeProjectConfig} from '@jest/test-utils'; import {createTestScheduler} from '../TestScheduler'; import * as testSchedulerHelper from '../testSchedulerHelper'; -jest.mock('@jest/reporters'); +jest + .mock('ci-info', () => ({GITHUB_ACTIONS: true})) + .mock('@jest/reporters') + .mock( + '/custom-reporter.js', + () => + jest.fn(() => ({ + onTestStart() {}, + })), + {virtual: true}, + ); + const mockSerialRunner = { isSerial: true, runTests: jest.fn(), @@ -35,47 +53,179 @@ beforeEach(() => { spyShouldRunInBand.mockClear(); }); -test('config for reporters supports `default`', async () => { - const undefinedReportersScheduler = await createTestScheduler( - { - reporters: undefined, - }, - {}, - ); - const numberOfReporters = - undefinedReportersScheduler._dispatcher._reporters.length; +describe('reporters', () => { + const CustomReporter = require('/custom-reporter.js'); - const stringDefaultReportersScheduler = await createTestScheduler( - { - reporters: ['default'], - }, - {}, - ); - expect(stringDefaultReportersScheduler._dispatcher._reporters.length).toBe( - numberOfReporters, - ); + afterEach(() => { + jest.clearAllMocks(); + }); - const defaultReportersScheduler = await createTestScheduler( - { - reporters: [['default', {}]], - }, - {}, - ); - expect(defaultReportersScheduler._dispatcher._reporters.length).toBe( - numberOfReporters, - ); + test('works with default value', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: undefined, + }), + {}, + {}, + ); - const emptyReportersScheduler = await createTestScheduler( - { - reporters: [], - }, - {}, - ); - expect(emptyReportersScheduler._dispatcher._reporters.length).toBe(0); + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('does not enable any reporters, if empty list is passed', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(0); + }); + + test('sets up default reporters', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up verbose reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['default', {}]], + verbose: true, + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(1); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up github actions reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [ + ['default', {}], + ['github-actions', {}], + ], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(1); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up notify reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + notify: true, + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(1); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up coverage reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + collectCoverage: true, + reporters: [['default', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(1); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('allows enabling summary reporter separately', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [['summary', {}]], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(0); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + }); + + test('sets up custom reporter', async () => { + await createTestScheduler( + makeGlobalConfig({ + reporters: [ + ['default', {}], + ['/custom-reporter.js', {}], + ], + }), + {}, + {}, + ); + + expect(DefaultReporter).toBeCalledTimes(1); + expect(VerboseReporter).toBeCalledTimes(0); + expect(GitHubActionsReporter).toBeCalledTimes(0); + expect(NotifyReporter).toBeCalledTimes(0); + expect(CoverageReporter).toBeCalledTimes(0); + expect(SummaryReporter).toBeCalledTimes(1); + expect(CustomReporter).toBeCalledTimes(1); + }); }); test('.addReporter() .removeReporter()', async () => { - const scheduler = await createTestScheduler({}, {}); + const scheduler = await createTestScheduler(makeGlobalConfig(), {}, {}); const reporter = new SummaryReporter(); scheduler.addReporter(reporter); expect(scheduler._dispatcher._reporters).toContain(reporter); @@ -84,7 +234,7 @@ test('.addReporter() .removeReporter()', async () => { }); test('schedule tests run in parallel per default', async () => { - const scheduler = await createTestScheduler({}, {}); + const scheduler = await createTestScheduler(makeGlobalConfig(), {}, {}); const test = { context: { config: makeProjectConfig({ @@ -107,7 +257,7 @@ test('schedule tests run in parallel per default', async () => { }); test('schedule tests run in serial if the runner flags them', async () => { - const scheduler = await createTestScheduler({}, {}); + const scheduler = await createTestScheduler(makeGlobalConfig(), {}, {}); const test = { context: { config: makeProjectConfig({ @@ -130,7 +280,11 @@ test('schedule tests run in serial if the runner flags them', async () => { }); test('should bail after `n` failures', async () => { - const scheduler = await createTestScheduler({bail: 2}, {}); + const scheduler = await createTestScheduler( + makeGlobalConfig({bail: 2}), + {}, + {}, + ); const test = { context: { config: makeProjectConfig({ @@ -162,7 +316,11 @@ test('should bail after `n` failures', async () => { }); test('should not bail if less than `n` failures', async () => { - const scheduler = await createTestScheduler({bail: 2}, {}); + const scheduler = await createTestScheduler( + makeGlobalConfig({bail: 2}), + {}, + {}, + ); const test = { context: { config: makeProjectConfig({ @@ -194,7 +352,7 @@ test('should not bail if less than `n` failures', async () => { }); test('should set runInBand to run in serial', async () => { - const scheduler = await createTestScheduler({}, {}); + const scheduler = await createTestScheduler(makeGlobalConfig(), {}, {}); const test = { context: { config: makeProjectConfig({ @@ -220,7 +378,7 @@ test('should set runInBand to run in serial', async () => { }); test('should set runInBand to not run in serial', async () => { - const scheduler = await createTestScheduler({}, {}); + const scheduler = await createTestScheduler(makeGlobalConfig(), {}, {}); const test = { context: { config: makeProjectConfig({ diff --git a/packages/jest-core/src/__tests__/watch.test.js b/packages/jest-core/src/__tests__/watch.test.js index dc46da7f1ec7..0ef865a7e74a 100644 --- a/packages/jest-core/src/__tests__/watch.test.js +++ b/packages/jest-core/src/__tests__/watch.test.js @@ -7,9 +7,8 @@ */ import chalk from 'chalk'; -import {JestHook, KEYS} from 'jest-watcher'; // eslint-disable-next-line import/order -import TestWatcher from '../TestWatcher'; +import {JestHook, KEYS, TestWatcher} from 'jest-watcher'; const runJestMock = jest.fn(); const watchPluginPath = `${__dirname}/__fixtures__/watchPlugin`; @@ -142,7 +141,9 @@ describe('Watch mode flows', () => { globalConfig, onComplete: expect.any(Function), outputStream: pipe, - testWatcher: new TestWatcher({isWatchMode: true}), + testWatcher: JSON.parse( + JSON.stringify(new TestWatcher({isWatchMode: true})), + ), }); }); @@ -156,7 +157,9 @@ describe('Watch mode flows', () => { globalConfig, onComplete: expect.any(Function), outputStream: pipe, - testWatcher: new TestWatcher({isWatchMode: true}), + testWatcher: JSON.parse( + JSON.stringify(new TestWatcher({isWatchMode: true})), + ), }); }); @@ -167,7 +170,9 @@ describe('Watch mode flows', () => { globalConfig, onComplete: expect.any(Function), outputStream: pipe, - testWatcher: new TestWatcher({isWatchMode: true}), + testWatcher: JSON.parse( + JSON.stringify(new TestWatcher({isWatchMode: true})), + ), }); expect(pipe.write.mock.calls.reverse()[0]).toMatchSnapshot(); }); @@ -183,7 +188,9 @@ describe('Watch mode flows', () => { globalConfig, onComplete: expect.any(Function), outputStream: pipe, - testWatcher: new TestWatcher({isWatchMode: true}), + testWatcher: JSON.parse( + JSON.stringify(new TestWatcher({isWatchMode: true})), + ), }); expect(pipe.write.mock.calls.reverse()[0]).toMatchSnapshot(); }); diff --git a/packages/jest-core/src/cli/index.ts b/packages/jest-core/src/cli/index.ts index 61bdb12d8d8f..478b7f0d9cc4 100644 --- a/packages/jest-core/src/cli/index.ts +++ b/packages/jest-core/src/cli/index.ts @@ -9,14 +9,14 @@ import chalk = require('chalk'); import exit = require('exit'); import rimraf = require('rimraf'); import {CustomConsole} from '@jest/console'; -import type {AggregatedResult} from '@jest/test-result'; +import type {AggregatedResult, TestContext} from '@jest/test-result'; import type {Config} from '@jest/types'; import type {ChangedFilesPromise} from 'jest-changed-files'; import {readConfigs} from 'jest-config'; import type HasteMap from 'jest-haste-map'; -import Runtime, {Context} from 'jest-runtime'; +import Runtime from 'jest-runtime'; import {createDirectory, preRunMessage} from 'jest-util'; -import TestWatcher from '../TestWatcher'; +import {TestWatcher} from 'jest-watcher'; import {formatHandleErrors} from '../collectHandles'; import getChangedFilesPromise from '../getChangedFilesPromise'; import getConfigsOfProjectsToRun from '../getConfigsOfProjectsToRun'; @@ -71,17 +71,24 @@ export async function runCLI( exit(0); } - let configsOfProjectsToRun = configs; - if (argv.selectProjects) { - const namesMissingWarning = getProjectNamesMissingWarning(configs); + const configsOfProjectsToRun = getConfigsOfProjectsToRun(configs, { + ignoreProjects: argv.ignoreProjects, + selectProjects: argv.selectProjects, + }); + if (argv.selectProjects || argv.ignoreProjects) { + const namesMissingWarning = getProjectNamesMissingWarning(configs, { + ignoreProjects: argv.ignoreProjects, + selectProjects: argv.selectProjects, + }); if (namesMissingWarning) { outputStream.write(namesMissingWarning); } - configsOfProjectsToRun = getConfigsOfProjectsToRun( - argv.selectProjects, - configs, + outputStream.write( + getSelectProjectsMessage(configsOfProjectsToRun, { + ignoreProjects: argv.ignoreProjects, + selectProjects: argv.selectProjects, + }), ); - outputStream.write(getSelectProjectsMessage(configsOfProjectsToRun)); } await _run10000( @@ -220,7 +227,7 @@ const _run10000 = async ( }; const runWatch = async ( - contexts: Array, + contexts: Array, _configs: Array, hasDeprecationWarnings: boolean, globalConfig: Config.GlobalConfig, @@ -258,7 +265,7 @@ const runWatch = async ( const runWithoutWatch = async ( globalConfig: Config.GlobalConfig, - contexts: Array, + contexts: Array, outputStream: NodeJS.WriteStream, onComplete: OnCompleteCallback, changedFilesPromise?: ChangedFilesPromise, diff --git a/packages/jest-core/src/collectHandles.ts b/packages/jest-core/src/collectHandles.ts index 88fe14a4b1de..61afcf7fd846 100644 --- a/packages/jest-core/src/collectHandles.ts +++ b/packages/jest-core/src/collectHandles.ts @@ -93,24 +93,19 @@ export default function collectHandles(): HandleCollectionResult { if (fromUser) { let isActive: () => boolean; - if (type === 'Timeout' || type === 'Immediate') { - // Timer that supports hasRef (Node v11+) - if ('hasRef' in resource) { - if (hasWeakRef) { - // @ts-expect-error: doesn't exist in v12 typings - const ref = new WeakRef(resource); - isActive = () => { - return ref.deref()?.hasRef() ?? false; - }; - } else { - isActive = resource.hasRef.bind(resource); - } + // Handle that supports hasRef + if ('hasRef' in resource) { + if (hasWeakRef) { + // @ts-expect-error: doesn't exist in v12 typings + const ref = new WeakRef(resource); + isActive = () => { + return ref.deref()?.hasRef() ?? false; + }; } else { - // Timer that doesn't support hasRef - isActive = alwaysActive; + isActive = resource.hasRef.bind(resource); } } else { - // Any other async resource + // Handle that doesn't support hasRef isActive = alwaysActive; } diff --git a/packages/jest-core/src/getConfigsOfProjectsToRun.ts b/packages/jest-core/src/getConfigsOfProjectsToRun.ts index b8b8288ccc68..75542c3d2e7d 100644 --- a/packages/jest-core/src/getConfigsOfProjectsToRun.ts +++ b/packages/jest-core/src/getConfigsOfProjectsToRun.ts @@ -9,12 +9,38 @@ import type {Config} from '@jest/types'; import getProjectDisplayName from './getProjectDisplayName'; export default function getConfigsOfProjectsToRun( - namesOfProjectsToRun: Array, projectConfigs: Array, + opts: { + ignoreProjects: Array | undefined; + selectProjects: Array | undefined; + }, ): Array { - const setOfProjectsToRun = new Set(namesOfProjectsToRun); + const projectFilter = createProjectFilter(opts); return projectConfigs.filter(config => { const name = getProjectDisplayName(config); - return name && setOfProjectsToRun.has(name); + return projectFilter(name); }); } + +function createProjectFilter(opts: { + ignoreProjects: Array | undefined; + selectProjects: Array | undefined; +}) { + const {selectProjects, ignoreProjects} = opts; + + const always = () => true; + + const selected = selectProjects + ? (name: string | undefined) => name && selectProjects.includes(name) + : always; + + const notIgnore = ignoreProjects + ? (name: string | undefined) => !(name && ignoreProjects.includes(name)) + : always; + + function test(name: string | undefined) { + return selected(name) && notIgnore(name); + } + + return test; +} diff --git a/packages/jest-core/src/getProjectNamesMissingWarning.ts b/packages/jest-core/src/getProjectNamesMissingWarning.ts index 53f0543d1cc8..947955dfefcf 100644 --- a/packages/jest-core/src/getProjectNamesMissingWarning.ts +++ b/packages/jest-core/src/getProjectNamesMissingWarning.ts @@ -11,6 +11,10 @@ import getProjectDisplayName from './getProjectDisplayName'; export default function getProjectNamesMissingWarning( projectConfigs: Array, + opts: { + ignoreProjects: Array | undefined; + selectProjects: Array | undefined; + }, ): string | undefined { const numberOfProjectsWithoutAName = projectConfigs.filter( config => !getProjectDisplayName(config), @@ -18,8 +22,15 @@ export default function getProjectNamesMissingWarning( if (numberOfProjectsWithoutAName === 0) { return undefined; } + const args: Array = []; + if (opts.selectProjects) { + args.push('--selectProjects'); + } + if (opts.ignoreProjects) { + args.push('--ignoreProjects'); + } return chalk.yellow( - `You provided values for --selectProjects but ${ + `You provided values for ${args.join(' and ')} but ${ numberOfProjectsWithoutAName === 1 ? 'a project does not have a name' : `${numberOfProjectsWithoutAName} projects do not have a name` diff --git a/packages/jest-core/src/getSelectProjectsMessage.ts b/packages/jest-core/src/getSelectProjectsMessage.ts index 5d3fff3577b4..b8a3a9f40c88 100644 --- a/packages/jest-core/src/getSelectProjectsMessage.ts +++ b/packages/jest-core/src/getSelectProjectsMessage.ts @@ -11,24 +11,46 @@ import getProjectDisplayName from './getProjectDisplayName'; export default function getSelectProjectsMessage( projectConfigs: Array, + opts: { + ignoreProjects: Array | undefined; + selectProjects: Array | undefined; + }, ): string { if (projectConfigs.length === 0) { - return getNoSelectionWarning(); + return getNoSelectionWarning(opts); } return getProjectsRunningMessage(projectConfigs); } -function getNoSelectionWarning(): string { - return chalk.yellow( - 'You provided values for --selectProjects but no projects were found matching the selection.\n', - ); +function getNoSelectionWarning(opts: { + ignoreProjects: Array | undefined; + selectProjects: Array | undefined; +}): string { + if (opts.ignoreProjects && opts.selectProjects) { + return chalk.yellow( + 'You provided values for --selectProjects and --ignoreProjects, but no projects were found matching the selection.\n' + + 'Are you ignoring all the selected projects?\n', + ); + } else if (opts.ignoreProjects) { + return chalk.yellow( + 'You provided values for --ignoreProjects, but no projects were found matching the selection.\n' + + 'Are you ignoring all projects?\n', + ); + } else if (opts.selectProjects) { + return chalk.yellow( + 'You provided values for --selectProjects but no projects were found matching the selection.\n', + ); + } else { + return chalk.yellow('No projects were found.\n'); + } } function getProjectsRunningMessage( projectConfigs: Array, ): string { if (projectConfigs.length === 1) { - const name = getProjectDisplayName(projectConfigs[0]); + const name = + getProjectDisplayName(projectConfigs[0]) ?? ''; return `Running one project: ${chalk.bold(name)}\n`; } const projectsList = projectConfigs diff --git a/packages/jest-core/src/index.ts b/packages/jest-core/src/index.ts index 6912817f6bfa..17a839d33cd7 100644 --- a/packages/jest-core/src/index.ts +++ b/packages/jest-core/src/index.ts @@ -7,6 +7,5 @@ export {default as SearchSource} from './SearchSource'; export {createTestScheduler} from './TestScheduler'; -export {default as TestWatcher} from './TestWatcher'; export {runCLI} from './cli'; export {default as getVersion} from './version'; diff --git a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap index dea4afbba9ef..b27bc6754017 100644 --- a/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap +++ b/packages/jest-core/src/lib/__tests__/__snapshots__/logDebugMessages.test.ts.snap @@ -13,9 +13,13 @@ exports[`prints the config object 1`] = ` "detectOpenHandles": false, "errorOnDeprecated": false, "extensionsToTreatAsEsm": [], + "fakeTimers": { + "enableGlobally": false + }, "forceCoverageMatch": [], "globals": {}, "haste": {}, + "id": "test_name", "injectGlobals": true, "moduleDirectories": [], "moduleFileExtensions": [ @@ -24,7 +28,6 @@ exports[`prints the config object 1`] = ` "moduleNameMapper": [], "modulePathIgnorePatterns": [], "modulePaths": [], - "name": "test_name", "prettierPath": "prettier", "resetMocks": false, "resetModules": false, @@ -52,7 +55,6 @@ exports[`prints the config object 1`] = ` "\\\\.test\\\\.js$" ], "testRunner": "myRunner", - "timers": "real", "transform": [], "transformIgnorePatterns": [], "watchPathIgnorePatterns": [] diff --git a/packages/jest-core/src/lib/createContext.ts b/packages/jest-core/src/lib/createContext.ts index 0b5e3a7c8444..be1a75061dd9 100644 --- a/packages/jest-core/src/lib/createContext.ts +++ b/packages/jest-core/src/lib/createContext.ts @@ -5,14 +5,15 @@ * LICENSE file in the root directory of this source tree. */ +import type {TestContext} from '@jest/test-result'; import type {Config} from '@jest/types'; import type {HasteMapObject} from 'jest-haste-map'; -import Runtime, {Context} from 'jest-runtime'; +import Runtime from 'jest-runtime'; export default function createContext( config: Config.ProjectConfig, {hasteFS, moduleMap}: HasteMapObject, -): Context { +): TestContext { return { config, hasteFS, diff --git a/packages/jest-core/src/plugins/FailedTestsInteractive.ts b/packages/jest-core/src/plugins/FailedTestsInteractive.ts index 8fcb9b3f9c88..fa0853be55ff 100644 --- a/packages/jest-core/src/plugins/FailedTestsInteractive.ts +++ b/packages/jest-core/src/plugins/FailedTestsInteractive.ts @@ -19,7 +19,7 @@ export default class FailedTestsInteractivePlugin extends BaseWatchPlugin { private _failedTestAssertions?: Array; private readonly _manager = new FailedTestsInteractiveMode(this._stdout); - apply(hooks: JestHookSubscriber): void { + override apply(hooks: JestHookSubscriber): void { hooks.onTestRunComplete(results => { this._failedTestAssertions = this.getFailedTestAssertions(results); @@ -27,7 +27,7 @@ export default class FailedTestsInteractivePlugin extends BaseWatchPlugin { }); } - getUsageInfo(): UsageData | null { + override getUsageInfo(): UsageData | null { if (this._failedTestAssertions?.length) { return {key: 'i', prompt: 'run failing tests interactively'}; } @@ -35,13 +35,13 @@ export default class FailedTestsInteractivePlugin extends BaseWatchPlugin { return null; } - onKey(key: string): void { + override onKey(key: string): void { if (this._manager.isActive()) { this._manager.put(key); } } - run( + override run( _: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { diff --git a/packages/jest-core/src/plugins/Quit.ts b/packages/jest-core/src/plugins/Quit.ts index ac4c0646351e..c35e654565d5 100644 --- a/packages/jest-core/src/plugins/Quit.ts +++ b/packages/jest-core/src/plugins/Quit.ts @@ -15,7 +15,7 @@ class QuitPlugin extends BaseWatchPlugin { this.isInternal = true; } - async run(): Promise { + override async run(): Promise { if (typeof this._stdin.setRawMode === 'function') { this._stdin.setRawMode(false); } @@ -23,7 +23,7 @@ class QuitPlugin extends BaseWatchPlugin { process.exit(0); } - getUsageInfo(): UsageData { + override getUsageInfo(): UsageData { return { key: 'q', prompt: 'quit watch mode', diff --git a/packages/jest-core/src/plugins/TestNamePattern.ts b/packages/jest-core/src/plugins/TestNamePattern.ts index 34e61f3c30c3..ea3ff104ac88 100644 --- a/packages/jest-core/src/plugins/TestNamePattern.ts +++ b/packages/jest-core/src/plugins/TestNamePattern.ts @@ -25,18 +25,18 @@ class TestNamePatternPlugin extends BaseWatchPlugin { this.isInternal = true; } - getUsageInfo(): UsageData { + override getUsageInfo(): UsageData { return { key: 't', prompt: 'filter by a test name regex pattern', }; } - onKey(key: string): void { + override onKey(key: string): void { this._prompt.put(key); } - run( + override run( globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { diff --git a/packages/jest-core/src/plugins/TestPathPattern.ts b/packages/jest-core/src/plugins/TestPathPattern.ts index b02b74b62443..d0866db9b27f 100644 --- a/packages/jest-core/src/plugins/TestPathPattern.ts +++ b/packages/jest-core/src/plugins/TestPathPattern.ts @@ -25,18 +25,18 @@ class TestPathPatternPlugin extends BaseWatchPlugin { this.isInternal = true; } - getUsageInfo(): UsageData { + override getUsageInfo(): UsageData { return { key: 'p', prompt: 'filter by a filename regex pattern', }; } - onKey(key: string): void { + override onKey(key: string): void { this._prompt.put(key); } - run( + override run( globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { diff --git a/packages/jest-core/src/plugins/UpdateSnapshots.ts b/packages/jest-core/src/plugins/UpdateSnapshots.ts index 1a637895d500..d783a091bd99 100644 --- a/packages/jest-core/src/plugins/UpdateSnapshots.ts +++ b/packages/jest-core/src/plugins/UpdateSnapshots.ts @@ -23,7 +23,7 @@ class UpdateSnapshotsPlugin extends BaseWatchPlugin { this._hasSnapshotFailure = false; } - run( + override run( _globalConfig: Config.GlobalConfig, updateConfigAndRun: UpdateConfigCallback, ): Promise { @@ -31,13 +31,13 @@ class UpdateSnapshotsPlugin extends BaseWatchPlugin { return Promise.resolve(false); } - apply(hooks: JestHookSubscriber): void { + override apply(hooks: JestHookSubscriber): void { hooks.onTestRunComplete(results => { this._hasSnapshotFailure = results.snapshot.failure; }); } - getUsageInfo(): UsageData | null { + override getUsageInfo(): UsageData | null { if (this._hasSnapshotFailure) { return { key: 'u', diff --git a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts index 63f52ff5cfcd..1961a13611cc 100644 --- a/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts +++ b/packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts @@ -42,7 +42,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { return failedTestPaths; } - apply(hooks: JestHookSubscriber): void { + override apply(hooks: JestHookSubscriber): void { hooks.onTestRunComplete(results => { this._failedSnapshotTestAssertions = this.getFailedSnapshotTestAssertions(results); @@ -52,13 +52,13 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { }); } - onKey(key: string): void { + override onKey(key: string): void { if (this._snapshotInteractiveMode.isActive()) { this._snapshotInteractiveMode.put(key); } } - run( + override run( _globalConfig: Config.GlobalConfig, updateConfigAndRun: Function, ): Promise { @@ -85,7 +85,7 @@ class UpdateSnapshotInteractivePlugin extends BaseWatchPlugin { } } - getUsageInfo(): UsageData | null { + override getUsageInfo(): UsageData | null { if (this._failedSnapshotTestAssertions?.length > 0) { return { key: 'i', diff --git a/packages/jest-core/src/runJest.ts b/packages/jest-core/src/runJest.ts index 7e7bf5b86501..7061d3d282e7 100644 --- a/packages/jest-core/src/runJest.ts +++ b/packages/jest-core/src/runJest.ts @@ -13,6 +13,7 @@ import {CustomConsole} from '@jest/console'; import { AggregatedResult, Test, + TestContext, TestResultsProcessor, formatTestResults, makeEmptyAggregatedTestResult, @@ -21,13 +22,11 @@ import type TestSequencer from '@jest/test-sequencer'; import type {Config} from '@jest/types'; import type {ChangedFiles, ChangedFilesPromise} from 'jest-changed-files'; import Resolver from 'jest-resolve'; -import type {Context} from 'jest-runtime'; import {requireOrImportModule, tryRealpath} from 'jest-util'; -import {JestHook, JestHookEmitter} from 'jest-watcher'; +import {JestHook, JestHookEmitter, TestWatcher} from 'jest-watcher'; import type FailedTestsCache from './FailedTestsCache'; import SearchSource from './SearchSource'; import {TestSchedulerContext, createTestScheduler} from './TestScheduler'; -import type TestWatcher from './TestWatcher'; import collectNodeHandles, {HandleCollectionResult} from './collectHandles'; import getNoTestsFoundMessage from './getNoTestsFoundMessage'; import runGlobalHook from './runGlobalHook'; @@ -137,7 +136,7 @@ export default async function runJest({ filter, }: { globalConfig: Config.GlobalConfig; - contexts: Array; + contexts: Array; outputStream: NodeJS.WriteStream; testWatcher: TestWatcher; jestHooks?: JestHookEmitter; @@ -280,11 +279,10 @@ export default async function runJest({ } } - const scheduler = await createTestScheduler( - globalConfig, - {startRun}, - testSchedulerContext, - ); + const scheduler = await createTestScheduler(globalConfig, { + startRun, + ...testSchedulerContext, + }); const results = await scheduler.scheduleTests(allTests, testWatcher); diff --git a/packages/jest-core/src/types.ts b/packages/jest-core/src/types.ts index f29b8b3e2535..98119f1c6375 100644 --- a/packages/jest-core/src/types.ts +++ b/packages/jest-core/src/types.ts @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import type {Test} from '@jest/test-result'; -import type {Context} from 'jest-runtime'; +import type {Test, TestContext} from '@jest/test-result'; export type Stats = { roots: number; @@ -17,7 +16,7 @@ export type Stats = { }; export type TestRunData = Array<{ - context: Context; + context: TestContext; matches: { allTests: number; tests: Array; diff --git a/packages/jest-core/src/watch.ts b/packages/jest-core/src/watch.ts index 357113ef8c9c..ca0a6a421f26 100644 --- a/packages/jest-core/src/watch.ts +++ b/packages/jest-core/src/watch.ts @@ -10,13 +10,13 @@ import ansiEscapes = require('ansi-escapes'); import chalk = require('chalk'); import exit = require('exit'); import slash = require('slash'); +import type {TestContext} from '@jest/test-result'; import type {Config} from '@jest/types'; import type { ChangeEvent as HasteChangeEvent, default as HasteMap, } from 'jest-haste-map'; import {formatExecError} from 'jest-message-util'; -import type {Context} from 'jest-runtime'; import { isInteractive, preRunMessage, @@ -28,12 +28,12 @@ import { AllowedConfigOptions, JestHook, KEYS, + TestWatcher, WatchPlugin, WatchPluginClass, } from 'jest-watcher'; import FailedTestsCache from './FailedTestsCache'; import SearchSource from './SearchSource'; -import TestWatcher from './TestWatcher'; import getChangedFilesPromise from './getChangedFilesPromise'; import activeFilters from './lib/activeFiltersMessage'; import createContext from './lib/createContext'; @@ -91,7 +91,7 @@ const RESERVED_KEY_PLUGINS = new Map< export default async function watch( initialGlobalConfig: Config.GlobalConfig, - contexts: Array, + contexts: Array, outputStream: NodeJS.WriteStream, hasteMapInstances: Array, stdin: NodeJS.ReadStream = process.stdin, diff --git a/packages/jest-create-cache-key-function/package.json b/packages/jest-create-cache-key-function/package.json index d739a1331cb9..b6547debe8f2 100644 --- a/packages/jest-create-cache-key-function/package.json +++ b/packages/jest-create-cache-key-function/package.json @@ -1,17 +1,17 @@ { "name": "@jest/create-cache-key-function", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-create-cache-key-function" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7" + "@jest/types": "^28.0.1" }, "devDependencies": { "@types/node": "*", - "jest-util": "^28.0.0-alpha.7" + "jest-util": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-create-cache-key-function/src/index.ts b/packages/jest-create-cache-key-function/src/index.ts index 8f1a6f1eed80..835edc088885 100644 --- a/packages/jest-create-cache-key-function/src/index.ts +++ b/packages/jest-create-cache-key-function/src/index.ts @@ -49,9 +49,10 @@ function getGlobalCacheKey(files: Array, values: Array) { ] .reduce( (hash, chunk) => hash.update('\0', 'utf8').update(chunk || ''), - createHash('md5'), + createHash('sha256'), ) - .digest('hex'); + .digest('hex') + .substring(0, 32); } function getCacheKeyFunction(globalCacheKey: string): GetCacheKeyFunction { @@ -61,7 +62,7 @@ function getCacheKeyFunction(globalCacheKey: string): GetCacheKeyFunction { const inferredOptions = options || configString; const {config, instrument} = inferredOptions; - return createHash('md5') + return createHash('sha256') .update(globalCacheKey) .update('\0', 'utf8') .update(sourceText) @@ -69,7 +70,8 @@ function getCacheKeyFunction(globalCacheKey: string): GetCacheKeyFunction { .update(config.rootDir ? relative(config.rootDir, sourcePath) : '') .update('\0', 'utf8') .update(instrument ? 'instrument' : '') - .digest('hex'); + .digest('hex') + .substring(0, 32); }; } diff --git a/packages/jest-diff/package.json b/packages/jest-diff/package.json index cf8a5383b42a..88c71dcbcf95 100644 --- a/packages/jest-diff/package.json +++ b/packages/jest-diff/package.json @@ -1,6 +1,6 @@ { "name": "jest-diff", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -18,12 +18,12 @@ }, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^28.0.0-alpha.6", - "jest-get-type": "^28.0.0-alpha.3", - "pretty-format": "^28.0.0-alpha.7" + "diff-sequences": "^28.0.0", + "jest-get-type": "^28.0.0", + "pretty-format": "^28.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "strip-ansi": "^6.0.0" }, "engines": { diff --git a/packages/jest-docblock/package.json b/packages/jest-docblock/package.json index 2bc0ee686ac3..94cf776ff21a 100644 --- a/packages/jest-docblock/package.json +++ b/packages/jest-docblock/package.json @@ -1,6 +1,6 @@ { "name": "jest-docblock", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", diff --git a/packages/jest-each/package.json b/packages/jest-each/package.json index b8f6de4f0f8d..5db77a0ac697 100644 --- a/packages/jest-each/package.json +++ b/packages/jest-each/package.json @@ -1,6 +1,6 @@ { "name": "jest-each", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "description": "Parameterised tests for Jest", "main": "./build/index.js", "types": "./build/index.d.ts", @@ -25,11 +25,11 @@ "author": "Matt Phillips (mattphillips)", "license": "MIT", "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "chalk": "^4.0.0", - "jest-get-type": "^28.0.0-alpha.3", - "jest-util": "^28.0.0-alpha.7", - "pretty-format": "^28.0.0-alpha.7" + "jest-get-type": "^28.0.0", + "jest-util": "^28.0.1", + "pretty-format": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-each/src/__tests__/__snapshots__/template.test.ts.snap b/packages/jest-each/src/__tests__/__snapshots__/template.test.ts.snap index c4e912980be2..bf551eeec425 100644 --- a/packages/jest-each/src/__tests__/__snapshots__/template.test.ts.snap +++ b/packages/jest-each/src/__tests__/__snapshots__/template.test.ts.snap @@ -54,7 +54,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .describe throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -67,7 +67,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .describe throws error when there are no arguments for given headings 1`] = ` @@ -129,7 +129,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .describe.only throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -142,7 +142,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .describe.only throws error when there are no arguments for given headings 1`] = ` @@ -204,7 +204,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .fdescribe throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -217,7 +217,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .fdescribe throws error when there are no arguments for given headings 1`] = ` @@ -279,7 +279,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .fit throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -292,7 +292,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .fit throws error when there are no arguments for given headings 1`] = ` @@ -354,7 +354,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .it throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -367,7 +367,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .it throws error when there are no arguments for given headings 1`] = ` @@ -429,7 +429,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .it.only throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -442,7 +442,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .it.only throws error when there are no arguments for given headings 1`] = ` @@ -504,7 +504,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -517,7 +517,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test throws error when there are no arguments for given headings 1`] = ` @@ -579,7 +579,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -592,7 +592,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent throws error when there are no arguments for given headings 1`] = ` @@ -654,7 +654,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent.only throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -667,7 +667,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent.only throws error when there are no arguments for given headings 1`] = ` @@ -729,7 +729,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent.skip throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -742,7 +742,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.concurrent.skip throws error when there are no arguments for given headings 1`] = ` @@ -804,7 +804,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.only throws error when there are fewer arguments than headings when given one row 1`] = ` @@ -817,7 +817,7 @@ Received: 1, ] -Missing 2 arguments" +Missing 1 argument" `; exports[`jest-each .test.only throws error when there are no arguments for given headings 1`] = ` diff --git a/packages/jest-each/src/validation.ts b/packages/jest-each/src/validation.ts index 036d3261f726..a2b0088cb2b4 100644 --- a/packages/jest-each/src/validation.ts +++ b/packages/jest-each/src/validation.ts @@ -54,9 +54,10 @@ export const validateTemplateTableArguments = ( headings: Array, data: TemplateData, ): void => { - const missingData = data.length % headings.length; + const incompleteData = data.length % headings.length; + const missingData = headings.length - incompleteData; - if (missingData > 0) { + if (incompleteData > 0) { throw new Error( `Not enough arguments supplied for given headings:\n${EXPECTED_COLOR( headings.join(' | '), diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index 3573314ac941..1a007201d088 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -1,6 +1,6 @@ { "name": "jest-environment-jsdom", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,17 +17,17 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/fake-timers": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/environment": "^28.0.1", + "@jest/fake-timers": "^28.0.1", + "@jest/types": "^28.0.1", "@types/jsdom": "^16.2.4", "@types/node": "*", - "jest-mock": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", + "jest-mock": "^28.0.1", + "jest-util": "^28.0.1", "jsdom": "^19.0.0" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7" + "@jest/test-utils": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-environment-jsdom/src/index.ts b/packages/jest-environment-jsdom/src/index.ts index 30a01b4b3d58..f16e876ecefe 100644 --- a/packages/jest-environment-jsdom/src/index.ts +++ b/packages/jest-environment-jsdom/src/index.ts @@ -111,16 +111,14 @@ export default class JSDOMEnvironment implements JestEnvironment { this.moduleMocker = new ModuleMocker(global as any); - const timerConfig = { - idToRef: (id: number) => id, - refToId: (ref: number) => ref, - }; - this.fakeTimers = new LegacyFakeTimers({ config: projectConfig, global: global as unknown as typeof globalThis, moduleMocker: this.moduleMocker, - timerConfig, + timerConfig: { + idToRef: (id: number) => id, + refToId: (ref: number) => ref, + }, }); this.fakeTimersModern = new ModernFakeTimers({ diff --git a/packages/jest-environment-node/package.json b/packages/jest-environment-node/package.json index 8f98b71474bb..1f62e1109278 100644 --- a/packages/jest-environment-node/package.json +++ b/packages/jest-environment-node/package.json @@ -1,6 +1,6 @@ { "name": "jest-environment-node", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,15 +17,15 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/fake-timers": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/environment": "^28.0.1", + "@jest/fake-timers": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", - "jest-mock": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7" + "jest-mock": "^28.0.1", + "jest-util": "^28.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7" + "@jest/test-utils": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index bd9718943383..ec2b61f09ac0 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -22,6 +22,37 @@ type Timer = { unref: () => Timer; }; +// some globals we do not want, either because deprecated or we set it ourselves +const denyList = new Set([ + 'GLOBAL', + 'root', + 'global', + 'Buffer', + 'ArrayBuffer', + 'Uint8Array', + // if env is loaded within a jest test + 'jest-symbol-do-not-touch', +]); + +const nodeGlobals = new Map( + Object.getOwnPropertyNames(globalThis) + .filter(global => !denyList.has(global)) + .map(nodeGlobalsKey => { + const descriptor = Object.getOwnPropertyDescriptor( + globalThis, + nodeGlobalsKey, + ); + + if (!descriptor) { + throw new Error( + `No property descriptor for ${nodeGlobalsKey}, this is a bug in Jest.`, + ); + } + + return [nodeGlobalsKey, descriptor]; + }), +); + export default class NodeEnvironment implements JestEnvironment { context: Context | null; fakeTimers: LegacyFakeTimers | null; @@ -37,64 +68,47 @@ export default class NodeEnvironment implements JestEnvironment { 'this', Object.assign(this.context, projectConfig.testEnvironmentOptions), )); + + const contextGlobals = new Set(Object.getOwnPropertyNames(global)); + for (const [nodeGlobalsKey, descriptor] of nodeGlobals) { + if (!contextGlobals.has(nodeGlobalsKey)) { + Object.defineProperty(global, nodeGlobalsKey, { + configurable: descriptor.configurable, + enumerable: descriptor.enumerable, + get() { + // @ts-expect-error + const val = globalThis[nodeGlobalsKey]; + + // override lazy getter + Object.defineProperty(global, nodeGlobalsKey, { + configurable: descriptor.configurable, + enumerable: descriptor.enumerable, + value: val, + writable: descriptor.writable, + }); + return val; + }, + set(val) { + // override lazy getter + Object.defineProperty(global, nodeGlobalsKey, { + configurable: descriptor.configurable, + enumerable: descriptor.enumerable, + value: val, + writable: true, + }); + }, + }); + } + } + global.global = global; - global.clearInterval = clearInterval; - global.clearTimeout = clearTimeout; - global.setInterval = setInterval; - global.setTimeout = setTimeout; global.Buffer = Buffer; - global.setImmediate = setImmediate; - global.clearImmediate = clearImmediate; global.ArrayBuffer = ArrayBuffer; // TextEncoder (global or via 'util') references a Uint8Array constructor // different than the global one used by users in tests. This makes sure the // same constructor is referenced by both. global.Uint8Array = Uint8Array; - // URL and URLSearchParams are global in Node >= 10 - global.URL = URL; - global.URLSearchParams = URLSearchParams; - - // TextDecoder and TextDecoder are global in Node >= 11 - global.TextEncoder = TextEncoder; - global.TextDecoder = TextDecoder; - - // queueMicrotask is global in Node >= 11 - global.queueMicrotask = queueMicrotask; - - // AbortController is global in Node >= 15 - if (typeof AbortController !== 'undefined') { - global.AbortController = AbortController; - } - // AbortSignal is global in Node >= 15 - if (typeof AbortSignal !== 'undefined') { - global.AbortSignal = AbortSignal; - } - // Event is global in Node >= 15.4 - if (typeof Event !== 'undefined') { - global.Event = Event; - } - // EventTarget is global in Node >= 15.4 - if (typeof EventTarget !== 'undefined') { - global.EventTarget = EventTarget; - } - // MessageChannel is global in Node >= 15 - if (typeof MessageChannel !== 'undefined') { - global.MessageChannel = MessageChannel; - } - // MessageEvent is global in Node >= 15 - if (typeof MessageEvent !== 'undefined') { - global.MessageEvent = MessageEvent; - } - // performance is global in Node >= 16 - if (typeof performance !== 'undefined') { - global.performance = performance; - } - // atob and btoa are global in Node >= 16 - if (typeof atob !== 'undefined' && typeof btoa !== 'undefined') { - global.atob = atob; - global.btoa = btoa; - } installCommonGlobals(global, projectConfig.globals); this.moduleMocker = new ModuleMocker(global); @@ -112,16 +126,14 @@ export default class NodeEnvironment implements JestEnvironment { const timerRefToId = (timer: Timer): number | undefined => (timer && timer.id) || undefined; - const timerConfig = { - idToRef: timerIdToRef, - refToId: timerRefToId, - }; - this.fakeTimers = new LegacyFakeTimers({ config: projectConfig, global, moduleMocker: this.moduleMocker, - timerConfig, + timerConfig: { + idToRef: timerIdToRef, + refToId: timerRefToId, + }, }); this.fakeTimersModern = new ModernFakeTimers({ diff --git a/packages/jest-environment/package.json b/packages/jest-environment/package.json index 37f913825012..1c2c0be986da 100644 --- a/packages/jest-environment/package.json +++ b/packages/jest-environment/package.json @@ -1,6 +1,6 @@ { "name": "@jest/environment", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,10 +17,10 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/fake-timers": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/fake-timers": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", - "jest-mock": "^28.0.0-alpha.7" + "jest-mock": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index ac7fe89c30cc..7a6e1cc8f7ef 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -28,6 +28,10 @@ export type ModuleWrapper = ( ...sandboxInjectedGlobals: Array ) => unknown; +export interface JestImportMeta extends ImportMeta { + jest: Jest; +} + export interface JestEnvironmentConfig { projectConfig: Config.ProjectConfig; globalConfig: Config.GlobalConfig; @@ -71,7 +75,7 @@ export interface Jest { */ autoMockOn(): Jest; /** - * Clears the `mock.calls`, `mock.instances` and `mock.results` properties of + * Clears the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` properties of * all mocks. Equivalent to calling `.mockClear()` on every mocked function. */ clearAllMocks(): Jest; @@ -140,7 +144,7 @@ export interface Jest { * need access to the real current time, you can invoke this function. * * @remarks - * Only available when using 'modern' fake timers implementation. + * Not available when using legacy fake timers implementation. */ getRealSystemTime(): number; /** @@ -228,15 +232,23 @@ export interface Jest { * Runs failed tests n-times until they pass or until the max number of * retries is exhausted. * + * If `logErrorsBeforeRetry` is enabled, Jest will log the error(s) that caused + * the test to fail to the console, providing visibility on why a retry occurred. + * retries is exhausted. + * * @remarks * Only available with `jest-circus` runner. */ - retryTimes(numRetries: number): Jest; + retryTimes( + numRetries: number, + options?: {logErrorsBeforeRetry?: boolean}, + ): Jest; + /** * Exhausts tasks queued by `setImmediate()`. * * @remarks - * Not available when using 'modern' timers implementation. + * Only available when using legacy fake timers implementation. */ runAllImmediates(): void; /** @@ -272,7 +284,7 @@ export interface Jest { * as they would have done without the call to `jest.setSystemTime()`. * * @remarks - * Only available when using 'modern' fake timers implementation. + * Not available when using legacy fake timers implementation. */ setSystemTime(now?: number | Date): void; /** @@ -302,11 +314,20 @@ export interface Jest { */ unmock(moduleName: string): Jest; /** - * Instructs Jest to use fake versions of the standard timer functions. + * Instructs Jest to use fake versions of the global date, performance, + * time and timer APIs. Fake timers implementation is backed by + * [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). + * + * @remarks + * Calling `jest.useFakeTimers()` once again in the same test file would reinstall + * fake timers using the provided options. */ - useFakeTimers(implementation?: 'modern' | 'legacy'): Jest; + useFakeTimers( + fakeTimersConfig?: Config.FakeTimersConfig | Config.LegacyFakeTimersConfig, + ): Jest; /** - * Instructs Jest to use the real versions of the standard timer functions. + * Instructs Jest to restore the original implementations of the global date, + * performance, time and timer APIs. */ useRealTimers(): Jest; } diff --git a/packages/jest-expect/__typetests__/tsconfig.json b/packages/jest-expect/__typetests__/tsconfig.json index fe8eab794254..165ba1343021 100644 --- a/packages/jest-expect/__typetests__/tsconfig.json +++ b/packages/jest-expect/__typetests__/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { + "composite": false, "noUnusedLocals": false, "noUnusedParameters": false, "skipLibCheck": true, diff --git a/packages/jest-expect/package.json b/packages/jest-expect/package.json index 25c9936d766c..32a7c6048b43 100644 --- a/packages/jest-expect/package.json +++ b/packages/jest-expect/package.json @@ -1,6 +1,6 @@ { "name": "@jest/expect", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,8 +17,8 @@ "./package.json": "./package.json" }, "dependencies": { - "expect": "^28.0.0-alpha.7", - "jest-snapshot": "^28.0.0-alpha.7" + "expect": "^28.0.1", + "jest-snapshot": "^28.0.1" }, "devDependencies": { "@tsd/typescript": "~4.6.2", diff --git a/packages/jest-fake-timers/package.json b/packages/jest-fake-timers/package.json index 264beaa4f5b0..012773834973 100644 --- a/packages/jest-fake-timers/package.json +++ b/packages/jest-fake-timers/package.json @@ -1,6 +1,6 @@ { "name": "@jest/fake-timers", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,15 +17,16 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@sinonjs/fake-timers": "^9.1.1", "@types/node": "*", - "jest-message-util": "^28.0.0-alpha.7", - "jest-mock": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7" + "jest-message-util": "^28.0.1", + "jest-mock": "^28.0.1", + "jest-util": "^28.0.1" }, "devDependencies": { - "@types/sinonjs__fake-timers": "^8.1.1" + "@jest/test-utils": "^28.0.1", + "@types/sinonjs__fake-timers": "^8.1.2" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-fake-timers/src/__tests__/__snapshots__/legacyFakeTimers.test.ts.snap b/packages/jest-fake-timers/src/__tests__/__snapshots__/legacyFakeTimers.test.ts.snap index 035160d483e5..4a1286a87aed 100644 --- a/packages/jest-fake-timers/src/__tests__/__snapshots__/legacyFakeTimers.test.ts.snap +++ b/packages/jest-fake-timers/src/__tests__/__snapshots__/legacyFakeTimers.test.ts.snap @@ -1,7 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = ` -"A function to advance timers was called but the timers API is not mocked with fake timers. Call \`jest.useFakeTimers()\` in this test or enable fake timers globally by setting \`"timers": "fake"\` in the configuration file. This warning is likely a result of a default configuration change in Jest 15. - -Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15" -`; +exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = `"A function to advance timers was called but the timers APIs are not mocked with fake timers. Call \`jest.useFakeTimers({legacyFakeTimers: true})\` in this test file or enable fake timers for all tests by setting {'enableGlobally': true, 'legacyFakeTimers': true} in Jest configuration file."`; diff --git a/packages/jest-fake-timers/src/__tests__/__snapshots__/modernFakeTimers.test.ts.snap b/packages/jest-fake-timers/src/__tests__/__snapshots__/modernFakeTimers.test.ts.snap index 8b00458786e1..daf980ad8701 100644 --- a/packages/jest-fake-timers/src/__tests__/__snapshots__/modernFakeTimers.test.ts.snap +++ b/packages/jest-fake-timers/src/__tests__/__snapshots__/modernFakeTimers.test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = `"A function to advance timers was called but the timers API is not mocked with fake timers. Call \`jest.useFakeTimers()\` in this test or enable fake timers globally by setting \`"timers": "fake"\` in the configuration file"`; +exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = `"A function to advance timers was called but the timers APIs are not replaced with fake timers. Call \`jest.useFakeTimers()\` in this test file or enable fake timers for all tests by setting 'fakeTimers': {'enableGlobally': true} in Jest configuration file."`; diff --git a/packages/jest-fake-timers/src/__tests__/modernFakeTimers.test.ts b/packages/jest-fake-timers/src/__tests__/modernFakeTimers.test.ts index 36b14aecc128..a2cddb4a871d 100644 --- a/packages/jest-fake-timers/src/__tests__/modernFakeTimers.test.ts +++ b/packages/jest-fake-timers/src/__tests__/modernFakeTimers.test.ts @@ -6,6 +6,7 @@ * */ +import {makeProjectConfig} from '@jest/test-utils'; import FakeTimers from '../modernFakeTimers'; describe('FakeTimers', () => { @@ -17,7 +18,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.setTimeout).not.toBe(undefined); }); @@ -29,7 +30,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.clearTimeout).not.toBe(undefined); }); @@ -41,7 +42,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.setInterval).not.toBe(undefined); }); @@ -53,7 +54,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.clearInterval).not.toBe(undefined); }); @@ -68,7 +69,7 @@ describe('FakeTimers', () => { }, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.process.nextTick).not.toBe(origNextTick); }); @@ -82,7 +83,7 @@ describe('FakeTimers', () => { setImmediate: origSetImmediate, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.setImmediate).not.toBe(origSetImmediate); }); @@ -98,7 +99,7 @@ describe('FakeTimers', () => { setImmediate: origSetImmediate, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); expect(global.clearImmediate).not.toBe(origClearImmediate); }); @@ -115,7 +116,7 @@ describe('FakeTimers', () => { setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder = []; @@ -146,7 +147,7 @@ describe('FakeTimers', () => { setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); timers.runAllTicks(); @@ -163,7 +164,7 @@ describe('FakeTimers', () => { setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -187,7 +188,10 @@ describe('FakeTimers', () => { setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global, maxLoops: 100}); + const timers = new FakeTimers({ + config: makeProjectConfig({fakeTimers: {timerLimit: 100}}), + global, + }); timers.useFakeTimers(); @@ -211,7 +215,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder = []; @@ -247,9 +251,7 @@ describe('FakeTimers', () => { const consoleWarn = console.warn; console.warn = jest.fn(); const timers = new FakeTimers({ - config: { - rootDir: __dirname, - }, + config: makeProjectConfig({rootDir: __dirname}), global: globalThis, }); timers.runAllTimers(); @@ -268,7 +270,7 @@ describe('FakeTimers', () => { setTimeout: nativeSetTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); timers.runAllTimers(); }); @@ -280,7 +282,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const fn = jest.fn(); @@ -301,7 +303,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const fn = jest.fn(); @@ -322,7 +324,7 @@ describe('FakeTimers', () => { setTimeout: nativeSetTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); // @sinonjs/fake-timers uses `setTimeout` during init to figure out if it's in Node or // browser env. So clear its calls before we install them into the env nativeSetTimeout.mockClear(); @@ -343,7 +345,10 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global, maxLoops: 100}); + const timers = new FakeTimers({ + config: makeProjectConfig({fakeTimers: {timerLimit: 1000}}), + global, + }); timers.useFakeTimers(); global.setTimeout(function infinitelyRecursingCallback() { @@ -354,7 +359,7 @@ describe('FakeTimers', () => { timers.runAllTimers(); }).toThrow( new Error( - 'Aborting after running 100 timers, assuming an infinite loop!', + 'Aborting after running 1000 timers, assuming an infinite loop!', ), ); }); @@ -366,7 +371,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const fn = jest.fn(); @@ -388,7 +393,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder = []; @@ -432,7 +437,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); timers.advanceTimersByTime(100); @@ -447,7 +452,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder: Array = []; @@ -487,7 +492,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder: Array = []; @@ -526,7 +531,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder: Array = []; @@ -554,7 +559,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); timers.advanceTimersToNextTimer(); @@ -569,7 +574,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -587,7 +592,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -608,7 +613,7 @@ describe('FakeTimers', () => { setImmediate: () => {}, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -627,7 +632,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -654,7 +659,7 @@ describe('FakeTimers', () => { setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const runOrder = []; @@ -719,7 +724,7 @@ describe('FakeTimers', () => { process, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); const fn = jest.fn(); @@ -748,7 +753,7 @@ describe('FakeTimers', () => { setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -775,7 +780,7 @@ describe('FakeTimers', () => { process: {nextTick: nativeProcessNextTick}, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -799,7 +804,7 @@ describe('FakeTimers', () => { setImmediate: nativeSetImmediate, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -829,7 +834,7 @@ describe('FakeTimers', () => { setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -856,7 +861,7 @@ describe('FakeTimers', () => { process: {nextTick: nativeProcessNextTick}, setTimeout, } as unknown as typeof globalThis; - const timers = new FakeTimers({global}); + const timers = new FakeTimers({config: makeProjectConfig(), global}); timers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -880,7 +885,7 @@ describe('FakeTimers', () => { setImmediate: nativeSetImmediate, setTimeout, } as unknown as typeof globalThis; - const fakeTimers = new FakeTimers({global}); + const fakeTimers = new FakeTimers({config: makeProjectConfig(), global}); fakeTimers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -897,7 +902,10 @@ describe('FakeTimers', () => { describe('getTimerCount', () => { it('returns the correct count', () => { - const timers = new FakeTimers({global: globalThis}); + const timers = new FakeTimers({ + config: makeProjectConfig(), + global: globalThis, + }); timers.useFakeTimers(); @@ -917,7 +925,10 @@ describe('FakeTimers', () => { }); it('includes immediates and ticks', () => { - const timers = new FakeTimers({global: globalThis}); + const timers = new FakeTimers({ + config: makeProjectConfig(), + global: globalThis, + }); timers.useFakeTimers(); @@ -929,7 +940,10 @@ describe('FakeTimers', () => { }); it('not includes cancelled immediates', () => { - const timers = new FakeTimers({global: globalThis}); + const timers = new FakeTimers({ + config: makeProjectConfig(), + global: globalThis, + }); timers.useFakeTimers(); diff --git a/packages/jest-fake-timers/src/__tests__/sinon-integration.test.ts b/packages/jest-fake-timers/src/__tests__/sinon-integration.test.ts new file mode 100644 index 000000000000..528e2d15937c --- /dev/null +++ b/packages/jest-fake-timers/src/__tests__/sinon-integration.test.ts @@ -0,0 +1,181 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {makeProjectConfig} from '@jest/test-utils'; +import FakeTimers from '../modernFakeTimers'; + +jest.spyOn(Date, 'now').mockImplementation(() => 123456); + +const mockInstall = jest.fn(); + +const mockWithGlobal = { + install: mockInstall, + timers: { + Date: jest.fn(), + clearImmediate: jest.fn(), + clearInterval: jest.fn(), + clearTimeout: jest.fn(), + hrtime: jest.fn(), + nextTick: jest.fn(), + performance: jest.fn(), + queueMicrotask: jest.fn(), + setImmediate: jest.fn(), + setInterval: jest.fn(), + setTimeout: jest.fn(), + }, +}; + +jest.mock('@sinonjs/fake-timers', () => { + return { + withGlobal: jest.fn(() => mockWithGlobal), + }; +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('`@sinonjs/fake-timers` integration', () => { + test('uses default global config, when `useFakeTimers()` is called without options', () => { + const timers = new FakeTimers({ + config: makeProjectConfig(), + global: globalThis, + }); + + timers.useFakeTimers(); + + expect(mockInstall).toBeCalledWith({ + advanceTimeDelta: undefined, + loopLimit: 100_000, + now: 123456, + shouldAdvanceTime: false, + shouldClearNativeTimers: true, + toFake: [ + 'Date', + 'clearImmediate', + 'clearInterval', + 'clearTimeout', + 'hrtime', + 'nextTick', + 'performance', + 'queueMicrotask', + 'setImmediate', + 'setInterval', + 'setTimeout', + ], + }); + }); + + test('uses custom global config, when `useFakeTimers()` is called without options', () => { + const timers = new FakeTimers({ + config: makeProjectConfig({ + fakeTimers: { + advanceTimers: true, + doNotFake: ['Date', 'nextTick'], + now: 0, + timerLimit: 100, + }, + }), + global: globalThis, + }); + + timers.useFakeTimers(); + + expect(mockInstall).toBeCalledWith({ + advanceTimeDelta: undefined, + loopLimit: 100, + now: 0, + shouldAdvanceTime: true, + shouldClearNativeTimers: true, + toFake: [ + 'clearImmediate', + 'clearInterval', + 'clearTimeout', + 'hrtime', + 'performance', + 'queueMicrotask', + 'setImmediate', + 'setInterval', + 'setTimeout', + ], + }); + }); + + test('overrides default global config, when `useFakeTimers()` is called with options,', () => { + const timers = new FakeTimers({ + config: makeProjectConfig(), + global: globalThis, + }); + + timers.useFakeTimers({ + advanceTimers: 40, + doNotFake: ['Date', 'queueMicrotask'], + now: new Date('1995-12-17'), + timerLimit: 2000, + }); + + expect(mockInstall).toBeCalledWith({ + advanceTimeDelta: 40, + loopLimit: 2000, + now: new Date('1995-12-17'), + shouldAdvanceTime: true, + shouldClearNativeTimers: true, + toFake: [ + 'clearImmediate', + 'clearInterval', + 'clearTimeout', + 'hrtime', + 'nextTick', + 'performance', + 'setImmediate', + 'setInterval', + 'setTimeout', + ], + }); + }); + + test('overrides custom global config, when `useFakeTimers()` is called with options,', () => { + const timers = new FakeTimers({ + config: makeProjectConfig({ + fakeTimers: { + advanceTimers: 20, + doNotFake: ['Date', 'nextTick'], + now: 0, + timerLimit: 1000, + }, + }), + global: globalThis, + }); + + timers.useFakeTimers({ + advanceTimers: false, + doNotFake: ['hrtime'], + now: 123456, + }); + + expect(mockInstall).toBeCalledWith({ + advanceTimeDelta: undefined, + loopLimit: 1000, + now: 123456, + shouldAdvanceTime: false, + shouldClearNativeTimers: true, + toFake: [ + 'Date', + 'clearImmediate', + 'clearInterval', + 'clearTimeout', + 'nextTick', + 'performance', + 'queueMicrotask', + 'setImmediate', + 'setInterval', + 'setTimeout', + ], + }); + }); +}); diff --git a/packages/jest-fake-timers/src/legacyFakeTimers.ts b/packages/jest-fake-timers/src/legacyFakeTimers.ts index ee957b0444fb..e4b719f5d6b3 100644 --- a/packages/jest-fake-timers/src/legacyFakeTimers.ts +++ b/packages/jest-fake-timers/src/legacyFakeTimers.ts @@ -402,16 +402,15 @@ export default class FakeTimers { // @ts-expect-error: condition always returns 'true' if (this._global.setTimeout !== this._fakeTimerAPIs?.setTimeout) { this._global.console.warn( - 'A function to advance timers was called but the timers API is not ' + - 'mocked with fake timers. Call `jest.useFakeTimers()` in this ' + - 'test or enable fake timers globally by setting ' + - '`"timers": "fake"` in ' + - 'the configuration file. This warning is likely a result of a ' + - 'default configuration change in Jest 15.\n\n' + - 'Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15\n' + - `Stack Trace:\n${formatStackTrace(new Error().stack!, this._config, { - noStackTrace: false, - })}`, + 'A function to advance timers was called but the timers APIs are not mocked ' + + 'with fake timers. Call `jest.useFakeTimers({legacyFakeTimers: true})` ' + + 'in this test file or enable fake timers for all tests by setting ' + + "{'enableGlobally': true, 'legacyFakeTimers': true} in " + + `Jest configuration file.\nStack Trace:\n${formatStackTrace( + new Error().stack!, + this._config, + {noStackTrace: false}, + )}`, ); } } diff --git a/packages/jest-fake-timers/src/modernFakeTimers.ts b/packages/jest-fake-timers/src/modernFakeTimers.ts index 93abce4dd4c9..f8363388c9ce 100644 --- a/packages/jest-fake-timers/src/modernFakeTimers.ts +++ b/packages/jest-fake-timers/src/modernFakeTimers.ts @@ -7,31 +7,30 @@ import { FakeTimerWithContext, + FakeMethod as FakeableAPI, InstalledClock, + FakeTimerInstallOpts as SinonFakeTimersConfig, withGlobal, } from '@sinonjs/fake-timers'; -import {StackTraceConfig, formatStackTrace} from 'jest-message-util'; +import type {Config} from '@jest/types'; +import {formatStackTrace} from 'jest-message-util'; export default class FakeTimers { private _clock!: InstalledClock; - private _config: StackTraceConfig; + private _config: Config.ProjectConfig; private _fakingTime: boolean; private _global: typeof globalThis; private _fakeTimers: FakeTimerWithContext; - private _maxLoops: number; constructor({ global, config, - maxLoops, }: { global: typeof globalThis; - config: StackTraceConfig; - maxLoops?: number; + config: Config.ProjectConfig; }) { this._global = global; this._config = config; - this._maxLoops = maxLoops || 100000; this._fakingTime = false; this._fakeTimers = withGlobal(global); @@ -93,20 +92,16 @@ export default class FakeTimers { } } - useFakeTimers(): void { - if (!this._fakingTime) { - const toFake = Object.keys(this._fakeTimers.timers) as Array< - keyof FakeTimerWithContext['timers'] - >; + useFakeTimers(fakeTimersConfig?: Config.FakeTimersConfig): void { + if (this._fakingTime) { + this._clock.uninstall(); + } - this._clock = this._fakeTimers.install({ - loopLimit: this._maxLoops, - now: Date.now(), - toFake, - }); + this._clock = this._fakeTimers.install( + this._toSinonFakeTimersConfig(fakeTimersConfig), + ); - this._fakingTime = true; - } + this._fakingTime = true; } reset(): void { @@ -138,10 +133,10 @@ export default class FakeTimers { private _checkFakeTimers() { if (!this._fakingTime) { this._global.console.warn( - 'A function to advance timers was called but the timers API is not ' + - 'mocked with fake timers. Call `jest.useFakeTimers()` in this test or ' + - 'enable fake timers globally by setting `"timers": "fake"` in the ' + - `configuration file\nStack Trace:\n${formatStackTrace( + 'A function to advance timers was called but the timers APIs are not replaced ' + + 'with fake timers. Call `jest.useFakeTimers()` in this test file or enable ' + + "fake timers for all tests by setting 'fakeTimers': {'enableGlobally': true} " + + `in Jest configuration file.\nStack Trace:\n${formatStackTrace( new Error().stack!, this._config, {noStackTrace: false}, @@ -151,4 +146,35 @@ export default class FakeTimers { return this._fakingTime; } + + private _toSinonFakeTimersConfig( + fakeTimersConfig: Config.FakeTimersConfig = {}, + ): SinonFakeTimersConfig { + fakeTimersConfig = { + ...this._config.fakeTimers, + ...fakeTimersConfig, + } as Config.FakeTimersConfig; + + const advanceTimeDelta = + typeof fakeTimersConfig.advanceTimers === 'number' + ? fakeTimersConfig.advanceTimers + : undefined; + + const toFake = new Set( + Object.keys(this._fakeTimers.timers) as Array, + ); + + fakeTimersConfig.doNotFake?.forEach(nameOfFakeableAPI => { + toFake.delete(nameOfFakeableAPI); + }); + + return { + advanceTimeDelta, + loopLimit: fakeTimersConfig.timerLimit || 100_000, + now: fakeTimersConfig.now ?? Date.now(), + shouldAdvanceTime: Boolean(fakeTimersConfig.advanceTimers), + shouldClearNativeTimers: true, + toFake: Array.from(toFake), + }; + } } diff --git a/packages/jest-fake-timers/tsconfig.json b/packages/jest-fake-timers/tsconfig.json index 0bdcddc53258..4a4b01562562 100644 --- a/packages/jest-fake-timers/tsconfig.json +++ b/packages/jest-fake-timers/tsconfig.json @@ -10,6 +10,7 @@ {"path": "../jest-message-util"}, {"path": "../jest-mock"}, {"path": "../jest-types"}, - {"path": "../jest-util"} + {"path": "../jest-util"}, + {"path": "../test-utils"} ] } diff --git a/packages/jest-get-type/package.json b/packages/jest-get-type/package.json index d48d42f546a9..ddecd9d0af7a 100644 --- a/packages/jest-get-type/package.json +++ b/packages/jest-get-type/package.json @@ -1,7 +1,7 @@ { "name": "jest-get-type", "description": "A utility function to get the type of a value", - "version": "28.0.0-alpha.3", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", diff --git a/packages/jest-globals/package.json b/packages/jest-globals/package.json index 8f30e0a3c787..cb54bb57ff29 100644 --- a/packages/jest-globals/package.json +++ b/packages/jest-globals/package.json @@ -1,6 +1,6 @@ { "name": "@jest/globals", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -20,9 +20,9 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/expect": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7" + "@jest/environment": "^28.0.1", + "@jest/expect": "^28.0.1", + "@jest/types": "^28.0.1" }, "publishConfig": { "access": "public" diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index 74b870927cd7..28b329db3b6a 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -1,6 +1,6 @@ { "name": "jest-haste-map", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,20 +17,20 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", - "@types/graceful-fs": "^4.1.2", + "@jest/types": "^28.0.1", + "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.0-alpha.6", - "jest-util": "^28.0.0-alpha.7", - "jest-worker": "^28.0.0-alpha.7", + "jest-regex-util": "^28.0.0", + "jest-util": "^28.0.1", + "jest-worker": "^28.0.1", "micromatch": "^4.0.4", "walker": "^1.0.7" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@types/fb-watchman": "^2.0.0", "@types/micromatch": "^4.0.1", "slash": "^3.0.0" diff --git a/packages/jest-haste-map/src/__tests__/includes_dotfiles.test.ts b/packages/jest-haste-map/src/__tests__/includes_dotfiles.test.ts index ee5868c63c9e..64bf2257d0ab 100644 --- a/packages/jest-haste-map/src/__tests__/includes_dotfiles.test.ts +++ b/packages/jest-haste-map/src/__tests__/includes_dotfiles.test.ts @@ -23,13 +23,13 @@ const commonOptions = { test('watchman crawler and node crawler both include dotfiles', async () => { const hasteMapWithWatchman = await HasteMap.create({ ...commonOptions, - name: 'withWatchman', + id: 'withWatchman', useWatchman: true, }); const hasteMapWithNode = await HasteMap.create({ ...commonOptions, - name: 'withNode', + id: 'withNode', useWatchman: false, }); diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 454995640051..a3b2c6efc923 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -13,9 +13,11 @@ function mockHashContents(contents) { return crypto.createHash('sha1').update(contents).digest('hex'); } -jest.mock('child_process', () => ({ - // If this does not throw, we'll use the (mocked) watchman crawler - execSync() {}, +const mockIsWatchmanInstalled = jest.fn().mockResolvedValue(true); + +jest.mock('../lib/isWatchmanInstalled', () => ({ + __esModule: true, + default: mockIsWatchmanInstalled, })); jest.mock('jest-worker', () => ({ @@ -206,8 +208,8 @@ describe('HasteMap', () => { defaultConfig = { extensions: ['js', 'json'], hasteImplModulePath: require.resolve('./haste_impl.js'), + id: 'haste-map-test', maxWorkers: 1, - name: 'haste-map-test', platforms: ['ios', 'android'], resetCache: false, rootDir: path.join('/', 'project'), @@ -300,11 +302,11 @@ describe('HasteMap', () => { const HasteMap = require('../').default; const hasteMap1 = await HasteMap.create({ ...defaultConfig, - name: '@scoped/package', + id: '@scoped/package', }); const hasteMap2 = await HasteMap.create({ ...defaultConfig, - name: '-scoped-package', + id: '-scoped-package', }); expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); }); @@ -612,6 +614,8 @@ describe('HasteMap', () => { }); }); + mockIsWatchmanInstalled.mockClear(); + const hasteMap = await HasteMap.create({ ...defaultConfig, computeSha1: true, @@ -621,6 +625,10 @@ describe('HasteMap', () => { const data = (await hasteMap.build()).__hasteMapForTest; + expect(mockIsWatchmanInstalled).toHaveBeenCalledTimes( + useWatchman ? 1 : 0, + ); + expect(data.files).toEqual( createMap({ [path.join('fruits', 'Banana.js')]: [ diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 84d2eb2f0f5a..f3762703050c 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -7,7 +7,6 @@ /* eslint-disable local/ban-types-eventually */ -import {execSync} from 'child_process'; import {createHash} from 'crypto'; import {EventEmitter} from 'events'; import {tmpdir} from 'os'; @@ -26,6 +25,7 @@ import {watchmanCrawl} from './crawlers/watchman'; import getMockName from './getMockName'; import * as fastPath from './lib/fast_path'; import getPlatformExtension from './lib/getPlatformExtension'; +import isWatchmanInstalled from './lib/isWatchmanInstalled'; import normalizePathSep from './lib/normalizePathSep'; import type { ChangeEvent, @@ -65,10 +65,10 @@ type Options = { forceNodeFilesystemAPI?: boolean; hasteImplModulePath?: string; hasteMapModulePath?: string; + id: string; ignorePattern?: HasteRegExp; maxWorkers: number; mocksPattern?: string; - name: string; platforms: Array; resetCache?: boolean; retainAllFiles: boolean; @@ -89,10 +89,10 @@ type InternalOptions = { extensions: Array; forceNodeFilesystemAPI: boolean; hasteImplModulePath?: string; + id: string; ignorePattern?: HasteRegExp; maxWorkers: number; mocksPattern: RegExp | null; - name: string; platforms: Array; resetCache?: boolean; retainAllFiles: boolean; @@ -124,14 +124,6 @@ const VCS_DIRECTORIES = ['.git', '.hg'] .map(vcs => escapePathForRegex(path.sep + vcs + path.sep)) .join('|'); -const canUseWatchman = ((): boolean => { - try { - execSync('watchman --version', {stdio: ['ignore']}); - return true; - } catch {} - return false; -})(); - function invariant(condition: unknown, message?: string): asserts condition { if (!condition) { throw new Error(message); @@ -221,6 +213,7 @@ export default class HasteMap extends EventEmitter { private _cachePath: string; private _changeInterval?: ReturnType; private _console: Console; + private _isWatchmanInstalledPromise: Promise | null = null; private _options: InternalOptions; private _watchers: Array; private _worker: WorkerInterface | null; @@ -258,11 +251,11 @@ export default class HasteMap extends EventEmitter { extensions: options.extensions, forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI, hasteImplModulePath: options.hasteImplModulePath, + id: options.id, maxWorkers: options.maxWorkers, mocksPattern: options.mocksPattern ? new RegExp(options.mocksPattern) : null, - name: options.name, platforms: options.platforms, resetCache: options.resetCache, retainAllFiles: options.retainAllFiles, @@ -305,7 +298,10 @@ export default class HasteMap extends EventEmitter { } private async setupCachePath(options: Options): Promise { - const rootDirHash = createHash('md5').update(options.rootDir).digest('hex'); + const rootDirHash = createHash('sha256') + .update(options.rootDir) + .digest('hex') + .substring(0, 32); let hasteImplHash = ''; let dependencyExtractorHash = ''; @@ -329,9 +325,9 @@ export default class HasteMap extends EventEmitter { this._cachePath = HasteMap.getCacheFilePath( this._options.cacheDirectory, - `haste-map-${this._options.name}-${rootDirHash}`, + `haste-map-${this._options.id}-${rootDirHash}`, VERSION, - this._options.name, + this._options.id, this._options.roots .map(root => fastPath.relative(options.rootDir, root)) .join(':'), @@ -348,13 +344,13 @@ export default class HasteMap extends EventEmitter { static getCacheFilePath( tmpdir: string, - name: string, + id: string, ...extra: Array ): string { - const hash = createHash('md5').update(extra.join('')); + const hash = createHash('sha256').update(extra.join('')); return path.join( tmpdir, - `${name.replace(/\W/g, '-')}-${hash.digest('hex')}`, + `${id.replace(/\W/g, '-')}-${hash.digest('hex').substring(0, 32)}`, ); } @@ -757,6 +753,8 @@ export default class HasteMap extends EventEmitter { // @ts-expect-error: assignment of a worker with custom properties. this._worker = new Worker(require.resolve('./worker'), { exposedMethods: ['getSha1', 'worker'], + // @ts-expect-error: option does not exist on the node 12 types + forkOptions: {serialization: 'json'}, maxRetries: 3, numWorkers: this._options.maxWorkers, }) as WorkerInterface; @@ -766,11 +764,10 @@ export default class HasteMap extends EventEmitter { return this._worker; } - private _crawl(hasteMap: InternalHasteMap) { + private async _crawl(hasteMap: InternalHasteMap) { const options = this._options; const ignore = this._ignore.bind(this); - const crawl = - canUseWatchman && this._options.useWatchman ? watchmanCrawl : nodeCrawl; + const crawl = (await this._shouldUseWatchman()) ? watchmanCrawl : nodeCrawl; const crawlerOptions: CrawlerOptions = { computeSha1: options.computeSha1, data: hasteMap, @@ -814,7 +811,7 @@ export default class HasteMap extends EventEmitter { /** * Watch mode */ - private _watch(hasteMap: InternalHasteMap): Promise { + private async _watch(hasteMap: InternalHasteMap): Promise { if (!this._options.watch) { return Promise.resolve(); } @@ -825,12 +822,11 @@ export default class HasteMap extends EventEmitter { this._options.retainAllFiles = true; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher - const Watcher = - canUseWatchman && this._options.useWatchman - ? WatchmanWatcher - : FSEventsWatcher.isSupported() - ? FSEventsWatcher - : NodeWatcher; + const Watcher = (await this._shouldUseWatchman()) + ? WatchmanWatcher + : FSEventsWatcher.isSupported() + ? FSEventsWatcher + : NodeWatcher; const extensions = this._options.extensions; const ignorePattern = this._options.ignorePattern; @@ -1113,6 +1109,16 @@ export default class HasteMap extends EventEmitter { ); } + private async _shouldUseWatchman(): Promise { + if (!this._options.useWatchman) { + return false; + } + if (!this._isWatchmanInstalledPromise) { + this._isWatchmanInstalledPromise = isWatchmanInstalled(); + } + return this._isWatchmanInstalledPromise; + } + private _createEmptyMap(): InternalHasteMap { return { clocks: new Map(), diff --git a/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js index f2b142dfc675..84f42db86d28 100644 --- a/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js @@ -6,9 +6,6 @@ */ import {extractor} from '../dependencyExtractor'; -import isRegExpSupported from '../isRegExpSupported'; - -const COMMENT_NO_NEG_LB = isRegExpSupported('(? { it('should not extract dependencies inside comments', () => { @@ -65,8 +62,8 @@ describe('dependencyExtractor', () => { }, depDefault from 'dep4'; // Bad - ${COMMENT_NO_NEG_LB} foo . import ('inv1'); - ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + foo . import ('inv1'); + foo . export ('inv2'); `; expect(extractor.extract(code)).toEqual( new Set(['dep1', 'dep2', 'dep3', 'dep4']), @@ -114,8 +111,8 @@ describe('dependencyExtractor', () => { }, depDefault from 'dep4'; // Bad - ${COMMENT_NO_NEG_LB} foo . export ('inv1'); - ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + foo . export ('inv1'); + foo . export ('inv2'); `; expect(extractor.extract(code)).toEqual( new Set(['dep1', 'dep2', 'dep3', 'dep4']), @@ -137,8 +134,8 @@ describe('dependencyExtractor', () => { }, depDefault from 'dep4'; // Bad - ${COMMENT_NO_NEG_LB} foo . export ('inv1'); - ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + foo . export ('inv1'); + foo . export ('inv2'); `; expect(extractor.extract(code)).toEqual( new Set(['dep1', 'dep2', 'dep3', 'dep4']), @@ -164,7 +161,7 @@ describe('dependencyExtractor', () => { if (await import(\`dep3\`)) {} // Bad - ${COMMENT_NO_NEG_LB} await foo . import('inv1') + await foo . import('inv1') await ximport('inv2'); importx('inv3'); import('inv4', 'inv5'); @@ -182,7 +179,7 @@ describe('dependencyExtractor', () => { if (require(\`dep3\`).cond) {} // Bad - ${COMMENT_NO_NEG_LB} foo . require('inv1') + foo . require('inv1') xrequire('inv2'); requirex('inv3'); require('inv4', 'inv5'); @@ -202,7 +199,7 @@ describe('dependencyExtractor', () => { .requireActual('dep4'); // Bad - ${COMMENT_NO_NEG_LB} foo . jest.requireActual('inv1') + foo . jest.requireActual('inv1') xjest.requireActual('inv2'); jest.requireActualx('inv3'); jest.requireActual('inv4', 'inv5'); @@ -224,7 +221,7 @@ describe('dependencyExtractor', () => { .requireMock('dep4'); // Bad - ${COMMENT_NO_NEG_LB} foo . jest.requireMock('inv1') + foo . jest.requireMock('inv1') xjest.requireMock('inv2'); jest.requireMockx('inv3'); jest.requireMock('inv4', 'inv5'); @@ -246,7 +243,7 @@ describe('dependencyExtractor', () => { .requireMock('dep4'); // Bad - ${COMMENT_NO_NEG_LB} foo . jest.genMockFromModule('inv1') + foo . jest.genMockFromModule('inv1') xjest.genMockFromModule('inv2'); jest.genMockFromModulex('inv3'); jest.genMockFromModule('inv4', 'inv5'); @@ -268,7 +265,7 @@ describe('dependencyExtractor', () => { .requireMock('dep4'); // Bad - ${COMMENT_NO_NEG_LB} foo . jest.createMockFromModule('inv1') + foo . jest.createMockFromModule('inv1') xjest.createMockFromModule('inv2'); jest.createMockFromModulex('inv3'); jest.createMockFromModule('inv4', 'inv5'); diff --git a/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js b/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js deleted file mode 100644 index b27295eaa320..000000000000 --- a/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import isRegExpSupported from '../isRegExpSupported'; - -describe('isRegExpSupported', () => { - it('should return true when passing valid regular expression', () => { - expect(isRegExpSupported('(?:foo|bar)')).toBe(true); - }); - - it('should return false when passing an invalid regular expression', () => { - expect(isRegExpSupported('(?_foo|bar)')).toBe(false); - }); -}); diff --git a/packages/jest-haste-map/src/lib/__tests__/isWatchmanInstalled.test.js b/packages/jest-haste-map/src/lib/__tests__/isWatchmanInstalled.test.js new file mode 100644 index 000000000000..e2f9d35b3800 --- /dev/null +++ b/packages/jest-haste-map/src/lib/__tests__/isWatchmanInstalled.test.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {execFile} from 'child_process'; +import isWatchmanInstalled from '../isWatchmanInstalled'; + +jest.mock('child_process'); + +describe('isWatchmanInstalled', () => { + beforeEach(() => jest.clearAllMocks()); + + it('executes watchman --version and returns true on success', async () => { + execFile.mockImplementation((file, args, cb) => { + expect(file).toBe('watchman'); + expect(args).toStrictEqual(['--version']); + cb(null, {stdout: 'v123'}); + }); + expect(await isWatchmanInstalled()).toBe(true); + expect(execFile).toHaveBeenCalledWith( + 'watchman', + ['--version'], + expect.any(Function), + ); + }); + + it('returns false when execFile fails', async () => { + execFile.mockImplementation((file, args, cb) => { + cb(new Error()); + }); + expect(await isWatchmanInstalled()).toBe(false); + expect(execFile).toHaveBeenCalled(); + }); +}); diff --git a/packages/jest-haste-map/src/lib/dependencyExtractor.ts b/packages/jest-haste-map/src/lib/dependencyExtractor.ts index 8a54303346dd..73dcf0db0e30 100644 --- a/packages/jest-haste-map/src/lib/dependencyExtractor.ts +++ b/packages/jest-haste-map/src/lib/dependencyExtractor.ts @@ -6,12 +6,8 @@ */ import type {DependencyExtractor} from '../types'; -import isRegExpSupported from './isRegExpSupported'; -// Negative look behind is only supported in Node 9+ -const NOT_A_DOT = isRegExpSupported('(? `([\`'"])([^'"\`]*?)(?:\\${pos})`; const WORD_SEPARATOR = '\\b'; diff --git a/packages/jest-haste-map/src/lib/isRegExpSupported.ts b/packages/jest-haste-map/src/lib/isWatchmanInstalled.ts similarity index 57% rename from packages/jest-haste-map/src/lib/isRegExpSupported.ts rename to packages/jest-haste-map/src/lib/isWatchmanInstalled.ts index b8db382928f3..8dee697d61f4 100644 --- a/packages/jest-haste-map/src/lib/isRegExpSupported.ts +++ b/packages/jest-haste-map/src/lib/isWatchmanInstalled.ts @@ -5,10 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -export default function isRegExpSupported(value: string): boolean { +import {execFile} from 'child_process'; +import {promisify} from 'util'; + +export default async function isWatchmanInstalled(): Promise { try { - // eslint-disable-next-line no-new - new RegExp(value); + await promisify(execFile)('watchman', ['--version']); return true; } catch { return false; diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index 9f6d1c8dcf14..c42fe0ac2896 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -1,6 +1,6 @@ { "name": "jest-jasmine2", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,22 +17,22 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/expect": "^28.0.0-alpha.7", - "@jest/source-map": "^28.0.0-alpha.6", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/environment": "^28.0.1", + "@jest/expect": "^28.0.1", + "@jest/source-map": "^28.0.0", + "@jest/test-result": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "is-generator-fn": "^2.0.0", - "jest-each": "^28.0.0-alpha.7", - "jest-matcher-utils": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", - "jest-snapshot": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "pretty-format": "^28.0.0-alpha.7", + "jest-each": "^28.0.1", + "jest-matcher-utils": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-runtime": "^28.0.1", + "jest-snapshot": "^28.0.1", + "jest-util": "^28.0.1", + "pretty-format": "^28.0.1", "throat": "^6.0.1" }, "devDependencies": { diff --git a/packages/jest-jasmine2/src/index.ts b/packages/jest-jasmine2/src/index.ts index d8f1a14bc4f5..cf7c8bf5b4b1 100644 --- a/packages/jest-jasmine2/src/index.ts +++ b/packages/jest-jasmine2/src/index.ts @@ -88,10 +88,12 @@ export default async function jasmine2( environment.global.describe.skip = environment.global.xdescribe; environment.global.describe.only = environment.global.fdescribe; - if (config.timers === 'fake' || config.timers === 'modern') { - environment.fakeTimersModern!.useFakeTimers(); - } else if (config.timers === 'legacy') { - environment.fakeTimers!.useFakeTimers(); + if (config.fakeTimers.enableGlobally) { + if (config.fakeTimers.legacyFakeTimers) { + environment.fakeTimers!.useFakeTimers(); + } else { + environment.fakeTimersModern!.useFakeTimers(); + } } env.beforeEach(() => { @@ -106,7 +108,10 @@ export default async function jasmine2( if (config.resetMocks) { runtime.resetAllMocks(); - if (config.timers === 'legacy') { + if ( + config.fakeTimers.enableGlobally && + config.fakeTimers.legacyFakeTimers + ) { environment.fakeTimers!.useFakeTimers(); } } diff --git a/packages/jest-leak-detector/package.json b/packages/jest-leak-detector/package.json index a28f49446ebc..b8f0cd756109 100644 --- a/packages/jest-leak-detector/package.json +++ b/packages/jest-leak-detector/package.json @@ -1,6 +1,6 @@ { "name": "jest-leak-detector", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,8 +17,8 @@ "./package.json": "./package.json" }, "dependencies": { - "jest-get-type": "^28.0.0-alpha.3", - "pretty-format": "^28.0.0-alpha.7" + "jest-get-type": "^28.0.0", + "pretty-format": "^28.0.1" }, "devDependencies": { "@types/weak-napi": "^2.0.0", diff --git a/packages/jest-matcher-utils/package.json b/packages/jest-matcher-utils/package.json index 5bfe3412db4b..4f1681ea9226 100644 --- a/packages/jest-matcher-utils/package.json +++ b/packages/jest-matcher-utils/package.json @@ -1,7 +1,7 @@ { "name": "jest-matcher-utils", "description": "A set of utility functions for expect and related packages", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -22,12 +22,12 @@ }, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^28.0.0-alpha.7", - "jest-get-type": "^28.0.0-alpha.3", - "pretty-format": "^28.0.0-alpha.7" + "jest-diff": "^28.0.1", + "jest-get-type": "^28.0.0", + "pretty-format": "^28.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@types/node": "*" }, "publishConfig": { diff --git a/packages/jest-message-util/package.json b/packages/jest-message-util/package.json index cbf467bee325..d835f460f3b2 100644 --- a/packages/jest-message-util/package.json +++ b/packages/jest-message-util/package.json @@ -1,6 +1,6 @@ { "name": "jest-message-util", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -21,12 +21,12 @@ }, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^28.0.0-alpha.7", + "pretty-format": "^28.0.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 51f9f2da7bc7..0f6f38c58c47 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -63,8 +63,8 @@ const STACK_PATH_REGEXP = /\s*at.*\(?(\:\d*\:\d*|native)\)?/; const EXEC_ERROR_MESSAGE = 'Test suite failed to run'; const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; -const indentAllLines = (lines: string, indent: string) => - lines.replace(NOT_EMPTY_LINE_REGEXP, indent); +export const indentAllLines = (lines: string): string => + lines.replace(NOT_EMPTY_LINE_REGEXP, MESSAGE_INDENT); const trim = (string: string) => (string || '').trim(); @@ -86,7 +86,7 @@ const getRenderedCallsite = ( {highlightCode: true}, ); - renderedCallsite = indentAllLines(renderedCallsite, MESSAGE_INDENT); + renderedCallsite = indentAllLines(renderedCallsite); renderedCallsite = `\n${renderedCallsite}\n`; return renderedCallsite; @@ -157,7 +157,7 @@ export const formatExecError = ( message = checkForCommonEnvironmentErrors(message); - message = indentAllLines(message, MESSAGE_INDENT); + message = indentAllLines(message); stack = stack && !options.noStackTrace @@ -360,7 +360,7 @@ export const formatResultsErrors = ( formatStackTrace(stack, config, options, testPath), )}\n`; - message = indentAllLines(message, MESSAGE_INDENT); + message = indentAllLines(message); const title = `${chalk.bold.red( TITLE_INDENT + diff --git a/packages/jest-mock/__typetests__/mock-functions.test.ts b/packages/jest-mock/__typetests__/mock-functions.test.ts index e1ace1d254f4..6be5a35b7b77 100644 --- a/packages/jest-mock/__typetests__/mock-functions.test.ts +++ b/packages/jest-mock/__typetests__/mock-functions.test.ts @@ -66,7 +66,8 @@ expectType never>>( ); expectError(fn('moduleName')); -const mockFn = fn((a: string, b?: number) => true); +declare const mockFnImpl: (this: Date, a: string, b?: number) => boolean; +const mockFn = fn(mockFnImpl); const mockAsyncFn = fn(async (p: boolean) => 'value'); expectType(mockFn('one', 2)); @@ -135,6 +136,8 @@ if (returnValue.type === 'throw') { expectType(returnValue.value); } +expectType>(mockFn.mock.contexts); + expectType boolean>>( mockFn.mockClear(), ); diff --git a/packages/jest-mock/__typetests__/tsconfig.json b/packages/jest-mock/__typetests__/tsconfig.json index fe8eab794254..165ba1343021 100644 --- a/packages/jest-mock/__typetests__/tsconfig.json +++ b/packages/jest-mock/__typetests__/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { + "composite": false, "noUnusedLocals": false, "noUnusedParameters": false, "skipLibCheck": true, diff --git a/packages/jest-mock/package.json b/packages/jest-mock/package.json index 99dd15bd7656..6f2f3002e1f1 100644 --- a/packages/jest-mock/package.json +++ b/packages/jest-mock/package.json @@ -1,6 +1,6 @@ { "name": "jest-mock", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,7 +17,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@types/node": "*" }, "devDependencies": { diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts index bbfc2677d2b9..07a4706f29a5 100644 --- a/packages/jest-mock/src/__tests__/index.test.ts +++ b/packages/jest-mock/src/__tests__/index.test.ts @@ -30,6 +30,17 @@ describe('moduleMocker', () => { expect(metadata.name).toBe('x'); }); + it('does not return broken name property', () => { + class By { + static name() { + return 'this is not a name'; + } + } + const metadata = moduleMocker.getMetadata(By); + expect(typeof By.name).toBe('function'); + expect(metadata).not.toHaveProperty('name'); + }); + it('mocks constant values', () => { const metadata = moduleMocker.getMetadata(Symbol.for('bowties.are.cool')); expect(metadata.value).toEqual(Symbol.for('bowties.are.cool')); @@ -424,20 +435,53 @@ describe('moduleMocker', () => { expect(fn.mock.instances[1]).toBe(instance2); }); + it('tracks context objects passed to mock calls', () => { + const fn = moduleMocker.fn(); + expect(fn.mock.instances).toEqual([]); + + const ctx0 = {}; + fn.apply(ctx0, []); + expect(fn.mock.contexts[0]).toBe(ctx0); + + const ctx1 = {}; + fn.call(ctx1); + expect(fn.mock.contexts[1]).toBe(ctx1); + + const ctx2 = {}; + const bound2 = fn.bind(ctx2); + bound2(); + expect(fn.mock.contexts[2]).toBe(ctx2); + + // null context + fn.apply(null, []); // eslint-disable-line no-useless-call + expect(fn.mock.contexts[3]).toBe(null); + fn.call(null); // eslint-disable-line no-useless-call + expect(fn.mock.contexts[4]).toBe(null); + fn.bind(null)(); + expect(fn.mock.contexts[5]).toBe(null); + + // Unspecified context is `undefined` in strict mode (like in this test) and `window` otherwise. + fn(); + expect(fn.mock.contexts[6]).toBe(undefined); + }); + it('supports clearing mock calls', () => { const fn = moduleMocker.fn(); expect(fn.mock.calls).toEqual([]); fn(1, 2, 3); expect(fn.mock.calls).toEqual([[1, 2, 3]]); + expect(fn.mock.contexts).toEqual([undefined]); fn.mockReturnValue('abcd'); fn.mockClear(); expect(fn.mock.calls).toEqual([]); + expect(fn.mock.contexts).toEqual([]); fn('a', 'b', 'c'); expect(fn.mock.calls).toEqual([['a', 'b', 'c']]); + expect(fn.mock.contexts).toEqual([undefined]); expect(fn()).toEqual('abcd'); }); diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 8358cbe36631..b4164f26463c 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -201,6 +201,10 @@ type MockFunctionState = { * List of all the object instances that have been instantiated from the mock. */ instances: Array>; + /** + * List of all the function contexts that have been applied to calls to the mock. + */ + contexts: Array>; /** * List of the call order indexes of the mock. Jest is indexing the order of * invocations of all mocks in a test file. The index is starting with `1`. @@ -569,6 +573,7 @@ export class ModuleMocker { private _defaultMockState(): MockFunctionState { return { calls: [], + contexts: [], instances: [], invocationCallOrder: [], results: [], @@ -636,6 +641,7 @@ export class ModuleMocker { const mockState = mocker._ensureMockState(f); const mockConfig = mocker._ensureMockConfig(f); mockState.instances.push(this); + mockState.contexts.push(this); mockState.calls.push(args); // Create and record an "incomplete" mock result immediately upon // calling rather than waiting for the mock to return. This avoids @@ -965,8 +971,12 @@ export class ModuleMocker { metadata.value = component; return metadata; } else if (type === 'function') { - // @ts-expect-error component is a function so it has a name - metadata.name = component.name; + // @ts-expect-error component is a function so it has a name, but not + // necessarily a string: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#function_names_in_classes + const componentName = component.name; + if (typeof componentName === 'string') { + metadata.name = componentName; + } if (this.isMockFunction(component)) { metadata.mockImpl = component.getMockImplementation() as T; } diff --git a/packages/jest-phabricator/package.json b/packages/jest-phabricator/package.json index 078f3cfd1ff6..b11fb32f2b86 100644 --- a/packages/jest-phabricator/package.json +++ b/packages/jest-phabricator/package.json @@ -1,6 +1,6 @@ { "name": "jest-phabricator", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -15,7 +15,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/test-result": "^28.0.0-alpha.7" + "@jest/test-result": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-regex-util/package.json b/packages/jest-regex-util/package.json index 37712ea167b0..94b8b76e3bb0 100644 --- a/packages/jest-regex-util/package.json +++ b/packages/jest-regex-util/package.json @@ -1,6 +1,6 @@ { "name": "jest-regex-util", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", diff --git a/packages/jest-repl/package.json b/packages/jest-repl/package.json index 7373f5316d88..c74af3542d9c 100644 --- a/packages/jest-repl/package.json +++ b/packages/jest-repl/package.json @@ -1,6 +1,6 @@ { "name": "jest-repl", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -19,15 +19,15 @@ "./bin/jest-runtime-cli": "./bin/jest-runtime-cli.js" }, "dependencies": { - "@jest/console": "^28.0.0-alpha.7", - "@jest/environment": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/console": "^28.0.1", + "@jest/environment": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", "chalk": "^4.0.0", - "jest-config": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-validate": "^28.0.0-alpha.7", + "jest-config": "^28.0.1", + "jest-runtime": "^28.0.1", + "jest-util": "^28.0.1", + "jest-validate": "^28.0.1", "repl": "^0.1.3", "yargs": "^17.3.1" }, diff --git a/packages/jest-repl/src/cli/repl.ts b/packages/jest-repl/src/cli/repl.ts index 94cb896c9e68..757af8df679e 100644 --- a/packages/jest-repl/src/cli/repl.ts +++ b/packages/jest-repl/src/cli/repl.ts @@ -78,7 +78,16 @@ if (jestProjectConfig.transform) { } } if (transformerPath) { - transformer = interopRequireDefault(require(transformerPath)).default; + const transformerOrFactory = interopRequireDefault( + require(transformerPath), + ).default; + + if (typeof transformerOrFactory.createTransformer === 'function') { + transformer = transformerOrFactory.createTransformer(transformerConfig); + } else { + transformer = transformerOrFactory; + } + if (typeof transformer.process !== 'function') { throw new TypeError( 'Jest: a transformer must export a `process` function.', diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index f0e22116a7c1..64a0b428c040 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -1,7 +1,7 @@ { "name": "@jest/reporters", "description": "Jest's reporters", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -13,33 +13,31 @@ }, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.0.0-alpha.7", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/console": "^28.0.1", + "@jest/test-result": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", + "@jridgewell/trace-mapping": "^0.3.7", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", - "glob": "^7.1.2", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^5.1.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-resolve": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-worker": "^28.0.0-alpha.7", + "jest-util": "^28.0.1", + "jest-worker": "^28.0.1", "slash": "^3.0.0", - "source-map": "^0.6.0", "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" + "v8-to-istanbul": "^9.0.0" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@types/exit": "^0.1.30", "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.3", @@ -49,6 +47,7 @@ "@types/istanbul-lib-source-maps": "^4.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node-notifier": "^8.0.0", + "jest-resolve": "^28.0.1", "mock-fs": "^5.1.2", "strip-ansi": "^6.0.0" }, @@ -65,7 +64,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/facebook/jest", + "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-reporters" }, "bugs": { diff --git a/packages/jest-reporters/src/BaseReporter.ts b/packages/jest-reporters/src/BaseReporter.ts index 4520c3e7146c..8f526555b12d 100644 --- a/packages/jest-reporters/src/BaseReporter.ts +++ b/packages/jest-reporters/src/BaseReporter.ts @@ -7,11 +7,13 @@ import type { AggregatedResult, + Test, TestCaseResult, + TestContext, TestResult, } from '@jest/test-result'; import {preRunMessage} from 'jest-util'; -import type {Context, Reporter, ReporterOnStartOptions, Test} from './types'; +import type {Reporter, ReporterOnStartOptions} from './types'; const {remove: preRunMessageRemove} = preRunMessage; @@ -40,7 +42,7 @@ export default class BaseReporter implements Reporter { onTestStart(_test?: Test): void {} onRunComplete( - _contexts?: Set, + _testContexts?: Set, _aggregatedResults?: AggregatedResult, ): Promise | void {} diff --git a/packages/jest-reporters/src/CoverageReporter.ts b/packages/jest-reporters/src/CoverageReporter.ts index 63da91389719..203435f08f23 100644 --- a/packages/jest-reporters/src/CoverageReporter.ts +++ b/packages/jest-reporters/src/CoverageReporter.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import {mergeProcessCovs} from '@bcoe/v8-coverage'; +import type {EncodedSourceMap} from '@jridgewell/trace-mapping'; import chalk = require('chalk'); import glob = require('glob'); import * as fs from 'graceful-fs'; @@ -14,11 +15,12 @@ import istanbulCoverage = require('istanbul-lib-coverage'); import istanbulReport = require('istanbul-lib-report'); import libSourceMaps = require('istanbul-lib-source-maps'); import istanbulReports = require('istanbul-reports'); -import type {RawSourceMap} from 'source-map'; import v8toIstanbul = require('v8-to-istanbul'); import type { AggregatedResult, RuntimeTransformResult, + Test, + TestContext, TestResult, V8CoverageResult, } from '@jest/test-result'; @@ -27,44 +29,30 @@ import {clearLine, isInteractive} from 'jest-util'; import {Worker} from 'jest-worker'; import BaseReporter from './BaseReporter'; import getWatermarks from './getWatermarks'; -import type { - Context, - CoverageReporterOptions, - CoverageWorker, - Test, -} from './types'; - -// This is fixed in a newer versions of source-map, but our dependencies are still stuck on old versions -interface FixedRawSourceMap extends Omit { - version: number; - file?: string; -} +import type {CoverageWorker, ReporterContext} from './types'; const FAIL_COLOR = chalk.bold.red; const RUNNING_TEST_COLOR = chalk.bold.dim; export default class CoverageReporter extends BaseReporter { + private _context: ReporterContext; private _coverageMap: istanbulCoverage.CoverageMap; private _globalConfig: Config.GlobalConfig; private _sourceMapStore: libSourceMaps.MapStore; - private _options: CoverageReporterOptions; private _v8CoverageResults: Array; static readonly filename = __filename; - constructor( - globalConfig: Config.GlobalConfig, - options?: CoverageReporterOptions, - ) { + constructor(globalConfig: Config.GlobalConfig, context: ReporterContext) { super(); + this._context = context; this._coverageMap = istanbulCoverage.createCoverageMap({}); this._globalConfig = globalConfig; this._sourceMapStore = libSourceMaps.createSourceMapStore(); this._v8CoverageResults = []; - this._options = options || {}; } - onTestResult(_test: Test, testResult: TestResult): void { + override onTestResult(_test: Test, testResult: TestResult): void { if (testResult.v8Coverage) { this._v8CoverageResults.push(testResult.v8Coverage); return; @@ -75,11 +63,11 @@ export default class CoverageReporter extends BaseReporter { } } - async onRunComplete( - contexts: Set, + override async onRunComplete( + testContexts: Set, aggregatedResults: AggregatedResult, ): Promise { - await this._addUntestedFiles(contexts); + await this._addUntestedFiles(testContexts); const {map, reportContext} = await this._getCoverageResult(); try { @@ -114,10 +102,12 @@ export default class CoverageReporter extends BaseReporter { this._checkThreshold(map); } - private async _addUntestedFiles(contexts: Set): Promise { + private async _addUntestedFiles( + testContexts: Set, + ): Promise { const files: Array<{config: Config.ProjectConfig; path: string}> = []; - contexts.forEach(context => { + testContexts.forEach(context => { const config = context.config; if ( this._globalConfig.collectCoverageFrom && @@ -154,6 +144,8 @@ export default class CoverageReporter extends BaseReporter { } else { worker = new Worker(require.resolve('./CoverageWorker'), { exposedMethods: ['worker'], + // @ts-expect-error: option does not exist on the node 12 types + forkOptions: {serialization: 'json'}, maxRetries: 2, numWorkers: this._globalConfig.maxWorkers, }); @@ -175,16 +167,15 @@ export default class CoverageReporter extends BaseReporter { try { const result = await worker.worker({ config, - globalConfig: this._globalConfig, - options: { - ...this._options, + context: { changedFiles: - this._options.changedFiles && - Array.from(this._options.changedFiles), + this._context.changedFiles && + Array.from(this._context.changedFiles), sourcesRelatedToTestsInChangedFiles: - this._options.sourcesRelatedToTestsInChangedFiles && - Array.from(this._options.sourcesRelatedToTestsInChangedFiles), + this._context.sourcesRelatedToTestsInChangedFiles && + Array.from(this._context.sourcesRelatedToTestsInChangedFiles), }, + globalConfig: this._globalConfig, path: filename, }); @@ -442,7 +433,7 @@ export default class CoverageReporter extends BaseReporter { mergedCoverages.result.map(async res => { const fileTransform = fileTransforms.get(res.url); - let sourcemapContent: FixedRawSourceMap | undefined = undefined; + let sourcemapContent: EncodedSourceMap | undefined = undefined; if ( fileTransform?.sourceMapPath && @@ -473,8 +464,6 @@ export default class CoverageReporter extends BaseReporter { const istanbulData = converter.toIstanbul(); - converter.destroy(); - return istanbulData; }), ); diff --git a/packages/jest-reporters/src/CoverageWorker.ts b/packages/jest-reporters/src/CoverageWorker.ts index b7f9741c0320..15c32e4f551a 100644 --- a/packages/jest-reporters/src/CoverageWorker.ts +++ b/packages/jest-reporters/src/CoverageWorker.ts @@ -11,17 +11,26 @@ import type {Config} from '@jest/types'; import generateEmptyCoverage, { CoverageWorkerResult, } from './generateEmptyCoverage'; -import type {CoverageReporterSerializedOptions} from './types'; +import type {ReporterContext} from './types'; + +type SerializeSet = T extends Set ? Array : T; + +type CoverageReporterContext = Pick< + ReporterContext, + 'changedFiles' | 'sourcesRelatedToTestsInChangedFiles' +>; + +type CoverageReporterSerializedContext = { + [K in keyof CoverageReporterContext]: SerializeSet; +}; export type CoverageWorkerData = { - globalConfig: Config.GlobalConfig; config: Config.ProjectConfig; + context: CoverageReporterSerializedContext; + globalConfig: Config.GlobalConfig; path: string; - options?: CoverageReporterSerializedOptions; }; -export type {CoverageWorkerResult}; - // Make sure uncaught errors are logged before we exit. process.on('uncaughtException', err => { console.error(err.stack); @@ -32,15 +41,15 @@ export function worker({ config, globalConfig, path, - options, + context, }: CoverageWorkerData): Promise { return generateEmptyCoverage( fs.readFileSync(path, 'utf8'), path, globalConfig, config, - options?.changedFiles && new Set(options.changedFiles), - options?.sourcesRelatedToTestsInChangedFiles && - new Set(options.sourcesRelatedToTestsInChangedFiles), + context.changedFiles && new Set(context.changedFiles), + context.sourcesRelatedToTestsInChangedFiles && + new Set(context.sourcesRelatedToTestsInChangedFiles), ); } diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index 19a46dac5ab0..432073401f3d 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -9,16 +9,22 @@ import chalk = require('chalk'); import {getConsoleOutput} from '@jest/console'; import type { AggregatedResult, + Test, TestCaseResult, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; +import { + formatStackTrace, + indentAllLines, + separateMessageFromStack, +} from 'jest-message-util'; import {clearLine, isInteractive} from 'jest-util'; import BaseReporter from './BaseReporter'; import Status from './Status'; import getResultHeader from './getResultHeader'; import getSnapshotStatus from './getSnapshotStatus'; -import type {ReporterOnStartOptions, Test} from './types'; +import type {ReporterOnStartOptions} from './types'; type write = NodeJS.WriteStream['write']; type FlushBufferedOutput = () => void; @@ -127,22 +133,22 @@ export default class DefaultReporter extends BaseReporter { } } - onRunStart( + override onRunStart( aggregatedResults: AggregatedResult, options: ReporterOnStartOptions, ): void { this._status.runStarted(aggregatedResults, options); } - onTestStart(test: Test): void { + override onTestStart(test: Test): void { this._status.testStarted(test.path, test.context.config); } - onTestCaseResult(test: Test, testCaseResult: TestCaseResult): void { + override onTestCaseResult(test: Test, testCaseResult: TestCaseResult): void { this._status.addTestCaseResult(test, testCaseResult); } - onRunComplete(): void { + override onRunComplete(): void { this.forceFlushBufferedOutput(); this._status.runFinished(); process.stdout.write = this._out; @@ -150,7 +156,7 @@ export default class DefaultReporter extends BaseReporter { clearLine(process.stderr); } - onTestResult( + override onTestResult( test: Test, testResult: TestResult, aggregatedResults: AggregatedResult, @@ -180,10 +186,37 @@ export default class DefaultReporter extends BaseReporter { } printTestFileHeader( - _testPath: string, + testPath: string, config: Config.ProjectConfig, result: TestResult, ): void { + // log retry errors if any exist + result.testResults.forEach(testResult => { + const testRetryReasons = testResult.retryReasons; + if (testRetryReasons && testRetryReasons.length > 0) { + this.log( + `${chalk.reset.inverse.bold.yellow( + ' LOGGING RETRY ERRORS ', + )} ${chalk.bold(testResult.fullName)}`, + ); + testRetryReasons.forEach((retryReasons, index) => { + let {message, stack} = separateMessageFromStack(retryReasons); + stack = this._globalConfig.noStackTrace + ? '' + : chalk.dim( + formatStackTrace(stack, config, this._globalConfig, testPath), + ); + + message = indentAllLines(message); + + this.log( + `${chalk.reset.inverse.bold.blueBright(` RETRY ${index + 1} `)}\n`, + ); + this.log(`${message}\n${stack}\n`); + }); + } + }); + this.log(getResultHeader(result, this._globalConfig, config)); if (result.console) { this.log( diff --git a/packages/jest-reporters/src/GitHubActionsReporter.ts b/packages/jest-reporters/src/GitHubActionsReporter.ts new file mode 100644 index 000000000000..7cff405fe962 --- /dev/null +++ b/packages/jest-reporters/src/GitHubActionsReporter.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import stripAnsi = require('strip-ansi'); +import type { + AggregatedResult, + TestContext, + TestResult, +} from '@jest/test-result'; +import BaseReporter from './BaseReporter'; + +const lineAndColumnInStackTrace = /^.*?:([0-9]+):([0-9]+).*$/; + +function replaceEntities(s: string): string { + // https://github.com/actions/toolkit/blob/b4639928698a6bfe1c4bdae4b2bfdad1cb75016d/packages/core/src/command.ts#L80-L85 + const substitutions: Array<[RegExp, string]> = [ + [/%/g, '%25'], + [/\r/g, '%0D'], + [/\n/g, '%0A'], + ]; + return substitutions.reduce((acc, sub) => acc.replace(...sub), s); +} + +export default class GitHubActionsReporter extends BaseReporter { + static readonly filename = __filename; + + override onRunComplete( + _testContexts?: Set, + aggregatedResults?: AggregatedResult, + ): void { + const messages = getMessages(aggregatedResults?.testResults); + + for (const message of messages) { + this.log(message); + } + } +} + +function getMessages(results: Array | undefined) { + if (!results) return []; + + return results.flatMap(({testFilePath, testResults}) => + testResults + .filter(r => r.status === 'failed') + .flatMap(r => r.failureMessages) + .map(m => stripAnsi(m)) + .map(m => replaceEntities(m)) + .map(m => lineAndColumnInStackTrace.exec(m)) + .filter((m): m is RegExpExecArray => m !== null) + .map( + ([message, line, col]) => + `\n::error file=${testFilePath},line=${line},col=${col}::${message}`, + ), + ); +} diff --git a/packages/jest-reporters/src/NotifyReporter.ts b/packages/jest-reporters/src/NotifyReporter.ts index 4bc77ac48999..337022af8916 100644 --- a/packages/jest-reporters/src/NotifyReporter.ts +++ b/packages/jest-reporters/src/NotifyReporter.ts @@ -8,11 +8,11 @@ import * as path from 'path'; import * as util from 'util'; import exit = require('exit'); -import type {AggregatedResult} from '@jest/test-result'; +import type {AggregatedResult, TestContext} from '@jest/test-result'; import type {Config} from '@jest/types'; import {pluralize} from 'jest-util'; import BaseReporter from './BaseReporter'; -import type {Context, TestSchedulerContext} from './types'; +import type {ReporterContext} from './types'; const isDarwin = process.platform === 'darwin'; @@ -20,28 +20,25 @@ const icon = path.resolve(__dirname, '../assets/jest_logo.png'); export default class NotifyReporter extends BaseReporter { private _notifier = loadNotifier(); - private _startRun: (globalConfig: Config.GlobalConfig) => unknown; private _globalConfig: Config.GlobalConfig; - private _context: TestSchedulerContext; + private _context: ReporterContext; static readonly filename = __filename; - constructor( - globalConfig: Config.GlobalConfig, - startRun: (globalConfig: Config.GlobalConfig) => unknown, - context: TestSchedulerContext, - ) { + constructor(globalConfig: Config.GlobalConfig, context: ReporterContext) { super(); this._globalConfig = globalConfig; - this._startRun = startRun; this._context = context; } - onRunComplete(contexts: Set, result: AggregatedResult): void { + override onRunComplete( + testContexts: Set, + result: AggregatedResult, + ): void { const success = result.numFailedTests === 0 && result.numRuntimeErrorTestSuites === 0; - const firstContext = contexts.values().next(); + const firstContext = testContexts.values().next(); const hasteFS = firstContext && firstContext.value && firstContext.value.hasteFS; @@ -142,8 +139,11 @@ export default class NotifyReporter extends BaseReporter { exit(0); return; } - if (metadata.activationValue === restartAnswer) { - this._startRun(this._globalConfig); + if ( + metadata.activationValue === restartAnswer && + this._context.startRun + ) { + this._context.startRun(this._globalConfig); } }, ); diff --git a/packages/jest-reporters/src/Status.ts b/packages/jest-reporters/src/Status.ts index ba57f2883418..e357b0d9ddc0 100644 --- a/packages/jest-reporters/src/Status.ts +++ b/packages/jest-reporters/src/Status.ts @@ -9,11 +9,12 @@ import chalk = require('chalk'); import stringLength = require('string-length'); import type { AggregatedResult, + Test, TestCaseResult, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; -import type {ReporterOnStartOptions, Test} from './types'; +import type {ReporterOnStartOptions} from './types'; import { getSummary, printDisplayName, diff --git a/packages/jest-reporters/src/SummaryReporter.ts b/packages/jest-reporters/src/SummaryReporter.ts index c46854a4b83f..cef51dda1b36 100644 --- a/packages/jest-reporters/src/SummaryReporter.ts +++ b/packages/jest-reporters/src/SummaryReporter.ts @@ -6,13 +6,17 @@ */ import chalk = require('chalk'); -import type {AggregatedResult, SnapshotSummary} from '@jest/test-result'; +import type { + AggregatedResult, + SnapshotSummary, + TestContext, +} from '@jest/test-result'; import type {Config} from '@jest/types'; import {testPathPatternToRegExp} from 'jest-util'; import BaseReporter from './BaseReporter'; import getResultHeader from './getResultHeader'; import getSnapshotSummary from './getSnapshotSummary'; -import type {Context, ReporterOnStartOptions} from './types'; +import type {ReporterOnStartOptions} from './types'; import {getSummary} from './utils'; const TEST_SUMMARY_THRESHOLD = 20; @@ -70,7 +74,7 @@ export default class SummaryReporter extends BaseReporter { } } - onRunStart( + override onRunStart( aggregatedResults: AggregatedResult, options: ReporterOnStartOptions, ): void { @@ -78,8 +82,8 @@ export default class SummaryReporter extends BaseReporter { this._estimatedTime = options.estimatedTime; } - onRunComplete( - contexts: Set, + override onRunComplete( + testContexts: Set, aggregatedResults: AggregatedResult, ): void { const {numTotalTestSuites, testResults, wasInterrupted} = aggregatedResults; @@ -111,7 +115,7 @@ export default class SummaryReporter extends BaseReporter { message += `\n${ wasInterrupted ? chalk.bold.red('Test run was interrupted.') - : this._getTestSummary(contexts, this._globalConfig) + : this._getTestSummary(testContexts, this._globalConfig) }`; } this.log(message); @@ -188,7 +192,7 @@ export default class SummaryReporter extends BaseReporter { } private _getTestSummary( - contexts: Set, + testContexts: Set, globalConfig: Config.GlobalConfig, ) { const getMatchingTestsInfo = () => { @@ -223,8 +227,8 @@ export default class SummaryReporter extends BaseReporter { } const contextInfo = - contexts.size > 1 - ? chalk.dim(' in ') + contexts.size + chalk.dim(' projects') + testContexts.size > 1 + ? chalk.dim(' in ') + testContexts.size + chalk.dim(' projects') : ''; return ( diff --git a/packages/jest-reporters/src/VerboseReporter.ts b/packages/jest-reporters/src/VerboseReporter.ts index 4476e895d98d..f7675355fa45 100644 --- a/packages/jest-reporters/src/VerboseReporter.ts +++ b/packages/jest-reporters/src/VerboseReporter.ts @@ -10,19 +10,19 @@ import type { AggregatedResult, AssertionResult, Suite, + Test, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; import {formatTime, specialChars} from 'jest-util'; import DefaultReporter from './DefaultReporter'; -import type {Test} from './types'; const {ICONS} = specialChars; export default class VerboseReporter extends DefaultReporter { - protected _globalConfig: Config.GlobalConfig; + protected override _globalConfig: Config.GlobalConfig; - static readonly filename = __filename; + static override readonly filename = __filename; constructor(globalConfig: Config.GlobalConfig) { super(globalConfig); @@ -31,7 +31,7 @@ export default class VerboseReporter extends DefaultReporter { // Verbose mode is for debugging. Buffering of output is undesirable. // See https://github.com/facebook/jest/issues/8208 - protected __wrapStdio( + protected override __wrapStdio( stream: NodeJS.WritableStream | NodeJS.WriteStream, ): void { const write = stream.write.bind(stream); @@ -71,7 +71,7 @@ export default class VerboseReporter extends DefaultReporter { return root; } - onTestResult( + override onTestResult( test: Test, result: TestResult, aggregatedResults: AggregatedResult, diff --git a/packages/jest-reporters/src/__tests__/CoverageWorker.test.js b/packages/jest-reporters/src/__tests__/CoverageWorker.test.js index d8b83408f52e..e83b1f13287c 100644 --- a/packages/jest-reporters/src/__tests__/CoverageWorker.test.js +++ b/packages/jest-reporters/src/__tests__/CoverageWorker.test.js @@ -11,7 +11,8 @@ jest.mock('graceful-fs').mock('../generateEmptyCoverage'); const globalConfig = {collectCoverage: true}; const config = {}; -const workerOptions = {config, globalConfig, path: 'banana.js'}; +const context = {}; +const workerOptions = {config, context, globalConfig, path: 'banana.js'}; let fs; let generateEmptyCoverage; diff --git a/packages/jest-reporters/src/__tests__/DefaultReporter.test.js b/packages/jest-reporters/src/__tests__/DefaultReporter.test.js index 8bf82aacb72f..addcfa415948 100644 --- a/packages/jest-reporters/src/__tests__/DefaultReporter.test.js +++ b/packages/jest-reporters/src/__tests__/DefaultReporter.test.js @@ -30,6 +30,7 @@ const testResult = { updated: 0, }, testFilePath: '/foo', + testResults: [], }; let stdout; diff --git a/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.js b/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.js new file mode 100644 index 000000000000..b7939b579458 --- /dev/null +++ b/packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.js @@ -0,0 +1,118 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +let GitHubActionsReporter; + +const write = process.stderr.write; +const globalConfig = { + rootDir: 'root', + watch: false, +}; + +let results = []; + +function requireReporter() { + jest.isolateModules(() => { + GitHubActionsReporter = require('../GitHubActionsReporter').default; + }); +} + +beforeEach(() => { + process.stderr.write = result => results.push(result); +}); + +afterEach(() => { + results = []; + process.stderr.write = write; +}); + +const aggregatedResults = { + numFailedTestSuites: 1, + numFailedTests: 1, + numPassedTestSuites: 0, + numTotalTestSuites: 1, + numTotalTests: 1, + snapshot: { + added: 0, + didUpdate: false, + failure: false, + filesAdded: 0, + filesRemoved: 0, + filesRemovedList: [], + filesUnmatched: 0, + filesUpdated: 0, + matched: 0, + total: 0, + unchecked: 0, + uncheckedKeysByFile: [], + unmatched: 0, + updated: 0, + }, + startTime: 0, + success: false, + testResults: [ + { + numFailingTests: 1, + numPassingTests: 0, + numPendingTests: 0, + numTodoTests: 0, + openHandles: [], + perfStats: { + end: 1234, + runtime: 1234, + slow: false, + start: 0, + }, + skipped: false, + snapshot: { + added: 0, + fileDeleted: false, + matched: 0, + unchecked: 0, + uncheckedKeys: [], + unmatched: 0, + updated: 0, + }, + testFilePath: '/home/runner/work/jest/jest/some.test.js', + testResults: [ + { + ancestorTitles: [Array], + duration: 7, + failureDetails: [Array], + failureMessages: [ + ` + Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n + \n + Expected: \u001b[32m\"b\"\u001b[39m\n + Received: \u001b[31m\"a\"\u001b[39m\n + at Object. (/home/runner/work/jest/jest/some.test.js:4:17)\n + at Object.asyncJestTest (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)\n + at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:45:12\n + at new Promise ()\n + at mapper (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:28:19)\n + at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:75:41\n + at processTicksAndRejections (internal/process/task_queues.js:93:5) + `, + ], + fullName: 'asserts that a === b', + location: null, + numPassingAsserts: 0, + status: 'failed', + title: 'asserts that a === b', + }, + ], + }, + ], +}; + +test('reporter extracts the correct filename, line, and column', () => { + requireReporter(); + const testReporter = new GitHubActionsReporter(globalConfig); + testReporter.onRunComplete(new Set(), aggregatedResults); + expect(results.join('').replace(/\\/g, '/')).toMatchSnapshot(); +}); diff --git a/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts b/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts index 3e9a22ea03fa..bc59000d41f8 100644 --- a/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts +++ b/packages/jest-reporters/src/__tests__/NotifyReporter.test.ts @@ -5,20 +5,22 @@ * LICENSE file in the root directory of this source tree. */ -import type {AggregatedResult} from '@jest/test-result'; +import type {AggregatedResult, TestContext} from '@jest/test-result'; import {makeGlobalConfig} from '@jest/test-utils'; import type {Config} from '@jest/types'; import Resolver from 'jest-resolve'; import NotifyReporter from '../NotifyReporter'; +import type {ReporterContext} from '../types'; jest.mock('../DefaultReporter'); jest.mock('node-notifier', () => ({ notify: jest.fn(), })); -const initialContext = { +const initialContext: ReporterContext = { firstRun: true, previousSuccess: false, + startRun: () => {}, }; const aggregatedResultsSuccess = { @@ -78,20 +80,20 @@ const testModes = ({ Partial>) => { const notify = require('node-notifier'); - const config = makeGlobalConfig({notify: true, notifyMode, rootDir}); + const globalConfig = makeGlobalConfig({notify: true, notifyMode, rootDir}); let previousContext = initialContext; arl.forEach((ar, i) => { - const newContext = Object.assign(previousContext, { + const newContext: ReporterContext = Object.assign(previousContext, { firstRun: i === 0, previousSuccess: previousContext.previousSuccess, }); - const reporter = new NotifyReporter(config, () => {}, newContext); + const reporter = new NotifyReporter(globalConfig, newContext); previousContext = newContext; - const contexts = new Set(); + const testContexts = new Set(); if (moduleName != null) { - contexts.add({ + testContexts.add({ hasteFS: { getModuleName() { return moduleName; @@ -101,10 +103,10 @@ const testModes = ({ return ['package.json']; }, }, - }); + } as unknown as TestContext); } - reporter.onRunComplete(contexts, ar); + reporter.onRunComplete(testContexts, ar); if (ar.numTotalTests === 0) { expect(notify.notify).not.toHaveBeenCalled(); @@ -214,12 +216,12 @@ describe('node-notifier is an optional dependency', () => { }); const ctor = () => { - const config = makeGlobalConfig({ + const globalConfig = makeGlobalConfig({ notify: true, notifyMode: 'success', rootDir: 'some-test', }); - return new NotifyReporter(config, () => {}, initialContext); + return new NotifyReporter(globalConfig, initialContext); }; test('without node-notifier uses mock function that throws an error', () => { diff --git a/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.js.snap new file mode 100644 index 000000000000..0deced8ea4cf --- /dev/null +++ b/packages/jest-reporters/src/__tests__/__snapshots__/GitHubActionsReporter.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`reporter extracts the correct filename, line, and column 1`] = ` +" +::error file=/home/runner/work/jest/jest/some.test.js,line=4,col=17::%0A Error: expect(received).toBe(expected) // Object.is equality%0A%0A %0A%0A Expected: "b"%0A%0A Received: "a"%0A%0A at Object. (/home/runner/work/jest/jest/some.test.js:4:17)%0A%0A at Object.asyncJestTest (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)%0A%0A at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:45:12%0A%0A at new Promise ()%0A%0A at mapper (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:28:19)%0A%0A at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:75:41%0A%0A at processTicksAndRejections (internal/process/task_queues.js:93:5)%0A +" +`; diff --git a/packages/jest-reporters/src/index.ts b/packages/jest-reporters/src/index.ts index 9185df007f1b..f536c362a7f8 100644 --- a/packages/jest-reporters/src/index.ts +++ b/packages/jest-reporters/src/index.ts @@ -6,6 +6,8 @@ */ import getResultHeader from './getResultHeader'; +import getSnapshotStatus from './getSnapshotStatus'; +import getSnapshotSummary from './getSnapshotSummary'; import { formatTestPath, getSummary, @@ -14,28 +16,33 @@ import { trimAndFormatPath, } from './utils'; -export type {Config} from '@jest/types'; export type { AggregatedResult, SnapshotSummary, + Test, + TestCaseResult, + TestContext, TestResult, } from '@jest/test-result'; +export type {Config} from '@jest/types'; export {default as BaseReporter} from './BaseReporter'; export {default as CoverageReporter} from './CoverageReporter'; export {default as DefaultReporter} from './DefaultReporter'; export {default as NotifyReporter} from './NotifyReporter'; export {default as SummaryReporter} from './SummaryReporter'; export {default as VerboseReporter} from './VerboseReporter'; +export {default as GitHubActionsReporter} from './GitHubActionsReporter'; export type { - Context, Reporter, ReporterOnStartOptions, + ReporterContext, SummaryOptions, - Test, } from './types'; export const utils = { formatTestPath, getResultHeader, + getSnapshotStatus, + getSnapshotSummary, getSummary, printDisplayName, relativePath, diff --git a/packages/jest-reporters/src/types.ts b/packages/jest-reporters/src/types.ts index 693454d007af..0eb323ff334d 100644 --- a/packages/jest-reporters/src/types.ts +++ b/packages/jest-reporters/src/types.ts @@ -7,13 +7,12 @@ import type { AggregatedResult, - SerializableError, + Test, TestCaseResult, + TestContext, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; -import type {FS as HasteFS, ModuleMap} from 'jest-haste-map'; -import type Resolver from 'jest-resolve'; import type {worker} from './CoverageWorker'; export type ReporterOnStartOptions = { @@ -21,41 +20,8 @@ export type ReporterOnStartOptions = { showStatus: boolean; }; -export type Context = { - config: Config.ProjectConfig; - hasteFS: HasteFS; - moduleMap: ModuleMap; - resolver: Resolver; -}; - -export type Test = { - context: Context; - duration?: number; - path: string; -}; - export type CoverageWorker = {worker: typeof worker}; -export type CoverageReporterOptions = { - changedFiles?: Set; - sourcesRelatedToTestsInChangedFiles?: Set; -}; - -export type CoverageReporterSerializedOptions = { - changedFiles?: Array; - sourcesRelatedToTestsInChangedFiles?: Array; -}; - -export type OnTestStart = (test: Test) => Promise; -export type OnTestFailure = ( - test: Test, - error: SerializableError, -) => Promise; -export type OnTestSuccess = ( - test: Test, - result: TestResult, -) => Promise; - export interface Reporter { readonly onTestResult?: ( test: Test, @@ -78,30 +44,23 @@ export interface Reporter { readonly onTestStart?: (test: Test) => Promise | void; readonly onTestFileStart?: (test: Test) => Promise | void; readonly onRunComplete: ( - contexts: Set, + testContexts: Set, results: AggregatedResult, ) => Promise | void; readonly getLastError: () => Error | void; } +export type ReporterContext = { + firstRun: boolean; + previousSuccess: boolean; + changedFiles?: Set; + sourcesRelatedToTestsInChangedFiles?: Set; + startRun?: (globalConfig: Config.GlobalConfig) => unknown; +}; + export type SummaryOptions = { currentTestCases?: Array<{test: Test; testCaseResult: TestCaseResult}>; estimatedTime?: number; roundTime?: boolean; width?: number; }; - -export type TestRunnerOptions = { - serial: boolean; -}; - -export type TestRunData = Array<{ - context: Context; - matches: {allTests: number; tests: Array; total: number}; -}>; - -export type TestSchedulerContext = { - firstRun: boolean; - previousSuccess: boolean; - changedFiles?: Set; -}; diff --git a/packages/jest-reporters/src/utils.ts b/packages/jest-reporters/src/utils.ts index 7701a3283a1a..158baaf9e6cd 100644 --- a/packages/jest-reporters/src/utils.ts +++ b/packages/jest-reporters/src/utils.ts @@ -8,10 +8,10 @@ import * as path from 'path'; import chalk = require('chalk'); import slash = require('slash'); -import type {AggregatedResult, TestCaseResult} from '@jest/test-result'; +import type {AggregatedResult, Test, TestCaseResult} from '@jest/test-result'; import type {Config} from '@jest/types'; import {formatTime, pluralize} from 'jest-util'; -import type {SummaryOptions, Test} from './types'; +import type {SummaryOptions} from './types'; const PROGRESS_BAR_WIDTH = 40; diff --git a/packages/jest-reporters/tsconfig.json b/packages/jest-reporters/tsconfig.json index d5c6ecc17d98..406051cc2489 100644 --- a/packages/jest-reporters/tsconfig.json +++ b/packages/jest-reporters/tsconfig.json @@ -8,7 +8,6 @@ "exclude": ["./**/__tests__/**/*"], "references": [ {"path": "../jest-console"}, - {"path": "../jest-haste-map"}, {"path": "../jest-resolve"}, {"path": "../jest-test-result"}, {"path": "../jest-transform"}, diff --git a/packages/jest-resolve-dependencies/package.json b/packages/jest-resolve-dependencies/package.json index d8c59f7dca86..cd20756e3024 100644 --- a/packages/jest-resolve-dependencies/package.json +++ b/packages/jest-resolve-dependencies/package.json @@ -1,6 +1,6 @@ { "name": "jest-resolve-dependencies", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,15 +17,15 @@ "./package.json": "./package.json" }, "dependencies": { - "jest-regex-util": "^28.0.0-alpha.6", - "jest-snapshot": "^28.0.0-alpha.7" + "jest-regex-util": "^28.0.0", + "jest-snapshot": "^28.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-resolve": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7" + "@jest/test-utils": "^28.0.1", + "@jest/types": "^28.0.1", + "jest-haste-map": "^28.0.1", + "jest-resolve": "^28.0.1", + "jest-runtime": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-resolve/__typetests__/resolver.test.ts b/packages/jest-resolve/__typetests__/resolver.test.ts new file mode 100644 index 000000000000..a6ef5cfe00a9 --- /dev/null +++ b/packages/jest-resolve/__typetests__/resolver.test.ts @@ -0,0 +1,108 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {expectAssignable, expectError, expectType} from 'tsd-lite'; +import type { + AsyncResolver, + JestResolver, + PackageFilter, + PackageJSON, + PathFilter, + ResolverOptions, + SyncResolver, +} from 'jest-resolve'; + +// PackageJSON + +expectAssignable({ + caption: 'test', + count: 100, + isTest: true, + location: {name: 'test', start: [1, 2], valid: false, x: 10, y: 20}, + values: [0, 10, 20, {x: 1, y: 2}, true, 'test', ['a', 'b']], +}); + +expectError({ + filter: () => {}, +}); + +// PackageFilter + +const packageFilter = (pkg: PackageJSON, file: string, dir: string) => pkg; + +expectAssignable(packageFilter); + +// PathFilter + +const pathFilter = (pkg: PackageJSON, path: string, relativePath: string) => + relativePath; + +expectAssignable(pathFilter); + +// ResolverOptions + +function customSyncResolver(path: string, options: ResolverOptions): string { + return path; +} +expectAssignable(customSyncResolver); + +async function customAsyncResolver( + path: string, + options: ResolverOptions, +): Promise { + return path; +} +expectAssignable(customAsyncResolver); + +// AsyncResolver + +const asyncResolver: AsyncResolver = async (path, options) => { + expectType(path); + + expectType(options.basedir); + expectType | undefined>(options.conditions); + expectType(options.defaultResolver); + expectType | undefined>(options.extensions); + expectType | undefined>(options.moduleDirectory); + expectType(options.packageFilter); + expectType(options.pathFilter); + expectType | undefined>(options.paths); + expectType(options.rootDir); + + return path; +}; + +const notReturningAsyncResolver = async () => {}; +expectError(notReturningAsyncResolver()); + +// SyncResolver + +const syncResolver: SyncResolver = (path, options) => { + expectType(path); + + expectType(options.basedir); + expectType | undefined>(options.conditions); + expectType(options.defaultResolver); + expectType | undefined>(options.extensions); + expectType | undefined>(options.moduleDirectory); + expectType(options.packageFilter); + expectType(options.pathFilter); + expectType | undefined>(options.paths); + expectType(options.rootDir); + + return path; +}; + +const notReturningSyncResolver = () => {}; +expectError(notReturningSyncResolver()); + +// JestResolver + +expectAssignable({async: asyncResolver}); +expectAssignable({sync: syncResolver}); +expectAssignable({async: asyncResolver, sync: syncResolver}); +expectError({}); diff --git a/packages/jest-resolve/__typetests__/tsconfig.json b/packages/jest-resolve/__typetests__/tsconfig.json new file mode 100644 index 000000000000..165ba1343021 --- /dev/null +++ b/packages/jest-resolve/__typetests__/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "composite": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "skipLibCheck": true, + + "types": [] + }, + "include": ["./**/*"] +} diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 963c984e88c5..39740b72d173 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -1,6 +1,6 @@ { "name": "jest-resolve", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -19,17 +19,19 @@ "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.0.0-alpha.7", + "jest-haste-map": "^28.0.1", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.0.0-alpha.7", - "jest-validate": "^28.0.0-alpha.7", + "jest-util": "^28.0.1", + "jest-validate": "^28.0.1", "resolve": "^1.20.0", "resolve.exports": "^1.1.0", "slash": "^3.0.0" }, "devDependencies": { + "@tsd/typescript": "~4.6.2", "@types/graceful-fs": "^4.1.3", - "@types/resolve": "^1.20.0" + "@types/resolve": "^1.20.2", + "tsd-lite": "^0.5.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/file.js b/packages/jest-resolve/src/__mocks__/self-ref/foo/file.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-no-exports/file.js b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-no-exports/file.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-no-exports/package.json b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-no-exports/package.json new file mode 100644 index 000000000000..9e734769dce1 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-no-exports/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo-no-exports" +} diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-own-pkg/file.js b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-own-pkg/file.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-own-pkg/package.json b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-own-pkg/package.json new file mode 100644 index 000000000000..31bd0de86850 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested-with-own-pkg/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo-other-name", + "exports": "./file.js" +} diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/nested/file.js b/packages/jest-resolve/src/__mocks__/self-ref/foo/nested/file.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/self-ref/foo/package.json b/packages/jest-resolve/src/__mocks__/self-ref/foo/package.json new file mode 100644 index 000000000000..94f7815d3f1b --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/self-ref/foo/package.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "exports": "./file.js" +} diff --git a/packages/jest-resolve/src/__mocks__/self-ref/package.json b/packages/jest-resolve/src/__mocks__/self-ref/package.json new file mode 100644 index 000000000000..1ff7d9e976ef --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/self-ref/package.json @@ -0,0 +1,3 @@ +{ + "name": "self-ref" +} diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 34a286c57209..3143c9c45e6c 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -37,6 +37,8 @@ beforeEach(() => { userResolver.mockClear(); userResolverAsync.async.mockClear(); mockResolveSync.mockClear(); + + Resolver.clearDefaultResolverCache(); }); describe('isCoreModule', () => { @@ -106,7 +108,6 @@ describe('findNodeModule', () => { const newPath = Resolver.findNodeModule('test', { basedir: '/', - browser: true, conditions: ['conditions, woooo'], extensions: ['js'], moduleDirectory: ['node_modules'], @@ -118,7 +119,6 @@ describe('findNodeModule', () => { expect(userResolver.mock.calls[0][0]).toBe('test'); expect(userResolver.mock.calls[0][1]).toStrictEqual({ basedir: '/', - browser: true, conditions: ['conditions, woooo'], defaultResolver, extensions: ['js'], @@ -251,6 +251,52 @@ describe('findNodeModule', () => { ); }); }); + + describe('self-reference', () => { + const selfRefRoot = path.resolve(__dirname, '../__mocks__/self-ref'); + + test('supports self-reference', () => { + const result = Resolver.findNodeModule('foo', { + basedir: path.resolve(selfRefRoot, './foo/index.js'), + conditions: [], + }); + + expect(result).toEqual(path.resolve(selfRefRoot, './foo/file.js')); + }); + + test('supports nested self-reference', () => { + const result = Resolver.findNodeModule('foo', { + basedir: path.resolve(selfRefRoot, './foo/nested/index.js'), + conditions: [], + }); + + expect(result).toEqual(path.resolve(selfRefRoot, './foo/file.js')); + }); + + test('fails if own pkg.json with different name', () => { + const result = Resolver.findNodeModule('foo', { + basedir: path.resolve( + selfRefRoot, + './foo/nested-with-own-pkg/index.js', + ), + conditions: [], + }); + + expect(result).toEqual(null); + }); + + test('fails if own pkg.json with no exports', () => { + const result = Resolver.findNodeModule('foo-no-exports', { + basedir: path.resolve( + selfRefRoot, + './foo/nested-with-no-exports/index.js', + ), + conditions: [], + }); + + expect(result).toEqual(null); + }); + }); }); describe('findNodeModuleAsync', () => { @@ -267,7 +313,6 @@ describe('findNodeModuleAsync', () => { const newPath = await Resolver.findNodeModuleAsync('test', { basedir: '/', - browser: true, conditions: ['conditions, woooo'], extensions: ['js'], moduleDirectory: ['node_modules'], @@ -279,7 +324,6 @@ describe('findNodeModuleAsync', () => { expect(userResolverAsync.async.mock.calls[0][0]).toBe('test'); expect(userResolverAsync.async.mock.calls[0][1]).toStrictEqual({ basedir: '/', - browser: true, conditions: ['conditions, woooo'], defaultResolver, extensions: ['js'], @@ -399,6 +443,21 @@ describe('resolveModule', () => { expect(resolvedWithSlash).toBe(fooSlashIndex); expect(resolvedWithSlash).toBe(resolvedWithDot); }); + + it('custom resolver can resolve node modules', () => { + userResolver.mockImplementation(() => 'module'); + + const moduleMap = ModuleMap.create('/'); + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + resolver: require.resolve('../__mocks__/userResolver'), + } as ResolverConfig); + const src = require.resolve('../'); + resolver.resolveModule(src, 'fs'); + + expect(userResolver).toHaveBeenCalled(); + expect(userResolver.mock.calls[0][0]).toBe('fs'); + }); }); describe('resolveModuleAsync', () => { diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index f20921cb6ff6..5f121278a95b 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -13,30 +13,77 @@ import { resolve as resolveExports, } from 'resolve.exports'; import { - PkgJson, + findClosestPackageJson, isDirectory, isFile, readPackageCached, realpathSync, } from './fileWalkers'; +import type {PackageJSON} from './types'; -// copy from `resolve`'s types so we don't have their types in our definition -// files -interface ResolverOptions { +/** + * Allows transforming parsed `package.json` contents. + * + * @param pkg - Parsed `package.json` contents. + * @param file - Path to `package.json` file. + * @param dir - Directory that contains the `package.json`. + * + * @returns Transformed `package.json` contents. + */ +export type PackageFilter = ( + pkg: PackageJSON, + file: string, + dir: string, +) => PackageJSON; + +/** + * Allows transforming a path within a package. + * + * @param pkg - Parsed `package.json` contents. + * @param path - Path being resolved. + * @param relativePath - Path relative from the `package.json` location. + * + * @returns Relative path that will be joined from the `package.json` location. + */ +export type PathFilter = ( + pkg: PackageJSON, + path: string, + relativePath: string, +) => string; + +export type ResolverOptions = { + /** Directory to begin resolving from. */ basedir: string; - browser?: boolean; + /** List of export conditions. */ conditions?: Array; + /** Instance of default resolver. */ defaultResolver: typeof defaultResolver; + /** List of file extensions to search in order. */ extensions?: Array; + /** + * List of directory names to be looked up for modules recursively. + * + * @defaultValue + * The default is `['node_modules']`. + */ moduleDirectory?: Array; + /** + * List of `require.paths` to use if nothing is found in `node_modules`. + * + * @defaultValue + * The default is `undefined`. + */ paths?: Array; + /** Allows transforming parsed `package.json` contents. */ + packageFilter?: PackageFilter; + /** Allows transforms a path within a package. */ + pathFilter?: PathFilter; + /** Current root directory. */ rootDir?: string; - packageFilter?: (pkg: PkgJson, dir: string) => PkgJson; - pathFilter?: (pkg: PkgJson, path: string, relativePath: string) => string; -} +}; type UpstreamResolveOptionsWithConditions = UpstreamResolveOptions & - Pick; + Pick; export type SyncResolver = (path: string, options: ResolverOptions) => string; export type AsyncResolver = ( @@ -90,7 +137,7 @@ export default defaultResolver; * helper functions */ -function readPackageSync(_: unknown, file: string): PkgJson { +function readPackageSync(_: unknown, file: string): PackageJSON { return readPackageCached(file); } @@ -112,6 +159,30 @@ function getPathInModule( moduleName = `${moduleName}/${segments.shift()}`; } + // self-reference + const closestPackageJson = findClosestPackageJson(options.basedir); + if (closestPackageJson) { + const pkg = readPackageCached(closestPackageJson); + + if (pkg.name === moduleName && pkg.exports) { + const subpath = segments.join('/') || '.'; + + const resolved = resolveExports( + pkg, + subpath, + createResolveOptions(options.conditions), + ); + + if (!resolved) { + throw new Error( + '`exports` exists, but no results - this is a bug in Jest. Please report an issue', + ); + } + + return pathResolve(dirname(closestPackageJson), resolved); + } + } + let packageJsonPath = ''; try { @@ -124,9 +195,6 @@ function getPathInModule( const pkg = readPackageCached(packageJsonPath); if (pkg.exports) { - // we need to make sure resolve ignores `main` - delete pkg.main; - const subpath = segments.join('/') || '.'; const resolved = resolveExports( @@ -135,10 +203,13 @@ function getPathInModule( createResolveOptions(options.conditions), ); - // TODO: should we throw if not? - if (resolved) { - return pathResolve(dirname(packageJsonPath), resolved); + if (!resolved) { + throw new Error( + '`exports` exists, but no results - this is a bug in Jest. Please report an issue', + ); } + + return pathResolve(dirname(packageJsonPath), resolved); } } } diff --git a/packages/jest-resolve/src/fileWalkers.ts b/packages/jest-resolve/src/fileWalkers.ts index 88c2110e9f72..f19e6b4701ae 100644 --- a/packages/jest-resolve/src/fileWalkers.ts +++ b/packages/jest-resolve/src/fileWalkers.ts @@ -8,6 +8,7 @@ import {dirname, resolve} from 'path'; import * as fs from 'graceful-fs'; import {tryRealpath} from 'jest-util'; +import type {PackageJSON} from './types'; export function clearFsCache(): void { checkedPaths.clear(); @@ -71,17 +72,15 @@ function realpathCached(path: string): string { return result; } -export type PkgJson = Record; - -const packageContents = new Map(); -export function readPackageCached(path: string): PkgJson { +const packageContents = new Map(); +export function readPackageCached(path: string): PackageJSON { let result = packageContents.get(path); if (result != null) { return result; } - result = JSON.parse(fs.readFileSync(path, 'utf8')) as PkgJson; + result = JSON.parse(fs.readFileSync(path, 'utf8')) as PackageJSON; packageContents.set(path, result); diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index 34600936314f..40b1a73bf5b3 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -7,7 +7,19 @@ import Resolver from './resolver'; -export type {ResolveModuleConfig} from './resolver'; +export type { + AsyncResolver, + SyncResolver, + PackageFilter, + PathFilter, + ResolverOptions, +} from './defaultResolver'; +export type { + FindNodeModuleConfig, + ResolveModuleConfig, + ResolverObject as JestResolver, +} from './resolver'; +export type {PackageJSON} from './types'; export * from './utils'; export default Resolver; diff --git a/packages/jest-resolve/src/resolver.ts b/packages/jest-resolve/src/resolver.ts index bf3c8e7cdc23..4c9f1a93cae3 100644 --- a/packages/jest-resolve/src/resolver.ts +++ b/packages/jest-resolve/src/resolver.ts @@ -24,9 +24,8 @@ import nodeModulesPaths from './nodeModulesPaths'; import shouldLoadAsEsm, {clearCachedLookups} from './shouldLoadAsEsm'; import type {ResolverConfig} from './types'; -type FindNodeModuleConfig = { +export type FindNodeModuleConfig = { basedir: string; - browser?: boolean; conditions?: Array; extensions?: Array; moduleDirectory?: Array; @@ -124,7 +123,6 @@ export default class Resolver { try { return resolver(path, { basedir: options.basedir, - browser: options.browser, conditions: options.conditions, defaultResolver, extensions: options.extensions, @@ -167,7 +165,6 @@ export default class Resolver { try { const result = await resolver(path, { basedir: options.basedir, - browser: options.browser, conditions: options.conditions, defaultResolver, extensions: options.extensions, @@ -218,7 +215,8 @@ export default class Resolver { // dependency graph because we don't have to look at modules that may not // exist and aren't mocked. const resolveNodeModule = (name: string, throwIfNotFound = false) => { - if (this.isCoreModule(name)) { + // Only skip default resolver + if (this.isCoreModule(name) && !this._options.resolver) { return name; } @@ -292,7 +290,8 @@ export default class Resolver { // dependency graph because we don't have to look at modules that may not // exist and aren't mocked. const resolveNodeModule = async (name: string, throwIfNotFound = false) => { - if (this.isCoreModule(name)) { + // Only skip default resolver + if (this.isCoreModule(name) && !this._options.resolver) { return name; } @@ -858,7 +857,7 @@ Please check your configuration for these entries: type ResolverSyncObject = {sync: SyncResolver; async?: AsyncResolver}; type ResolverAsyncObject = {sync?: SyncResolver; async: AsyncResolver}; -type ResolverObject = ResolverSyncObject | ResolverAsyncObject; +export type ResolverObject = ResolverSyncObject | ResolverAsyncObject; function loadResolver( resolver: string | undefined | null, diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index 8469e2b9dc3d..b7279945463f 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -21,3 +21,11 @@ type ModuleNameMapperConfig = { regex: RegExp; moduleName: string | Array; }; + +// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 +type JSONValue = string | number | boolean | JSONObject | Array; +interface JSONObject { + [key: string]: JSONValue; +} + +export type PackageJSON = JSONObject; diff --git a/packages/jest-resolve/src/utils.ts b/packages/jest-resolve/src/utils.ts index e695343003a3..969b5b299492 100644 --- a/packages/jest-resolve/src/utils.ts +++ b/packages/jest-resolve/src/utils.ts @@ -98,6 +98,11 @@ export const resolveTestEnvironment = ({ testEnvironment: string; requireResolveFunction: (moduleName: string) => string; }): string => { + // we don't want to resolve the actual `jsdom` module if `jest-environment-jsdom` is not installed, but `jsdom` package is + if (filePath === 'jsdom') { + filePath = 'jest-environment-jsdom'; + } + try { return resolveWithPrefix(undefined, { filePath, @@ -108,7 +113,7 @@ export const resolveTestEnvironment = ({ rootDir, }); } catch (error: any) { - if (filePath === 'jsdom' || filePath === 'jest-environment-jsdom') { + if (filePath === 'jest-environment-jsdom') { error.message += '\n\nAs of Jest 28 "jest-environment-jsdom" is no longer shipped by default, make sure to install it separately.'; } diff --git a/packages/jest-runner/__typetests__/jest-runner.test.ts b/packages/jest-runner/__typetests__/jest-runner.test.ts new file mode 100644 index 000000000000..97ad722655ab --- /dev/null +++ b/packages/jest-runner/__typetests__/jest-runner.test.ts @@ -0,0 +1,133 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {expectType} from 'tsd-lite'; +import {CallbackTestRunner, EmittingTestRunner} from 'jest-runner'; +import type { + CallbackTestRunnerInterface, + Config, + EmittingTestRunnerInterface, + OnTestFailure, + OnTestStart, + OnTestSuccess, + Test, + TestEvents, + TestRunnerContext, + TestRunnerOptions, + TestWatcher, + UnsubscribeFn, +} from 'jest-runner'; + +const globalConfig = {} as Config.GlobalConfig; +const runnerContext = {} as TestRunnerContext; + +// CallbackRunner + +class CallbackRunner extends CallbackTestRunner { + async runTests( + tests: Array, + watcher: TestWatcher, + onStart: OnTestStart, + onResult: OnTestSuccess, + onFailure: OnTestFailure, + options: TestRunnerOptions, + ): Promise { + expectType(this._globalConfig); + expectType(this._context); + + return; + } +} + +const callbackRunner = new CallbackRunner(globalConfig, runnerContext); + +expectType(callbackRunner.isSerial); +expectType(callbackRunner.supportsEventEmitters); + +// CallbackTestRunnerInterface + +class CustomCallbackRunner implements CallbackTestRunnerInterface { + readonly #maxConcurrency: number; + readonly #globalConfig: Config.GlobalConfig; + + constructor(globalConfig: Config.GlobalConfig) { + this.#globalConfig = globalConfig; + this.#maxConcurrency = globalConfig.maxWorkers; + } + + async runTests( + tests: Array, + watcher: TestWatcher, + onStart: OnTestStart, + onResult: OnTestSuccess, + onFailure: OnTestFailure, + options: TestRunnerOptions, + ): Promise { + expectType(this.#globalConfig); + expectType(this.#maxConcurrency); + + return; + } +} + +// EmittingRunner + +class EmittingRunner extends EmittingTestRunner { + async runTests( + tests: Array, + watcher: TestWatcher, + options: TestRunnerOptions, + ): Promise { + expectType(this._globalConfig); + expectType(this._context); + + return; + } + + on( + eventName: string, + listener: (eventData: TestEvents[Name]) => void | Promise, + ): UnsubscribeFn { + return () => {}; + } +} + +const emittingRunner = new EmittingRunner(globalConfig, runnerContext); + +expectType(emittingRunner.isSerial); +expectType(emittingRunner.supportsEventEmitters); + +// EmittingTestRunnerInterface + +class CustomEmittingRunner implements EmittingTestRunnerInterface { + readonly #maxConcurrency: number; + readonly #globalConfig: Config.GlobalConfig; + readonly supportsEventEmitters = true; + + constructor(globalConfig: Config.GlobalConfig) { + this.#globalConfig = globalConfig; + this.#maxConcurrency = globalConfig.maxWorkers; + } + + async runTests( + tests: Array, + watcher: TestWatcher, + options: TestRunnerOptions, + ): Promise { + expectType(this.#globalConfig); + expectType(this.#maxConcurrency); + + return; + } + + on( + eventName: string, + listener: (eventData: TestEvents[Name]) => void | Promise, + ): UnsubscribeFn { + return () => {}; + } +} diff --git a/packages/jest-runner/__typetests__/tsconfig.json b/packages/jest-runner/__typetests__/tsconfig.json new file mode 100644 index 000000000000..165ba1343021 --- /dev/null +++ b/packages/jest-runner/__typetests__/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "composite": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "skipLibCheck": true, + + "types": [] + }, + "include": ["./**/*"] +} diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index 71cadb54a11d..f1fe8476a2f9 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -1,6 +1,6 @@ { "name": "jest-runner", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,32 +17,35 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/console": "^28.0.0-alpha.7", - "@jest/environment": "^28.0.0-alpha.7", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/console": "^28.0.1", + "@jest/environment": "^28.0.1", + "@jest/test-result": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.8.1", + "emittery": "^0.10.2", "graceful-fs": "^4.2.9", - "jest-docblock": "^28.0.0-alpha.6", - "jest-environment-node": "^28.0.0-alpha.7", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-leak-detector": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-resolve": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", - "jest-worker": "^28.0.0-alpha.7", - "source-map-support": "^0.5.6", + "jest-docblock": "^28.0.0", + "jest-environment-node": "^28.0.1", + "jest-haste-map": "^28.0.1", + "jest-leak-detector": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-resolve": "^28.0.1", + "jest-runtime": "^28.0.1", + "jest-util": "^28.0.1", + "jest-watcher": "^28.0.1", + "jest-worker": "^28.0.1", + "source-map-support": "0.5.13", "throat": "^6.0.1" }, "devDependencies": { + "@tsd/typescript": "~4.6.2", "@types/exit": "^0.1.30", - "@types/graceful-fs": "^4.1.2", + "@types/graceful-fs": "^4.1.3", "@types/source-map-support": "^0.5.0", - "jest-jasmine2": "^28.0.0-alpha.7" + "jest-jasmine2": "^28.0.1", + "tsd-lite": "^0.5.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-runner/src/__tests__/testRunner.test.ts b/packages/jest-runner/src/__tests__/testRunner.test.ts index 16fd75e183ad..85ee76e0f5d3 100644 --- a/packages/jest-runner/src/__tests__/testRunner.test.ts +++ b/packages/jest-runner/src/__tests__/testRunner.test.ts @@ -6,7 +6,9 @@ * */ -import {TestWatcher} from '@jest/core'; +import type {TestContext} from '@jest/test-result'; +import {makeGlobalConfig, makeProjectConfig} from '@jest/test-utils'; +import {TestWatcher} from 'jest-watcher'; import TestRunner from '../index'; let mockWorkerFarm; @@ -26,58 +28,48 @@ jest.mock('jest-worker', () => ({ jest.mock('../testWorker', () => {}); test('injects the serializable module map into each worker in watch mode', async () => { - const globalConfig = {maxWorkers: 2, watch: true}; - const config = {rootDir: '/path/'}; - const serializableModuleMap = jest.fn(); + const globalConfig = makeGlobalConfig({maxWorkers: 2, watch: true}); + const config = makeProjectConfig({rootDir: '/path/'}); const runContext = {}; - const context = { + const mockTestContext = { config, - moduleMap: {toJSON: () => serializableModuleMap}, - }; + moduleMap: {toJSON: jest.fn()}, + } as unknown as TestContext; - await new TestRunner(globalConfig).runTests( + await new TestRunner(globalConfig, runContext).runTests( [ - {context, path: './file.test.js'}, - {context, path: './file2.test.js'}, + {context: mockTestContext, path: './file.test.js'}, + {context: mockTestContext, path: './file2.test.js'}, ], new TestWatcher({isWatchMode: globalConfig.watch}), - undefined, - undefined, - undefined, {serial: false}, ); - expect(mockWorkerFarm.worker.mock.calls).toEqual([ - [ - { - config, - context: runContext, - globalConfig, - path: './file.test.js', - }, - ], - [ - { - config, - context: runContext, - globalConfig, - path: './file2.test.js', - }, - ], - ]); + expect(mockWorkerFarm.worker).toBeCalledTimes(2); + + expect(mockWorkerFarm.worker).nthCalledWith(1, { + config, + context: runContext, + globalConfig, + path: './file.test.js', + }); + + expect(mockWorkerFarm.worker).nthCalledWith(2, { + config, + context: runContext, + globalConfig, + path: './file2.test.js', + }); }); test('assign process.env.JEST_WORKER_ID = 1 when in runInBand mode', async () => { - const globalConfig = {maxWorkers: 1, watch: false}; - const config = {rootDir: '/path/'}; - const context = {config}; + const globalConfig = makeGlobalConfig({maxWorkers: 1, watch: false}); + const config = makeProjectConfig({rootDir: '/path/'}); + const context = {config} as TestContext; - await new TestRunner(globalConfig).runTests( + await new TestRunner(globalConfig, {}).runTests( [{context, path: './file.test.js'}], new TestWatcher({isWatchMode: globalConfig.watch}), - undefined, - undefined, - undefined, {serial: true}, ); diff --git a/packages/jest-runner/src/index.ts b/packages/jest-runner/src/index.ts index 88785a096342..4f1bb6bf074e 100644 --- a/packages/jest-runner/src/index.ts +++ b/packages/jest-runner/src/index.ts @@ -14,19 +14,12 @@ import type { TestFileEvent, TestResult, } from '@jest/test-result'; -import type {Config} from '@jest/types'; import {deepCyclicCopy} from 'jest-util'; +import type {TestWatcher} from 'jest-watcher'; import {PromiseWithCustomMessage, Worker} from 'jest-worker'; import runTest from './runTest'; import type {SerializableResolver, worker} from './testWorker'; -import type { - OnTestFailure, - OnTestStart, - OnTestSuccess, - TestRunnerContext, - TestRunnerOptions, - TestWatcher, -} from './types'; +import {EmittingTestRunner, TestRunnerOptions, UnsubscribeFn} from './types'; const TEST_WORKER_PATH = require.resolve('./testWorker'); @@ -34,54 +27,36 @@ interface WorkerInterface extends Worker { worker: typeof worker; } +export type {Test, TestEvents} from '@jest/test-result'; +export type {Config} from '@jest/types'; +export type {TestWatcher} from 'jest-watcher'; +export {CallbackTestRunner, EmittingTestRunner} from './types'; export type { + CallbackTestRunnerInterface, + EmittingTestRunnerInterface, OnTestFailure, OnTestStart, OnTestSuccess, - TestWatcher, TestRunnerContext, TestRunnerOptions, + JestTestRunner, + UnsubscribeFn, } from './types'; -export default class TestRunner { - private readonly _globalConfig: Config.GlobalConfig; - private readonly _context: TestRunnerContext; - private readonly eventEmitter = new Emittery(); - readonly __PRIVATE_UNSTABLE_API_supportsEventEmitters__: boolean = true; - - readonly isSerial?: boolean; - - constructor(globalConfig: Config.GlobalConfig, context?: TestRunnerContext) { - this._globalConfig = globalConfig; - this._context = context || {}; - } +export default class TestRunner extends EmittingTestRunner { + readonly #eventEmitter = new Emittery(); async runTests( tests: Array, watcher: TestWatcher, - onStart: OnTestStart | undefined, - onResult: OnTestSuccess | undefined, - onFailure: OnTestFailure | undefined, options: TestRunnerOptions, ): Promise { return await (options.serial - ? this._createInBandTestRun(tests, watcher, onStart, onResult, onFailure) - : this._createParallelTestRun( - tests, - watcher, - onStart, - onResult, - onFailure, - )); + ? this.#createInBandTestRun(tests, watcher) + : this.#createParallelTestRun(tests, watcher)); } - private async _createInBandTestRun( - tests: Array, - watcher: TestWatcher, - onStart?: OnTestStart, - onResult?: OnTestSuccess, - onFailure?: OnTestFailure, - ) { + async #createInBandTestRun(tests: Array, watcher: TestWatcher) { process.env.JEST_WORKER_ID = '1'; const mutex = throat(1); return tests.reduce( @@ -92,28 +67,15 @@ export default class TestRunner { if (watcher.isInterrupted()) { throw new CancelRun(); } - // Remove `if(onStart)` in Jest 27 - if (onStart) { - await onStart(test); - - return runTest( - test.path, - this._globalConfig, - test.context.config, - test.context.resolver, - this._context, - undefined, - ); - } // `deepCyclicCopy` used here to avoid mem-leak const sendMessageToJest: TestFileEvent = (eventName, args) => - this.eventEmitter.emit( + this.#eventEmitter.emit( eventName, deepCyclicCopy(args, {keepPrototype: false}), ); - await this.eventEmitter.emit('test-file-start', [test]); + await this.#eventEmitter.emit('test-file-start', [test]); return runTest( test.path, @@ -124,39 +86,22 @@ export default class TestRunner { sendMessageToJest, ); }) - .then(result => { - if (onResult) { - return onResult(test, result); - } - - return this.eventEmitter.emit('test-file-success', [ - test, - result, - ]); - }) - .catch(err => { - if (onFailure) { - return onFailure(test, err); - } - - return this.eventEmitter.emit('test-file-failure', [test, err]); - }), + .then( + result => + this.#eventEmitter.emit('test-file-success', [test, result]), + error => + this.#eventEmitter.emit('test-file-failure', [test, error]), + ), ), Promise.resolve(), ); } - private async _createParallelTestRun( - tests: Array, - watcher: TestWatcher, - onStart?: OnTestStart, - onResult?: OnTestSuccess, - onFailure?: OnTestFailure, - ) { + async #createParallelTestRun(tests: Array, watcher: TestWatcher) { const resolvers: Map = new Map(); for (const test of tests) { - if (!resolvers.has(test.context.config.name)) { - resolvers.set(test.context.config.name, { + if (!resolvers.has(test.context.config.id)) { + resolvers.set(test.context.config.id, { config: test.context.config, serializableModuleMap: test.context.moduleMap.toJSON(), }); @@ -165,14 +110,11 @@ export default class TestRunner { const worker = new Worker(TEST_WORKER_PATH, { exposedMethods: ['worker'], - forkOptions: {stdio: 'pipe'}, + // @ts-expect-error: option does not exist on the node 12 types + forkOptions: {serialization: 'json', stdio: 'pipe'}, maxRetries: 3, numWorkers: this._globalConfig.maxWorkers, - setupArgs: [ - { - serializableResolvers: Array.from(resolvers.values()), - }, - ], + setupArgs: [{serializableResolvers: Array.from(resolvers.values())}], }) as WorkerInterface; if (worker.getStdout()) worker.getStdout().pipe(process.stdout); @@ -188,12 +130,7 @@ export default class TestRunner { return Promise.reject(); } - // Remove `if(onStart)` in Jest 27 - if (onStart) { - await onStart(test); - } else { - await this.eventEmitter.emit('test-file-start', [test]); - } + await this.#eventEmitter.emit('test-file-start', [test]); const promise = worker.worker({ config: test.context.config, @@ -212,9 +149,9 @@ export default class TestRunner { if (promise.UNSTABLE_onCustomMessage) { // TODO: Get appropriate type for `onCustomMessage` - promise.UNSTABLE_onCustomMessage(([event, payload]: any) => { - this.eventEmitter.emit(event, payload); - }); + promise.UNSTABLE_onCustomMessage(([event, payload]: any) => + this.#eventEmitter.emit(event, payload), + ); } return promise; @@ -230,21 +167,11 @@ export default class TestRunner { const runAllTests = Promise.all( tests.map(test => - runTestInWorker(test) - .then(result => { - if (onResult) { - return onResult(test, result); - } - - return this.eventEmitter.emit('test-file-success', [test, result]); - }) - .catch(error => { - if (onFailure) { - return onFailure(test, error); - } - - return this.eventEmitter.emit('test-file-failure', [test, error]); - }), + runTestInWorker(test).then( + result => + this.#eventEmitter.emit('test-file-success', [test, result]), + error => this.#eventEmitter.emit('test-file-failure', [test, error]), + ), ), ); @@ -268,8 +195,8 @@ export default class TestRunner { on( eventName: Name, listener: (eventData: TestEvents[Name]) => void | Promise, - ): Emittery.UnsubscribeFn { - return this.eventEmitter.on(eventName, listener); + ): UnsubscribeFn { + return this.#eventEmitter.on(eventName, listener); } } diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index dfa50645bf46..f6e0d40848cc 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -79,7 +79,7 @@ async function runTestInternal( globalConfig: Config.GlobalConfig, projectConfig: Config.ProjectConfig, resolver: Resolver, - context?: TestRunnerContext, + context: TestRunnerContext, sendMessageToJest?: TestFileEvent, ): Promise { const testSource = fs.readFileSync(path, 'utf8'); @@ -188,13 +188,13 @@ async function runTestInternal( transformer, cacheFS, { - changedFiles: context?.changedFiles, + changedFiles: context.changedFiles, collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, coverageProvider: globalConfig.coverageProvider, sourcesRelatedToTestsInChangedFiles: - context?.sourcesRelatedToTestsInChangedFiles, + context.sourcesRelatedToTestsInChangedFiles, }, path, ); @@ -236,7 +236,6 @@ async function runTestInternal( runtime .requireInternalModule( require.resolve('source-map-support'), - 'source-map-support', ) .install(sourcemapOptions); @@ -363,7 +362,7 @@ export default async function runTest( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, resolver: Resolver, - context?: TestRunnerContext, + context: TestRunnerContext, sendMessageToJest?: TestFileEvent, ): Promise { const {leakDetector, result} = await runTestInternal( diff --git a/packages/jest-runner/src/testWorker.ts b/packages/jest-runner/src/testWorker.ts index 687cbfdcdc55..f18c92ce9f05 100644 --- a/packages/jest-runner/src/testWorker.ts +++ b/packages/jest-runner/src/testWorker.ts @@ -30,7 +30,7 @@ type WorkerData = { config: Config.ProjectConfig; globalConfig: Config.GlobalConfig; path: string; - context?: TestRunnerSerializedContext; + context: TestRunnerSerializedContext; }; // Make sure uncaught errors are logged before we exit. @@ -59,9 +59,9 @@ const formatError = (error: string | ErrorWithCode): SerializableError => { const resolvers = new Map(); const getResolver = (config: Config.ProjectConfig) => { - const resolver = resolvers.get(config.name); + const resolver = resolvers.get(config.id); if (!resolver) { - throw new Error(`Cannot find resolver for: ${config.name}`); + throw new Error(`Cannot find resolver for: ${config.id}`); } return resolver; }; @@ -77,7 +77,7 @@ export function setup(setupData: { const moduleMap = HasteMap.getStatic(config).getModuleMapFromJSON( serializableModuleMap, ); - resolvers.set(config.name, Runtime.createResolver(config, moduleMap)); + resolvers.set(config.id, Runtime.createResolver(config, moduleMap)); } } @@ -97,7 +97,7 @@ export async function worker({ globalConfig, config, getResolver(config), - context && { + { ...context, changedFiles: context.changedFiles && new Set(context.changedFiles), sourcesRelatedToTestsInChangedFiles: diff --git a/packages/jest-runner/src/types.ts b/packages/jest-runner/src/types.ts index 4bb5f2c58879..18a0ffe4e139 100644 --- a/packages/jest-runner/src/types.ts +++ b/packages/jest-runner/src/types.ts @@ -5,24 +5,27 @@ * LICENSE file in the root directory of this source tree. */ -import type Emittery = require('emittery'); import type {JestEnvironment} from '@jest/environment'; import type { SerializableError, Test, + TestEvents, TestFileEvent, TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; import type RuntimeType from 'jest-runtime'; +import type {TestWatcher} from 'jest-watcher'; export type ErrorWithCode = Error & {code?: string}; export type OnTestStart = (test: Test) => Promise; + export type OnTestFailure = ( test: Test, serializableError: SerializableError, ) => Promise; + export type OnTestSuccess = ( test: Test, testResult: TestResult, @@ -41,22 +44,91 @@ export type TestRunnerOptions = { serial: boolean; }; -// make sure all props here are present in the type below it as well export type TestRunnerContext = { changedFiles?: Set; sourcesRelatedToTestsInChangedFiles?: Set; }; +type SerializeSet = T extends Set ? Array : T; + export type TestRunnerSerializedContext = { - changedFiles?: Array; - sourcesRelatedToTestsInChangedFiles?: Array; + [K in keyof TestRunnerContext]: SerializeSet; }; -// TODO: Should live in `@jest/core` or `jest-watcher` -type WatcherState = {interrupted: boolean}; -export interface TestWatcher extends Emittery<{change: WatcherState}> { - state: WatcherState; - setState(state: WatcherState): void; - isInterrupted(): boolean; - isWatchMode(): boolean; +export type UnsubscribeFn = () => void; + +export interface CallbackTestRunnerInterface { + readonly isSerial?: boolean; + readonly supportsEventEmitters?: boolean; + + runTests( + tests: Array, + watcher: TestWatcher, + onStart: OnTestStart, + onResult: OnTestSuccess, + onFailure: OnTestFailure, + options: TestRunnerOptions, + ): Promise; +} + +export interface EmittingTestRunnerInterface { + readonly isSerial?: boolean; + readonly supportsEventEmitters: true; + + runTests( + tests: Array, + watcher: TestWatcher, + options: TestRunnerOptions, + ): Promise; + + on( + eventName: Name, + listener: (eventData: TestEvents[Name]) => void | Promise, + ): UnsubscribeFn; +} + +abstract class BaseTestRunner { + readonly isSerial?: boolean; + abstract readonly supportsEventEmitters: boolean; + + constructor( + protected readonly _globalConfig: Config.GlobalConfig, + protected readonly _context: TestRunnerContext, + ) {} +} + +export abstract class CallbackTestRunner + extends BaseTestRunner + implements CallbackTestRunnerInterface +{ + readonly supportsEventEmitters = false; + + abstract runTests( + tests: Array, + watcher: TestWatcher, + onStart: OnTestStart, + onResult: OnTestSuccess, + onFailure: OnTestFailure, + options: TestRunnerOptions, + ): Promise; } + +export abstract class EmittingTestRunner + extends BaseTestRunner + implements EmittingTestRunnerInterface +{ + readonly supportsEventEmitters = true; + + abstract runTests( + tests: Array, + watcher: TestWatcher, + options: TestRunnerOptions, + ): Promise; + + abstract on( + eventName: Name, + listener: (eventData: TestEvents[Name]) => void | Promise, + ): UnsubscribeFn; +} + +export type JestTestRunner = CallbackTestRunner | EmittingTestRunner; diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json index 7ce0efdb2782..36b3ff8099d2 100644 --- a/packages/jest-runner/tsconfig.json +++ b/packages/jest-runner/tsconfig.json @@ -20,6 +20,7 @@ {"path": "../jest-transform"}, {"path": "../jest-types"}, {"path": "../jest-util"}, + {"path": "../jest-watcher"}, {"path": "../jest-worker"} ] } diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 8e35b220bd36..9e6b6e123565 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -1,6 +1,6 @@ { "name": "jest-runtime", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,35 +17,35 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/environment": "^28.0.0-alpha.7", - "@jest/fake-timers": "^28.0.0-alpha.7", - "@jest/globals": "^28.0.0-alpha.7", - "@jest/source-map": "^28.0.0-alpha.6", - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/environment": "^28.0.1", + "@jest/fake-timers": "^28.0.1", + "@jest/globals": "^28.0.1", + "@jest/source-map": "^28.0.0", + "@jest/test-result": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "execa": "^5.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-mock": "^28.0.0-alpha.7", - "jest-regex-util": "^28.0.0-alpha.6", - "jest-resolve": "^28.0.0-alpha.7", - "jest-snapshot": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", + "jest-haste-map": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-mock": "^28.0.1", + "jest-regex-util": "^28.0.0", + "jest-resolve": "^28.0.1", + "jest-snapshot": "^28.0.1", + "jest-util": "^28.0.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@types/glob": "^7.1.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "^14.0.27", - "jest-environment-node": "^28.0.0-alpha.7" + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "jest-environment-node": "^28.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-runtime/src/__mocks__/createRuntime.js b/packages/jest-runtime/src/__mocks__/createRuntime.js index 67f9e2be9c1c..b22afc6154bf 100644 --- a/packages/jest-runtime/src/__mocks__/createRuntime.js +++ b/packages/jest-runtime/src/__mocks__/createRuntime.js @@ -37,7 +37,7 @@ const setupModuleNameMapper = (config, rootDir) => { return []; }; -const setupTransform = (config, rootDir) => { +const setupTransform = (config, rootDir, cwd) => { if (config?.transform) { const transform = config.transform; return Object.keys(transform).map(regex => [ @@ -45,33 +45,27 @@ const setupTransform = (config, rootDir) => { path.resolve(rootDir, transform[regex]), ]); } - return [['^.+\\.[jt]sx?$', require.resolve('babel-jest')]]; + return [['^.+\\.[jt]sx?$', require.resolve('babel-jest'), {root: cwd}]]; }; module.exports = async function createRuntime(filename, projectConfig) { const rootDir = path.resolve(path.dirname(filename), 'test_root'); + const cwd = path.resolve(__dirname, '../../../..'); const moduleNameMapper = setupModuleNameMapper(projectConfig, rootDir); - const transform = setupTransform(projectConfig, rootDir); + const transform = setupTransform(projectConfig, rootDir, cwd); projectConfig = makeProjectConfig({ cacheDirectory: getCacheDirectory(), - cwd: path.resolve(__dirname, '..', '..', '..', '..'), + cwd, haste: { - hasteImplModulePath: path.resolve( - __dirname, - '..', - '..', - '..', - 'jest-haste-map', - 'src', - '__tests__', - 'haste_impl.js', + hasteImplModulePath: require.resolve( + '../../../jest-haste-map/src/__tests__/haste_impl.js', ), }, + id: `Runtime-${filename.replace(/\W/, '-')}.tests`, moduleDirectories: ['node_modules'], moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'], - name: `Runtime-${filename.replace(/\W/, '-')}.tests`, rootDir, ...projectConfig, moduleNameMapper, diff --git a/packages/jest-runtime/src/__tests__/NODE_PATH_dir/package.json b/packages/jest-runtime/src/__tests__/NODE_PATH_dir/package.json index 103175ed9050..65767296d91f 100644 --- a/packages/jest-runtime/src/__tests__/NODE_PATH_dir/package.json +++ b/packages/jest-runtime/src/__tests__/NODE_PATH_dir/package.json @@ -1,6 +1,5 @@ { "name": "NODE_PATH_dir", "version": "1.0.0", - "dependencies": { - } + "dependencies": {} } diff --git a/packages/jest-runtime/src/__tests__/test_root/package.json b/packages/jest-runtime/src/__tests__/test_root/package.json index 48df13f673ca..78a8484cd760 100644 --- a/packages/jest-runtime/src/__tests__/test_root/package.json +++ b/packages/jest-runtime/src/__tests__/test_root/package.json @@ -1,6 +1,5 @@ { "name": "test_root", "version": "1.0.0", - "dependencies": { - } + "dependencies": {} } diff --git a/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js b/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js index ec9b48f69cc2..7597efa50e3f 100644 --- a/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js +++ b/packages/jest-runtime/src/__tests__/test_root/test_json_preprocessor.js @@ -10,5 +10,5 @@ module.exports.process = source => { const json = JSON.parse(source); Object.keys(json).forEach(k => (json[k] = k)); - return JSON.stringify(json); + return {code: JSON.stringify(json)}; }; diff --git a/packages/jest-runtime/src/__tests__/test_root/test_preprocessor.js b/packages/jest-runtime/src/__tests__/test_root/test_preprocessor.js index cf5e591fe05b..f5ce630fc144 100644 --- a/packages/jest-runtime/src/__tests__/test_root/test_preprocessor.js +++ b/packages/jest-runtime/src/__tests__/test_root/test_preprocessor.js @@ -7,4 +7,6 @@ 'use strict'; -module.exports.process = () => "throw new Error('preprocessor must not run.');"; +module.exports.process = () => ({ + code: "throw new Error('preprocessor must not run.');", +}); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 7fc8a91f6af3..1dc2c9a56322 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -27,13 +27,18 @@ import stripBOM = require('strip-bom'); import type { Jest, JestEnvironment, + JestImportMeta, Module, ModuleWrapper, } from '@jest/environment'; import type {LegacyFakeTimers, ModernFakeTimers} from '@jest/fake-timers'; import type * as JestGlobals from '@jest/globals'; import type {SourceMapRegistry} from '@jest/source-map'; -import type {RuntimeTransformResult, V8CoverageResult} from '@jest/test-result'; +import type { + RuntimeTransformResult, + TestContext, + V8CoverageResult, +} from '@jest/test-result'; import { CallerTransformOptions, ScriptTransformer, @@ -57,9 +62,6 @@ import { decodePossibleOutsideJestVmPath, findSiblingsWithFileExtension, } from './helpers'; -import type {Context} from './types'; - -export type {Context} from './types'; const esmIsAvailable = typeof SourceTextModule === 'function'; @@ -117,6 +119,7 @@ type ResolveOptions = Parameters[1] & { const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); +const logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); const NODE_MODULES = `${path.sep}node_modules${path.sep}`; @@ -279,10 +282,9 @@ export default class Runtime { this._shouldUnmockTransitiveDependenciesCache = new Map(); this._transitiveShouldMock = new Map(); - this._fakeTimersImplementation = - config.timers === 'legacy' - ? this._environment.fakeTimers - : this._environment.fakeTimersModern; + this._fakeTimersImplementation = config.fakeTimers.legacyFakeTimers + ? this._environment.fakeTimers + : this._environment.fakeTimersModern; this._unmockList = unmockRegExpCache.get(config); if (!this._unmockList && config.unmockedModulePathPatterns) { @@ -333,7 +335,7 @@ export default class Runtime { watch?: boolean; watchman: boolean; }, - ): Promise { + ): Promise { createDirectory(config.cacheDirectory); const instance = await Runtime.createHasteMap(config, { console: options.console, @@ -377,10 +379,10 @@ export default class Runtime { forceNodeFilesystemAPI: config.haste.forceNodeFilesystemAPI, hasteImplModulePath: config.haste.hasteImplModulePath, hasteMapModulePath: config.haste.hasteMapModulePath, + id: config.id, ignorePattern, maxWorkers: options?.maxWorkers || 1, mocksPattern: escapePathForRegex(`${path.sep}__mocks__${path.sep}`), - name: config.name, platforms: config.haste.platforms || ['ios', 'android'], resetCache: options?.resetCache, retainAllFiles: config.haste.retainAllFiles || false, @@ -499,8 +501,18 @@ export default class Runtime { return this.linkAndEvaluateModule(module); }, - initializeImportMeta(meta: ImportMeta) { + initializeImportMeta: (meta: JestImportMeta) => { meta.url = pathToFileURL(modulePath).href; + + let jest = this.jestObjectCaches.get(modulePath); + + if (!jest) { + jest = this._createJestObjectFor(modulePath); + + this.jestObjectCaches.set(modulePath, jest); + } + + meta.jest = jest; }, }); @@ -625,6 +637,7 @@ export default class Runtime { return this.linkAndEvaluateModule(module); }, initializeImportMeta(meta: ImportMeta) { + // no `jest` here as it's not loaded in a file meta.url = specifier; }, }); @@ -2065,13 +2078,17 @@ export default class Runtime { return this._fakeTimersImplementation!; }; - const useFakeTimers: Jest['useFakeTimers'] = (type = 'modern') => { - if (type === 'legacy') { + const useFakeTimers: Jest['useFakeTimers'] = fakeTimersConfig => { + fakeTimersConfig = { + ...this._config.fakeTimers, + ...fakeTimersConfig, + } as Config.FakeTimersConfig; + if (fakeTimersConfig?.legacyFakeTimers) { this._fakeTimersImplementation = this._environment.fakeTimers; } else { this._fakeTimersImplementation = this._environment.fakeTimersModern; } - this._fakeTimersImplementation!.useFakeTimers(); + this._fakeTimersImplementation!.useFakeTimers(fakeTimersConfig); return jestObject; }; const useRealTimers = () => { @@ -2097,14 +2114,15 @@ export default class Runtime { }); const setTimeout = (timeout: number) => { - // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 this._environment.global[testTimeoutSymbol] = timeout; return jestObject; }; - const retryTimes = (numTestRetries: number) => { - // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 + const retryTimes: Jest['retryTimes'] = (numTestRetries, options) => { this._environment.global[retryTimesSymbol] = numTestRetries; + this._environment.global[logErrorsBeforeRetrySymbol] = + options?.logErrorsBeforeRetry; + return jestObject; }; @@ -2134,7 +2152,7 @@ export default class Runtime { return fakeTimers.getRealSystemTime(); } else { throw new TypeError( - 'getRealSystemTime is not available when not using modern timers', + '`jest.getRealSystemTime()` is not available when using legacy fake timers.', ); } }, @@ -2156,7 +2174,7 @@ export default class Runtime { fakeTimers.runAllImmediates(); } else { throw new TypeError( - 'runAllImmediates is not available when using modern timers', + '`jest.runAllImmediates()` is only available when using legacy fake timers.', ); } }, @@ -2172,7 +2190,7 @@ export default class Runtime { fakeTimers.setSystemTime(now); } else { throw new TypeError( - 'setSystemTime is not available when not using modern timers', + '`jest.setSystemTime()` is not available when using legacy fake timers.', ); } }, diff --git a/packages/jest-runtime/src/types.ts b/packages/jest-runtime/src/types.ts deleted file mode 100644 index b78f08090446..000000000000 --- a/packages/jest-runtime/src/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import type {Config} from '@jest/types'; -import type {FS as HasteFS, ModuleMap} from 'jest-haste-map'; -import type Resolver from 'jest-resolve'; - -export type Context = { - config: Config.ProjectConfig; - hasteFS: HasteFS; - moduleMap: ModuleMap; - resolver: Resolver; -}; diff --git a/packages/jest-schemas/package.json b/packages/jest-schemas/package.json index f88c3a9dbe31..d1b2e195de4f 100644 --- a/packages/jest-schemas/package.json +++ b/packages/jest-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@jest/schemas", - "version": "28.0.0-alpha.3", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", diff --git a/packages/jest-serializer/.npmignore b/packages/jest-serializer/.npmignore deleted file mode 100644 index 80bf61eb922c..000000000000 --- a/packages/jest-serializer/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -**/__mocks__/** -**/__tests__/** -__typetests__ -src -tsconfig.json -tsconfig.tsbuildinfo -api-extractor.json diff --git a/packages/jest-serializer/README.md b/packages/jest-serializer/README.md deleted file mode 100644 index 5eee3c1c9cfc..000000000000 --- a/packages/jest-serializer/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# jest-serializer - -> DEPRECATED: Use `v8` APIs directly: https://nodejs.org/api/v8.html#serialization-api - -Module for serializing and deserializing object into memory and disk. The Node core `v8` implementations are used. This seriializer have the advantage of being able to serialize `Map`, `Set`, `undefined`, `NaN`, etc.. - -## Install - -```sh -$ yarn add jest-serializer -``` - -## API - -Three kinds of API groups are exposed: - -### In-memory serialization: `serialize` and `deserialize` - -This set of functions take or return a `Buffer`. All the process happens in memory. This is useful when willing to transfer over HTTP, TCP or via UNIX pipes. - -```javascript -import {deserialize, serialize} from 'jest-serializer'; - -const myObject = { - foo: 'bar', - baz: [0, true, '2', [], {}], -}; - -const buffer = serialize(myObject); -const myCopyObject = deserialize(buffer); -``` - -### Synchronous persistent filesystem: `readFileSync` and `writeFileSync` - -This set of functions allow to send to disk a serialization result and retrieve it back, in a synchronous way. It mimics the `fs` API so it looks familiar. - -```javascript -import {readFileSync, writeFileSync} from 'jest-serializer'; - -const myObject = { - foo: 'bar', - baz: [0, true, '2', [], {}], -}; - -const myFile = '/tmp/obj'; - -writeFileSync(myFile, myObject); -const myCopyObject = readFileSync(myFile); -``` diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json deleted file mode 100644 index ba4bdccf1e6b..000000000000 --- a/packages/jest-serializer/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "jest-serializer", - "version": "28.0.0-alpha.6", - "repository": { - "type": "git", - "url": "https://github.com/facebook/jest.git", - "directory": "packages/jest-serializer" - }, - "devDependencies": { - "@types/graceful-fs": "^4.1.3" - }, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" - }, - "license": "MIT", - "main": "./build/index.js", - "types": "./build/index.d.ts", - "exports": { - ".": { - "types": "./build/index.d.ts", - "default": "./build/index.js" - }, - "./package.json": "./package.json" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/jest-serializer/src/__tests__/index.test.ts b/packages/jest-serializer/src/__tests__/index.test.ts deleted file mode 100644 index dae4124b8446..000000000000 --- a/packages/jest-serializer/src/__tests__/index.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {tmpdir} from 'os'; -import * as path from 'path'; -import * as fs from 'graceful-fs'; -import {format as prettyFormat} from 'pretty-format'; -import serializer from '..'; - -const objs = [ - 3, - null, - [0, true, '2', [3.14, {}, null]], - {key1: 'foo', key2: 'bar', key3: {array: [null, {}]}}, - {minusInf: -Infinity, nan: NaN, plusInf: +Infinity}, - {date: new Date(1234567890), re: /foo/gi}, - { - // @ts-expect-error - testing NaN - map: new Map([ - [NaN, 4], - [undefined, 'm'], - ]), - set: new Set([undefined, NaN]), - }, - {buf: Buffer.from([0, 255, 127])}, -]; - -const file = path.join(tmpdir(), '__jest-serialize-test__'); - -afterEach(() => { - try { - fs.unlinkSync(file); - } catch { - // Do nothing if file does not exist. - } -}); - -describe('Using V8 implementation', () => { - it('throws the error with an invalid serialization', () => { - // No chance this is a valid serialization, neither in JSON nor V8. - const invalidBuffer = Buffer.from([0, 85, 170, 255]); - - fs.writeFileSync(file, invalidBuffer); - - expect(() => serializer.deserialize(invalidBuffer)).toThrow(); - expect(() => serializer.readFileSync(file)).toThrow(); - }); - - objs.forEach((obj, i) => { - describe(`Object ${i}`, () => { - it('serializes/deserializes in memory', () => { - const buf = serializer.serialize(obj); - - expect(buf).toBeInstanceOf(Buffer); - - expect(prettyFormat(serializer.deserialize(buf))).toEqual( - prettyFormat(obj), - ); - }); - - it('serializes/deserializes in disk', () => { - serializer.writeFileSync(file, obj); - - expect(prettyFormat(serializer.readFileSync(file))).toEqual( - prettyFormat(obj), - ); - }); - }); - }); -}); diff --git a/packages/jest-serializer/src/index.ts b/packages/jest-serializer/src/index.ts deleted file mode 100644 index 62ae49c41e4f..000000000000 --- a/packages/jest-serializer/src/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {deserialize as v8Deserialize, serialize as v8Serialize} from 'v8'; -import * as fs from 'graceful-fs'; - -// JSON and V8 serializers are both stable when it comes to compatibility. The -// current JSON specification is well defined in RFC 8259, and V8 ensures that -// the versions are compatible by encoding the serialization version in the own -// generated buffer. - -// In memory functions. - -export function deserialize(buffer: Buffer): unknown { - return v8Deserialize(buffer); -} - -export function serialize(content: unknown): Buffer { - return v8Serialize(content); -} - -// Synchronous filesystem functions. - -export function readFileSync(filePath: string): unknown { - return v8Deserialize(fs.readFileSync(filePath)); -} - -export function writeFileSync(filePath: string, content: unknown): void { - return fs.writeFileSync(filePath, v8Serialize(content)); -} - -export default { - deserialize, - readFileSync, - serialize, - writeFileSync, -}; diff --git a/packages/jest-serializer/tsconfig.json b/packages/jest-serializer/tsconfig.json deleted file mode 100644 index bb13eb783ccc..000000000000 --- a/packages/jest-serializer/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "build" - }, - "include": ["./src/**/*"], - "exclude": ["./**/__tests__/**/*"] -} diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 8d1d71f9ac16..8116ba205388 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "jest-snapshot", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,40 +17,40 @@ "./package.json": "./package.json" }, "dependencies": { - "@babel/core": "^7.7.2", + "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/expect-utils": "^28.0.0-alpha.7", - "@jest/transform": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", - "@types/babel__traverse": "^7.0.4", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.0.1", + "@jest/transform": "^28.0.1", + "@jest/types": "^28.0.1", + "@types/babel__traverse": "^7.0.6", "@types/prettier": "^2.1.5", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^28.0.0-alpha.7", + "expect": "^28.0.1", "graceful-fs": "^4.2.9", - "jest-diff": "^28.0.0-alpha.7", - "jest-get-type": "^28.0.0-alpha.3", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-matcher-utils": "^28.0.0-alpha.7", - "jest-message-util": "^28.0.0-alpha.7", - "jest-util": "^28.0.0-alpha.7", + "jest-diff": "^28.0.1", + "jest-get-type": "^28.0.0", + "jest-haste-map": "^28.0.1", + "jest-matcher-utils": "^28.0.1", + "jest-message-util": "^28.0.1", + "jest-util": "^28.0.1", "natural-compare": "^1.4.0", - "pretty-format": "^28.0.0-alpha.7", - "semver": "^7.3.2" + "pretty-format": "^28.0.1", + "semver": "^7.3.5" }, "devDependencies": { "@babel/preset-flow": "^7.7.2", - "@babel/preset-react": "^7.7.2", - "@jest/test-utils": "^28.0.0-alpha.7", + "@babel/preset-react": "^7.12.1", + "@jest/test-utils": "^28.0.1", "@types/graceful-fs": "^4.1.3", "@types/natural-compare": "^1.4.0", "@types/semver": "^7.1.0", "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", - "prettier": "^2.0.0" + "prettier": "^2.1.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-source-map/package.json b/packages/jest-source-map/package.json index 91f7a80446f1..aae1e14d1b64 100644 --- a/packages/jest-source-map/package.json +++ b/packages/jest-source-map/package.json @@ -1,6 +1,6 @@ { "name": "@jest/source-map", - "version": "28.0.0-alpha.6", + "version": "28.0.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,12 +17,12 @@ "./package.json": "./package.json" }, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" + "graceful-fs": "^4.2.9" }, "devDependencies": { - "@types/graceful-fs": "^4.1.2" + "@types/graceful-fs": "^4.1.3" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/jest-source-map/src/__tests__/getCallsite.test.ts b/packages/jest-source-map/src/__tests__/getCallsite.test.ts index 00f265f9b2d9..0482b12f7323 100644 --- a/packages/jest-source-map/src/__tests__/getCallsite.test.ts +++ b/packages/jest-source-map/src/__tests__/getCallsite.test.ts @@ -5,11 +5,19 @@ * LICENSE file in the root directory of this source tree. */ +import {originalPositionFor} from '@jridgewell/trace-mapping'; import * as fs from 'graceful-fs'; -import SourceMap from 'source-map'; import getCallsite from '../getCallsite'; jest.mock('graceful-fs'); +jest.mock('@jridgewell/trace-mapping', () => { + const actual = jest.requireActual('@jridgewell/trace-mapping'); + + return { + ...actual, + originalPositionFor: jest.fn(actual.originalPositionFor), + }; +}); describe('getCallsite', () => { test('without source map', () => { @@ -35,30 +43,35 @@ describe('getCallsite', () => { }); test('reads source map file to determine line and column', () => { - (fs.readFileSync as jest.Mock).mockImplementation(() => 'file data'); + (fs.readFileSync as jest.Mock).mockImplementation(() => + JSON.stringify({ + file: 'file.js', + mappings: 'AAAA,OAAO,MAAM,KAAK,GAAG,QAAd', + names: [], + sources: ['file.js'], + sourcesContent: ["export const hello = 'foobar';\\n"], + version: 3, + }), + ); const sourceMapColumn = 1; const sourceMapLine = 2; - SourceMap.SourceMapConsumer = class { - originalPositionFor(params: Record) { - expect(params).toMatchObject({ - column: expect.any(Number), - line: expect.any(Number), - }); - - return { - column: sourceMapColumn, - line: sourceMapLine, - }; - } - }; + jest.mocked(originalPositionFor).mockImplementation(() => ({ + column: sourceMapColumn, + line: sourceMapLine, + })); const site = getCallsite(0, new Map([[__filename, 'mockedSourceMapFile']])); expect(site.getFileName()).toEqual(__filename); expect(site.getColumnNumber()).toEqual(sourceMapColumn); expect(site.getLineNumber()).toEqual(sourceMapLine); + expect(originalPositionFor).toHaveBeenCalledTimes(1); + expect(originalPositionFor).toHaveBeenCalledWith(expect.anything(), { + column: expect.any(Number), + line: expect.any(Number), + }); expect(fs.readFileSync).toHaveBeenCalledWith('mockedSourceMapFile', 'utf8'); }); }); diff --git a/packages/jest-source-map/src/getCallsite.ts b/packages/jest-source-map/src/getCallsite.ts index 44d4420fb201..930e5c1eb5bb 100644 --- a/packages/jest-source-map/src/getCallsite.ts +++ b/packages/jest-source-map/src/getCallsite.ts @@ -5,23 +5,23 @@ * LICENSE file in the root directory of this source tree. */ +import {TraceMap, originalPositionFor} from '@jridgewell/trace-mapping'; import callsites = require('callsites'); import {readFileSync} from 'graceful-fs'; -import {SourceMapConsumer} from 'source-map'; import type {SourceMapRegistry} from './types'; // Copied from https://github.com/rexxars/sourcemap-decorate-callsites/blob/5b9735a156964973a75dc62fd2c7f0c1975458e8/lib/index.js#L113-L158 const addSourceMapConsumer = ( callsite: callsites.CallSite, - consumer: SourceMapConsumer, + tracer: TraceMap, ) => { const getLineNumber = callsite.getLineNumber; const getColumnNumber = callsite.getColumnNumber; - let position: ReturnType | null = null; + let position: ReturnType | null = null; function getPosition() { if (!position) { - position = consumer.originalPositionFor({ + position = originalPositionFor(tracer, { column: getColumnNumber.call(callsite) || -1, line: getLineNumber.call(callsite) || -1, }); @@ -57,8 +57,7 @@ export default function getCallsite( if (sourceMapFileName) { try { const sourceMap = readFileSync(sourceMapFileName, 'utf8'); - // @ts-expect-error: Not allowed to pass string - addSourceMapConsumer(stack, new SourceMapConsumer(sourceMap)); + addSourceMapConsumer(stack, new TraceMap(sourceMap)); } catch { // ignore } diff --git a/packages/jest-test-result/package.json b/packages/jest-test-result/package.json index ff7679b2c649..12ce5cff0bfe 100644 --- a/packages/jest-test-result/package.json +++ b/packages/jest-test-result/package.json @@ -1,6 +1,6 @@ { "name": "@jest/test-result", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,8 +17,8 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/console": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/console": "^28.0.1", + "@jest/types": "^28.0.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, diff --git a/packages/jest-test-result/src/index.ts b/packages/jest-test-result/src/index.ts index 26edbc397fa6..0ffa340c2c03 100644 --- a/packages/jest-test-result/src/index.ts +++ b/packages/jest-test-result/src/index.ts @@ -25,6 +25,7 @@ export type { Status, Suite, Test, + TestContext, TestEvents, TestFileEvent, TestResult, diff --git a/packages/jest-test-result/src/types.ts b/packages/jest-test-result/src/types.ts index 40abef5f541a..2494a47f7c65 100644 --- a/packages/jest-test-result/src/types.ts +++ b/packages/jest-test-result/src/types.ts @@ -183,12 +183,12 @@ export type SnapshotSummary = { }; export type Test = { - context: Context; + context: TestContext; duration?: number; path: string; }; -type Context = { +export type TestContext = { config: Config.ProjectConfig; hasteFS: HasteFS; moduleMap: ModuleMap; diff --git a/packages/jest-test-sequencer/package.json b/packages/jest-test-sequencer/package.json index 6793d7b923ea..7cf78ca71136 100644 --- a/packages/jest-test-sequencer/package.json +++ b/packages/jest-test-sequencer/package.json @@ -1,6 +1,6 @@ { "name": "@jest/test-sequencer", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,14 +17,13 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/test-result": "^28.0.0-alpha.7", + "@jest/test-result": "^28.0.1", "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-runtime": "^28.0.0-alpha.7", + "jest-haste-map": "^28.0.1", "slash": "^3.0.0" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", + "@jest/test-utils": "^28.0.1", "@types/graceful-fs": "^4.1.3" }, "engines": { diff --git a/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.ts b/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.ts index 7d7a937ab5a9..52a48762e22c 100644 --- a/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.ts +++ b/packages/jest-test-sequencer/src/__tests__/test_sequencer.test.ts @@ -7,9 +7,8 @@ import * as path from 'path'; import * as mockedFs from 'graceful-fs'; -import type {Test} from '@jest/test-result'; +import type {Test, TestContext} from '@jest/test-result'; import {makeProjectConfig} from '@jest/test-utils'; -import type {Context} from 'jest-runtime'; import TestSequencer from '../index'; jest.mock('graceful-fs', () => ({ @@ -24,24 +23,24 @@ let sequencer: TestSequencer; const fs = jest.mocked(mockedFs); -const context: Context = { +const context: TestContext = { config: makeProjectConfig({ cache: true, cacheDirectory: '/cache', haste: {}, - name: 'test', + id: 'test', }), hasteFS: { getSize: path => path.length, }, }; -const secondContext: Context = { +const secondContext: TestContext = { config: makeProjectConfig({ cache: true, cacheDirectory: '/cache2', haste: {}, - name: 'test2', + id: 'test2', }), hasteFS: { getSize: path => path.length, diff --git a/packages/jest-test-sequencer/src/index.ts b/packages/jest-test-sequencer/src/index.ts index 4b103f8a7725..2a643c4f777d 100644 --- a/packages/jest-test-sequencer/src/index.ts +++ b/packages/jest-test-sequencer/src/index.ts @@ -9,9 +9,8 @@ import * as crypto from 'crypto'; import * as path from 'path'; import * as fs from 'graceful-fs'; import slash = require('slash'); -import type {AggregatedResult, Test} from '@jest/test-result'; +import type {AggregatedResult, Test, TestContext} from '@jest/test-result'; import HasteMap from 'jest-haste-map'; -import type {Context} from 'jest-runtime'; const FAIL = 0; const SUCCESS = 1; @@ -39,14 +38,14 @@ export type ShardOptions = { * is called to store/update this information on the cache map. */ export default class TestSequencer { - private _cache: Map = new Map(); + private _cache: Map = new Map(); - _getCachePath(context: Context): string { - const {config} = context; + _getCachePath(testContext: TestContext): string { + const {config} = testContext; const HasteMapClass = HasteMap.getStatic(config); return HasteMapClass.getCacheFilePath( config.cacheDirectory, - `perf-cache-${config.name}`, + `perf-cache-${config.id}`, ); } diff --git a/packages/jest-test-sequencer/tsconfig.json b/packages/jest-test-sequencer/tsconfig.json index 70822da7c647..7d3736031d3b 100644 --- a/packages/jest-test-sequencer/tsconfig.json +++ b/packages/jest-test-sequencer/tsconfig.json @@ -8,7 +8,6 @@ "exclude": ["./**/__tests__/**/*"], "references": [ {"path": "../jest-haste-map"}, - {"path": "../jest-runtime"}, {"path": "../jest-test-result"}, {"path": "../test-utils"} ] diff --git a/packages/jest-transform/package.json b/packages/jest-transform/package.json index 47e4f5ff93be..0b10a876f96e 100644 --- a/packages/jest-transform/package.json +++ b/packages/jest-transform/package.json @@ -1,6 +1,6 @@ { "name": "@jest/transform", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,28 +17,28 @@ "./package.json": "./package.json" }, "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^28.0.0-alpha.7", + "@babel/core": "^7.11.6", + "@jest/types": "^28.0.1", + "@jridgewell/trace-mapping": "^0.3.7", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.0.0-alpha.7", - "jest-regex-util": "^28.0.0-alpha.6", - "jest-util": "^28.0.0-alpha.7", + "jest-haste-map": "^28.0.1", + "jest-regex-util": "^28.0.0", + "jest-util": "^28.0.1", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", - "source-map": "^0.6.1", "write-file-atomic": "^4.0.1" }, "devDependencies": { - "@jest/test-utils": "^28.0.0-alpha.7", - "@types/babel__core": "^7.1.0", + "@jest/test-utils": "^28.0.1", + "@types/babel__core": "^7.1.14", "@types/convert-source-map": "^1.5.1", "@types/fast-json-stable-stringify": "^2.0.0", - "@types/graceful-fs": "^4.1.2", + "@types/graceful-fs": "^4.1.3", "@types/micromatch": "^4.0.1", "@types/write-file-atomic": "^4.0.0", "dedent": "^0.7.0" diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index bfd425984283..fc674e22ebac 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -42,6 +42,7 @@ import type { TransformResult, TransformedSource, Transformer, + TransformerFactory, } from './types'; // Use `require` to avoid TS rootDir const {version: VERSION} = require('../package.json'); @@ -72,6 +73,13 @@ async function waitForPromiseWithCleanup( } } +// type predicate +function isTransformerFactory( + t: Transformer | TransformerFactory, +): t is TransformerFactory { + return typeof (t as TransformerFactory).createTransformer === 'function'; +} + class ScriptTransformer { private readonly _cache: ProjectCache; private readonly _transformCache = new Map< @@ -108,19 +116,21 @@ class ScriptTransformer { transformerCacheKey: string | undefined, ): string { if (transformerCacheKey) { - return createHash('md5') + return createHash('sha256') .update(transformerCacheKey) .update(CACHE_VERSION) - .digest('hex'); + .digest('hex') + .substring(0, 32); } - return createHash('md5') + return createHash('sha256') .update(fileData) .update(transformOptions.configString) .update(transformOptions.instrument ? 'instrument' : '') .update(filename) .update(CACHE_VERSION) - .digest('hex'); + .digest('hex') + .substring(0, 32); } private _getCacheKey( @@ -203,7 +213,7 @@ class ScriptTransformer { const HasteMapClass = HasteMap.getStatic(this._config); const baseCacheDir = HasteMapClass.getCacheFilePath( this._config.cacheDirectory, - `jest-transform-cache-${this._config.name}`, + `jest-transform-cache-${this._config.id}`, VERSION, ); // Create sub folders based on the cacheKey to avoid creating one @@ -259,14 +269,13 @@ class ScriptTransformer { await Promise.all( this._config.transform.map( async ([, transformPath, transformerConfig]) => { - let transformer: Transformer = await requireOrImportModule( - transformPath, - ); + let transformer: Transformer | TransformerFactory = + await requireOrImportModule(transformPath); if (!transformer) { throw new Error(makeInvalidTransformerError(transformPath)); } - if (typeof transformer.createTransformer === 'function') { + if (isTransformerFactory(transformer)) { transformer = transformer.createTransformer(transformerConfig); } if ( @@ -372,9 +381,7 @@ class ScriptTransformer { }; if (transformer && shouldCallTransform) { - if (typeof processed === 'string') { - transformed.code = processed; - } else if (processed != null && typeof processed.code === 'string') { + if (processed != null && typeof processed.code === 'string') { transformed = processed; } else { throw new Error(makeInvalidReturnValueError()); @@ -476,7 +483,7 @@ class ScriptTransformer { }; } - let processed = null; + let processed: TransformedSource | null = null; let shouldCallTransform = false; @@ -759,7 +766,10 @@ class ScriptTransformer { } }, { - exts: this._config.moduleFileExtensions.map(ext => `.${ext}`), + // Exclude `mjs` extension when addHook because pirates don't support hijack es module + exts: this._config.moduleFileExtensions + .filter(ext => ext !== 'mjs') + .map(ext => `.${ext}`), ignoreNodeModules: false, matcher: filename => { if (transforming) { @@ -861,7 +871,10 @@ const stripShebang = (content: string) => { * could get corrupted, out-of-sync, etc. */ function writeCodeCacheFile(cachePath: string, code: string) { - const checksum = createHash('md5').update(code).digest('hex'); + const checksum = createHash('sha256') + .update(code) + .digest('hex') + .substring(0, 32); writeCacheFile(cachePath, `${checksum}\n${code}`); } @@ -877,7 +890,10 @@ function readCodeCacheFile(cachePath: string): string | null { return null; } const code = content.substring(33); - const checksum = createHash('md5').update(code).digest('hex'); + const checksum = createHash('sha256') + .update(code) + .digest('hex') + .substring(0, 32); if (checksum === content.substring(0, 32)) { return code; } diff --git a/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts b/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts index 3e3533f781ce..5cb141be586e 100644 --- a/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts +++ b/packages/jest-transform/src/__tests__/ScriptTransformer.test.ts @@ -12,7 +12,7 @@ import type {Options, ShouldInstrumentOptions, Transformer} from '../types'; jest .mock('graceful-fs', () => ({ - ...jest.requireActual('graceful-fs'), + ...jest.requireActual('graceful-fs'), /* eslint-disable sort-keys */ readFileSync: jest.fn((path, options) => { mockInvariant(typeof path === 'string'); @@ -55,7 +55,7 @@ jest }, })) .mock('jest-util', () => ({ - ...jest.requireActual('jest-util'), + ...jest.requireActual('jest-util'), createDirectory: jest.fn(), })) .mock('path', () => jest.requireActual('path').posix); @@ -67,13 +67,15 @@ jest.mock( const transformer: Transformer = { getCacheKey: jest.fn(() => 'ab'), - process: (content, filename, config) => require('dedent')` + process: (content, filename, config) => ({ + code: require('dedent')` const TRANSFORMED = { filename: '${escapeStrings(filename)}', script: '${escapeStrings(content)}', config: '${escapeStrings(JSON.stringify(config))}', }; `, + }), }; return transformer; @@ -88,14 +90,15 @@ jest.mock( const transformer: Transformer = { getCacheKeyAsync: jest.fn().mockResolvedValue('ab'), - processAsync: async (content, filename, config) => - require('dedent')` + processAsync: async (content, filename, config) => ({ + code: require('dedent')` const TRANSFORMED = { filename: '${escapeStrings(filename)}', script: '${escapeStrings(content)}', config: '${escapeStrings(JSON.stringify(config))}', }; `, + }), }; return transformer; @@ -107,7 +110,7 @@ jest.mock( 'configureable-preprocessor', () => ({ createTransformer: jest.fn(() => ({ - process: jest.fn(() => 'processedCode'), + process: jest.fn().mockReturnValue({code: 'processedCode'}), })), }), {virtual: true}, @@ -117,7 +120,7 @@ jest.mock( 'cache_fs_preprocessor', () => ({ getCacheKey: jest.fn(() => 'ab'), - process: jest.fn(() => 'processedCode'), + process: jest.fn().mockReturnValue({code: 'processedCode'}), }), {virtual: true}, ); @@ -126,7 +129,7 @@ jest.mock( 'cache_fs_async_preprocessor', () => ({ getCacheKeyAsync: jest.fn().mockResolvedValue('ab'), - processAsync: jest.fn().mockResolvedValue('processedCode'), + processAsync: jest.fn().mockResolvedValue({code: 'processedCode'}), }), {virtual: true}, ); @@ -154,12 +157,14 @@ jest.mock( () => { const transformer: Transformer = { getCacheKey: jest.fn(() => 'cd'), - process: (content, filename) => jest.requireActual('dedent')` + process: (content, filename) => ({ + code: require('dedent')` module.exports = { filename: ${filename}, rawFirstLine: ${content.split('\n')[0]}, }; `, + }), }; return transformer; @@ -177,14 +182,14 @@ jest.mock('skipped-required-props-preprocessor', () => ({}), {virtual: true}); // Bad preprocessor jest.mock( 'skipped-required-props-preprocessor-only-sync', - () => ({process: () => ''}), + () => ({process: () => ({code: ''})}), {virtual: true}, ); // Bad preprocessor jest.mock( 'skipped-required-props-preprocessor-only-async', - () => ({processAsync: async () => ''}), + () => ({processAsync: async () => ({code: ''})}), {virtual: true}, ); @@ -203,7 +208,7 @@ jest.mock( 'skipped-process-method-preprocessor', () => ({ createTransformer() { - return {process: jest.fn(() => 'code')}; + return {process: jest.fn().mockReturnValue({code: 'code'})}; }, }), {virtual: true}, @@ -213,7 +218,7 @@ jest.mock( 'factory-for-async-preprocessor', () => ({ createTransformer() { - return {processAsync: jest.fn().mockResolvedValue('code')}; + return {processAsync: jest.fn().mockResolvedValue({code: 'code'})}; }, }), {virtual: true}, @@ -273,7 +278,7 @@ describe('ScriptTransformer', () => { config = makeProjectConfig({ cache: true, cacheDirectory: '/cache/', - name: 'test', + id: 'test', rootDir: '/', transformIgnorePatterns: ['/node_modules/'], }); @@ -376,7 +381,7 @@ describe('ScriptTransformer', () => { ); }); - it("throws an error if `process` doesn't return a string or an object containing `code` key with processed string", async () => { + it("throws an error if `process` doesn't return an object containing `code` key with processed string", async () => { config = { ...config, transform: [['\\.js$', 'passthrough-preprocessor', {}]], @@ -385,6 +390,7 @@ describe('ScriptTransformer', () => { const incorrectReturnValues = [ [undefined, '/fruits/banana.js'], + ['code', '/fruits/banana.js'], [{a: 'a'}, '/fruits/kiwi.js'], [[], '/fruits/grapefruit.js'], ]; @@ -394,13 +400,10 @@ describe('ScriptTransformer', () => { require('passthrough-preprocessor').process.mockReturnValue(returnValue); expect(() => scriptTransformer.transform(filePath, getCoverageOptions()), - ).toThrow('must return a string'); + ).toThrowErrorMatchingSnapshot(); }); - const correctReturnValues = [ - ['code', '/fruits/banana.js'], - [{code: 'code'}, '/fruits/kiwi.js'], - ]; + const correctReturnValues = [[{code: 'code'}, '/fruits/kiwi.js']]; correctReturnValues.forEach(([returnValue, filePath]) => { mockInvariant(typeof filePath === 'string'); @@ -411,15 +414,15 @@ describe('ScriptTransformer', () => { }); }); - it("throws an error if `processAsync` doesn't return a promise of string or object containing `code` key with processed string", async () => { + it("throws an error if `processAsync` doesn't return a promise of object containing `code` key with processed string", async () => { const incorrectReturnValues: Array<[any, string]> = [ [undefined, '/fruits/banana.js'], + ['code', '/fruits/avocado.js'], [{a: 'a'}, '/fruits/kiwi.js'], [[], '/fruits/grapefruit.js'], ]; const correctReturnValues: Array<[any, string]> = [ - ['code', '/fruits/avocado.js'], [{code: 'code'}, '/fruits/mango.js'], ]; @@ -453,10 +456,7 @@ describe('ScriptTransformer', () => { const promisesToReject = incorrectReturnValues .map(buildPromise) - .map(promise => - // Jest must throw error - expect(promise).rejects.toThrow(), - ); + .map(promise => expect(promise).rejects.toThrowErrorMatchingSnapshot()); const promisesToResolve = correctReturnValues .map(buildPromise) @@ -790,7 +790,9 @@ describe('ScriptTransformer', () => { sourceMap, ).toString('base64')}`; - require('preprocessor-with-sourcemaps').process.mockReturnValue(content); + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: content, + }); const result = scriptTransformer.transform( '/fruits/banana.js', @@ -823,7 +825,9 @@ describe('ScriptTransformer', () => { sourceMap, ).toString('base64')}`; - require('preprocessor-with-sourcemaps').process.mockReturnValue(content); + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: content, + }); const result = await scriptTransformer.transformAsync( '/fruits/banana.js', @@ -857,7 +861,7 @@ describe('ScriptTransformer', () => { ).toString('base64')}`; require('async-preprocessor-with-sourcemaps').processAsync.mockResolvedValue( - content, + {code: content}, ); const result = await scriptTransformer.transformAsync( @@ -897,7 +901,9 @@ describe('ScriptTransformer', () => { .toString('base64') .slice(0, 16)}`; - require('preprocessor-with-sourcemaps').process.mockReturnValue(content); + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: content, + }); const result = scriptTransformer.transform( '/fruits/banana.js', @@ -935,7 +941,9 @@ describe('ScriptTransformer', () => { .toString('base64') .slice(0, 16)}`; - require('preprocessor-with-sourcemaps').process.mockReturnValue(content); + require('preprocessor-with-sourcemaps').process.mockReturnValue({ + code: content, + }); const result = await scriptTransformer.transformAsync( '/fruits/banana.js', @@ -974,7 +982,7 @@ describe('ScriptTransformer', () => { .slice(0, 16)}`; require('async-preprocessor-with-sourcemaps').processAsync.mockResolvedValue( - content, + {code: content}, ); const result = await scriptTransformer.transformAsync( diff --git a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap index 7d89d8bbf339..d79e6c7ee43f 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap @@ -34,12 +34,16 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "displayName": undefined, "errorOnDeprecated": false, "extensionsToTreatAsEsm": Array [], + "fakeTimers": Object { + "enableGlobally": false, + }, "filter": undefined, "forceCoverageMatch": Array [], "globalSetup": undefined, "globalTeardown": undefined, "globals": Object {}, "haste": Object {}, + "id": "test", "injectGlobals": true, "moduleDirectories": Array [], "moduleFileExtensions": Array [ @@ -48,7 +52,6 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "moduleNameMapper": Array [], "modulePathIgnorePatterns": Array [], "modulePaths": Array [], - "name": "test", "prettierPath": "prettier", "resetMocks": false, "resetModules": false, @@ -76,7 +79,6 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "\\.test\\.js$", ], "testRunner": "jest-circus/runner", - "timers": "real", "transform": Array [ Array [ "\\.js$", @@ -92,7 +94,7 @@ exports[`ScriptTransformer in async mode, passes expected transform options to g "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", + "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -118,7 +120,7 @@ exports[`ScriptTransformer in async mode, uses the supplied async preprocessor 1 "const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_async_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"fakeTimers\\":{\\"enableGlobally\\":false},\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"id\\":\\"test\\",\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', };" `; @@ -128,7 +130,7 @@ exports[`ScriptTransformer in async mode, uses the supplied preprocessor 1`] = ` "const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"fakeTimers\\":{\\"enableGlobally\\":false},\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"id\\":\\"test\\",\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', };" `; @@ -165,12 +167,16 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "displayName": undefined, "errorOnDeprecated": false, "extensionsToTreatAsEsm": Array [], + "fakeTimers": Object { + "enableGlobally": false, + }, "filter": undefined, "forceCoverageMatch": Array [], "globalSetup": undefined, "globalTeardown": undefined, "globals": Object {}, "haste": Object {}, + "id": "test", "injectGlobals": true, "moduleDirectories": Array [], "moduleFileExtensions": Array [ @@ -179,7 +185,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "moduleNameMapper": Array [], "modulePathIgnorePatterns": Array [], "modulePaths": Array [], - "name": "test", "prettierPath": "prettier", "resetMocks": false, "resetModules": false, @@ -207,7 +212,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "\\.test\\.js$", ], "testRunner": "jest-circus/runner", - "timers": "real", "transform": Array [ Array [ "\\.js$", @@ -223,7 +227,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", + "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -270,12 +274,16 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "displayName": undefined, "errorOnDeprecated": false, "extensionsToTreatAsEsm": Array [], + "fakeTimers": Object { + "enableGlobally": false, + }, "filter": undefined, "forceCoverageMatch": Array [], "globalSetup": undefined, "globalTeardown": undefined, "globals": Object {}, "haste": Object {}, + "id": "test", "injectGlobals": true, "moduleDirectories": Array [], "moduleFileExtensions": Array [ @@ -284,7 +292,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "moduleNameMapper": Array [], "modulePathIgnorePatterns": Array [], "modulePaths": Array [], - "name": "test", "prettierPath": "prettier", "resetMocks": false, "resetModules": false, @@ -312,7 +319,6 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "\\.test\\.js$", ], "testRunner": "jest-circus/runner", - "timers": "real", "transform": Array [ Array [ "\\.js$", @@ -328,7 +334,7 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync "unmockedModulePathPatterns": undefined, "watchPathIgnorePatterns": Array [], }, - "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", + "configString": "{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_async_preprocessor",{"configKey":"configValue"}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]}", "coverageProvider": "babel", "instrument": true, "supportsDynamicImport": false, @@ -350,6 +356,86 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync } `; +exports[`ScriptTransformer throws an error if \`process\` doesn't return an object containing \`code\` key with processed string 1`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`process\` doesn't return an object containing \`code\` key with processed string 2`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`process\` doesn't return an object containing \`code\` key with processed string 3`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`process\` doesn't return an object containing \`code\` key with processed string 4`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`processAsync\` doesn't return a promise of object containing \`code\` key with processed string 1`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`processAsync\` doesn't return a promise of object containing \`code\` key with processed string 2`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`processAsync\` doesn't return a promise of object containing \`code\` key with processed string 3`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + +exports[`ScriptTransformer throws an error if \`processAsync\` doesn't return a promise of object containing \`code\` key with processed string 4`] = ` +"● Invalid return value: + Code transformer's \`process\` method must return an object containing \`code\` key + with processed string. If \`processAsync\` method is implemented it must return + a Promise resolving to an object containing \`code\` key with processed string. + Code Transformation Documentation: + https://jestjs.io/docs/code-transformation +" +`; + exports[`ScriptTransformer throws an error if createTransformer returns object without \`process\` method 1`] = ` "● Invalid transformer module: "skipped-required-create-transformer-props-preprocessor" specified in the "transform" object of Jest configuration @@ -676,7 +762,7 @@ exports[`ScriptTransformer uses mixture of sync/async preprocessors 1`] = ` "const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_async_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"fakeTimers\\":{\\"enableGlobally\\":false},\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"id\\":\\"test\\",\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_async_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', };" `; @@ -693,7 +779,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` "const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_preprocessor",{}],["\\\\.css$","css-preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"fakeTimers\\":{\\"enableGlobally\\":false},\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"id\\":\\"test\\",\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}],[\\"\\\\\\\\.css$\\",\\"css-preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', };" `; @@ -710,7 +796,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` "const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = "banana";', - config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"forceCoverageMatch":[],"globals":{},"haste":{},"injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"name":"test","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","timers":"real","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', + config: '{"collectCoverage":false,"collectCoverageFrom":[],"coverageProvider":"babel","supportsDynamicImport":false,"supportsExportNamespaceFrom":false,"supportsStaticESM":false,"supportsTopLevelAwait":false,"instrument":false,"cacheFS":{},"config":{"automock":false,"cache":true,"cacheDirectory":"/cache/","clearMocks":false,"coveragePathIgnorePatterns":[],"cwd":"/test_root_dir/","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extensionsToTreatAsEsm":[],"fakeTimers":{"enableGlobally":false},"forceCoverageMatch":[],"globals":{},"haste":{},"id":"test","injectGlobals":true,"moduleDirectories":[],"moduleFileExtensions":["js"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"modulePaths":[],"prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/","roots":[],"runner":"jest-runner","runtime":"/test_module_loader_path","sandboxInjectedGlobals":[],"setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"skipNodeResolution":false,"slowTestThreshold":5,"snapshotFormat":{},"snapshotSerializers":[],"testEnvironment":"node","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":[],"testPathIgnorePatterns":[],"testRegex":["\\\\.test\\\\.js$"],"testRunner":"jest-circus/runner","transform":[["\\\\.js$","test_preprocessor",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"configString":"{\\"automock\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extensionsToTreatAsEsm\\":[],\\"fakeTimers\\":{\\"enableGlobally\\":false},\\"forceCoverageMatch\\":[],\\"globals\\":{},\\"haste\\":{},\\"id\\":\\"test\\",\\"injectGlobals\\":true,\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"runtime\\":\\"/test_module_loader_path\\",\\"sandboxInjectedGlobals\\":[],\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"slowTestThreshold\\":5,\\"snapshotFormat\\":{},\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-circus/runner\\",\\"transform\\":[[\\"\\\\\\\\.js$\\",\\"test_preprocessor\\",{}]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"watchPathIgnorePatterns\\":[]}","transformerConfig":{}}', };" `; diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 90ac16070cee..e81fe036ba4f 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -18,6 +18,7 @@ export type { AsyncTransformer, ShouldInstrumentOptions, Options as TransformationOptions, + TransformerCreator, TransformOptions, TransformResult, TransformedSource, diff --git a/packages/jest-transform/src/runtimeErrorsAndWarnings.ts b/packages/jest-transform/src/runtimeErrorsAndWarnings.ts index 1b31299130ef..c57292ac5b14 100644 --- a/packages/jest-transform/src/runtimeErrorsAndWarnings.ts +++ b/packages/jest-transform/src/runtimeErrorsAndWarnings.ts @@ -19,9 +19,9 @@ export const makeInvalidReturnValueError = (): string => chalk.red( [ chalk.bold(`${BULLET}Invalid return value:`), - " Code transformer's `process` function must return a string or an object", - ' with `code` key containing a string. If `processAsync` function is implemented,', - ' it must return a Promise resolving to one of these values.', + " Code transformer's `process` method must return an object containing `code` key ", + ' with processed string. If `processAsync` method is implemented it must return ', + ' a Promise resolving to an object containing `code` key with processed string.', '', ].join('\n') + DOCUMENTATION_NOTE, ); diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 1d28cbdb30ea..a86d56e973d1 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import type {RawSourceMap} from 'source-map'; +import type {EncodedSourceMap} from '@jridgewell/trace-mapping'; import type {Config, TransformTypes} from '@jest/types'; export interface ShouldInstrumentOptions @@ -26,15 +26,15 @@ export interface Options isInternalModule?: boolean; } -// This is fixed in source-map@0.7.x, but we can't upgrade yet since it's async -interface FixedRawSourceMap extends Omit { +// `babel` and `@jridgewell/trace-mapping` disagrees - `number` vs `3` +interface FixedRawSourceMap extends Omit { version: number; } -// TODO: For Jest 26 normalize this (always structured data, never a string) -export type TransformedSource = - | {code: string; map?: FixedRawSourceMap | string | null} - | string; +export type TransformedSource = { + code: string; + map?: FixedRawSourceMap | string | null; +}; export type TransformResult = TransformTypes.TransformResult; @@ -57,18 +57,19 @@ export interface RequireAndTranspileModuleOptions export type StringMap = Map; -export interface TransformOptions +export interface TransformOptions extends ReducedTransformOptions { - /** a cached file system which is used in jest-runtime - useful to improve performance */ + /** Cached file system which is used by `jest-runtime` to improve performance. */ cacheFS: StringMap; + /** Jest configuration of currently running project. */ config: Config.ProjectConfig; - /** A stringified version of the configuration - useful in cache busting */ + /** Stringified version of the `config` - useful in cache busting. */ configString: string; - /** the options passed through Jest's config by the user */ - transformerConfig: OptionType; + /** Transformer configuration passed through `transform` option by the user. */ + transformerConfig: TransformerConfig; } -export interface SyncTransformer { +export interface SyncTransformer { /** * Indicates if the transformer is capable of instrumenting the code for code coverage. * @@ -76,34 +77,33 @@ export interface SyncTransformer { * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. */ canInstrument?: boolean; - createTransformer?: (options?: OptionType) => SyncTransformer; getCacheKey?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => string; getCacheKeyAsync?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => Promise; process: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => TransformedSource; processAsync?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => Promise; } -export interface AsyncTransformer { +export interface AsyncTransformer { /** * Indicates if the transformer is capable of instrumenting the code for code coverage. * @@ -111,33 +111,54 @@ export interface AsyncTransformer { * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. */ canInstrument?: boolean; - createTransformer?: (options?: OptionType) => AsyncTransformer; getCacheKey?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => string; getCacheKeyAsync?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => Promise; process?: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => TransformedSource; processAsync: ( sourceText: string, sourcePath: string, - options: TransformOptions, + options: TransformOptions, ) => Promise; } -export type Transformer = - | SyncTransformer - | AsyncTransformer; +/** + * We have both sync (`process`) and async (`processAsync`) code transformation, which both can be provided. + * `require` will always use `process`, and `import` will use `processAsync` if it exists, otherwise fall back to `process`. + * Meaning, if you use `import` exclusively you do not need `process`, but in most cases supplying both makes sense: + * Jest transpiles on demand rather than ahead of time, so the sync one needs to exist. + * + * For more info on the sync vs async model, see https://jestjs.io/docs/code-transformation#writing-custom-transformers + */ +export type Transformer = + | SyncTransformer + | AsyncTransformer; + +export type TransformerCreator< + X extends Transformer, + TransformerConfig = unknown, +> = (transformerConfig?: TransformerConfig) => X; + +/** + * Instead of having your custom transformer implement the Transformer interface + * directly, you can choose to export a factory function to dynamically create + * transformers. This is to allow having a transformer config in your jest config. + */ +export type TransformerFactory = { + createTransformer: TransformerCreator; +}; diff --git a/packages/jest-types/__typetests__/config.test.ts b/packages/jest-types/__typetests__/config.test.ts index dd69249ea7b6..0dd56d024ce9 100644 --- a/packages/jest-types/__typetests__/config.test.ts +++ b/packages/jest-types/__typetests__/config.test.ts @@ -5,9 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {expectAssignable} from 'tsd-lite'; +import {expectAssignable, expectError} from 'tsd-lite'; import type {Config} from '@jest/types'; +expectAssignable({}); + expectAssignable({ coverageThreshold: { './src/api/very-important-module.js': { @@ -39,3 +41,79 @@ expectAssignable({ }, ], }); + +const doNotFake: Array = [ + 'Date', + 'hrtime', + 'nextTick', + 'performance', + 'queueMicrotask', + 'requestAnimationFrame', + 'cancelAnimationFrame', + 'requestIdleCallback', + 'cancelIdleCallback', + 'setImmediate', + 'clearImmediate', + 'setInterval', + 'clearInterval', + 'setTimeout', + 'clearTimeout', +]; + +expectAssignable({ + fakeTimers: { + advanceTimers: true, + doNotFake, + enableGlobally: true, + now: 1483228800000, + timerLimit: 1000, + }, +}); + +expectAssignable({ + fakeTimers: { + advanceTimers: 40, + now: Date.now(), + }, +}); + +expectError({ + fakeTimers: { + now: new Date(), + }, +}); + +expectAssignable({ + fakeTimers: { + enableGlobally: true, + legacyFakeTimers: true, + }, +}); + +expectError({ + fakeTimers: { + advanceTimers: true, + legacyFakeTimers: true, + }, +}); + +expectError({ + fakeTimers: { + doNotFake, + legacyFakeTimers: true, + }, +}); + +expectError({ + fakeTimers: { + legacyFakeTimers: true, + now: 1483228800000, + }, +}); + +expectError({ + fakeTimers: { + legacyFakeTimers: true, + timerLimit: 1000, + }, +}); diff --git a/packages/jest-types/__typetests__/jest.test.ts b/packages/jest-types/__typetests__/jest.test.ts index 6c2a9f17b75a..ce95ca1a828a 100644 --- a/packages/jest-types/__typetests__/jest.test.ts +++ b/packages/jest-types/__typetests__/jest.test.ts @@ -7,7 +7,7 @@ import {expectError, expectType} from 'tsd-lite'; import {jest} from '@jest/globals'; -import type {Mock, ModuleMocker, SpyInstance, fn, spyOn} from 'jest-mock'; +import type {Mock, ModuleMocker, SpyInstance} from 'jest-mock'; expectType( jest @@ -43,8 +43,7 @@ expectType( .setTimeout(6000) .unmock('moduleName') .useFakeTimers() - .useFakeTimers('modern') - .useFakeTimers('legacy') + .useFakeTimers({legacyFakeTimers: true}) .useRealTimers(), ); @@ -118,6 +117,7 @@ expectError(jest.unmock()); // Mock Functions +expectType(jest.retryTimes(3, {logErrorsBeforeRetry: true})); expectType(jest.clearAllMocks()); expectError(jest.clearAllMocks('moduleName')); @@ -248,9 +248,53 @@ expectType(jest.setSystemTime(new Date(1995, 11, 17))); expectError(jest.setSystemTime('1995-12-17T03:24:00')); expectType(jest.useFakeTimers()); -expectType(jest.useFakeTimers('modern')); -expectType(jest.useFakeTimers('legacy')); -expectError(jest.useFakeTimers('latest')); + +expectType(jest.useFakeTimers({advanceTimers: true})); +expectType(jest.useFakeTimers({advanceTimers: 10})); +expectError(jest.useFakeTimers({advanceTimers: 'fast'})); + +expectType(jest.useFakeTimers({doNotFake: ['Date']})); +expectType( + jest.useFakeTimers({ + doNotFake: [ + 'Date', + 'hrtime', + 'nextTick', + 'performance', + 'queueMicrotask', + 'requestAnimationFrame', + 'cancelAnimationFrame', + 'requestIdleCallback', + 'cancelIdleCallback', + 'setImmediate', + 'clearImmediate', + 'setInterval', + 'clearInterval', + 'setTimeout', + 'clearTimeout', + ], + }), +); +expectError(jest.useFakeTimers({doNotFake: ['globalThis']})); + +expectType(jest.useFakeTimers({legacyFakeTimers: true})); +expectError(jest.useFakeTimers({legacyFakeTimers: 1000})); +expectError(jest.useFakeTimers({doNotFake: ['Date'], legacyFakeTimers: true})); +expectError(jest.useFakeTimers({enableGlobally: true, legacyFakeTimers: true})); +expectError(jest.useFakeTimers({legacyFakeTimers: true, now: 1483228800000})); +expectError(jest.useFakeTimers({legacyFakeTimers: true, timerLimit: 1000})); + +expectType(jest.useFakeTimers({now: 1483228800000})); +expectType(jest.useFakeTimers({now: Date.now()})); +expectType(jest.useFakeTimers({now: new Date(1995, 11, 17)})); +expectError(jest.useFakeTimers({now: '1995-12-17T03:24:00'})); + +expectType(jest.useFakeTimers({timerLimit: 1000})); +expectError(jest.useFakeTimers({timerLimit: true})); + +expectError(jest.useFakeTimers({enableGlobally: true})); +expectError(jest.useFakeTimers('legacy')); +expectError(jest.useFakeTimers('modern')); expectType(jest.useRealTimers()); expectError(jest.useRealTimers(true)); diff --git a/packages/jest-types/__typetests__/tsconfig.json b/packages/jest-types/__typetests__/tsconfig.json index fe8eab794254..165ba1343021 100644 --- a/packages/jest-types/__typetests__/tsconfig.json +++ b/packages/jest-types/__typetests__/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { + "composite": false, "noUnusedLocals": false, "noUnusedParameters": false, "skipLibCheck": true, diff --git a/packages/jest-types/package.json b/packages/jest-types/package.json index 121bf752b188..0f9bfe00b391 100644 --- a/packages/jest-types/package.json +++ b/packages/jest-types/package.json @@ -1,6 +1,6 @@ { "name": "@jest/types", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -20,7 +20,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/schemas": "^28.0.0-alpha.3", + "@jest/schemas": "^28.0.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index 2f319ce6f4a3..7ed5023d6caf 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -18,6 +18,7 @@ export type TestMode = BlockMode; export type TestName = Global.TestName; export type TestNameLike = Global.TestNameLike; export type TestFn = Global.TestFn; +export type ConcurrentTestFn = Global.ConcurrentTestFn; export type HookFn = Global.HookFn; export type AsyncFn = TestFn | HookFn; export type SharedHookType = 'afterAll' | 'beforeAll'; @@ -71,6 +72,7 @@ export type SyncEvent = testName: TestName; fn: TestFn; mode?: TestMode; + concurrent: boolean; timeout: number | undefined; failing: boolean; } @@ -183,6 +185,7 @@ export type TestResult = { invocations: number; status: TestStatus; location?: {column: number; line: number} | null; + retryReasons: Array; testPath: Array; }; @@ -216,6 +219,7 @@ export type State = { testTimeout: number; unhandledErrors: Array; includeTestLocationInResult: boolean; + maxConcurrency: number; }; export type DescribeBlock = { @@ -235,9 +239,11 @@ export type TestEntry = { type: 'test'; asyncError: Exception; // Used if the test failure contains no usable stack trace errors: Array; + retryReasons: Array; fn: TestFn; invocations: number; mode: TestMode; + concurrent: boolean; name: TestName; parent: DescribeBlock; startedAt?: number | null; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 80243eb45512..1d3f19457b95 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -12,7 +12,94 @@ import type {SnapshotFormat} from '@jest/schemas'; type CoverageProvider = 'babel' | 'v8'; -type Timers = 'real' | 'fake' | 'modern' | 'legacy'; +export type FakeableAPI = + | 'Date' + | 'hrtime' + | 'nextTick' + | 'performance' + | 'queueMicrotask' + | 'requestAnimationFrame' + | 'cancelAnimationFrame' + | 'requestIdleCallback' + | 'cancelIdleCallback' + | 'setImmediate' + | 'clearImmediate' + | 'setInterval' + | 'clearInterval' + | 'setTimeout' + | 'clearTimeout'; + +export type GlobalFakeTimersConfig = { + /** + * Whether fake timers should be enabled globally for all test files. + * + * @defaultValue + * The default is `false`. + * */ + enableGlobally?: boolean; +}; + +export type FakeTimersConfig = { + /** + * If set to `true` all timers will be advanced automatically + * by 20 milliseconds every 20 milliseconds. A custom time delta + * may be provided by passing a number. + * + * @defaultValue + * The default is `false`. + */ + advanceTimers?: boolean | number; + /** + * List of names of APIs (e.g. `Date`, `nextTick()`, `setImmediate()`, + * `setTimeout()`) that should not be faked. + * + * @defaultValue + * The default is `[]`, meaning all APIs are faked. + * */ + doNotFake?: Array; + /** + * Sets current system time to be used by fake timers. + * + * @defaultValue + * The default is `Date.now()`. + */ + now?: number | Date; + /** + * The maximum number of recursive timers that will be run when calling + * `jest.runAllTimers()`. + * + * @defaultValue + * The default is `100_000` timers. + */ + timerLimit?: number; + /** + * Use the old fake timers implementation instead of one backed by + * [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). + * + * @defaultValue + * The default is `false`. + */ + legacyFakeTimers?: false; +}; + +export type LegacyFakeTimersConfig = { + /** + * Use the old fake timers implementation instead of one backed by + * [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). + * + * @defaultValue + * The default is `false`. + */ + legacyFakeTimers?: true; +}; + +type FakeTimers = GlobalFakeTimersConfig & + ( + | (FakeTimersConfig & { + now?: Exclude; + }) + | LegacyFakeTimersConfig + ); export type HasteConfig = { /** Whether to hash files using SHA-1. */ @@ -76,6 +163,7 @@ export type DefaultOptions = { errorOnDeprecated: boolean; expand: boolean; extensionsToTreatAsEsm: Array; + fakeTimers: FakeTimers; forceCoverageMatch: Array; globals: ConfigGlobals; haste: HasteConfig; @@ -112,7 +200,6 @@ export type DefaultOptions = { testRegex: Array; testRunner: string; testSequencer: string; - timers: Timers; transformIgnorePatterns: Array; useStderr: boolean; watch: boolean; @@ -158,6 +245,7 @@ export type InitialOptions = Partial<{ displayName: string | DisplayName; expand: boolean; extensionsToTreatAsEsm: Array; + fakeTimers: FakeTimers; filter: string; findRelatedTests: boolean; forceCoverageMatch: Array; @@ -167,6 +255,7 @@ export type InitialOptions = Partial<{ globalSetup: string | null | undefined; globalTeardown: string | null | undefined; haste: HasteConfig; + id: string; injectGlobals: boolean; reporters: Array; logHeapUsage: boolean; @@ -181,7 +270,6 @@ export type InitialOptions = Partial<{ }; modulePathIgnorePatterns: Array; modulePaths: Array; - name: string; noStackTrace: boolean; notify: boolean; notifyMode: string; @@ -189,10 +277,6 @@ export type InitialOptions = Partial<{ onlyFailures: boolean; outputFile: string; passWithNoTests: boolean; - /** - * @deprecated Use `transformIgnorePatterns` options instead. - */ - preprocessorIgnorePatterns: Array; preset: string | null | undefined; prettierPath: string | null | undefined; projects: Array; @@ -207,15 +291,7 @@ export type InitialOptions = Partial<{ runTestsByPath: boolean; runtime: string; sandboxInjectedGlobals: Array; - /** - * @deprecated Use `transform` options instead. - */ - scriptPreprocessor: string; setupFiles: Array; - /** - * @deprecated Use `setupFilesAfterEnv` options instead. - */ - setupTestFrameworkScriptFile: string; setupFilesAfterEnv: Array; silent: boolean; skipFilter: boolean; @@ -231,17 +307,12 @@ export type InitialOptions = Partial<{ testLocationInResults: boolean; testMatch: Array; testNamePattern: string; - /** - * @deprecated Use `roots` options instead. - */ - testPathDirs: Array; testPathIgnorePatterns: Array; testRegex: string | Array; testResultsProcessor: string; testRunner: string; testSequencer: string; testTimeout: number; - timers: Timers; transform: { [regex: string]: string | TransformerConfig; }; @@ -324,7 +395,7 @@ export type GlobalConfig = { passWithNoTests: boolean; projects: Array; replname?: string; - reporters?: Array; + reporters?: Array; runTestsByPath: boolean; rootDir: string; shard?: ShardConfig; @@ -363,19 +434,20 @@ export type ProjectConfig = { displayName?: DisplayName; errorOnDeprecated: boolean; extensionsToTreatAsEsm: Array; + fakeTimers: FakeTimers; filter?: string; forceCoverageMatch: Array; globalSetup?: string; globalTeardown?: string; globals: ConfigGlobals; haste: HasteConfig; + id: string; injectGlobals: boolean; moduleDirectories: Array; moduleFileExtensions: Array; moduleNameMapper: Array<[string, string]>; modulePathIgnorePatterns: Array; modulePaths?: Array; - name: string; prettierPath: string; resetMocks: boolean; resetModules: boolean; @@ -401,7 +473,6 @@ export type ProjectConfig = { testPathIgnorePatterns: Array; testRegex: Array; testRunner: string; - timers: Timers; transform: Array<[string, string, Record]>; transformIgnorePatterns: Array; watchPathIgnorePatterns: Array; @@ -440,6 +511,7 @@ export type Argv = Arguments< globalSetup: string | null | undefined; globalTeardown: string | null | undefined; haste: string; + ignoreProjects: Array; init: boolean; injectGlobals: boolean; json: boolean; @@ -458,8 +530,9 @@ export type Argv = Arguments< onlyFailures: boolean; outputFile: string; preset: string | null | undefined; - projects: Array; prettierPath: string | null | undefined; + projects: Array; + reporters: Array; resetMocks: boolean; resetModules: boolean; resolver: string | null | undefined; @@ -486,7 +559,6 @@ export type Argv = Arguments< testRunner: string; testSequencer: string; testTimeout: number | null | undefined; - timers: string; transform: string; transformIgnorePatterns: Array; unmockedModulePathPatterns: Array | null | undefined; diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index b115969bf9a5..16dc1447141d 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -69,7 +69,7 @@ export interface HookBase { export interface ItBase { (testName: TestNameLike, fn: TestFn, timeout?: number): void; each: Each; - failing: Omit; + failing(testName: TestNameLike, fn: TestFn, timeout?: number): void; } export interface It extends ItBase { @@ -124,5 +124,5 @@ export interface GlobalAdditions extends TestFrameworkGlobals { export interface Global extends GlobalAdditions, Omit { - [extras: string]: unknown; + [extras: PropertyKey]: unknown; } diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index c8b5f123a1c9..ab8135420bcb 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -24,6 +24,7 @@ export type AssertionResult = { invocations?: number; location?: Callsite | null; numPassingAsserts: number; + retryReasons?: Array; status: Status; title: string; }; diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index ac98886612ec..13dbae86bbb1 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -1,6 +1,6 @@ { "name": "jest-util", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,7 +17,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -25,7 +25,7 @@ "picomatch": "^2.2.3" }, "devDependencies": { - "@types/graceful-fs": "^4.1.2", + "@types/graceful-fs": "^4.1.3", "@types/picomatch": "^2.2.2" }, "engines": { diff --git a/packages/jest-util/src/deepCyclicCopy.ts b/packages/jest-util/src/deepCyclicCopy.ts index d9f59bc409bb..53077c33c255 100644 --- a/packages/jest-util/src/deepCyclicCopy.ts +++ b/packages/jest-util/src/deepCyclicCopy.ts @@ -17,7 +17,7 @@ export default function deepCyclicCopy( options: DeepCyclicCopyOptions = {blacklist: EMPTY, keepPrototype: false}, cycles: WeakMap = new WeakMap(), ): T { - if (typeof value !== 'object' || value === null) { + if (typeof value !== 'object' || value === null || Buffer.isBuffer(value)) { return value; } else if (cycles.has(value)) { return cycles.get(value); diff --git a/packages/jest-validate/package.json b/packages/jest-validate/package.json index 48374155fa90..0f21ab3f7a65 100644 --- a/packages/jest-validate/package.json +++ b/packages/jest-validate/package.json @@ -1,6 +1,6 @@ { "name": "jest-validate", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,12 +17,12 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^28.0.0-alpha.3", + "jest-get-type": "^28.0.0", "leven": "^3.1.0", - "pretty-format": "^28.0.0-alpha.7" + "pretty-format": "^28.0.1" }, "devDependencies": { "@types/yargs": "^17.0.8" diff --git a/packages/jest-validate/src/__tests__/fixtures/jestConfig.ts b/packages/jest-validate/src/__tests__/fixtures/jestConfig.ts index 6f2b022d2ee8..dfcc3a5a69c5 100644 --- a/packages/jest-validate/src/__tests__/fixtures/jestConfig.ts +++ b/packages/jest-validate/src/__tests__/fixtures/jestConfig.ts @@ -22,12 +22,12 @@ const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); const defaultConfig = { automock: false, bail: 0, - browser: false, cacheDirectory: path.join(tmpdir(), 'jest'), clearMocks: false, coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], coverageReporters: ['json', 'text', 'lcov', 'clover'], expand: false, + fakeTimers: {enableGlobally: false}, globals: {}, haste: {}, moduleDirectories: ['node_modules'], @@ -48,7 +48,6 @@ const defaultConfig = { testPathIgnorePatterns: [NODE_MODULES_REGEXP], testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', testResultsProcessor: null, - timers: 'real', transformIgnorePatterns: [NODE_MODULES_REGEXP], useStderr: false, verbose: null, @@ -59,7 +58,6 @@ const defaultConfig = { const validConfig = { automock: false, bail: 0, - browser: false, cache: true, cacheDirectory: '/tmp/user/jest', clearMocks: false, @@ -77,9 +75,11 @@ const validConfig = { }, }, expand: false, + fakeTimers: {enableGlobally: false}, forceExit: false, globals: {}, haste: {}, + id: 'string', logHeapUsage: true, moduleDirectories: ['node_modules'], moduleFileExtensions: ['js', 'json', 'jsx', 'node'], @@ -89,7 +89,6 @@ const validConfig = { }, modulePathIgnorePatterns: ['/build/'], modulePaths: ['/shared/vendor/modules'], - name: 'string', noStackTrace: false, notify: false, notifyMode: 'failure-change', @@ -111,7 +110,6 @@ const validConfig = { testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', testResultsProcessor: 'processor-node-module', testRunner: 'circus', - timers: 'real', transform: { '\\.js$': '/preprocessor.js', }, diff --git a/packages/jest-validate/src/utils.ts b/packages/jest-validate/src/utils.ts index e327bce87f7e..d6890f7914cd 100644 --- a/packages/jest-validate/src/utils.ts +++ b/packages/jest-validate/src/utils.ts @@ -25,8 +25,8 @@ export const formatPrettyObject = (value: unknown): string => : JSON.stringify(value, null, 2).split('\n').join('\n '); export class ValidationError extends Error { - name: string; - message: string; + override name: string; + override message: string; constructor(name: string, message: string, comment?: string | null) { super(); diff --git a/packages/jest-watcher/package.json b/packages/jest-watcher/package.json index ee49673ad755..01fafe90cafd 100644 --- a/packages/jest-watcher/package.json +++ b/packages/jest-watcher/package.json @@ -1,7 +1,7 @@ { "name": "jest-watcher", "description": "Delightful JavaScript Testing.", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -12,17 +12,18 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/test-result": "^28.0.0-alpha.7", - "@jest/types": "^28.0.0-alpha.7", + "@jest/test-result": "^28.0.1", + "@jest/types": "^28.0.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^28.0.0-alpha.7", + "emittery": "^0.10.2", + "jest-util": "^28.0.1", "string-length": "^4.0.1" }, "repository": { "type": "git", - "url": "https://github.com/facebook/jest", + "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-watcher" }, "bugs": { diff --git a/packages/jest-core/src/TestWatcher.ts b/packages/jest-watcher/src/TestWatcher.ts similarity index 87% rename from packages/jest-core/src/TestWatcher.ts rename to packages/jest-watcher/src/TestWatcher.ts index 03f19b1f840b..af91597dddc1 100644 --- a/packages/jest-core/src/TestWatcher.ts +++ b/packages/jest-watcher/src/TestWatcher.ts @@ -5,13 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import emittery = require('emittery'); +import Emittery = require('emittery'); type State = { interrupted: boolean; }; -export default class TestWatcher extends emittery<{change: State}> { +export default class TestWatcher extends Emittery<{change: State}> { state: State; private _isWatchMode: boolean; diff --git a/packages/jest-watcher/src/index.ts b/packages/jest-watcher/src/index.ts index 05ecbadbe682..0940eb17c89b 100644 --- a/packages/jest-watcher/src/index.ts +++ b/packages/jest-watcher/src/index.ts @@ -8,6 +8,7 @@ export {default as BaseWatchPlugin} from './BaseWatchPlugin'; export {default as JestHook} from './JestHooks'; export {default as PatternPrompt} from './PatternPrompt'; +export {default as TestWatcher} from './TestWatcher'; export * from './constants'; export type { AllowedConfigOptions, diff --git a/packages/jest-worker/.npmignore b/packages/jest-worker/.npmignore index b2b3ff7d1089..8dda7de8f4fe 100644 --- a/packages/jest-worker/.npmignore +++ b/packages/jest-worker/.npmignore @@ -1,6 +1,6 @@ **/__mocks__/** **/__tests__/** -**/__performance_tests__/** +__benchmarks__ __typetests__ src tsconfig.json diff --git a/packages/jest-worker/README.md b/packages/jest-worker/README.md index 302e3682bf99..c8a29401c19d 100644 --- a/packages/jest-worker/README.md +++ b/packages/jest-worker/README.md @@ -73,7 +73,7 @@ List of method names that can be called on the child processes from the parent p #### `forkOptions: ForkOptions` (optional) -Allow customizing all options passed to `child_process.fork`. By default, some values are set (`cwd`, `env` and `execArgv`), but you can override them and customize the rest. For a list of valid values, check [the Node documentation](https://nodejs.org/api/child_process.html#child_processforkmodulepath-args-options). +Allow customizing all options passed to `child_process.fork`. By default, some values are set (`cwd`, `env`, `execArgv` and `serialization`), but you can override them and customize the rest. For a list of valid values, check [the Node documentation](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options). #### `maxRetries: number` (optional) diff --git a/packages/jest-worker/src/__performance_tests__/test.js b/packages/jest-worker/__benchmarks__/test.js similarity index 92% rename from packages/jest-worker/src/__performance_tests__/test.js rename to packages/jest-worker/__benchmarks__/test.js index 77bd431357a8..3d72d37844e0 100644 --- a/packages/jest-worker/src/__performance_tests__/test.js +++ b/packages/jest-worker/__benchmarks__/test.js @@ -5,22 +5,25 @@ * LICENSE file in the root directory of this source tree. */ +/** + * To start the test, build the repo and run: + * node --expose-gc test.js empty 100000 + * node --expose-gc test.js loadTest 10000 + */ + 'use strict'; const assert = require('assert'); const {performance} = require('perf_hooks'); -// eslint-disable-next-line import/no-extraneous-dependencies const workerFarm = require('worker-farm'); -const JestWorker = require('../../build').Worker; +const JestWorker = require('../').Worker; -// Typical tests: node --expose-gc test.js empty 100000 -// node --expose-gc test.js loadTest 10000 assert(process.argv[2], 'Pass a child method name'); assert(process.argv[3], 'Pass the number of iterations'); const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); const method = process.argv[2]; -const calls = +process.argv[3]; +const calls = Number(process.argv[3]); const threads = 6; const iterations = 10; @@ -127,7 +130,7 @@ function profileEnd(x) { async function main() { if (!globalThis.gc) { - console.warn('GC not present, start with node --expose-gc'); + throw new Error('GC not present, start with node --expose-gc'); } const wFResults = []; diff --git a/packages/jest-worker/src/__performance_tests__/workers/jest_worker.js b/packages/jest-worker/__benchmarks__/workers/jest_worker.js similarity index 100% rename from packages/jest-worker/src/__performance_tests__/workers/jest_worker.js rename to packages/jest-worker/__benchmarks__/workers/jest_worker.js diff --git a/packages/jest-worker/src/__performance_tests__/workers/pi.js b/packages/jest-worker/__benchmarks__/workers/pi.js similarity index 100% rename from packages/jest-worker/src/__performance_tests__/workers/pi.js rename to packages/jest-worker/__benchmarks__/workers/pi.js diff --git a/packages/jest-worker/src/__performance_tests__/workers/worker_farm.js b/packages/jest-worker/__benchmarks__/workers/worker_farm.js similarity index 100% rename from packages/jest-worker/src/__performance_tests__/workers/worker_farm.js rename to packages/jest-worker/__benchmarks__/workers/worker_farm.js diff --git a/packages/jest-worker/package.json b/packages/jest-worker/package.json index 6cf6c4818039..af21834f3685 100644 --- a/packages/jest-worker/package.json +++ b/packages/jest-worker/package.json @@ -1,6 +1,6 @@ { "name": "jest-worker", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -25,7 +25,7 @@ "@types/merge-stream": "^1.1.2", "@types/supports-color": "^8.1.0", "get-stream": "^6.0.0", - "jest-leak-detector": "^28.0.0-alpha.7", + "jest-leak-detector": "^28.0.1", "worker-farm": "^1.6.0" }, "engines": { diff --git a/packages/jest-worker/src/WorkerPool.ts b/packages/jest-worker/src/WorkerPool.ts index 0bf2a72c3ed1..694858b9a863 100644 --- a/packages/jest-worker/src/WorkerPool.ts +++ b/packages/jest-worker/src/WorkerPool.ts @@ -27,7 +27,7 @@ class WorkerPool extends BaseWorkerPool implements WorkerPoolInterface { this.getWorkerById(workerId).send(request, onStart, onEnd, onCustomMessage); } - createWorker(workerOptions: WorkerOptions): WorkerInterface { + override createWorker(workerOptions: WorkerOptions): WorkerInterface { let Worker; if (this._options.enableWorkerThreads) { Worker = require('./workers/NodeThreadsWorker').default; diff --git a/packages/jest-worker/src/__tests__/leak-integration.test.ts b/packages/jest-worker/src/__tests__/leak-integration.test.ts index af5e827bc296..ef10567fe5fc 100644 --- a/packages/jest-worker/src/__tests__/leak-integration.test.ts +++ b/packages/jest-worker/src/__tests__/leak-integration.test.ts @@ -53,6 +53,8 @@ describe('Worker leaks', () => { worker = new Worker(workerFile, { enableWorkerThreads: false, exposedMethods: ['fn'], + // @ts-expect-error: option does not exist on the node 12 types + forkOptions: {serialization: 'json'}, }); }); afterEach(async () => { diff --git a/packages/jest-worker/src/workers/ChildProcessWorker.ts b/packages/jest-worker/src/workers/ChildProcessWorker.ts index 8d07b0a12e86..c3a2f2ab4d78 100644 --- a/packages/jest-worker/src/workers/ChildProcessWorker.ts +++ b/packages/jest-worker/src/workers/ChildProcessWorker.ts @@ -92,6 +92,9 @@ export default class ChildProcessWorker implements WorkerInterface { }, // Suppress --debug / --inspect flags while preserving others (like --harmony). execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)), + // default to advanced serialization in order to match worker threads + // @ts-expect-error: option does not exist on the node 12 types + serialization: 'advanced', silent: true, ...this._options.forkOptions, }); diff --git a/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js b/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js index e5e7e1e99694..1caa8e654380 100644 --- a/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js +++ b/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js @@ -70,6 +70,7 @@ it('passes fork options down to child_process.fork, adding the defaults', () => env: {...process.env, FORCE_COLOR: supportsColor.stdout ? '1' : undefined}, // Default option. execArgv: ['-p'], // Filtered option. execPath: 'hello', // Added option. + serialization: 'advanced', // Default option. silent: true, // Default option. }); }); diff --git a/packages/jest-worker/src/workers/__tests__/processChild.test.js b/packages/jest-worker/src/workers/__tests__/processChild.test.js index ba579e052efe..55b29107bdf7 100644 --- a/packages/jest-worker/src/workers/__tests__/processChild.test.js +++ b/packages/jest-worker/src/workers/__tests__/processChild.test.js @@ -241,8 +241,6 @@ it('returns results immediately when function is synchronous', () => { }); it('returns results when it gets resolved if function is asynchronous', async () => { - jest.useRealTimers(); - process.emit('message', [ CHILD_MESSAGE_INITIALIZE, true, // Not really used here, but for flow type purity. diff --git a/packages/jest-worker/src/workers/__tests__/threadChild.test.js b/packages/jest-worker/src/workers/__tests__/threadChild.test.js index 657ce23a33bb..fe0107a926f2 100644 --- a/packages/jest-worker/src/workers/__tests__/threadChild.test.js +++ b/packages/jest-worker/src/workers/__tests__/threadChild.test.js @@ -268,8 +268,6 @@ it('returns results immediately when function is synchronous', () => { }); it('returns results when it gets resolved if function is asynchronous', async () => { - jest.useRealTimers(); - thread.emit('message', [ CHILD_MESSAGE_INITIALIZE, true, // Not really used here, but for flow type purity. diff --git a/packages/jest-worker/tsconfig.json b/packages/jest-worker/tsconfig.json index 1eb1744765cb..8f5708f84441 100644 --- a/packages/jest-worker/tsconfig.json +++ b/packages/jest-worker/tsconfig.json @@ -5,6 +5,6 @@ "outDir": "build" }, "include": ["./src/**/*"], - "exclude": ["./**/__performance_tests__/**/*", "./**/__tests__/**/*"], + "exclude": ["./**/__tests__/**/*"], "references": [{"path": "../jest-leak-detector"}] } diff --git a/packages/jest/package.json b/packages/jest/package.json index bcaa39dcd172..bb8bec81e243 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -1,7 +1,7 @@ { "name": "jest", "description": "Delightful JavaScript Testing.", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -13,9 +13,9 @@ "./bin/jest": "./bin/jest.js" }, "dependencies": { - "@jest/core": "^28.0.0-alpha.7", + "@jest/core": "^28.0.1", "import-local": "^3.0.2", - "jest-cli": "^28.0.0-alpha.7" + "jest-cli": "^28.0.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -31,7 +31,8 @@ }, "repository": { "type": "git", - "url": "https://github.com/facebook/jest" + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest" }, "homepage": "https://jestjs.io/", "license": "MIT", diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts index 9c4f958cb98d..d101fd921a01 100644 --- a/packages/jest/src/index.ts +++ b/packages/jest/src/index.ts @@ -7,7 +7,6 @@ export { SearchSource, - TestWatcher, createTestScheduler, getVersion, runCLI, diff --git a/packages/pretty-format/.npmignore b/packages/pretty-format/.npmignore index e4c4063f4efd..8dda7de8f4fe 100644 --- a/packages/pretty-format/.npmignore +++ b/packages/pretty-format/.npmignore @@ -1,8 +1,8 @@ **/__mocks__/** **/__tests__/** +__benchmarks__ __typetests__ src -perf tsconfig.json tsconfig.tsbuildinfo api-extractor.json diff --git a/packages/pretty-format/perf/test.js b/packages/pretty-format/__benchmarks__/test.js similarity index 96% rename from packages/pretty-format/perf/test.js rename to packages/pretty-format/__benchmarks__/test.js index bbe5d5b50e74..e5f8a3ced407 100644 --- a/packages/pretty-format/perf/test.js +++ b/packages/pretty-format/__benchmarks__/test.js @@ -5,13 +5,20 @@ * LICENSE file in the root directory of this source tree. */ +/** + * To start the test, build the repo and run: + * node test.js + */ + +'use strict'; + const util = require('util'); const chalk = require('chalk'); const React = require('react'); const ReactTestRenderer = require('react-test-renderer'); const {formatTime} = require('jest-util'); -const prettyFormat = require('../build'); -const ReactTestComponent = require('../build/plugins/ReactTestComponent'); +const prettyFormat = require('../').format; +const {ReactTestComponent} = require('../').plugins; const worldGeoJson = require('./world.geo.json'); const NANOSECONDS = 1000000000; diff --git a/packages/pretty-format/perf/world.geo.json b/packages/pretty-format/__benchmarks__/world.geo.json similarity index 100% rename from packages/pretty-format/perf/world.geo.json rename to packages/pretty-format/__benchmarks__/world.geo.json diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index ce24c3d35e5f..d5c04c156b0e 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -1,6 +1,6 @@ { "name": "pretty-format", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -20,20 +20,21 @@ }, "author": "James Kyle ", "dependencies": { - "@jest/schemas": "^28.0.0-alpha.3", + "@jest/schemas": "^28.0.0", "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "react-is": "^18.0.0" }, "devDependencies": { - "@types/react": "*", + "@types/react": "^17.0.3", "@types/react-is": "^17.0.0", - "@types/react-test-renderer": "*", + "@types/react-test-renderer": "17.0.2", + "expect": "^28.0.1", "immutable": "^4.0.0", - "jest-util": "^28.0.0-alpha.7", - "react": "*", - "react-dom": "*", - "react-test-renderer": "*" + "jest-util": "^28.0.1", + "react": "17.0.2", + "react-dom": "^17.0.1", + "react-test-renderer": "17.0.2" }, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0" diff --git a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts index dd0495fb17d4..42da89bd9b25 100644 --- a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts +++ b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {AsymmetricMatcher as AbstractAsymmetricMatcher} from 'expect'; import prettyFormat, {plugins} from '../'; import type {OptionsReceived} from '../types'; @@ -131,6 +132,42 @@ test('stringNotMatching(string)', () => { expect(result).toEqual('StringNotMatching /jest/'); }); +test('closeTo(number, precision)', () => { + const result = prettyFormat(expect.closeTo(1.2345, 4), options); + expect(result).toEqual('NumberCloseTo 1.2345 (4 digits)'); +}); + +test('notCloseTo(number, precision)', () => { + const result = prettyFormat(expect.not.closeTo(1.2345, 1), options); + expect(result).toEqual('NumberNotCloseTo 1.2345 (1 digit)'); +}); + +test('closeTo(number)', () => { + const result = prettyFormat(expect.closeTo(1.2345), options); + expect(result).toEqual('NumberCloseTo 1.2345 (2 digits)'); +}); + +test('closeTo(Infinity)', () => { + const result = prettyFormat(expect.closeTo(-Infinity), options); + expect(result).toEqual('NumberCloseTo -Infinity (2 digits)'); +}); + +test('closeTo(scientific number)', () => { + const result = prettyFormat(expect.closeTo(1.56e-3, 4), options); + expect(result).toEqual('NumberCloseTo 0.00156 (4 digits)'); +}); + +test('closeTo(very small scientific number)', () => { + const result = prettyFormat(expect.closeTo(1.56e-10, 4), options); + expect(result).toEqual('NumberCloseTo 1.56e-10 (4 digits)'); +}); + +test('correctly handles inability to pretty-print matcher', () => { + expect(() => prettyFormat(new DummyMatcher(1), options)).toThrow( + 'Asymmetric matcher DummyMatcher does not implement toAsymmetricMatcher()', + ); +}); + test('supports multiple nested asymmetric matchers', () => { const result = prettyFormat( { @@ -311,3 +348,21 @@ test('min option', () => { '{"test": {"nested": ObjectContaining {"a": ArrayContaining [1], "b": Anything, "c": Any, "d": StringContaining "jest", "e": StringMatching /jest/, "f": ObjectContaining {"test": "case"}}}}', ); }); + +class DummyMatcher extends AbstractAsymmetricMatcher { + constructor(sample: number) { + super(sample); + } + + asymmetricMatch(other: number) { + return this.sample === other; + } + + toString() { + return 'DummyMatcher'; + } + + override getExpectedType() { + return 'number'; + } +} diff --git a/packages/pretty-format/src/plugins/AsymmetricMatcher.ts b/packages/pretty-format/src/plugins/AsymmetricMatcher.ts index 7457cdd8973b..331b64e1dbb5 100644 --- a/packages/pretty-format/src/plugins/AsymmetricMatcher.ts +++ b/packages/pretty-format/src/plugins/AsymmetricMatcher.ts @@ -80,6 +80,12 @@ export const serialize: NewPlugin['serialize'] = ( ); } + if (typeof val.toAsymmetricMatcher !== 'function') { + throw new Error( + `Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()`, + ); + } + return val.toAsymmetricMatcher(); }; diff --git a/packages/pretty-format/tsconfig.json b/packages/pretty-format/tsconfig.json index b91ffb6b1f33..6f91b2984094 100644 --- a/packages/pretty-format/tsconfig.json +++ b/packages/pretty-format/tsconfig.json @@ -6,5 +6,6 @@ }, "include": ["./src/**/*"], "exclude": ["./**/__tests__/**/*"], + // no `expect`, only used in tests "references": [{"path": "../jest-schemas"}, {"path": "../jest-util"}] } diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index d05164c8ab92..471ab937db76 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,8 +1,7 @@ { "name": "@jest/test-utils", - "version": "28.0.0-alpha.7", + "version": "28.0.1", "private": true, - "license": "MIT", "main": "./build/index.js", "types": "./build/index.d.ts", "exports": { @@ -13,12 +12,12 @@ "./package.json": "./package.json" }, "dependencies": { - "@jest/types": "^28.0.0-alpha.7", + "@jest/types": "^28.0.1", "@types/node": "*", "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", - "pretty-format": "^28.0.0-alpha.7", - "semver": "^7.3.2" + "pretty-format": "^28.0.1", + "semver": "^7.3.5" }, "devDependencies": { "@types/semver": "^7.1.0" diff --git a/packages/test-utils/src/config.ts b/packages/test-utils/src/config.ts index f86bbd3f0c65..30a8f10865de 100644 --- a/packages/test-utils/src/config.ts +++ b/packages/test-utils/src/config.ts @@ -78,19 +78,20 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { displayName: undefined, errorOnDeprecated: false, extensionsToTreatAsEsm: [], + fakeTimers: {enableGlobally: false}, filter: undefined, forceCoverageMatch: [], globalSetup: undefined, globalTeardown: undefined, globals: {}, haste: {}, + id: 'test_name', injectGlobals: true, moduleDirectories: [], moduleFileExtensions: ['js'], moduleNameMapper: [], modulePathIgnorePatterns: [], modulePaths: [], - name: 'test_name', prettierPath: 'prettier', resetMocks: false, resetModules: false, @@ -116,7 +117,6 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { testPathIgnorePatterns: [], testRegex: ['\\.test\\.js$'], testRunner: 'jest-circus/runner', - timers: 'real', transform: [], transformIgnorePatterns: [], unmockedModulePathPatterns: undefined, diff --git a/patches/react-native.patch b/patches/react-native.patch deleted file mode 100644 index 0720ad2a58f4..000000000000 --- a/patches/react-native.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/jest/preprocessor.js b/jest/preprocessor.js -index 5920c0a6f23c056f27366fabf32dd13c6f86465b..2658e52f9127ac58849e7f830f6342d8b683672c 100644 ---- a/jest/preprocessor.js -+++ b/jest/preprocessor.js -@@ -64,8 +64,6 @@ module.exports = { - [require('@babel/plugin-transform-flow-strip-types')], - [ - require('@babel/plugin-proposal-class-properties'), -- // use `this.foo = bar` instead of `this.defineProperty('foo', ...)` -- {loose: true}, - ], - [require('@babel/plugin-transform-computed-properties')], - [require('@babel/plugin-transform-destructuring')], diff --git a/scripts/build.js b/scripts/build.mjs similarity index 83% rename from scripts/build.js rename to scripts/build.mjs index fa6c025ea2cf..4fa3eb2662d1 100644 --- a/scripts/build.js +++ b/scripts/build.mjs @@ -12,36 +12,39 @@ * Non-js files not matching IGNORE_PATTERN will be copied without transpiling. * * Example: - * node ./scripts/build.js - * node ./scripts/build.js /users/123/jest/packages/jest-111/src/111.js - * - * NOTE: this script is node@6 compatible + * node ./scripts/build.mjs + * node ./scripts/build.mjs /users/123/jest/packages/jest-111/src/111.js */ -'use strict'; - -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); -const babel = require('@babel/core'); -const chalk = require('chalk'); -const glob = require('glob'); -const micromatch = require('micromatch'); -const prettier = require('prettier'); -const transformOptions = require('../babel.config.js'); -const {getPackages, adjustToTerminalWidth, OK} = require('./buildUtils'); +import assert from 'assert'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import babel from '@babel/core'; +import chalk from 'chalk'; +import glob from 'glob'; +import fs from 'graceful-fs'; +import micromatch from 'micromatch'; +import prettier from 'prettier'; +import transformOptions from '../babel.config.js'; +import { + OK, + PACKAGES_DIR, + adjustToTerminalWidth, + getPackages, +} from './buildUtils.mjs'; const SRC_DIR = 'src'; const BUILD_DIR = 'build'; const JS_FILES_PATTERN = '**/*.js'; const TS_FILES_PATTERN = '**/*.ts'; const IGNORE_PATTERN = '**/__{tests,mocks}__/**'; -const PACKAGES_DIR = path.resolve(__dirname, '../packages'); const INLINE_REQUIRE_EXCLUDE_LIST = /packages\/expect|(jest-(circus|diff|get-type|jasmine2|matcher-utils|message-util|regex-util|snapshot))|pretty-format\//; -const prettierConfig = prettier.resolveConfig.sync(__filename); +const prettierConfig = prettier.resolveConfig.sync( + fileURLToPath(import.meta.url), +); prettierConfig.trailingComma = 'none'; prettierConfig.parser = 'babel'; @@ -110,7 +113,10 @@ function buildFile(file, silent) { // The excluded modules are injected into the user's sandbox // We need to guard some globals there. options.plugins.push( - require.resolve('./babel-plugin-jest-native-globals'), + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + 'babel-plugin-jest-native-globals.js', + ), ); } else { options.plugins = options.plugins.map(plugin => { diff --git a/scripts/buildTs.js b/scripts/buildTs.mjs similarity index 92% rename from scripts/buildTs.js rename to scripts/buildTs.mjs index 5faf8bd38bd0..c8fb4267c076 100644 --- a/scripts/buildTs.js +++ b/scripts/buildTs.mjs @@ -5,18 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const assert = require('assert'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const chalk = require('chalk'); -const execa = require('execa'); -const globby = require('globby'); -const stripJsonComments = require('strip-json-comments'); -const throat = require('throat'); -const {getPackages} = require('./buildUtils'); +import assert from 'assert'; +import os from 'os'; +import path from 'path'; +import chalk from 'chalk'; +import execa from 'execa'; +import globby from 'globby'; +import fs from 'graceful-fs'; +import stripJsonComments from 'strip-json-comments'; +import throat from 'throat'; +import {getPackages} from './buildUtils.mjs'; (async () => { const packages = getPackages(); @@ -64,6 +62,13 @@ const {getPackages} = require('./buildUtils'); } } + // dev dep + if (pkg.name === 'pretty-format') { + if (dep === 'expect') { + return false; + } + } + return true; }) .map(dep => @@ -86,7 +91,7 @@ const {getPackages} = require('./buildUtils'); assert.deepStrictEqual( references, jestDependenciesOfPackage, - `Expected declared references to match dependencies in packages ${ + `Expected declared references to match dependencies in package ${ pkg.name }. Got:\n\n${references.join( '\n', diff --git a/scripts/buildUtils.js b/scripts/buildUtils.mjs similarity index 83% rename from scripts/buildUtils.js rename to scripts/buildUtils.mjs index 1136a369308d..67a721d1eb3b 100644 --- a/scripts/buildUtils.js +++ b/scripts/buildUtils.mjs @@ -5,26 +5,30 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); -const {sync: readPkg} = require('read-pkg'); -const stringLength = require('string-length'); -const rootPackage = require('../package.json'); - -const PACKAGES_DIR = path.resolve(__dirname, '../packages'); - -const OK = chalk.reset.inverse.bold.green(' DONE '); +import assert from 'assert'; +import {createRequire} from 'module'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import chalk from 'chalk'; +import fs from 'graceful-fs'; +import {sync as readPkg} from 'read-pkg'; +import stringLength from 'string-length'; + +export const PACKAGES_DIR = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../packages', +); + +export const OK = chalk.reset.inverse.bold.green(' DONE '); // Get absolute paths of all directories under packages/* -module.exports.getPackages = function getPackages() { +export function getPackages() { const packages = fs .readdirSync(PACKAGES_DIR) .map(file => path.resolve(PACKAGES_DIR, file)) .filter(f => fs.lstatSync(path.resolve(f)).isDirectory()); + const require = createRequire(import.meta.url); + const rootPackage = require('../package.json'); const nodeEngineRequirement = rootPackage.engines.node; @@ -104,9 +108,9 @@ module.exports.getPackages = function getPackages() { return {packageDir, pkg}; }); -}; +} -module.exports.adjustToTerminalWidth = function adjustToTerminalWidth(str) { +export function adjustToTerminalWidth(str) { const columns = process.stdout.columns || 80; const WIDTH = columns - stringLength(OK) + 1; const strs = str.match(new RegExp(`(.{1,${WIDTH}})`, 'g')); @@ -115,7 +119,4 @@ module.exports.adjustToTerminalWidth = function adjustToTerminalWidth(str) { lastString += Array(WIDTH - lastString.length).join(chalk.dim('.')); } return strs.slice(0, -1).concat(lastString).join('\n'); -}; - -module.exports.OK = OK; -module.exports.PACKAGES_DIR = PACKAGES_DIR; +} diff --git a/scripts/bundleTs.js b/scripts/bundleTs.mjs similarity index 81% rename from scripts/bundleTs.js rename to scripts/bundleTs.mjs index bdf20f05cc56..4961d41ad2c6 100644 --- a/scripts/bundleTs.js +++ b/scripts/bundleTs.mjs @@ -5,25 +5,26 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { +import {createRequire} from 'module'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import { CompilerState, Extractor, ExtractorConfig, -} = require('@microsoft/api-extractor'); -const chalk = require('chalk'); -const {sync: pkgDir} = require('pkg-dir'); -const prettier = require('prettier'); -const rimraf = require('rimraf'); -const {getPackages} = require('./buildUtils'); +} from '@microsoft/api-extractor'; +import chalk from 'chalk'; +import fs from 'graceful-fs'; +import {sync as pkgDir} from 'pkg-dir'; +import prettier from 'prettier'; +import rimraf from 'rimraf'; +import {getPackages} from './buildUtils.mjs'; const prettierConfig = prettier.resolveConfig.sync( - __filename.replace(/\.js$/, '.d.ts'), + fileURLToPath(import.meta.url).replace(/\.js$/, '.d.ts'), ); +const require = createRequire(import.meta.url); const typescriptCompilerFolder = pkgDir(require.resolve('typescript')); const copyrightSnippet = ` @@ -35,14 +36,19 @@ const copyrightSnippet = ` */ `.trim(); +const typesNodeReferenceDirective = '/// '; + +const excludedPackages = new Set(['@jest/globals']); + (async () => { const packages = getPackages(); - const packagesWithTs = packages.filter(p => - fs.existsSync(path.resolve(p.packageDir, 'tsconfig.json')), - ); + const isTsPackage = p => + fs.existsSync(path.resolve(p.packageDir, 'tsconfig.json')); - const typesNodeReferenceDirective = '/// '; + const packagesToBundle = packages.filter( + p => isTsPackage(p) && !excludedPackages.has(p.pkg.name), + ); console.log(chalk.inverse(' Extracting TypeScript definition files ')); @@ -104,14 +110,17 @@ const copyrightSnippet = ` }; await fs.promises.writeFile( - path.resolve(__dirname, '../api-extractor.json'), + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../api-extractor.json', + ), JSON.stringify(sharedExtractorConfig, null, 2), ); let compilerState; await Promise.all( - packagesWithTs.map(async ({packageDir, pkg}) => { + packagesToBundle.map(async ({packageDir, pkg}) => { const configFile = path.resolve(packageDir, 'api-extractor.json'); await fs.promises.writeFile( @@ -130,7 +139,7 @@ const copyrightSnippet = ` if (!compilerState) { compilerState = CompilerState.create(extractorConfig, { - additionalEntryPoints: packagesWithTs.map(({pkg, packageDir}) => + additionalEntryPoints: packagesToBundle.map(({pkg, packageDir}) => path.resolve(packageDir, pkg.types), ), typescriptCompilerFolder, diff --git a/scripts/checkCopyrightHeaders.js b/scripts/checkCopyrightHeaders.mjs similarity index 95% rename from scripts/checkCopyrightHeaders.js rename to scripts/checkCopyrightHeaders.mjs index 8d490d570f6c..19d9549a5093 100755 --- a/scripts/checkCopyrightHeaders.js +++ b/scripts/checkCopyrightHeaders.mjs @@ -5,11 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const {execSync} = require('child_process'); -const fs = require('fs'); -const {isBinaryFileSync} = require('isbinaryfile'); +import {execSync} from 'child_process'; +import fs from 'graceful-fs'; +import {isBinaryFileSync} from 'isbinaryfile'; const getFileContents = path => fs.readFileSync(path, {encoding: 'utf-8'}); const isDirectory = path => fs.lstatSync(path).isDirectory(); @@ -73,6 +71,7 @@ const GENERIC_IGNORED_EXTENSIONS = [ 'ipynb', 'htm', 'toml', + 'pro', ].map(extension => createRegExp(`\.${extension}$`)); const GENERIC_IGNORED_PATTERNS = [ @@ -149,7 +148,7 @@ function check() { ${invalidFiles.join('\n ')} -Please include the header or exclude the files in \`scripts/checkCopyrightHeaders.js\``); +Please include the header or exclude the files in \`scripts/checkCopyrightHeaders.mjs\``); process.exit(1); } } diff --git a/scripts/cleanE2e.js b/scripts/cleanE2e.mjs similarity index 65% rename from scripts/cleanE2e.js rename to scripts/cleanE2e.mjs index 6292e749343d..09ee9d620f1d 100644 --- a/scripts/cleanE2e.js +++ b/scripts/cleanE2e.mjs @@ -5,11 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const {normalize, resolve} = require('path'); -const {sync: glob} = require('glob'); -const {sync: rimraf} = require('rimraf'); +import {dirname, normalize, resolve} from 'path'; +import {fileURLToPath} from 'url'; +import glob from 'glob'; +import rimraf from 'rimraf'; const excludedModules = [ 'e2e/global-setup-node-modules/node_modules/', @@ -22,12 +21,13 @@ const excludedModules = [ 'e2e/retain-all-files/node_modules/', ].map(dir => normalize(dir)); -const e2eNodeModules = glob('e2e/*/node_modules/') - .concat(glob('e2e/*/*/node_modules/')) +const e2eNodeModules = glob + .sync('e2e/*/node_modules/') + .concat(glob.sync('e2e/*/*/node_modules/')) .filter(dir => !excludedModules.includes(dir)) - .map(dir => resolve(__dirname, '..', dir)) + .map(dir => resolve(dirname(fileURLToPath(import.meta.url)), '..', dir)) .sort(); e2eNodeModules.forEach(dir => { - rimraf(dir, {glob: false}); + rimraf.sync(dir, {glob: false}); }); diff --git a/scripts/mapCoverage.js b/scripts/mapCoverage.mjs similarity index 87% rename from scripts/mapCoverage.js rename to scripts/mapCoverage.mjs index cf0a5e47f028..b78fe3da6418 100644 --- a/scripts/mapCoverage.js +++ b/scripts/mapCoverage.mjs @@ -25,9 +25,11 @@ * produce a full coverage report. */ -const istanbulCoverage = require('istanbul-lib-coverage'); -const istanbulReport = require('istanbul-lib-report'); -const istanbulReports = require('istanbul-reports'); +import {createRequire} from 'module'; +import istanbulCoverage from 'istanbul-lib-coverage'; +import istanbulReport from 'istanbul-lib-report'; +import istanbulReports from 'istanbul-reports'; +const require = createRequire(import.meta.url); const coverage = require('../coverage/coverage-final.json'); const map = istanbulCoverage.createCoverageMap(); diff --git a/scripts/remove-examples.js b/scripts/remove-examples.js deleted file mode 100644 index d77654f6fdc9..000000000000 --- a/scripts/remove-examples.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -const {writeFileSync} = require('fs'); -const {resolve} = require('path'); -const rimraf = require('rimraf'); - -const configFile = require.resolve('../jest.config'); - -const config = require(configFile); - -delete config.projects; - -writeFileSync(configFile, `module.exports = ${JSON.stringify(config)};\n`); - -rimraf.sync(resolve(__dirname, '../examples/')); diff --git a/scripts/remove-examples.mjs b/scripts/remove-examples.mjs new file mode 100644 index 000000000000..662b85bacc10 --- /dev/null +++ b/scripts/remove-examples.mjs @@ -0,0 +1,25 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import {fileURLToPath} from 'url'; +import fs from 'graceful-fs'; +import rimraf from 'rimraf'; +import config from '../jest.config.mjs'; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +const configFile = path.resolve(dirname, '../jest.config.mjs'); + +delete config.projects; + +fs.writeFileSync( + configFile, + `export default ${JSON.stringify(config, null, 2)};\n`, +); + +rimraf.sync(path.resolve(dirname, '../examples/')); diff --git a/scripts/verifyOldTs.js b/scripts/verifyOldTs.mjs similarity index 90% rename from scripts/verifyOldTs.js rename to scripts/verifyOldTs.mjs index 113febee9909..9ad40b74fe6a 100644 --- a/scripts/verifyOldTs.js +++ b/scripts/verifyOldTs.mjs @@ -5,15 +5,16 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); -const execa = require('execa'); -const rimraf = require('rimraf'); -const stripJsonComments = require('strip-json-comments'); -const tempy = require('tempy'); +import {createRequire} from 'module'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import chalk from 'chalk'; +import execa from 'execa'; +import fs from 'graceful-fs'; +import rimraf from 'rimraf'; +import stripJsonComments from 'strip-json-comments'; +import tempy from 'tempy'; +const require = createRequire(import.meta.url); const baseTsConfig = JSON.parse( stripJsonComments( @@ -32,10 +33,13 @@ const tsConfig = { }; /* eslint-enable */ -const tsVersion = '4.2'; +const tsVersion = '4.3'; function smoketest() { - const jestDirectory = path.resolve(__dirname, '../packages/jest'); + const jestDirectory = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../packages/jest', + ); const cwd = tempy.directory(); @@ -71,7 +75,7 @@ function smoketest() { } function typeTests() { - const cwd = path.resolve(__dirname, '../'); + const cwd = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'); const rootPackageJson = require('../package.json'); const currentTsdTypescriptVersion = diff --git a/scripts/verifyPnP.js b/scripts/verifyPnP.mjs similarity index 80% rename from scripts/verifyPnP.js rename to scripts/verifyPnP.mjs index cd49c4aae500..294ca451544c 100644 --- a/scripts/verifyPnP.js +++ b/scripts/verifyPnP.mjs @@ -5,18 +5,20 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import chalk from 'chalk'; +import dedent from 'dedent'; +import execa from 'execa'; +import fs from 'graceful-fs'; +import yaml from 'js-yaml'; +import rimraf from 'rimraf'; +import tempy from 'tempy'; -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); -const dedent = require('dedent'); -const execa = require('execa'); -const yaml = require('js-yaml'); -const rimraf = require('rimraf'); -const tempy = require('tempy'); - -const rootDirectory = path.resolve(__dirname, '..'); +const rootDirectory = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +); const cwd = tempy.directory(); diff --git a/scripts/watch.js b/scripts/watch.mjs similarity index 83% rename from scripts/watch.js rename to scripts/watch.mjs index 8c93185108fd..a4734d14ea3d 100644 --- a/scripts/watch.js +++ b/scripts/watch.mjs @@ -9,14 +9,18 @@ * Watch files for changes and rebuild (copy from 'src/' to `build/`) if changed */ -const {execSync} = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); -const chokidar = require('chokidar'); -const {PACKAGES_DIR, getPackages} = require('./buildUtils'); +import {execSync} from 'child_process'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import chalk from 'chalk'; +import chokidar from 'chokidar'; +import fs from 'graceful-fs'; +import {PACKAGES_DIR, getPackages} from './buildUtils.mjs'; -const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`; +const BUILD_CMD = `node ${path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + 'build.mjs', +)}`; let filesToBuild = new Map(); diff --git a/tsconfig.json b/tsconfig.json index cff2131d1fb2..574e1e6ec482 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ /* Additional Checks */ "noUnusedLocals": true, "noUnusedParameters": true, + "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, diff --git a/website/blog/2022-04-25-jest-28.md b/website/blog/2022-04-25-jest-28.md new file mode 100644 index 000000000000..fb7c0d9db041 --- /dev/null +++ b/website/blog/2022-04-25-jest-28.md @@ -0,0 +1,153 @@ +--- +title: 'Jest 28: Shedding weight and improving compatibility 🫶' +author: Simen Bekkhus +authorURL: https://github.com/SimenB +authorImageURL: https://avatars.githubusercontent.com/u/1404810 +--- + +Jest 28 is finally here, and it comes with some long requested features such as support for [sharding](/docs/cli#--shard) a test run across multiple machines, [package `exports`](https://nodejs.org/api/packages.html#exports) and the ability to customize the behavior of [fake timers](/docs/jest-object#fake-timers). These are just some personal highlights, and we'll be highlighting more in this blog post. + +Additionally, as announced in the [Jest 27 blog post](/blog/2021/05/25/jest-27) last year, we have removed some packages that no longer are used by default from the default installation. As a result the installation size has dropped by about 1/3. + + + +## Breaking changes + +The list of breaking changes is long (and can be seen fully in the [changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md#2800)), but for migration purposes, we've also written [a guide](/docs/upgrading-to-jest28) you can follow. Hopefully this makes the upgrade experience as frictionless as possible! + +Main breaking changes likely to impact your migration are dropped support for Node 10 and 15 (but _not_ Node 12, which will be EOL in a few days) and some renamed configuration options. + +Please note that both of the removed modules (`jest-environment-jsdom` and `jest-jasmine2`) are still actively maintained and tested in the same way, so the only breaking change here is that you'll need to explicitly install them. + +The guide should hopefully make migration trivial, but note that if you use any of the packages Jest consists of directly (such as `jest-worker` or `pretty-format`), instead of just running `jest`, then you need to go through the changelog to view any breaking changes. + +## Features + +Now let's talk about the new features in Jest 28, which is way more exciting! And there's quite a few of them, so buckle up. + +### Sharding of test run + +Jest now includes a new [`--shard`](/docs/cli#--shard) CLI option, contributed by [Mario Nebl](https://github.com/marionebl). It allows you to run parts of your test across different machine, and has been one of Jest's oldest feature requests. + +Jest's own test suite on CI went from about 10 minutes to 3 on Ubuntu, and on Windows from 20 minutes to 7. + +### `package.json` `exports` + +Jest shipped minimal support of [`exports`](https://nodejs.org/api/packages.html#exports) in 27.3. However, it only supported the "main" entry point (`.`), and only if no `main` field was present in `package.json`. With Jest 28 we're excited to finally be shipping full support! + +Related, in Jest 27, we provided either `require` or `import` condition. In Jest 28, `jest-environment-node` will now automatically provide `node` and `node-addons` conditions, while `jest-environment-jsdom` will provide the `browser` condition. + +This has been one of the biggest compatibility issues of Jest, and hopefully this is now resolved once and for all. + +### Fake timers + +Jest 26 introduced the concept of "modern" fake timers, which uses [`@sinonjs/fake-timers`](https://www.npmjs.com/package/@sinonjs/fake-timers) under the hood, and Jest 27 made it the default. In Jest 28, we are now exposing more of the underlying implementation through both configuration and runtime APIs. Huge thanks to [Tom Mrazauskas](https://github.com/mrazauskas) who contributed this feature! + +This allows you to not mock out `process.nextTick` which improves compatibility with fake `Promise`s, or to enable `advanceTimers` which automatically advance timers. + +Please see [the `fakeTimers` configuration](/docs/configuration#faketimers-object) for details. + +### GitHub Actions Reporter + +Jest now ships with a reporter to be used on GitHub Actions, which will use annotations to print test errors inline. + +![GitHub Actions test error screenshot](/img/blog/28-gh-actions-reporter.png) + +You can activate this reporter by passing `github-actions` in the [`reporters` configuration option](/docs/configuration#reporters-arraymodulename--modulename-options). + +Huge thanks to [Bernie Reiter](https://github.com/ockham) and other contributors for sticking by us and finally landing this feature. + +### Inline `testEnvironmentOptions` + +You can now pass [`testEnvironmentOptions`](/docs/configuration#testenvironmentoptions-object) inline in a file, similar to how you can set test environment. This is useful if you want to e.g. change the URL in a single file. + +```js +/** + * @jest-environment jsdom + * @jest-environment-options {"url": "https://jestjs.io/"} + */ + +test('use jsdom and set the URL in this test file', () => { + expect(window.location.href).toBe('https://jestjs.io/'); +}); +``` + +### All Node.js globals + +If you are using the new [`fetch`](https://nodejs.org/en/blog/announcements/v18-release-announce/#fetch-experimental) implementation in Node v18, you might have noticed that this function is not available in Jest. It has been a long-standing issue that we have to manually copy over any globals into the test globals. With Jest 28, this is no longer an issue as we now inspect the global environment Jest itself is running in, and copy over any globals that are missing in the test environment. + +### ECMAScript Modules + +Not much has changed in Jest's support for native ESM since Jest 27 came out. We continue to be blocked by [stabilization in Node](https://github.com/nodejs/node/issues/37648), and are hopeful this situation will improve sooner rather than later! + +However, we have been able to add a couple of new features in Jest 28. + +#### `data:` URLs + +[Tommaso Bossi](https://github.com/tbossi) has contributed support for [`data` URLs](https://nodejs.org/api/esm.html#data-imports), meaning you can now inline define some JavaScript to run without using `eval`. + +#### `import.meta.jest` + +While you have been able to access `jest` via `import {jest} from '@jest/globals'` in Jest, we've received feedback that this is less ergonomical than the (seemingly, but not really) global `jest` variable available in CJS. So Jest 28 ships with `import.meta.jest` to allow easier access. + +### Miscellaneous + +That's quite a lot of features, and are my personal highlights. However, we still have many more which I'll quickly go through: + +#### Asynchronous resolvers + +[Ian VanSchooten](https://github.com/IanVS) has contributed support for [asynchronous resolvers](/docs/configuration#resolver-string), which enables tools like [Vite](https://vitejs.dev/) to have better integrations with Jest. + +#### Asynchronous setup files + +If you have some async work you want to do when using `setupFiles`, you can now export an `async function`, which Jest will call and await before loading any tests. + +Note that this feature is only available for CJS. For ESM, we recommend using top-level `await` instead. + +#### Using `globalThis` + +Internally, Jest has been using `global` to refer to the [global environment](https://developer.mozilla.org/en-US/docs/Glossary/Global_object). However, since this only exists in Node, and not browsers (`window`), this led to incompatibility when attempting to use Jest's modules in another environment. + +Jest 28 uses [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) instead, which works in all environments. + +#### JSDOM 19 + +While, as mentioned, Jest no longer ships `jest-environment-jsdom` in the default installation, it is still actively maintained. As part of that, Jest 28 has upgraded from `jsdom@16` to `jsdom@19`. + +## TypeScript + +If you use Jest with TypeScript, either in your tests or when writing plugins such as custom runners, Jest 28 comes with extensive improvements to our types. Here's a non-exhaustive list of the changes in Jest 28. + +### `expect` + +When using `expect`'s own types (either directly, or via `import {expect} from '@jest/globals'`), it's now finally possible to add custom matchers. See our [example](https://github.com/facebook/jest/tree/main/examples/expect-extend) for how to do this. + +### Custom plugins + +If you write a custom runner, test reporter, resolver or something else, we now export more types that should help you type these more correctly. This is a moving target, so if you are the author of something pluggable in Jest and the types aren't as useful as they could be, please file an issue! + +### `jest-runner-tsd` + +[`jest-runner-tsd`](https://github.com/jest-community/jest-runner-tsd) is a custom runner for running type tests. This is what Jest uses itself to test our types, and we hope it can also be used by others! As its name implies, it is based on [`tsd`](https://npmjs.com/package/tsd), although it under the hood uses the fork [`tsd-lite`](https://npmjs.com/package/tsd-lite). + +--- + +All of these improvements and fixes has been contributed by [Tom Mrazauskas](https://github.com/mrazauskas). Thank you so much, Tom! 👏 + +Lastly, the minimum support version of TypeScript is now 4.3. + +## `jest-light-runner` + +The last thing we want to highlight in this blog post, is a very cool new Jest runner, created by [Nicolò Ribaudo](https://github.com/nicolo-ribaudo), called [`jest-light-runner`](https://www.npmjs.com/package/jest-light-runner). This takes almost all of the DX Jest is known for, and speeds it way up by being a smaller abstraction on top of Node. Babel's tests became almost twice as fast after migrating. While there are caveats, the existence of this runner should make it even easier for people who have smaller Node modules to test to choose Jest. Thanks, Nicolò! + +## Future + +While Jest 28 came almost a year after Jest 27, Jest 29 will be coming sooner, probably in just a few months. The current plan then is to just have one breaking change (except dropping Node versions), and that is to default [`snapshotFormat`](/docs/configuration#snapshotformat-object) to `{escapeString: false, printBasicPrototype: false}`. This makes snapshots both more readable and more copy-pasteable. + +This will of course be possible to override if you don't want to change, but you can also use those options today if you don't want to wait! + +## Acknowledgements + +Jest 28 contains contributions from more than 60 people, of which more than two thirds are first time contributors. Thank you so much to all contributors, old and new. Without you the project wouldn't be nearly as good as it is! I'd particularly like to thank [Tom Mrazauskas](https://github.com/mrazauskas) and [Feng Yu](https://github.com/F3n67u) for all their contributions, from code, to issue triaging to debugging, that made Jest 28 what it is. Thank you! 🙏 + +Thanks for reading, and happy Jesting! 🃏 diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 00cf5cc76d72..8d57541950c8 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -69,6 +69,9 @@ module.exports = { path.resolve('src/components/v1/legacyCSS.css'), path.resolve('static/css/custom.css'), path.resolve('static/css/jest.css'), + require.resolve( + 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css' + ), ], }, gtag: { diff --git a/website/package.json b/website/package.json index 0021728e996f..a64446b8fdce 100644 --- a/website/package.json +++ b/website/package.json @@ -36,13 +36,14 @@ "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.17", "clsx": "^1.1.1", "globby": "^11.0.1", - "react": "^17.0.1", + "react": "17.0.2", "react-dom": "^17.0.1", "react-github-btn": "^1.2.0", + "react-lite-youtube-embed": "^2.2.1-a", "react-markdown": "^8.0.0" }, "devDependencies": { - "@babel/core": "^7.0.0", + "@babel/core": "^7.11.6", "@crowdin/cli": "^3.5.2", "@types/react": "^17.0.3", "graphql": "^16.3.0", diff --git a/website/sidebars.json b/website/sidebars.json index d40f62713a60..7e109761b982 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -32,6 +32,9 @@ "tutorial-react", "tutorial-react-native", "testing-frameworks" + ], + "Upgrade Guides": [ + "upgrading-to-jest28" ] }, "api": [ diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 0ab8788f28fb..ac555ee4b141 100755 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -11,6 +11,7 @@ import Head from '@docusaurus/Head'; import Link from '@docusaurus/Link'; import backers from '@site/backers.json'; import Translate from '@docusaurus/Translate'; +import LiteYouTubeEmbed from 'react-lite-youtube-embed'; import {setupLandingAnimation} from '@site/src/pages/animations/_landingAnimation'; @@ -512,14 +513,7 @@ class Index extends React.Component {
- + If you'd like to learn how to build a testing framework like Jest from scratch, check out this video: - + There is also a [written guide you can follow](https://cpojer.net/posts/building-a-javascript-testing-framework). It teaches the fundamental concepts of Jest and explains how various parts of Jest can be used to compose a custom testing framework. diff --git a/website/versioned_docs/version-25.x/CLI.md b/website/versioned_docs/version-25.x/CLI.md index 26b677e6dfca..5fbd6b2f4efe 100644 --- a/website/versioned_docs/version-25.x/CLI.md +++ b/website/versioned_docs/version-25.x/CLI.md @@ -98,7 +98,11 @@ jest --update-snapshot --detectOpenHandles ## Options -_Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ +:::note + +CLI options take precedence over values from the [Configuration](Configuration.md). + +::: import TOCInline from '@theme/TOCInline'; @@ -118,7 +122,13 @@ Alias: `-b`. Exit the test suite immediately upon `n` number of failing test sui ### `--cache` -Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. _Note: the cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower._ +Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. + +:::note + +The cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower. + +::: If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. @@ -136,7 +146,13 @@ When this option is provided, Jest will assume it is running in a CI environment ### `--clearCache` -Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. _Note: clearing the cache will reduce performance._ +Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. + +:::caution + +Clearing the cache will reduce performance. + +::: ### `--clearMocks` @@ -198,7 +214,13 @@ Find and run the tests that cover a space separated list of source files that we ### `--forceExit` -Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. _Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down._ +Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. + +:::note + +This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down. + +::: ### `--help` @@ -222,7 +244,7 @@ Run all tests affected by file changes in the last commit made. Behaves similarl ### `--listTests` -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. +Lists all test files that Jest will run given the arguments, and exits. ### `--logHeapUsage` @@ -284,7 +306,11 @@ Alias: `-i`. Run all tests serially in the current process, rather than creating Run only the tests that were specified with their exact paths. -_Note: The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files_ +:::tip + +The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files. + +::: ### `--setupFilesAfterEnv ... ` @@ -319,7 +345,11 @@ The glob patterns Jest uses to detect test files. Please refer to the [`testMatc Alias: `-t`. Run only tests with a name that matches the regex. For example, suppose you want to run only tests related to authorization which will have names like `"GET /api/posts with auth"`, then you can use `jest -t=auth`. -_Note: The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks._ +:::note + +The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks. + +::: ### `--testPathIgnorePatterns=|[array]` diff --git a/website/versioned_docs/version-25.x/Configuration.md b/website/versioned_docs/version-25.x/Configuration.md index 6f9a9c607b1f..546b74d6ebac 100644 --- a/website/versioned_docs/version-25.x/Configuration.md +++ b/website/versioned_docs/version-25.x/Configuration.md @@ -97,9 +97,17 @@ test('if utils mocked automatically', () => { }); ``` -_Note: Node modules are automatically mocked when you have a manual mock in place (e.g.: `__mocks__/lodash.js`). More info [here](manual-mocks#mocking-node-modules)._ +:::note -_Note: Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`._ +Node modules are automatically mocked when you have a manual mock in place (e.g.: `__mocks__/lodash.js`). More info [here](manual-mocks#mocking-node-modules). + +::: + +:::note + +Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`. + +::: ### `bail` \[number | boolean] @@ -153,9 +161,17 @@ Example: This will collect coverage information for all the files inside the project's `rootDir`, except the ones that match `**/node_modules/**` or `**/vendor/**`. -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__tests__/**", "**/*.js"]` will not exclude `__tests__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/*.js`.)_ +:::note + +Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__tests__/**", "**/*.js"]` will not exclude `__tests__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/*.js`.) + +::: -_Note: This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`._ +:::note + +This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`. + +:::
Help: @@ -205,7 +221,11 @@ Default: `["clover", "json", "lcov", "text"]` A list of reporter names that Jest uses when writing coverage reports. Any [istanbul reporter](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) can be used. -_Note: Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output._ +:::note + +Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output. + +::: Additional options can be passed using the tuple form. For example, you may hide coverage report lines for all fully-covered files: @@ -416,11 +436,23 @@ Default: `undefined` This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. This function gets Jest's `globalConfig` object as a parameter. -_Note: A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ +:::note + +A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project. + +::: -_Note: Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites._ +:::note -_Note: While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation._ +Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites. + +::: + +:::note + +While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation. + +::: Example: @@ -445,9 +477,17 @@ Default: `undefined` This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. This function gets Jest's `globalConfig` object as a parameter. -_Note: A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ +:::tip + +A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project. + +::: -_Note: The same caveat concerning transformation of `node_modules` as for `globalSetup` applies to `globalTeardown`._ +:::note + +The same caveat concerning transformation of `node_modules` as for `globalSetup` applies to `globalTeardown`. + +::: ### `haste` \[object] @@ -527,7 +567,11 @@ Example: The order in which the mappings are defined matters. Patterns are checked one by one until one fits. The most specific rule should be listed first. This is true for arrays of module names as well. -_Note: If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub._ +:::caution + +If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub. + +::: ### `modulePathIgnorePatterns` \[array<string>] @@ -625,7 +669,11 @@ The projects feature can also be used to run multiple configurations or multiple } ``` -_Note: When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._ +:::note + +When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests. + +::: ### `reporters` \[array<moduleName | \[moduleName, options]>] @@ -747,7 +795,11 @@ The root directory that Jest should scan for tests and modules within. If you pu Oftentimes, you'll want to set this to `'src'` or `'lib'`, corresponding to where in your repository the code is stored. -_Note that using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`._ +:::note + +Using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`. + +::: ### `roots` \[array<string>] @@ -757,9 +809,17 @@ A list of paths to directories that Jest should use to search for files in. There are times where you only want Jest to search in a single sub-directory (such as cases where you have a `src/` directory in your repo), but prevent it from accessing the rest of the repo. -_Note: While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`)._ +:::note + +While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`). + +::: + +:::note + +By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`. -_Note: By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`._ +::: ### `runner` \[string] @@ -772,7 +832,11 @@ This option allows you to use a custom runner instead of Jest's default test run - [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc) - [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier) -_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._ +:::note + +The `runner` property value can omit the `jest-runner-` prefix of the package name. + +::: To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature: @@ -805,7 +869,11 @@ If you want a path to be [relative to the root directory of your project](#rootd For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. -_Note: `setupTestFrameworkScriptFile` is deprecated in favor of `setupFilesAfterEnv`._ +:::note + +`setupTestFrameworkScriptFile` is deprecated in favor of `setupFilesAfterEnv`. + +::: Example `setupFilesAfterEnv` array in a jest.config.js: @@ -943,7 +1011,11 @@ To use this class as your custom environment, refer to it by its full path withi */ ``` -_Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ +:::note + +TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment. + +::: Example: @@ -1013,7 +1085,11 @@ Default: `1` The exit code Jest returns on test failure. -_Note: This does not change the exit code in the case of Jest errors (e.g. invalid configuration)._ +:::note + +This does not change the exit code in the case of Jest errors (e.g. invalid configuration). + +::: ### `testMatch` [array<string>] @@ -1025,7 +1101,11 @@ See the [micromatch](https://github.com/micromatch/micromatch) package for detai See also [`testRegex` [string | array<string>]](#testregex-string--arraystring), but note that you cannot specify both options. -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__fixtures__/**", "**/__tests__/**/*.js"]` will not exclude `__fixtures__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/__tests__/**/*.js`.)_ +:::note + +Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__fixtures__/**", "**/__tests__/**/*.js"]` will not exclude `__fixtures__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/__tests__/**/*.js`.) + +::: ### `testPathIgnorePatterns` \[array<string>] @@ -1053,7 +1133,11 @@ The following is a visualization of the default regex: └── component.js # not test ``` -_Note: `testRegex` will try to detect test files using the **absolute file path**, therefore, having a folder with a name that matches it will run all the files as tests_ +:::note + +`testRegex` will try to detect test files using the **absolute file path**, therefore, having a folder with a name that matches it will run all the files as tests + +::: ### `testResultsProcessor` \[string] @@ -1196,9 +1280,17 @@ Examples of such compilers include: You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}` -_Note: a transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues)._ +:::tip -_Note: when adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or Typescript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup)_ +A transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues). + +::: + +:::note + +When adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or Typescript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup). + +::: ### `transformIgnorePatterns` \[array<string>] @@ -1271,7 +1363,11 @@ Examples of watch plugins include: - [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) - [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) -_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._ +:::note + +The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name. + +::: ### `watchman` \[boolean] diff --git a/website/versioned_docs/version-25.x/GettingStarted.md b/website/versioned_docs/version-25.x/GettingStarted.md index 266a05d817e3..db798b49b5c1 100644 --- a/website/versioned_docs/version-25.x/GettingStarted.md +++ b/website/versioned_docs/version-25.x/GettingStarted.md @@ -158,6 +158,10 @@ However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transf [ts-jest](https://github.com/kulshekhar/ts-jest) is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript. +```bash +yarn add --dev ts-jest +``` + #### Type definitions You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. diff --git a/website/versioned_docs/version-25.x/TestingAsyncCode.md b/website/versioned_docs/version-25.x/TestingAsyncCode.md index 432db44b3944..ef8762eb7d87 100644 --- a/website/versioned_docs/version-25.x/TestingAsyncCode.md +++ b/website/versioned_docs/version-25.x/TestingAsyncCode.md @@ -5,18 +5,82 @@ title: Testing Asynchronous Code It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this. -## Callbacks +## Promises + +Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will fail. + +For example, let's say that `fetchData` returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: + +```js +test('the data is peanut butter', () => { + return fetchData().then(data => { + expect(data).toBe('peanut butter'); + }); +}); +``` + +## Async/Await + +Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: + +```js +test('the data is peanut butter', async () => { + const data = await fetchData(); + expect(data).toBe('peanut butter'); +}); -The most common asynchronous pattern is callbacks. +test('the fetch fails with an error', async () => { + expect.assertions(1); + try { + await fetchData(); + } catch (e) { + expect(e).toMatch('error'); + } +}); +``` + +You can combine `async` and `await` with `.resolves` or `.rejects`. + +```js +test('the data is peanut butter', async () => { + await expect(fetchData()).resolves.toBe('peanut butter'); +}); + +test('the fetch fails with an error', async () => { + await expect(fetchData()).rejects.toMatch('error'); +}); +``` + +In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. + +:::caution + +Be sure to return (or `await`) the promise - if you omit the `return`/`await` statement, your test will complete before the promise returned from `fetchData` resolves or rejects. + +::: + +If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. + +```js +test('the fetch fails with an error', () => { + expect.assertions(1); + return fetchData().catch(e => expect(e).toMatch('error')); +}); +``` + +## Callbacks -For example, let's say that you have a `fetchData(callback)` function that fetches some data and calls `callback(data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. +If you don't use promises, you can use callbacks. For example, let's say that `fetchData`, instead of returning a promise, expects a callback, i.e. fetches some data and calls `callback(null, data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. By default, Jest tests complete once they reach the end of their execution. That means this test will _not_ work as intended: ```js // Don't do this! test('the data is peanut butter', () => { - function callback(data) { + function callback(error, data) { + if (error) { + throw error; + } expect(data).toBe('peanut butter'); } @@ -30,7 +94,11 @@ There is an alternate form of `test` that fixes this. Instead of putting the tes ```js test('the data is peanut butter', done => { - function callback(data) { + function callback(error, data) { + if (error) { + done(error); + return; + } try { expect(data).toBe('peanut butter'); done(); @@ -49,31 +117,6 @@ If the `expect` statement fails, it throws an error and `done()` is not called. _Note: `done()` should not be mixed with Promises as this tends to lead to memory leaks in your tests._ -## Promises - -If your code uses promises, there is a more straightforward way to handle asynchronous tests. Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -For example, let's say that `fetchData`, instead of using a callback, returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: - -```js -test('the data is peanut butter', () => { - return fetchData().then(data => { - expect(data).toBe('peanut butter'); - }); -}); -``` - -Be sure to return the promise - if you omit this `return` statement, your test will complete before the promise returned from `fetchData` resolves and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. - -```js -test('the fetch fails with an error', () => { - expect.assertions(1); - return fetchData().catch(e => expect(e).toMatch('error')); -}); -``` - ## `.resolves` / `.rejects` You can also use the `.resolves` matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. @@ -94,38 +137,4 @@ test('the fetch fails with an error', () => { }); ``` -## Async/Await - -Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: - -```js -test('the data is peanut butter', async () => { - const data = await fetchData(); - expect(data).toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - expect.assertions(1); - try { - await fetchData(); - } catch (e) { - expect(e).toMatch('error'); - } -}); -``` - -You can combine `async` and `await` with `.resolves` or `.rejects`. - -```js -test('the data is peanut butter', async () => { - await expect(fetchData()).resolves.toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - await expect(fetchData()).rejects.toMatch('error'); -}); -``` - -In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. - None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler. diff --git a/website/versioned_docs/version-25.x/TestingFrameworks.md b/website/versioned_docs/version-25.x/TestingFrameworks.md index 5b085c8658a3..6ae4358fdf7d 100644 --- a/website/versioned_docs/version-25.x/TestingFrameworks.md +++ b/website/versioned_docs/version-25.x/TestingFrameworks.md @@ -42,4 +42,8 @@ Jest is a universal testing platform, with the ability to adapt to any JavaScrip ## Hapi.js -- [Testing Hapi.js With Jest](http://niralar.com/testing-hapi-js-with-jest/) by Niralar ([Sivasankar](http://sivasankar.in/)) +- [Testing Hapi.js With Jest](https://github.com/sivasankars/testing-hapi.js-with-jest) by Niralar + +## Next.js + +- [Jest and React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library) by Next.js docs diff --git a/website/versioned_docs/version-26.x/Architecture.md b/website/versioned_docs/version-26.x/Architecture.md index 300abd75d9f0..8d9a7c1d5eba 100644 --- a/website/versioned_docs/version-26.x/Architecture.md +++ b/website/versioned_docs/version-26.x/Architecture.md @@ -3,26 +3,14 @@ id: architecture title: Architecture --- +import LiteYouTubeEmbed from 'react-lite-youtube-embed'; + If you are interested in learning more about how Jest works, understand its architecture, and how Jest is split up into individual reusable packages, check out this video: - + If you'd like to learn how to build a testing framework like Jest from scratch, check out this video: - + There is also a [written guide you can follow](https://cpojer.net/posts/building-a-javascript-testing-framework). It teaches the fundamental concepts of Jest and explains how various parts of Jest can be used to compose a custom testing framework. diff --git a/website/versioned_docs/version-26.x/CLI.md b/website/versioned_docs/version-26.x/CLI.md index bb0730a2a966..616edf089126 100644 --- a/website/versioned_docs/version-26.x/CLI.md +++ b/website/versioned_docs/version-26.x/CLI.md @@ -234,7 +234,7 @@ Run all tests affected by file changes in the last commit made. Behaves similarl ### `--listTests` -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. +Lists all test files that Jest will run given the arguments, and exits. ### `--logHeapUsage` diff --git a/website/versioned_docs/version-26.x/GettingStarted.md b/website/versioned_docs/version-26.x/GettingStarted.md index e4c54cc22985..f6183a371810 100644 --- a/website/versioned_docs/version-26.x/GettingStarted.md +++ b/website/versioned_docs/version-26.x/GettingStarted.md @@ -158,6 +158,10 @@ However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transf [ts-jest](https://github.com/kulshekhar/ts-jest) is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript. +```bash +yarn add --dev ts-jest +``` + ### Using TypeScript: type definitions You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. diff --git a/website/versioned_docs/version-26.x/TestingAsyncCode.md b/website/versioned_docs/version-26.x/TestingAsyncCode.md index 432db44b3944..ef8762eb7d87 100644 --- a/website/versioned_docs/version-26.x/TestingAsyncCode.md +++ b/website/versioned_docs/version-26.x/TestingAsyncCode.md @@ -5,18 +5,82 @@ title: Testing Asynchronous Code It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this. -## Callbacks +## Promises + +Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will fail. + +For example, let's say that `fetchData` returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: + +```js +test('the data is peanut butter', () => { + return fetchData().then(data => { + expect(data).toBe('peanut butter'); + }); +}); +``` + +## Async/Await + +Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: + +```js +test('the data is peanut butter', async () => { + const data = await fetchData(); + expect(data).toBe('peanut butter'); +}); -The most common asynchronous pattern is callbacks. +test('the fetch fails with an error', async () => { + expect.assertions(1); + try { + await fetchData(); + } catch (e) { + expect(e).toMatch('error'); + } +}); +``` + +You can combine `async` and `await` with `.resolves` or `.rejects`. + +```js +test('the data is peanut butter', async () => { + await expect(fetchData()).resolves.toBe('peanut butter'); +}); + +test('the fetch fails with an error', async () => { + await expect(fetchData()).rejects.toMatch('error'); +}); +``` + +In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. + +:::caution + +Be sure to return (or `await`) the promise - if you omit the `return`/`await` statement, your test will complete before the promise returned from `fetchData` resolves or rejects. + +::: + +If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. + +```js +test('the fetch fails with an error', () => { + expect.assertions(1); + return fetchData().catch(e => expect(e).toMatch('error')); +}); +``` + +## Callbacks -For example, let's say that you have a `fetchData(callback)` function that fetches some data and calls `callback(data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. +If you don't use promises, you can use callbacks. For example, let's say that `fetchData`, instead of returning a promise, expects a callback, i.e. fetches some data and calls `callback(null, data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. By default, Jest tests complete once they reach the end of their execution. That means this test will _not_ work as intended: ```js // Don't do this! test('the data is peanut butter', () => { - function callback(data) { + function callback(error, data) { + if (error) { + throw error; + } expect(data).toBe('peanut butter'); } @@ -30,7 +94,11 @@ There is an alternate form of `test` that fixes this. Instead of putting the tes ```js test('the data is peanut butter', done => { - function callback(data) { + function callback(error, data) { + if (error) { + done(error); + return; + } try { expect(data).toBe('peanut butter'); done(); @@ -49,31 +117,6 @@ If the `expect` statement fails, it throws an error and `done()` is not called. _Note: `done()` should not be mixed with Promises as this tends to lead to memory leaks in your tests._ -## Promises - -If your code uses promises, there is a more straightforward way to handle asynchronous tests. Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -For example, let's say that `fetchData`, instead of using a callback, returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: - -```js -test('the data is peanut butter', () => { - return fetchData().then(data => { - expect(data).toBe('peanut butter'); - }); -}); -``` - -Be sure to return the promise - if you omit this `return` statement, your test will complete before the promise returned from `fetchData` resolves and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. - -```js -test('the fetch fails with an error', () => { - expect.assertions(1); - return fetchData().catch(e => expect(e).toMatch('error')); -}); -``` - ## `.resolves` / `.rejects` You can also use the `.resolves` matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. @@ -94,38 +137,4 @@ test('the fetch fails with an error', () => { }); ``` -## Async/Await - -Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: - -```js -test('the data is peanut butter', async () => { - const data = await fetchData(); - expect(data).toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - expect.assertions(1); - try { - await fetchData(); - } catch (e) { - expect(e).toMatch('error'); - } -}); -``` - -You can combine `async` and `await` with `.resolves` or `.rejects`. - -```js -test('the data is peanut butter', async () => { - await expect(fetchData()).resolves.toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - await expect(fetchData()).rejects.toMatch('error'); -}); -``` - -In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. - None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler. diff --git a/website/versioned_docs/version-26.x/TestingFrameworks.md b/website/versioned_docs/version-26.x/TestingFrameworks.md index 5b085c8658a3..6ae4358fdf7d 100644 --- a/website/versioned_docs/version-26.x/TestingFrameworks.md +++ b/website/versioned_docs/version-26.x/TestingFrameworks.md @@ -42,4 +42,8 @@ Jest is a universal testing platform, with the ability to adapt to any JavaScrip ## Hapi.js -- [Testing Hapi.js With Jest](http://niralar.com/testing-hapi-js-with-jest/) by Niralar ([Sivasankar](http://sivasankar.in/)) +- [Testing Hapi.js With Jest](https://github.com/sivasankars/testing-hapi.js-with-jest) by Niralar + +## Next.js + +- [Jest and React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library) by Next.js docs diff --git a/website/versioned_docs/version-27.0/CLI.md b/website/versioned_docs/version-27.0/CLI.md deleted file mode 100644 index 05952b17e30d..000000000000 --- a/website/versioned_docs/version-27.0/CLI.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -id: cli -title: Jest CLI Options ---- - -The `jest` command line runner has a number of useful options. You can run `jest --help` to view all available options. Many of the options shown below can also be used together to run tests exactly the way you want. Every one of Jest's [Configuration](Configuration.md) options can also be specified through the CLI. - -Here is a brief overview: - -## Running from the command line - -Run all tests (default): - -```bash -jest -``` - -Run only the tests that were specified with a pattern or filename: - -```bash -jest my-test #or -jest path/to/my-test.js -``` - -Run tests related to changed files based on hg/git (uncommitted files): - -```bash -jest -o -``` - -Run tests related to `path/to/fileA.js` and `path/to/fileB.js`: - -```bash -jest --findRelatedTests path/to/fileA.js path/to/fileB.js -``` - -Run tests that match this spec name (match against the name in `describe` or `test`, basically). - -```bash -jest -t name-of-spec -``` - -Run watch mode: - -```bash -jest --watch #runs jest -o by default -jest --watchAll #runs all tests -``` - -Watch mode also enables to specify the name or path to a file to focus on a specific set of tests. - -## Using with yarn - -If you run Jest via `yarn test`, you can pass the command line arguments directly as Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -yarn test -u -t="ColorPicker" -``` - -## Using with npm scripts - -If you run Jest via `npm test`, you can still use the command line arguments by inserting a `--` between `npm test` and the Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -npm test -- -u -t="ColorPicker" -``` - -## Camelcase & dashed args support - -Jest supports both camelcase and dashed arg formats. The following examples will have an equal result: - -```bash -jest --collect-coverage -jest --collectCoverage -``` - -Arguments can also be mixed: - -```bash -jest --update-snapshot --detectOpenHandles -``` - -## Options - -_Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `jest ` - -When you run `jest` with an argument, that argument is treated as a regular expression to match against files in your project. It is possible to run test suites by providing a pattern. Only the files that the pattern matches will be picked up and executed. Depending on your terminal, you may need to quote this argument: `jest "my.*(complex)?pattern"`. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--bail` - -Alias: `-b`. Exit the test suite immediately upon `n` number of failing test suite. Defaults to `1`. - -### `--cache` - -Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. _Note: the cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower._ - -If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. - -### `--changedFilesWithAncestor` - -Runs tests related to the current changes and the changes made in the last commit. Behaves similarly to `--onlyChanged`. - -### `--changedSince` - -Runs tests related to the changes since the provided branch or commit hash. If the current branch has diverged from the given branch, then only changes made locally will be tested. Behaves similarly to `--onlyChanged`. - -### `--ci` - -When this option is provided, Jest will assume it is running in a CI environment. This changes the behavior when a new snapshot is encountered. Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`. - -### `--clearCache` - -Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. _Note: clearing the cache will reduce performance._ - -### `--clearMocks` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `--collectCoverageFrom=` - -A glob pattern relative to `rootDir` matching the files that coverage info needs to be collected from. - -### `--colors` - -Forces test results output highlighting even if stdout is not a TTY. - -### `--config=` - -Alias: `-c`. The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the `rootDir` for the project. This can also be a JSON-encoded value which Jest will use as configuration. - -### `--coverage[=]` - -Alias: `--collectCoverage`. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. - -### `--coverageProvider=` - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `--debug` - -Print debugging info about your Jest config. - -### `--detectOpenHandles` - -Attempt to collect and print open handles preventing Jest from exiting cleanly. Use this in cases where you need to use `--forceExit` in order for Jest to exit to potentially track down the reason. This implies `--runInBand`, making tests run serially. Implemented using [`async_hooks`](https://nodejs.org/api/async_hooks.html). This option has a significant performance penalty and should only be used for debugging. - -### `--env=` - -The test environment used for all tests. This can point to any file or node module. Examples: `jsdom`, `node` or `path/to/my-environment.js`. - -### `--errorOnDeprecated` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `--expand` - -Alias: `-e`. Use this flag to show full diffs and errors instead of a patch. - -### `--filter=` - -Path to a module exporting a filtering function. This method receives a list of tests which can be manipulated to exclude tests from running. Especially useful when used in conjunction with a testing infrastructure to filter known broken. - -### `--findRelatedTests ` - -Find and run the tests that cover a space separated list of source files that were passed in as arguments. Useful for pre-commit hook integration to run the minimal amount of tests necessary. Can be used together with `--coverage` to include a test coverage for the source files, no duplicate `--collectCoverageFrom` arguments needed. - -### `--forceExit` - -Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. _Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down._ - -### `--help` - -Show the help information, similar to this page. - -### `--init` - -Generate a basic configuration file. Based on your project, Jest will ask you a few questions that will help to generate a `jest.config.js` file with a short description for each option. - -### `--injectGlobals` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus` test runner._ - -### `--json` - -Prints the test results in JSON. This mode will send all other test output and user messages to stderr. - -### `--outputFile=` - -Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testresultsprocessor-string). - -### `--lastCommit` - -Run all tests affected by file changes in the last commit made. Behaves similarly to `--onlyChanged`. - -### `--listTests` - -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. - -### `--logHeapUsage` - -Logs the heap usage after every test. Useful to debug memory leaks. Use together with `--runInBand` and `--expose-gc` in node. - -### `--maxConcurrency=` - -Prevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use `test.concurrent`. - -### `--maxWorkers=|` - -Alias: `-w`. Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `--maxWorkers=50%` - -### `--noStackTrace` - -Disables stack trace in test results output. - -### `--notify` - -Activates notifications for test results. Good for when you don't want your consciousness to be able to focus on anything except JavaScript testing. - -### `--onlyChanged` - -Alias: `-o`. Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a git/hg repository at the moment and requires a static dependency graph (ie. no dynamic requires). - -### `--passWithNoTests` - -Allows the test suite to pass when no files are found. - -### `--projects ... ` - -Run tests from one or more projects, found in the specified paths; also takes path globs. This option is the CLI equivalent of the [`projects`](configuration#projects-arraystring--projectconfig) configuration option. Note that if configuration files are found in the specified paths, _all_ projects specified within those configuration files will be run. - -### `--reporters` - -Run tests with specified reporters. [Reporter options](configuration#reporters-arraymodulename--modulename-options) are not available via CLI. Example with multiple reporters: - -`jest --reporters="default" --reporters="jest-junit"` - -### `--resetMocks` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `--restoreMocks` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `--roots` - -A list of paths to directories that Jest should use to search for files in. - -### `--runInBand` - -Alias: `-i`. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - -### `--runTestsByPath` - -Run only the tests that were specified with their exact paths. - -_Note: The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files_ - -### `--selectProjects ... ` - -Run only the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. - -### `--setupFilesAfterEnv ... ` - -A list of paths to modules that run some code to configure or to set up the testing framework before each test. Beware that files imported by the setup scripts will not be mocked during testing. - -### `--showConfig` - -Print your Jest config and then exits. - -### `--silent` - -Prevent tests from printing messages through the console. - -### `--testLocationInResults` - -Adds a `location` field to test results. Useful if you want to report the location of a test in a reporter. - -Note that `column` is 0-indexed while `line` is not. - -```json -{ - "column": 4, - "line": 5 -} -``` - -### `--testMatch glob1 ... globN` - -The glob patterns Jest uses to detect test files. Please refer to the [`testMatch` configuration](Configuration.md#testmatch-arraystring) for details. - -### `--testNamePattern=` - -Alias: `-t`. Run only tests with a name that matches the regex. For example, suppose you want to run only tests related to authorization which will have names like `"GET /api/posts with auth"`, then you can use `jest -t=auth`. - -_Note: The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks._ - -### `--testPathIgnorePatterns=|[array]` - -A single or array of regexp pattern strings that are tested against all tests paths before executing the test. Contrary to `--testPathPattern`, it will only run those tests with a path that does not match with the provided regexp expressions. - -To pass as an array use escaped parentheses and space delimited regexps such as `\(/node_modules/ /tests/e2e/\)`. Alternatively, you can omit parentheses by combining regexps into a single regexp like `/node_modules/|/tests/e2e/`. These two examples are equivalent. - -### `--testPathPattern=` - -A regexp pattern string that is matched against all tests paths before executing the test. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--testRunner=` - -Lets you specify a custom test runner. - -### `--testSequencer=` - -Lets you specify a custom test sequencer. Please refer to the documentation of the corresponding configuration property for details. - -### `--testTimeout=` - -Default timeout of a test in milliseconds. Default value: 5000. - -### `--updateSnapshot` - -Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots. - -### `--useStderr` - -Divert all output to stderr. - -### `--verbose` - -Display individual test results with the test suite hierarchy. - -### `--version` - -Alias: `-v`. Print the version and exit. - -### `--watch` - -Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option instead. - -### `--watchAll` - -Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. - -Use `--watchAll=false` to explicitly disable the watch mode. Note that in most CI environments, this is automatically handled for you. - -### `--watchman` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. Defaults to `true`. Disable using `--no-watchman`. diff --git a/website/versioned_docs/version-27.0/CodeTransformation.md b/website/versioned_docs/version-27.0/CodeTransformation.md deleted file mode 100644 index 3b1e0f8e3100..000000000000 --- a/website/versioned_docs/version-27.0/CodeTransformation.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -id: code-transformation -title: Code Transformation ---- - -Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node.js out of the box (such as JSX, types from TypeScript, Vue templates etc.) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers. - -Jest supports this via the [`transform` configuration option](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object). - -A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. - -Jest will cache the result of a transformation and attempt to invalidate that result based on a number of factors, such as the source of the file being transformed and changing configuration. - -## Defaults - -Jest ships with one transformer out of the box - `babel-jest`. It will automatically load your project's Babel configuration and transform any file matching the following RegEx: `/\.[jt]sx?$/` meaning any `.js`, `.jsx`, `.ts` and `.tsx` file. In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). - -If you override the `transform` configuration option `babel-jest` will no longer be active, and you'll need to add it manually if you wish to use Babel. - -## Writing custom transformers - -You can write your own transformer. The API of a transformer is as follows: - -```ts -interface SyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => SyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -interface AsyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => AsyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -type Transformer = - | SyncTransformer - | AsyncTransformer; - -interface TransformOptions { - /** - * If a transformer does module resolution and reads files, it should populate `cacheFS` so that - * Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of - * - */ - cacheFS: Map; - config: Config.ProjectConfig; - /** A stringified version of the configuration - useful in cache busting */ - configString: string; - instrument: boolean; - // names are copied from babel: https://babeljs.io/docs/en/options#caller - supportsDynamicImport: boolean; - supportsExportNamespaceFrom: boolean; - supportsStaticESM: boolean; - supportsTopLevelAwait: boolean; - /** the options passed through Jest's config by the user */ - transformerConfig: OptionType; -} - -type TransformedSource = - | {code: string; map?: RawSourceMap | string | null} - | string; - -// Config.ProjectConfig can be seen in code [here](https://github.com/facebook/jest/blob/v26.6.3/packages/jest-types/src/Config.ts#L323) -// RawSourceMap comes from [`source-map`](https://github.com/mozilla/source-map/blob/0.6.1/source-map.d.ts#L6-L12) -``` - -As can be seen, only `process` or `processAsync` is mandatory to implement, although we highly recommend implementing `getCacheKey` as well, so we don't waste resources transpiling the same source file when we can read its previous result from disk. You can use [`@jest/create-cache-key-function`](https://www.npmjs.com/package/@jest/create-cache-key-function) to help implement it. - -Note that [ECMAScript module](ECMAScriptModules.md) support is indicated by the passed in `supports*` options. Specifically `supportsDynamicImport: true` means the transformer can return `import()` expressions, which is supported by both ESM and CJS. If `supportsStaticESM: true` it means top level `import` statements are supported and the code will be interpreted as ESM and not CJS. See [Node's docs](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs) for details on the differences. - -### Examples - -### TypeScript with type checking - -While `babel-jest` by default will transpile TypeScript files, Babel will not verify the types. If you want that you can use [`ts-jest`](https://github.com/kulshekhar/ts-jest). - -#### Transforming images to their path - -Importing images is a way to include them in your browser bundle, but they are not valid JavaScript. One way of handling it in Jest is to replace the imported value with its filename. - -```js title="fileTransformer.js" -const path = require('path'); - -module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; - }, -}; -``` - -```js title="jest.config.js" -module.exports = { - transform: { - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': - '/fileTransformer.js', - }, -}; -``` diff --git a/website/versioned_docs/version-27.0/Configuration.md b/website/versioned_docs/version-27.0/Configuration.md deleted file mode 100644 index ab8d4eda9693..000000000000 --- a/website/versioned_docs/version-27.0/Configuration.md +++ /dev/null @@ -1,1424 +0,0 @@ ---- -id: configuration -title: Configuring Jest ---- - -Jest's configuration can be defined in the `package.json` file of your project, or through a `jest.config.js`, or `jest.config.ts` file or through the `--config ` option. If you'd like to use your `package.json` to store Jest's config, the `"jest"` key should be used on the top level so Jest will know how to find your settings: - -```json -{ - "name": "my-project", - "jest": { - "verbose": true - } -} -``` - -Or through JavaScript: - -```js title="jest.config.js" -// Sync object -/** @type {import('@jest/types').Config.InitialOptions} */ -const config = { - verbose: true, -}; - -module.exports = config; - -// Or async function -module.exports = async () => { - return { - verbose: true, - }; -}; -``` - -Or through TypeScript (if `ts-node` is installed): - -```ts title="jest.config.ts" -import type {Config} from '@jest/types'; - -// Sync object -const config: Config.InitialOptions = { - verbose: true, -}; -export default config; - -// Or async function -export default async (): Promise => { - return { - verbose: true, - }; -}; -``` - -Please keep in mind that the resulting configuration must be JSON-serializable. - -When using the `--config` option, the JSON file must not contain a "jest" key: - -```json -{ - "bail": 1, - "verbose": true -} -``` - -## Options - -These options let you control Jest's behavior in your `package.json` file. The Jest philosophy is to work great by default, but sometimes you just need more configuration power. - -### Defaults - -You can retrieve Jest's default options to expand them if needed: - -```js title="jest.config.js" -const {defaults} = require('jest-config'); -module.exports = { - // ... - moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'], - // ... -}; -``` - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `automock` \[boolean] - -Default: `false` - -This option tells Jest that all imported modules in your tests should be mocked automatically. All modules used in your tests will have a replacement implementation, keeping the API surface. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js -//__tests__/automocking.test.js -import utils from '../utils'; - -test('if utils mocked automatically', () => { - // Public methods of `utils` are now mock functions - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized.mock).toBeTruthy(); - - // You can provide them with your own implementation - // or pass the expected return value - utils.authorize.mockReturnValue('mocked_token'); - utils.isAuthorized.mockReturnValue(true); - - expect(utils.authorize()).toBe('mocked_token'); - expect(utils.isAuthorized('not_wizard')).toBeTruthy(); -}); -``` - -_Note: Node modules are automatically mocked when you have a manual mock in place (e.g.: `__mocks__/lodash.js`). More info [here](manual-mocks#mocking-node-modules)._ - -_Note: Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`._ - -### `bail` \[number | boolean] - -Default: `0` - -By default, Jest runs all tests and produces all errors into the console upon completion. The bail config option can be used here to have Jest stop running tests after `n` failures. Setting bail to `true` is the same as setting bail to `1`. - -### `cacheDirectory` \[string] - -Default: `"/tmp/"` - -The directory where Jest should store its cached dependency information. - -Jest attempts to scan your dependency tree once (up-front) and cache it in order to ease some of the filesystem churn that needs to happen while running tests. This config option lets you customize where Jest stores that cache data on disk. - -### `clearMocks` \[boolean] - -Default: `false` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `collectCoverage` \[boolean] - -Default: `false` - -Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. - -### `collectCoverageFrom` \[array] - -Default: `undefined` - -An array of [glob patterns](https://github.com/micromatch/micromatch) indicating a set of files for which coverage information should be collected. If a file matches the specified glob pattern, coverage information will be collected for it even if no tests exist for this file and it's never required in the test suite. - -Example: - -```json -{ - "collectCoverageFrom": [ - "**/*.{js,jsx}", - "!**/node_modules/**", - "!**/vendor/**" - ] -} -``` - -This will collect coverage information for all the files inside the project's `rootDir`, except the ones that match `**/node_modules/**` or `**/vendor/**`. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__tests__/**", "**/*.js"]` will not exclude `__tests__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/*.js`.)_ - -_Note: This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`._ - -
- Help: - If you are seeing coverage output such as... - -``` -=============================== Coverage summary =============================== -Statements : Unknown% ( 0/0 ) -Branches : Unknown% ( 0/0 ) -Functions : Unknown% ( 0/0 ) -Lines : Unknown% ( 0/0 ) -================================================================================ -Jest: Coverage data for global was not found. -``` - -Most likely your glob patterns are not matching any files. Refer to the [micromatch](https://github.com/micromatch/micromatch) documentation to ensure your globs are compatible. - -
- -### `coverageDirectory` \[string] - -Default: `undefined` - -The directory where Jest should output its coverage files. - -### `coveragePathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all file paths before executing the test. If the file path matches any of the patterns, coverage information will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `coverageProvider` \[string] - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `coverageReporters` \[array<string | \[string, options]>] - -Default: `["clover", "json", "lcov", "text"]` - -A list of reporter names that Jest uses when writing coverage reports. Any [istanbul reporter](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) can be used. - -_Note: Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output._ - -Additional options can be passed using the tuple form. For example, you may hide coverage report lines for all fully-covered files: - -```json -{ - "coverageReporters": ["clover", "json", "lcov", ["text", {"skipFull": true}]] -} -``` - -For more information about the options object shape refer to `CoverageReporterWithOptions` type in the [type definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Config.ts). - -### `coverageThreshold` \[object] - -Default: `undefined` - -This will be used to configure minimum threshold enforcement for coverage results. Thresholds can be specified as `global`, as a [glob](https://github.com/isaacs/node-glob#glob-primer), and as a directory or file path. If thresholds aren't met, jest will fail. Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed. - -For example, with the following configuration jest will fail if there is less than 80% branch, line, and function coverage, or if there are more than 10 uncovered statements: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 80, - "functions": 80, - "lines": 80, - "statements": -10 - } - } - } -} -``` - -If globs or paths are specified alongside `global`, coverage data for matching paths will be subtracted from overall coverage and thresholds will be applied independently. Thresholds for globs are applied to all files matching the glob. If the file specified by path is not found, an error is returned. - -For example, with the following configuration: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 50, - "functions": 50, - "lines": 50, - "statements": 50 - }, - "./src/components/": { - "branches": 40, - "statements": 40 - }, - "./src/reducers/**/*.js": { - "statements": 90 - }, - "./src/api/very-important-module.js": { - "branches": 100, - "functions": 100, - "lines": 100, - "statements": 100 - } - } - } -} -``` - -Jest will fail if: - -- The `./src/components` directory has less than 40% branch or statement coverage. -- One of the files matching the `./src/reducers/**/*.js` glob has less than 90% statement coverage. -- The `./src/api/very-important-module.js` file has less than 100% coverage. -- Every remaining file combined has less than 50% coverage (`global`). - -### `dependencyExtractor` \[string] - -Default: `undefined` - -This option allows the use of a custom dependency extractor. It must be a node module that exports an object with an `extract` function. E.g.: - -```javascript -const crypto = require('crypto'); -const fs = require('fs'); - -module.exports = { - extract(code, filePath, defaultExtract) { - const deps = defaultExtract(code, filePath); - // Scan the file and add dependencies in `deps` (which is a `Set`) - return deps; - }, - getCacheKey() { - return crypto - .createHash('md5') - .update(fs.readFileSync(__filename)) - .digest('hex'); - }, -}; -``` - -The `extract` function should return an iterable (`Array`, `Set`, etc.) with the dependencies found in the code. - -That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded. - -### `displayName` \[string, object] - -default: `undefined` - -Allows for a label to be printed alongside a test while it is running. This becomes more useful in multi-project repositories where there can be many jest configuration files. This visually tells which project a test belongs to. Here are sample valid values. - -```js -module.exports = { - displayName: 'CLIENT', -}; -``` - -or - -```js -module.exports = { - displayName: { - name: 'CLIENT', - color: 'blue', - }, -}; -``` - -As a secondary option, an object with the properties `name` and `color` can be passed. This allows for a custom configuration of the background color of the displayName. `displayName` defaults to white when its value is a string. Jest uses [chalk](https://github.com/chalk/chalk) to provide the color. As such, all of the valid options for colors supported by chalk are also supported by jest. - -### `errorOnDeprecated` \[boolean] - -Default: `false` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `extensionsToTreatAsEsm` \[array<string>] - -Default: `[]` - -Jest will run `.mjs` and `.js` files with nearest `package.json`'s `type` field set to `module` as ECMAScript Modules. If you have any other files that should run with native ESM, you need to specify their file extension here. - -> Note: Jest's ESM support is still experimental, see [its docs for more details](ECMAScriptModules.md). - -```json -{ - ... - "jest": { - "extensionsToTreatAsEsm": [".ts"] - } -} -``` - -### `extraGlobals` \[array<string>] - -Default: `undefined` - -Test files run inside a [vm](https://nodejs.org/api/vm.html), which slows calls to global context properties (e.g. `Math`). With this option you can specify extra properties to be defined inside the vm for faster lookups. - -For example, if your tests call `Math` often, you can pass it by setting `extraGlobals`. - -```json -{ - ... - "jest": { - "extraGlobals": ["Math"] - } -} -``` - -### `forceCoverageMatch` \[array<string>] - -Default: `['']` - -Test files are normally ignored from collecting code coverage. With this option, you can overwrite this behavior and include otherwise ignored files in code coverage. - -For example, if you have tests in source files named with `.t.js` extension as following: - -```javascript title="sum.t.js" -export function sum(a, b) { - return a + b; -} - -if (process.env.NODE_ENV === 'test') { - test('sum', () => { - expect(sum(1, 2)).toBe(3); - }); -} -``` - -You can collect coverage from those files with setting `forceCoverageMatch`. - -```json -{ - ... - "jest": { - "forceCoverageMatch": ["**/*.t.js"] - } -} -``` - -### `globals` \[object] - -Default: `{}` - -A set of global variables that need to be available in all test environments. - -For example, the following would create a global `__DEV__` variable set to `true` in all test environments: - -```json -{ - ... - "jest": { - "globals": { - "__DEV__": true - } - } -} -``` - -Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will _not_ be persisted across test runs for other test files. In addition, the `globals` object must be json-serializable, so it can't be used to specify global functions. For that, you should use `setupFiles`. - -### `globalSetup` \[string] - -Default: `undefined` - -This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites._ - -_Note: While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation._ - -Example: - -```js title="setup.js" -// can be synchronous -module.exports = async () => { - // ... - // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongod; -}; -``` - -```js title="teardown.js" -module.exports = async function () { - await global.__MONGOD__.stop(); -}; -``` - -### `globalTeardown` \[string] - -Default: `undefined` - -This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: The same caveat concerning transformation of `node_modules` as for `globalSetup` applies to `globalTeardown`._ - -### `haste` \[object] - -Default: `undefined` - -This will be used to configure the behavior of `jest-haste-map`, Jest's internal file crawler/cache system. The following options are supported: - -```ts -type HasteConfig = { - /** Whether to hash files using SHA-1. */ - computeSha1?: boolean; - /** The platform to use as the default, e.g. 'ios'. */ - defaultPlatform?: string | null; - /** Force use of Node's `fs` APIs rather than shelling out to `find` */ - forceNodeFilesystemAPI?: boolean; - /** - * Whether to follow symlinks when crawling for files. - * This options cannot be used in projects which use watchman. - * Projects with `watchman` set to true will error if this option is set to true. - */ - enableSymlinks?: boolean; - /** Path to a custom implementation of Haste. */ - hasteImplModulePath?: string; - /** All platforms to target, e.g ['ios', 'android']. */ - platforms?: Array; - /** Whether to throw on error on module collision. */ - throwOnModuleCollision?: boolean; - /** Custom HasteMap module */ - hasteMapModulePath?: string; -}; -``` - -### `injectGlobals` \[boolean] - -Default: `true` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus`. test runner_ - -### `maxConcurrency` \[number] - -Default: `5` - -A number limiting the number of tests that are allowed to run at the same time when using `test.concurrent`. Any test above this limit will be queued and executed once a slot is released. - -### `maxWorkers` \[number | string] - -Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `"maxWorkers": "50%"` - -### `moduleDirectories` \[array<string>] - -Default: `["node_modules"]` - -An array of directory names to be searched recursively up from the requiring module's location. Setting this option will _override_ the default, if you wish to still search `node_modules` for packages include it along with any other options: `["node_modules", "bower_components"]` - -### `moduleFileExtensions` \[array<string>] - -Default: `["js", "jsx", "ts", "tsx", "json", "node"]` - -An array of file extensions your modules use. If you require modules without specifying a file extension, these are the extensions Jest will look for, in left-to-right order. - -We recommend placing the extensions most commonly used in your project on the left, so if you are using TypeScript, you may want to consider moving "ts" and/or "tsx" to the beginning of the array. - -### `moduleNameMapper` \[object<string, string | array<string>>] - -Default: `null` - -A map from regular expressions to module names or to arrays of module names that allow to stub out resources, like images or styles with a single module. - -Modules that are mapped to an alias are unmocked by default, regardless of whether automocking is enabled or not. - -Use `` string token to refer to [`rootDir`](#rootdir-string) value if you want to use file paths. - -Additionally, you can substitute captured regex groups using numbered backreferences. - -Example: - -```json -{ - "moduleNameMapper": { - "^image![a-zA-Z0-9$_-]+$": "GlobalImageStub", - "^[./a-zA-Z0-9$_-]+\\.png$": "/RelativeImageStub.js", - "module_name_(.*)": "/substituted_module_$1.js", - "assets/(.*)": [ - "/images/$1", - "/photos/$1", - "/recipes/$1" - ] - } -} -``` - -The order in which the mappings are defined matters. Patterns are checked one by one until one fits. The most specific rule should be listed first. This is true for arrays of module names as well. - -_Note: If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub._ - -### `modulePathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all module paths before those paths are to be considered 'visible' to the module loader. If a given module's path matches any of the patterns, it will not be `require()`-able in the test environment. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/"]`. - -### `modulePaths` \[array<string>] - -Default: `[]` - -An alternative API to setting the `NODE_PATH` env variable, `modulePaths` is an array of absolute paths to additional locations to search when resolving modules. Use the `` string token to include the path to your project's root directory. Example: `["/app/"]`. - -### `notify` \[boolean] - -Default: `false` - -Activates notifications for test results. - -**Beware:** Jest uses [node-notifier](https://github.com/mikaelbr/node-notifier) to display desktop notifications. On Windows, it creates a new start menu entry on the first use and not display the notification. Notifications will be properly displayed on subsequent runs - -### `notifyMode` \[string] - -Default: `failure-change` - -Specifies notification mode. Requires `notify: true`. - -#### Modes - -- `always`: always send a notification. -- `failure`: send a notification when tests fail. -- `success`: send a notification when tests pass. -- `change`: send a notification when the status changed. -- `success-change`: send a notification when tests pass or once when it fails. -- `failure-change`: send a notification when tests fail or once when it passes. - -### `preset` \[string] - -Default: `undefined` - -A preset that is used as a base for Jest's configuration. A preset should point to an npm module that has a `jest-preset.json`, `jest-preset.js`, `jest-preset.cjs` or `jest-preset.mjs` file at the root. - -For example, this preset `foo-bar/jest-preset.js` will be configured as follows: - -```json -{ - "preset": "foo-bar" -} -``` - -Presets may also be relative to filesystem paths. - -```json -{ - "preset": "./node_modules/foo-bar/jest-preset.js" -} -``` - -### `prettierPath` \[string] - -Default: `'prettier'` - -Sets the path to the [`prettier`](https://prettier.io/) node module used to update inline snapshots. - -### `projects` \[array<string | ProjectConfig>] - -Default: `undefined` - -When the `projects` configuration is provided with an array of paths or glob patterns, Jest will run tests in all of the specified projects at the same time. This is great for monorepos or when working on multiple projects at the same time. - -```json -{ - "projects": ["", "/examples/*"] -} -``` - -This example configuration will run Jest in the root directory as well as in every folder in the examples directory. You can have an unlimited amount of projects running in the same Jest instance. - -The projects feature can also be used to run multiple configurations or multiple [runners](#runner-string). For this purpose, you can pass an array of configuration objects. For example, to run both tests and ESLint (via [jest-runner-eslint](https://github.com/jest-community/jest-runner-eslint)) in the same invocation of Jest: - -```json -{ - "projects": [ - { - "displayName": "test" - }, - { - "displayName": "lint", - "runner": "jest-runner-eslint", - "testMatch": ["/**/*.js"] - } - ] -} -``` - -_Note: When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._ - -### `reporters` \[array<moduleName | \[moduleName, options]>] - -Default: `undefined` - -Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs. - -If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name. - -This will override default reporters: - -```json -{ - "reporters": ["/my-custom-reporter.js"] -} -``` - -This will use custom reporter in addition to default reporters that Jest provides: - -```json -{ - "reporters": ["default", "/my-custom-reporter.js"] -} -``` - -Additionally, custom reporters can be configured by passing an `options` object as a second argument: - -```json -{ - "reporters": [ - "default", - ["/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}] - ] -} -``` - -Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments: - -Example reporter: - -```js title="my-custom-reporter.js" -class MyCustomReporter { - constructor(globalConfig, options) { - this._globalConfig = globalConfig; - this._options = options; - } - - onRunComplete(contexts, results) { - console.log('Custom reporter output:'); - console.log('GlobalConfig: ', this._globalConfig); - console.log('Options: ', this._options); - } -} - -module.exports = MyCustomReporter; -// or export default MyCustomReporter; -``` - -Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods - -```js -class MyCustomReporter { - // ... - getLastError() { - if (this._shouldFail) { - return new Error('my-custom-reporter.js reported an error'); - } - } -} -``` - -For the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts) - -### `resetMocks` \[boolean] - -Default: `false` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `resetModules` \[boolean] - -Default: `false` - -By default, each test file gets its own independent module registry. Enabling `resetModules` goes a step further and resets the module registry before running each individual test. This is useful to isolate modules for every test so that the local module state doesn't conflict between tests. This can be done programmatically using [`jest.resetModules()`](JestObjectAPI.md#jestresetmodules). - -### `resolver` \[string] - -Default: `undefined` - -This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: - -```json -{ - "basedir": string, - "defaultResolver": "function(request, options)", - "extensions": [string], - "moduleDirectory": [string], - "paths": [string], - "packageFilter": "function(pkg, pkgdir)", - "rootDir": [string] -} -``` - -The function should either return a path to the module that should be resolved or throw an error if the module can't be found. - -Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)`. - -For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: - -```json -{ - ... - "jest": { - "resolver": "/resolver.js" - } -} -``` - -```js title="resolver.js" -const browserResolve = require('browser-resolve'); - -module.exports = browserResolve.sync; -``` - -By combining `defaultResolver` and `packageFilter` we can implement a `package.json` "pre-processor" that allows us to change how the default resolver will resolve modules. For example, imagine we want to use the field `"module"` if it is present, otherwise fallback to `"main"`: - -```json -{ - ... - "jest": { - "resolver": "my-module-resolve" - } -} -``` - -```js -// my-module-resolve package - -module.exports = (request, options) => { - // Call the defaultResolver, so we leverage its cache, error handling, etc. - return options.defaultResolver(request, { - ...options, - // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb) - packageFilter: pkg => { - return { - ...pkg, - // Alter the value of `main` before resolving the package - main: pkg.module || pkg.main, - }; - }, - }); -}; -``` - -### `restoreMocks` \[boolean] - -Default: `false` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `rootDir` \[string] - -Default: The root of the directory containing your Jest [config file](#) _or_ the `package.json` _or_ the [`pwd`](http://en.wikipedia.org/wiki/Pwd) if no `package.json` is found - -The root directory that Jest should scan for tests and modules within. If you put your Jest config inside your `package.json` and want the root directory to be the root of your repo, the value for this config param will default to the directory of the `package.json`. - -Oftentimes, you'll want to set this to `'src'` or `'lib'`, corresponding to where in your repository the code is stored. - -_Note that using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`._ - -### `roots` \[array<string>] - -Default: `[""]` - -A list of paths to directories that Jest should use to search for files in. - -There are times where you only want Jest to search in a single sub-directory (such as cases where you have a `src/` directory in your repo), but prevent it from accessing the rest of the repo. - -_Note: While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`)._ - -_Note: By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`._ - -### `runner` \[string] - -Default: `"jest-runner"` - -This option allows you to use a custom runner instead of Jest's default test runner. Examples of runners include: - -- [`jest-runner-eslint`](https://github.com/jest-community/jest-runner-eslint) -- [`jest-runner-mocha`](https://github.com/rogeliog/jest-runner-mocha) -- [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc) -- [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier) - -_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._ - -To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature: - -```ts -async function runTests( - tests: Array, - watcher: TestWatcher, - onStart: OnTestStart, - onResult: OnTestSuccess, - onFailure: OnTestFailure, - options: TestRunnerOptions, -): Promise; -``` - -If you need to restrict your test-runner to only run in serial rather than being executed in parallel your class should have the property `isSerial` to be set as `true`. - -### `setupFiles` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment before executing [`setupFilesAfterEnv`](#setupfilesafterenv-array) and before the test code itself. - -### `setupFilesAfterEnv` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing framework before each test file in the suite is executed. Since [`setupFiles`](#setupfiles-array) executes before the test framework is installed in the environment, this script file presents you the opportunity of running some code immediately after the test framework has been installed in the environment but before the test code itself. - -If you want a path to be [relative to the root directory of your project](#rootdir-string), please include `` inside a path's string, like `"/a-configs-folder"`. - -For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. - -Example `setupFilesAfterEnv` array in a jest.config.js: - -```js -module.exports = { - setupFilesAfterEnv: ['./jest.setup.js'], -}; -``` - -Example `jest.setup.js` file - -```js -jest.setTimeout(10000); // in milliseconds -``` - -### `slowTestThreshold` \[number] - -Default: `5` - -The number of seconds after which a test is considered as slow and reported as such in the results. - -### `snapshotResolver` \[string] - -Default: `undefined` - -The path to a module that can resolve test<->snapshot path. This config option lets you customize where Jest stores snapshot files on disk. - -Example snapshot resolver module: - -```js -module.exports = { - // resolves from test to snapshot path - resolveSnapshotPath: (testPath, snapshotExtension) => - testPath.replace('__tests__', '__snapshots__') + snapshotExtension, - - // resolves from snapshot to test path - resolveTestPath: (snapshotFilePath, snapshotExtension) => - snapshotFilePath - .replace('__snapshots__', '__tests__') - .slice(0, -snapshotExtension.length), - - // Example test path, used for preflight consistency check of the implementation above - testPathForConsistencyCheck: 'some/__tests__/example.test.js', -}; -``` - -### `snapshotSerializers` \[array<string>] - -Default: `[]` - -A list of paths to snapshot serializer modules Jest should use for snapshot testing. - -Jest has default serializers for built-in JavaScript types, HTML elements (Jest 20.0.0+), ImmutableJS (Jest 20.0.0+) and for React elements. See [snapshot test tutorial](TutorialReactNative.md#snapshot-test) for more information. - -Example serializer module: - -```js -// my-serializer-module -module.exports = { - serialize(val, config, indentation, depth, refs, printer) { - return `Pretty foo: ${printer(val.foo)}`; - }, - - test(val) { - return val && Object.prototype.hasOwnProperty.call(val, 'foo'); - }, -}; -``` - -`printer` is a function that serializes a value using existing plugins. - -To use `my-serializer-module` as a serializer, configuration would be as follows: - -```json -{ - ... - "jest": { - "snapshotSerializers": ["my-serializer-module"] - } -} -``` - -Finally tests would look as follows: - -```js -test(() => { - const bar = { - foo: { - x: 1, - y: 2, - }, - }; - - expect(bar).toMatchSnapshot(); -}); -``` - -Rendered snapshot: - -```json -Pretty foo: Object { - "x": 1, - "y": 2, -} -``` - -To make a dependency explicit instead of implicit, you can call [`expect.addSnapshotSerializer`](ExpectAPI.md#expectaddsnapshotserializerserializer) to add a module for an individual test file instead of adding its path to `snapshotSerializers` in Jest configuration. - -More about serializers API can be found [here](https://github.com/facebook/jest/tree/main/packages/pretty-format/README.md#serialize). - -### `testEnvironment` \[string] - -Default: `"node"` - -The test environment that will be used for testing. The default environment in Jest is a Node.js environment. If you are building a web app, you can use a browser-like environment through [`jsdom`](https://github.com/jsdom/jsdom) instead. - -By adding a `@jest-environment` docblock at the top of the file, you can specify another environment to be used for all tests in that file: - -```js -/** - * @jest-environment jsdom - */ - -test('use jsdom in this test file', () => { - const element = document.createElement('div'); - expect(element).not.toBeNull(); -}); -``` - -You can create your own module that will be used for setting up the test environment. The module must export a class with `setup`, `teardown` and `getVmContext` methods. You can also pass variables from this module to your test suites by assigning them to `this.global` object – this will make them available in your test suites as global variables. - -The class may optionally expose an asynchronous `handleTestEvent` method to bind to events fired by [`jest-circus`](https://github.com/facebook/jest/tree/main/packages/jest-circus). Normally, `jest-circus` test runner would pause until a promise returned from `handleTestEvent` gets fulfilled, **except for the next events**: `start_describe_definition`, `finish_describe_definition`, `add_hook`, `add_test` or `error` (for the up-to-date list you can look at [SyncEvent type in the types definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Circus.ts)). That is caused by backward compatibility reasons and `process.on('unhandledRejection', callback)` signature, but that usually should not be a problem for most of the use cases. - -Any docblock pragmas in test files will be passed to the environment constructor and can be used for per-test configuration. If the pragma does not have a value, it will be present in the object with its value set to an empty string. If the pragma is not present, it will not be present in the object. - -To use this class as your custom environment, refer to it by its full path within the project. For example, if your class is stored in `my-custom-environment.js` in some subfolder of your project, then the annotation might look like this: - -```js -/** - * @jest-environment ./src/test/my-custom-environment - */ -``` - -_Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ - -Example: - -```js -// my-custom-environment -const NodeEnvironment = require('jest-environment-node'); - -class CustomEnvironment extends NodeEnvironment { - constructor(config, context) { - super(config, context); - this.testPath = context.testPath; - this.docblockPragmas = context.docblockPragmas; - } - - async setup() { - await super.setup(); - await someSetupTasks(this.testPath); - this.global.someGlobalObject = createGlobalObject(); - - // Will trigger if docblock contains @my-custom-pragma my-pragma-value - if (this.docblockPragmas['my-custom-pragma'] === 'my-pragma-value') { - // ... - } - } - - async teardown() { - this.global.someGlobalObject = destroyGlobalObject(); - await someTeardownTasks(); - await super.teardown(); - } - - getVmContext() { - return super.getVmContext(); - } - - async handleTestEvent(event, state) { - if (event.name === 'test_start') { - // ... - } - } -} - -module.exports = CustomEnvironment; -``` - -```js -// my-test-suite -/** - * @jest-environment ./my-custom-environment - */ -let someGlobalObject; - -beforeAll(() => { - someGlobalObject = global.someGlobalObject; -}); -``` - -### `testEnvironmentOptions` \[Object] - -Default: `{}` - -Test environment options that will be passed to the `testEnvironment`. The relevant options depend on the environment. For example, you can override options given to [jsdom](https://github.com/jsdom/jsdom) such as `{userAgent: "Agent/007"}`. - -### `testFailureExitCode` \[number] - -Default: `1` - -The exit code Jest returns on test failure. - -_Note: This does not change the exit code in the case of Jest errors (e.g. invalid configuration)._ - -### `testMatch` \[array<string>] - -(default: `[ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]`) - -The glob patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. - -See the [micromatch](https://github.com/micromatch/micromatch) package for details of the patterns you can specify. - -See also [`testRegex` [string | array<string>]](#testregex-string--arraystring), but note that you cannot specify both options. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__fixtures__/**", "**/__tests__/**/*.js"]` will not exclude `__fixtures__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/__tests__/**/*.js`.)_ - -### `testPathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all test paths before executing the test. If the test path matches any of the patterns, it will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `testRegex` \[string | array<string>] - -Default: `(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$` - -The pattern or patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array<string>]](#testmatch-arraystring), but note that you cannot specify both options. - -The following is a visualization of the default regex: - -```bash -├── __tests__ -│ └── component.spec.js # test -│ └── anything # test -├── package.json # not test -├── foo.test.js # test -├── bar.spec.jsx # test -└── component.js # not test -``` - -_Note: `testRegex` will try to detect test files using the **absolute file path**, therefore, having a folder with a name that matches it will run all the files as tests_ - -### `testResultsProcessor` \[string] - -Default: `undefined` - -This option allows the use of a custom results processor. This processor must be a node module that exports a function expecting an object with the following structure as the first argument and return it: - -```json -{ - "success": boolean, - "startTime": epoch, - "numTotalTestSuites": number, - "numPassedTestSuites": number, - "numFailedTestSuites": number, - "numRuntimeErrorTestSuites": number, - "numTotalTests": number, - "numPassedTests": number, - "numFailedTests": number, - "numPendingTests": number, - "numTodoTests": number, - "openHandles": Array, - "testResults": [{ - "numFailingTests": number, - "numPassingTests": number, - "numPendingTests": number, - "testResults": [{ - "title": string (message in it block), - "status": "failed" | "pending" | "passed", - "ancestorTitles": [string (message in describe blocks)], - "failureMessages": [string], - "numPassingAsserts": number, - "location": { - "column": number, - "line": number - } - }, - ... - ], - "perfStats": { - "start": epoch, - "end": epoch - }, - "testFilePath": absolute path to test file, - "coverage": {} - }, - "testExecError:" (exists if there was a top-level failure) { - "message": string - "stack": string - } - ... - ] -} -``` - -`testResultsProcessor` and `reporters` are very similar to each other. One difference is that a test result processor only gets called after all tests finished. Whereas a reporter has the ability to receive test results after individual tests and/or test suites are finished. - -### `testRunner` \[string] - -Default: `jest-circus/runner` - -This option allows the use of a custom test runner. The default is `jest-circus`. A custom test runner can be provided by specifying a path to a test runner implementation. - -The test runner module must export a function with the following signature: - -```ts -function testRunner( - globalConfig: GlobalConfig, - config: ProjectConfig, - environment: Environment, - runtime: Runtime, - testPath: string, -): Promise; -``` - -An example of such function can be found in our default [jasmine2 test runner package](https://github.com/facebook/jest/blob/main/packages/jest-jasmine2/src/index.ts). - -### `testSequencer` \[string] - -Default: `@jest/test-sequencer` - -This option allows you to use a custom sequencer instead of Jest's default. `sort` may optionally return a Promise. - -Example: - -Sort test path alphabetically. - -```js title="testSequencer.js" -const Sequencer = require('@jest/test-sequencer').default; - -class CustomSequencer extends Sequencer { - sort(tests) { - // Test structure information - // https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21 - const copyTests = Array.from(tests); - return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); - } -} - -module.exports = CustomSequencer; -``` - -Use it in your Jest config file like this: - -```json -{ - "testSequencer": "path/to/testSequencer.js" -} -``` - -### `testTimeout` \[number] - -Default: `5000` - -Default timeout of a test in milliseconds. - -### `testURL` \[string] - -Default: `http://localhost` - -This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. - -### `timers` \[string] - -Default: `real` - -Setting this value to `fake` or `modern` enables fake timers for all tests by default. Fake timers are useful when a piece of code sets a long timeout that we don't want to wait for in a test. You can learn more about fake timers [here](JestObjectAPI.md#jestusefaketimersimplementation-modern--legacy). - -If the value is `legacy`, the old implementation will be used as implementation instead of one backed by [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -### `transform` \[object<string, pathToTransformer | \[pathToTransformer, object]>] - -Default: `{"\\.[jt]sx?$": "babel-jest"}` - -A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/main/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md). - -Examples of such compilers include: - -- [Babel](https://babeljs.io/) -- [TypeScript](http://www.typescriptlang.org/) -- To build your own please visit the [Custom Transformer](CodeTransformation.md#writing-custom-transformers) section - -You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}` - -_Note: a transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues)._ - -_Note: when adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or Typescript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup)_ - -A transformer must be an object with at least a `process` function, and it's also recommended to include a `getCacheKey` function. If your transformer is written in ESM you should have a default export with that object. - -If the tests are written using [native ESM](ECMAScriptModules.md) the transformer can export `processAsync` and `getCacheKeyAsync` instead or in addition to the synchronous variants. - -### `transformIgnorePatterns` \[array<string>] - -Default: `["/node_modules/", "\\.pnp\\.[^\\\/]+$"]` - -An array of regexp pattern strings that are matched against all source file paths before transformation. If the file path matches **any** of the patterns, it will not be transformed. - -Providing regexp patterns that overlap with each other may result in files not being transformed that you expected to be transformed. For example: - -```json -{ - "transformIgnorePatterns": ["/node_modules/(?!(foo|bar)/)", "/bar/"] -} -``` - -The first pattern will match (and therefore not transform) files inside `/node_modules` except for those in `/node_modules/foo/` and `/node_modules/bar/`. The second pattern will match (and therefore not transform) files inside any path with `/bar/` in it. With the two together, files in `/node_modules/bar/` will not be transformed because it does match the second pattern, even though it was excluded by the first. - -Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled code. Since all files inside `node_modules` are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use `transformIgnorePatterns` to allow transpiling such modules. You'll find a good example of this use case in [React Native Guide](/docs/tutorial-react-native#transformignorepatterns-customization). - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. - -Example: - -```json -{ - "transformIgnorePatterns": [ - "/bower_components/", - "/node_modules/" - ] -} -``` - -### `unmockedModulePathPatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them. If a module's path matches any of the patterns in this list, it will not be automatically mocked by the module loader. - -This is useful for some commonly used 'utility' modules that are almost always used as implementation details almost all the time (like underscore/lo-dash, etc). It's generally a best practice to keep this list as small as possible and always use explicit `jest.mock()`/`jest.unmock()` calls in individual tests. Explicit per-test setup is far easier for other readers of the test to reason about the environment the test will run in. - -It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. - -### `verbose` \[boolean] - -Default: `false` - -Indicates whether each individual test should be reported during the run. All errors will also still be shown on the bottom after execution. Note that if there is only one test file being run it will default to `true`. - -### `watchPathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests. - -These patterns match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/node_modules/"]`. - -Even if nothing is specified here, the watcher will ignore changes to the version control folders (.git, .hg). Other hidden files and directories, i.e. those that begin with a dot (`.`), are watched by default. Remember to escape the dot when you add them to `watchPathIgnorePatterns` as it is a special RegExp character. - -Example: - -```json -{ - "watchPathIgnorePatterns": ["/\\.tmp/", "/bar/"] -} -``` - -### `watchPlugins` \[array<string | \[string, Object]>] - -Default: `[]` - -This option allows you to use custom watch plugins. Read more about watch plugins [here](watch-plugins). - -Examples of watch plugins include: - -- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master) -- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects) -- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) -- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) -- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) - -_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._ - -### `watchman` \[boolean] - -Default: `true` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. - -### `//` \[string] - -No default - -This option allows comments in `package.json`. Include the comment text as the value of this key anywhere in `package.json`. - -Example: - -```json -{ - "name": "my-project", - "jest": { - "//": "Comment goes here", - "verbose": true - } -} -``` diff --git a/website/versioned_docs/version-27.0/ECMAScriptModules.md b/website/versioned_docs/version-27.0/ECMAScriptModules.md deleted file mode 100644 index a2f513282a09..000000000000 --- a/website/versioned_docs/version-27.0/ECMAScriptModules.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: ecmascript-modules -title: ECMAScript Modules ---- - -Jest ships with _experimental_ support for ECMAScript Modules (ESM). - -> Note that due to its experimental nature there are many bugs and missing features in Jest's implementation, both known and unknown. You should check out the [tracking issue](https://github.com/facebook/jest/issues/9430) and the [label](https://github.com/facebook/jest/labels/ES%20Modules) on the issue tracker for the latest status. - -> Also note that the APIs Jest uses to implement ESM support is still [considered experimental by Node](https://nodejs.org/api/vm.html#vm_class_vm_module) (as of version `14.13.1`). - -With the warnings out of the way, this is how you activate ESM support in your tests. - -1. Ensure you either disable [code transforms](./configuration#transform-objectstring-pathtotransformer--pathtotransformer-object) by passing `transform: {}` or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS). -1. Execute `node` with `--experimental-vm-modules`, e.g. `node --experimental-vm-modules node_modules/jest/bin/jest.js` or `NODE_OPTIONS=--experimental-vm-modules npx jest` etc.. - - On Windows, you can use [`cross-env`](https://github.com/kentcdodds/cross-env) to be able to set environment variables. - - If you use Yarn, you can use `yarn node --experimental-vm-modules $(yarn bin jest)`. This command will also work if you use [Yarn Plug'n'Play](https://yarnpkg.com/features/pnp). - -1. Beyond that, we attempt to follow `node`'s logic for activating "ESM mode" (such as looking at `type` in `package.json` or `mjs` files), see [their docs](https://nodejs.org/api/esm.html#esm_enabling) for details. -1. If you want to treat other file extensions (such as `.jsx` or `.ts`) as ESM, please use the [`extensionsToTreatAsEsm` option](Configuration.md#extensionstotreatasesm-arraystring). - -## Differences between ESM and CommonJS - -Most of the differences are explained in [Node's documentation](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs), but in addition to the things mentioned there, Jest injects a special variable into all executed files - the [`jest` object](JestObjectAPI.md). To access this object in ESM, you need to import it from the `@jest/globals` module. - -```js -import {jest} from '@jest/globals'; - -jest.useFakeTimers(); - -// etc. -``` - -Please note that we currently don't support `jest.mock` in a clean way in ESM, but that is something we intend to add proper support for in the future. Follow [this issue](https://github.com/facebook/jest/issues/10025) for updates. diff --git a/website/versioned_docs/version-27.0/ExpectAPI.md b/website/versioned_docs/version-27.0/ExpectAPI.md deleted file mode 100644 index 0d1adaef5782..000000000000 --- a/website/versioned_docs/version-27.0/ExpectAPI.md +++ /dev/null @@ -1,1393 +0,0 @@ ---- -id: expect -title: Expect ---- - -When you're writing tests, you often need to check that values meet certain conditions. `expect` gives you access to a number of "matchers" that let you validate different things. - -For additional Jest matchers maintained by the Jest Community check out [`jest-extended`](https://github.com/jest-community/jest-extended). - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `expect(value)` - -The `expect` function is used every time you want to test a value. You will rarely call `expect` by itself. Instead, you will use `expect` along with a "matcher" function to assert something about a value. - -It's easier to understand this with an example. Let's say you have a method `bestLaCroixFlavor()` which is supposed to return the string `'grapefruit'`. Here's how you would test that: - -```js -test('the best flavor is grapefruit', () => { - expect(bestLaCroixFlavor()).toBe('grapefruit'); -}); -``` - -In this case, `toBe` is the matcher function. There are a lot of different matcher functions, documented below, to help you test different things. - -The argument to `expect` should be the value that your code produces, and any argument to the matcher should be the correct value. If you mix them up, your tests will still work, but the error messages on failing tests will look strange. - -### `expect.extend(matchers)` - -You can use `expect.extend` to add your own matchers to Jest. For example, let's say that you're testing a number utility library and you're frequently asserting that numbers appear within particular ranges of other numbers. You could abstract that into a `toBeWithinRange` matcher: - -```js -expect.extend({ - toBeWithinRange(received, floor, ceiling) { - const pass = received >= floor && received <= ceiling; - if (pass) { - return { - message: () => - `expected ${received} not to be within range ${floor} - ${ceiling}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be within range ${floor} - ${ceiling}`, - pass: false, - }; - } - }, -}); - -test('numeric ranges', () => { - expect(100).toBeWithinRange(90, 110); - expect(101).not.toBeWithinRange(0, 100); - expect({apples: 6, bananas: 3}).toEqual({ - apples: expect.toBeWithinRange(1, 10), - bananas: expect.not.toBeWithinRange(11, 20), - }); -}); -``` - -_Note_: In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this: - -```ts -interface CustomMatchers { - toBeWithinRange(floor: number, ceiling: number): R; -} - -declare global { - namespace jest { - interface Expect extends CustomMatchers {} - interface Matchers extends CustomMatchers {} - interface InverseAsymmetricMatchers extends CustomMatchers {} - } -} -``` - -#### Async Matchers - -`expect.extend` also supports async matchers. Async matchers return a Promise so you will need to await the returned value. Let's use an example matcher to illustrate the usage of them. We are going to implement a matcher called `toBeDivisibleByExternalValue`, where the divisible number is going to be pulled from an external source. - -```js -expect.extend({ - async toBeDivisibleByExternalValue(received) { - const externalValue = await getExternalValueFromRemoteSource(); - const pass = received % externalValue == 0; - if (pass) { - return { - message: () => - `expected ${received} not to be divisible by ${externalValue}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be divisible by ${externalValue}`, - pass: false, - }; - } - }, -}); - -test('is divisible by external value', async () => { - await expect(100).toBeDivisibleByExternalValue(); - await expect(101).not.toBeDivisibleByExternalValue(); -}); -``` - -#### Custom Matchers API - -Matchers should return an object (or a Promise of an object) with two keys. `pass` indicates whether there was a match or not, and `message` provides a function with no arguments that returns an error message in case of failure. Thus, when `pass` is false, `message` should return the error message for when `expect(x).yourMatcher()` fails. And when `pass` is true, `message` should return the error message for when `expect(x).not.yourMatcher()` fails. - -Matchers are called with the argument passed to `expect(x)` followed by the arguments passed to `.yourMatcher(y, z)`: - -```js -expect.extend({ - yourMatcher(x, y, z) { - return { - pass: true, - message: () => '', - }; - }, -}); -``` - -These helper functions and properties can be found on `this` inside a custom matcher: - -#### `this.isNot` - -A boolean to let you know this matcher was called with the negated `.not` modifier allowing you to display a clear and correct matcher hint (see example code). - -#### `this.promise` - -A string allowing you to display a clear and correct matcher hint: - -- `'rejects'` if matcher was called with the promise `.rejects` modifier -- `'resolves'` if matcher was called with the promise `.resolves` modifier -- `''` if matcher was not called with a promise modifier - -#### `this.equals(a, b)` - -This is a deep-equality function that will return `true` if two objects have the same values (recursively). - -#### `this.expand` - -A boolean to let you know this matcher was called with an `expand` option. When Jest is called with the `--expand` flag, `this.expand` can be used to determine if Jest is expected to show full diffs and errors. - -#### `this.utils` - -There are a number of helpful tools exposed on `this.utils` primarily consisting of the exports from [`jest-matcher-utils`](https://github.com/facebook/jest/tree/main/packages/jest-matcher-utils). - -The most useful ones are `matcherHint`, `printExpected` and `printReceived` to format the error messages nicely. For example, take a look at the implementation for the `toBe` matcher: - -```js -const {diff} = require('jest-diff'); -expect.extend({ - toBe(received, expected) { - const options = { - comment: 'Object.is equality', - isNot: this.isNot, - promise: this.promise, - }; - - const pass = Object.is(received, expected); - - const message = pass - ? () => - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - `Expected: not ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}` - : () => { - const diffString = diff(expected, received, { - expand: this.expand, - }); - return ( - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}`) - ); - }; - - return {actual: received, message, pass}; - }, -}); -``` - -This will print something like this: - -```bash - expect(received).toBe(expected) - - Expected value to be (using Object.is): - "banana" - Received: - "apple" -``` - -When an assertion fails, the error message should give as much signal as necessary to the user so they can resolve their issue quickly. You should craft a precise failure message to make sure users of your custom assertions have a good developer experience. - -#### Custom snapshot matchers - -To use snapshot testing inside of your custom matcher you can import `jest-snapshot` and use it from within your matcher. - -Here's a snapshot matcher that trims a string to store for a given length, `.toMatchTrimmedSnapshot(length)`: - -```js -const {toMatchSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedSnapshot(received, length) { - return toMatchSnapshot.call( - this, - received.substring(0, length), - 'toMatchTrimmedSnapshot', - ); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10); -}); - -/* -Stored snapshot will look like: - -exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`; -*/ -``` - -It's also possible to create custom matchers for inline snapshots, the snapshots will be correctly added to the custom matchers. However, inline snapshot will always try to append to the first argument or the second when the first argument is the property matcher, so it's not possible to accept custom arguments in the custom matchers. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedInlineSnapshot(received, ...rest) { - return toMatchInlineSnapshot.call(this, received.substring(0, 10), ...rest); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot( - `"extra long"` - ); - */ -}); -``` - -#### async - -If your custom inline snapshot matcher is async i.e. uses `async`-`await` you might encounter an error like "Multiple inline snapshots for the same call are not supported". Jest needs additional context information to find where the custom inline snapshot matcher was used to update the snapshots properly. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - async toMatchObservationInlineSnapshot(fn, ...rest) { - // The error (and its stacktrace) must be created before any `await` - this.error = new Error(); - - // The implementation of `observe` doesn't matter. - // It only matters that the custom snapshot matcher is async. - const observation = await observe(async () => { - await fn(); - }); - - return toMatchInlineSnapshot.call(this, recording, ...rest); - }, -}); - -it('observes something', async () => { - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(`"async action"`); - */ -}); -``` - -#### Bail out - -Usually `jest` tries to match every snapshot that is expected in a test. - -Sometimes it might not make sense to continue the test if a prior snapshot failed. For example, when you make snapshots of a state-machine after various transitions you can abort the test once one transition produced the wrong state. - -In that case you can implement a custom snapshot matcher that throws on the first mismatch instead of collecting every mismatch. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchStateInlineSnapshot(...args) { - this.dontThrow = () => {}; - - return toMatchInlineSnapshot.call(this, ...args); - }, -}); - -let state = 'initial'; - -function transition() { - // Typo in the implementation should cause the test to fail - if (state === 'INITIAL') { - state = 'pending'; - } else if (state === 'pending') { - state = 'done'; - } -} - -it('transitions as expected', () => { - expect(state).toMatchStateInlineSnapshot(`"initial"`); - - transition(); - // Already produces a mismatch. No point in continuing the test. - expect(state).toMatchStateInlineSnapshot(`"loading"`); - - transition(); - expect(state).toMatchStateInlineSnapshot(`"done"`); -}); -``` - -### `expect.anything()` - -`expect.anything()` matches anything but `null` or `undefined`. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a non-null argument: - -```js -test('map calls its argument with a non-null argument', () => { - const mock = jest.fn(); - [1].map(x => mock(x)); - expect(mock).toBeCalledWith(expect.anything()); -}); -``` - -### `expect.any(constructor)` - -`expect.any(constructor)` matches anything that was created with the given constructor or if it's a primitive that is of the passed type. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a number: - -```js -class Cat {} -function getCat(fn) { - return fn(new Cat()); -} - -test('randocall calls its callback with a class instance', () => { - const mock = jest.fn(); - getCat(mock); - expect(mock).toBeCalledWith(expect.any(Cat)); -}); - -function randocall(fn) { - return fn(Math.floor(Math.random() * 6 + 1)); -} - -test('randocall calls its callback with a number', () => { - const mock = jest.fn(); - randocall(mock); - expect(mock).toBeCalledWith(expect.any(Number)); -}); -``` - -### `expect.arrayContaining(array)` - -`expect.arrayContaining(array)` matches a received array which contains all of the elements in the expected array. That is, the expected array is a **subset** of the received array. Therefore, it matches a received array which contains elements that are **not** in the expected array. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match a property in `objectContaining` or `toMatchObject` - -```js -describe('arrayContaining', () => { - const expected = ['Alice', 'Bob']; - it('matches even if received contains additional elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected)); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected)); - }); -}); -``` - -```js -describe('Beware of a misunderstanding! A sequence of dice rolls', () => { - const expected = [1, 2, 3, 4, 5, 6]; - it('matches even with an unexpected number 7', () => { - expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match without an expected number 2', () => { - expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.assertions(number)` - -`expect.assertions(number)` verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a function `doAsync` that receives two callbacks `callback1` and `callback2`, it will asynchronously call both of them in an unknown order. We can test this with: - -```js -test('doAsync calls both callbacks', () => { - expect.assertions(2); - function callback1(data) { - expect(data).toBeTruthy(); - } - function callback2(data) { - expect(data).toBeTruthy(); - } - - await doAsync(callback1, callback2); -}); -``` - -The `expect.assertions(2)` call ensures that both callbacks actually get called. - -### `expect.hasAssertions()` - -`expect.hasAssertions()` verifies that at least one assertion is called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a few functions that all deal with state. `prepareState` calls a callback with a state object, `validateState` runs on that state object, and `waitOnState` returns a promise that waits until all `prepareState` callbacks complete. We can test this with: - -```js -test('prepareState prepares a valid state', () => { - expect.hasAssertions(); - prepareState(state => { - expect(validateState(state)).toBeTruthy(); - }); - return waitOnState(); -}); -``` - -The `expect.hasAssertions()` call ensures that the `prepareState` callback actually gets called. - -### `expect.not.arrayContaining(array)` - -`expect.not.arrayContaining(array)` matches a received array which does not contain all of the elements in the expected array. That is, the expected array **is not a subset** of the received array. - -It is the inverse of `expect.arrayContaining`. - -```js -describe('not.arrayContaining', () => { - const expected = ['Samantha']; - - it('matches if the actual array does not contain the expected elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual( - expect.not.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.not.objectContaining(object)` - -`expect.not.objectContaining(object)` matches any received object that does not recursively match the expected properties. That is, the expected object **is not a subset** of the received object. Therefore, it matches a received object which contains properties that are **not** in the expected object. - -It is the inverse of `expect.objectContaining`. - -```js -describe('not.objectContaining', () => { - const expected = {foo: 'bar'}; - - it('matches if the actual object does not contain expected key: value pairs', () => { - expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected)); - }); -}); -``` - -### `expect.not.stringContaining(string)` - -`expect.not.stringContaining(string)` matches the received value if it is not a string or if it is a string that does not contain the exact expected string. - -It is the inverse of `expect.stringContaining`. - -```js -describe('not.stringContaining', () => { - const expected = 'Hello world!'; - - it('matches if the received value does not contain the expected substring', () => { - expect('How are you?').toEqual(expect.not.stringContaining(expected)); - }); -}); -``` - -### `expect.not.stringMatching(string | regexp)` - -`expect.not.stringMatching(string | regexp)` matches the received value if it is not a string or if it is a string that does not match the expected string or regular expression. - -It is the inverse of `expect.stringMatching`. - -```js -describe('not.stringMatching', () => { - const expected = /Hello world!/; - - it('matches if the received value does not match the expected regex', () => { - expect('How are you?').toEqual(expect.not.stringMatching(expected)); - }); -}); -``` - -### `expect.objectContaining(object)` - -`expect.objectContaining(object)` matches any received object that recursively matches the expected properties. That is, the expected object is a **subset** of the received object. Therefore, it matches a received object which contains properties that **are present** in the expected object. - -Instead of literal property values in the expected object, you can use matchers, `expect.anything()`, and so on. - -For example, let's say that we expect an `onPress` function to be called with an `Event` object, and all we need to verify is that the event has `event.x` and `event.y` properties. We can do that with: - -```js -test('onPress gets called with the right thing', () => { - const onPress = jest.fn(); - simulatePresses(onPress); - expect(onPress).toBeCalledWith( - expect.objectContaining({ - x: expect.any(Number), - y: expect.any(Number), - }), - ); -}); -``` - -### `expect.stringContaining(string)` - -`expect.stringContaining(string)` matches the received value if it is a string that contains the exact expected string. - -### `expect.stringMatching(string | regexp)` - -`expect.stringMatching(string | regexp)` matches the received value if it is a string that matches the expected string or regular expression. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match an element in `arrayContaining` -- to match a property in `objectContaining` or `toMatchObject` - -This example also shows how you can nest multiple asymmetric matchers, with `expect.stringMatching` inside the `expect.arrayContaining`. - -```js -describe('stringMatching in arrayContaining', () => { - const expected = [ - expect.stringMatching(/^Alic/), - expect.stringMatching(/^[BR]ob/), - ]; - it('matches even if received contains additional elements', () => { - expect(['Alicia', 'Roberto', 'Evelina']).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Roberto', 'Evelina']).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.addSnapshotSerializer(serializer)` - -You can call `expect.addSnapshotSerializer` to add a module that formats application-specific data structures. - -For an individual test file, an added module precedes any modules from `snapshotSerializers` configuration, which precede the default snapshot serializers for built-in JavaScript types and for React elements. The last module added is the first module tested. - -```js -import serializer from 'my-serializer-module'; -expect.addSnapshotSerializer(serializer); - -// affects expect(value).toMatchSnapshot() assertions in the test file -``` - -If you add a snapshot serializer in individual test files instead of adding it to `snapshotSerializers` configuration: - -- You make the dependency explicit instead of implicit. -- You avoid limits to configuration that might cause you to eject from [create-react-app](https://github.com/facebookincubator/create-react-app). - -See [configuring Jest](Configuration.md#snapshotserializers-arraystring) for more information. - -### `.not` - -If you know how to test something, `.not` lets you test its opposite. For example, this code tests that the best La Croix flavor is not coconut: - -```js -test('the best flavor is not coconut', () => { - expect(bestLaCroixFlavor()).not.toBe('coconut'); -}); -``` - -### `.resolves` - -Use `resolves` to unwrap the value of a fulfilled promise so any other matcher can be chained. If the promise is rejected the assertion fails. - -For example, this code tests that the promise resolves and that the resulting value is `'lemon'`: - -```js -test('resolves to lemon', () => { - // make sure to add a return statement - return expect(Promise.resolve('lemon')).resolves.toBe('lemon'); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.resolves`: - -```js -test('resolves to lemon', async () => { - await expect(Promise.resolve('lemon')).resolves.toBe('lemon'); - await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus'); -}); -``` - -### `.rejects` - -Use `.rejects` to unwrap the reason of a rejected promise so any other matcher can be chained. If the promise is fulfilled the assertion fails. - -For example, this code tests that the promise rejects with reason `'octopus'`: - -```js -test('rejects to octopus', () => { - // make sure to add a return statement - return expect(Promise.reject(new Error('octopus'))).rejects.toThrow( - 'octopus', - ); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.rejects`. - -```js -test('rejects to octopus', async () => { - await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); -}); -``` - -### `.toBe(value)` - -Use `.toBe` to compare primitive values or to check referential identity of object instances. It calls `Object.is` to compare values, which is even better for testing than `===` strict equality operator. - -For example, this code will validate some properties of the `can` object: - -```js -const can = { - name: 'pamplemousse', - ounces: 12, -}; - -describe('the can', () => { - test('has 12 ounces', () => { - expect(can.ounces).toBe(12); - }); - - test('has a sophisticated name', () => { - expect(can.name).toBe('pamplemousse'); - }); -}); -``` - -Don't use `.toBe` with floating-point numbers. For example, due to rounding, in JavaScript `0.2 + 0.1` is not strictly equal to `0.3`. If you have floating point numbers, try `.toBeCloseTo` instead. - -Although the `.toBe` matcher **checks** referential identity, it **reports** a deep comparison of values if the assertion fails. If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, to assert whether or not elements are the same instance: - -- rewrite `expect(received).toBe(expected)` as `expect(Object.is(received, expected)).toBe(true)` -- rewrite `expect(received).not.toBe(expected)` as `expect(Object.is(received, expected)).toBe(false)` - -### `.toHaveBeenCalled()` - -Also under the alias: `.toBeCalled()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say you have a `drinkAll(drink, flavour)` function that takes a `drink` function and applies it to all available beverages. You might want to check that `drink` gets called for `'lemon'`, but not for `'octopus'`, because `'octopus'` flavour is really weird and why would anything be octopus-flavoured? You can do that with this test suite: - -```js -function drinkAll(callback, flavour) { - if (flavour !== 'octopus') { - callback(flavour); - } -} - -describe('drinkAll', () => { - test('drinks something lemon-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'lemon'); - expect(drink).toHaveBeenCalled(); - }); - - test('does not drink something octopus-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'octopus'); - expect(drink).not.toHaveBeenCalled(); - }); -}); -``` - -### `.toHaveBeenCalledTimes(number)` - -Also under the alias: `.toBeCalledTimes(number)` - -Use `.toHaveBeenCalledTimes` to ensure that a mock function got called exact number of times. - -For example, let's say you have a `drinkEach(drink, Array)` function that takes a `drink` function and applies it to array of passed beverages. You might want to check that drink function was called exact number of times. You can do that with this test suite: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenCalledTimes(2); -}); -``` - -### `.toHaveBeenCalledWith(arg1, arg2, ...)` - -Also under the alias: `.toBeCalledWith()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say that you can register a beverage with a `register` function, and `applyToAll(f)` should apply the function `f` to all registered beverages. To make sure this works, you could write: - -```js -test('registration applies correctly to orange La Croix', () => { - const beverage = new LaCroix('orange'); - register(beverage); - const f = jest.fn(); - applyToAll(f); - expect(f).toHaveBeenCalledWith(beverage); -}); -``` - -### `.toHaveBeenLastCalledWith(arg1, arg2, ...)` - -Also under the alias: `.lastCalledWith(arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenLastCalledWith` to test what arguments it was last called with. For example, let's say you have a `applyToAllFlavors(f)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the last flavor it operates on is `'mango'`. You can write: - -```js -test('applying to all flavors does mango last', () => { - const drink = jest.fn(); - applyToAllFlavors(drink); - expect(drink).toHaveBeenLastCalledWith('mango'); -}); -``` - -### `.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)` - -Also under the alias: `.nthCalledWith(nthCall, arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenNthCalledWith` to test what arguments it was nth called with. For example, let's say you have a `drinkEach(drink, Array)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the first flavor it operates on is `'lemon'` and the second one is `'octopus'`. You can write: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenNthCalledWith(1, 'lemon'); - expect(drink).toHaveBeenNthCalledWith(2, 'octopus'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveReturned()` - -Also under the alias: `.toReturn()` - -If you have a mock function, you can use `.toHaveReturned` to test that the mock function successfully returned (i.e., did not throw an error) at least one time. For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drinks returns', () => { - const drink = jest.fn(() => true); - - drink(); - - expect(drink).toHaveReturned(); -}); -``` - -### `.toHaveReturnedTimes(number)` - -Also under the alias: `.toReturnTimes(number)` - -Use `.toHaveReturnedTimes` to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. Any calls to the mock function that throw an error are not counted toward the number of times the function returned. - -For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drink returns twice', () => { - const drink = jest.fn(() => true); - - drink(); - drink(); - - expect(drink).toHaveReturnedTimes(2); -}); -``` - -### `.toHaveReturnedWith(value)` - -Also under the alias: `.toReturnWith(value)` - -Use `.toHaveReturnedWith` to ensure that a mock function returned a specific value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix', () => { - const beverage = {name: 'La Croix'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage); - - expect(drink).toHaveReturnedWith('La Croix'); -}); -``` - -### `.toHaveLastReturnedWith(value)` - -Also under the alias: `.lastReturnedWith(value)` - -Use `.toHaveLastReturnedWith` to test the specific value that a mock function last returned. If the last call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix (Orange) last', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveLastReturnedWith('La Croix (Orange)'); -}); -``` - -### `.toHaveNthReturnedWith(nthCall, value)` - -Also under the alias: `.nthReturnedWith(nthCall, value)` - -Use `.toHaveNthReturnedWith` to test the specific value that a mock function returned for the nth call. If the nth call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns expected nth calls', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)'); - expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveLength(number)` - -Use `.toHaveLength` to check that an object has a `.length` property and it is set to a certain numeric value. - -This is especially useful for checking arrays or strings size. - -```js -expect([1, 2, 3]).toHaveLength(3); -expect('abc').toHaveLength(3); -expect('').not.toHaveLength(5); -``` - -### `.toHaveProperty(keyPath, value?)` - -Use `.toHaveProperty` to check if property at provided reference `keyPath` exists for an object. For checking deeply nested properties in an object you may use [dot notation](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors) or an array containing the keyPath for deep references. - -You can provide an optional `value` argument to compare the received property value (recursively for all properties of object instances, also known as deep equality, like the `toEqual` matcher). - -The following example contains a `houseForSale` object with nested properties. We are using `toHaveProperty` to check for the existence and values of various properties in the object. - -```js -// Object containing house features to be tested -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - 'nice.oven': true, - }, - 'ceiling.height': 2, -}; - -test('this house has my desired features', () => { - // Example Referencing - expect(houseForSale).toHaveProperty('bath'); - expect(houseForSale).toHaveProperty('bedrooms', 4); - - expect(houseForSale).not.toHaveProperty('pool'); - - // Deep referencing using dot notation - expect(houseForSale).toHaveProperty('kitchen.area', 20); - expect(houseForSale).toHaveProperty('kitchen.amenities', [ - 'oven', - 'stove', - 'washer', - ]); - - expect(houseForSale).not.toHaveProperty('kitchen.open'); - - // Deep referencing using an array containing the keyPath - expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20); - expect(houseForSale).toHaveProperty( - ['kitchen', 'amenities'], - ['oven', 'stove', 'washer'], - ); - expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven'); - expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']); - expect(houseForSale).not.toHaveProperty(['kitchen', 'open']); - - // Referencing keys with dot in the key itself - expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall'); -}); -``` - -### `.toBeCloseTo(number, numDigits?)` - -Use `toBeCloseTo` to compare floating point numbers for approximate equality. - -The optional `numDigits` argument limits the number of digits to check **after** the decimal point. For the default value `2`, the test criterion is `Math.abs(expected - received) < 0.005` (that is, `10 ** -2 / 2`). - -Intuitive equality comparisons often fail, because arithmetic on decimal (base 10) values often have rounding errors in limited precision binary (base 2) representation. For example, this test fails: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBe(0.3); // Fails! -}); -``` - -It fails because in JavaScript, `0.2 + 0.1` is actually `0.30000000000000004`. - -For example, this test passes with a precision of 5 digits: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBeCloseTo(0.3, 5); -}); -``` - -Because floating point errors are the problem that `toBeCloseTo` solves, it does not support big integer values. - -### `.toBeDefined()` - -Use `.toBeDefined` to check that a variable is not undefined. For example, if you want to check that a function `fetchNewFlavorIdea()` returns _something_, you can write: - -```js -test('there is a new flavor idea', () => { - expect(fetchNewFlavorIdea()).toBeDefined(); -}); -``` - -You could write `expect(fetchNewFlavorIdea()).not.toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeFalsy()` - -Use `.toBeFalsy` when you don't care what a value is and you want to ensure a value is false in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (!getErrors()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `getErrors` returns, specifically - it might return `false`, `null`, or `0`, and your code would still work. So if you want to test there are no errors after drinking some La Croix, you could write: - -```js -test('drinking La Croix does not lead to errors', () => { - drinkSomeLaCroix(); - expect(getErrors()).toBeFalsy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeGreaterThan(number | bigint)` - -Use `toBeGreaterThan` to compare `received > expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of more than 10 ounces: - -```js -test('ounces per can is more than 10', () => { - expect(ouncesPerCan()).toBeGreaterThan(10); -}); -``` - -### `.toBeGreaterThanOrEqual(number | bigint)` - -Use `toBeGreaterThanOrEqual` to compare `received >= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at least 12 ounces: - -```js -test('ounces per can is at least 12', () => { - expect(ouncesPerCan()).toBeGreaterThanOrEqual(12); -}); -``` - -### `.toBeLessThan(number | bigint)` - -Use `toBeLessThan` to compare `received < expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of less than 20 ounces: - -```js -test('ounces per can is less than 20', () => { - expect(ouncesPerCan()).toBeLessThan(20); -}); -``` - -### `.toBeLessThanOrEqual(number | bigint)` - -Use `toBeLessThanOrEqual` to compare `received <= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at most 12 ounces: - -```js -test('ounces per can is at most 12', () => { - expect(ouncesPerCan()).toBeLessThanOrEqual(12); -}); -``` - -### `.toBeInstanceOf(Class)` - -Use `.toBeInstanceOf(Class)` to check that an object is an instance of a class. This matcher uses `instanceof` underneath. - -```js -class A {} - -expect(new A()).toBeInstanceOf(A); -expect(() => {}).toBeInstanceOf(Function); -expect(new A()).toBeInstanceOf(Function); // throws -``` - -### `.toBeNull()` - -`.toBeNull()` is the same as `.toBe(null)` but the error messages are a bit nicer. So use `.toBeNull()` when you want to check that something is null. - -```js -function bloop() { - return null; -} - -test('bloop returns null', () => { - expect(bloop()).toBeNull(); -}); -``` - -### `.toBeTruthy()` - -Use `.toBeTruthy` when you don't care what a value is and you want to ensure a value is true in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (thirstInfo()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `thirstInfo` returns, specifically - it might return `true` or a complex object, and your code would still work. So if you want to test that `thirstInfo` will be truthy after drinking some La Croix, you could write: - -```js -test('drinking La Croix leads to having thirst info', () => { - drinkSomeLaCroix(); - expect(thirstInfo()).toBeTruthy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeUndefined()` - -Use `.toBeUndefined` to check that a variable is undefined. For example, if you want to check that a function `bestDrinkForFlavor(flavor)` returns `undefined` for the `'octopus'` flavor, because there is no good octopus-flavored drink: - -```js -test('the best drink for octopus flavor is undefined', () => { - expect(bestDrinkForFlavor('octopus')).toBeUndefined(); -}); -``` - -You could write `expect(bestDrinkForFlavor('octopus')).toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeNaN()` - -Use `.toBeNaN` when checking a value is `NaN`. - -```js -test('passes when value is NaN', () => { - expect(NaN).toBeNaN(); - expect(1).not.toBeNaN(); -}); -``` - -### `.toContain(item)` - -Use `.toContain` when you want to check that an item is in an array. For testing the items in the array, this uses `===`, a strict equality check. `.toContain` can also check whether a string is a substring of another string. - -For example, if `getAllFlavors()` returns an array of flavors and you want to be sure that `lime` is in there, you can write: - -```js -test('the flavor list contains lime', () => { - expect(getAllFlavors()).toContain('lime'); -}); -``` - -This matcher also accepts others iterables such as strings, sets, node lists and HTML collections. - -### `.toContainEqual(item)` - -Use `.toContainEqual` when you want to check that an item with a specific structure and values is contained in an array. For testing the items in the array, this matcher recursively checks the equality of all fields, rather than checking for object identity. - -```js -describe('my beverage', () => { - test('is delicious and not sour', () => { - const myBeverage = {delicious: true, sour: false}; - expect(myBeverages()).toContainEqual(myBeverage); - }); -}); -``` - -### `.toEqual(value)` - -Use `.toEqual` to compare recursively all properties of object instances (also known as "deep" equality). It calls `Object.is` to compare primitive values, which is even better for testing than `===` strict equality operator. - -For example, `.toEqual` and `.toBe` behave differently in this test suite, so all the tests pass: - -```js -const can1 = { - flavor: 'grapefruit', - ounces: 12, -}; -const can2 = { - flavor: 'grapefruit', - ounces: 12, -}; - -describe('the La Croix cans on my desk', () => { - test('have all the same properties', () => { - expect(can1).toEqual(can2); - }); - test('are not the exact same can', () => { - expect(can1).not.toBe(can2); - }); -}); -``` - -> Note: `.toEqual` won't perform a _deep equality_ check for two errors. Only the `message` property of an Error is considered for equality. It is recommended to use the `.toThrow` matcher for testing against errors. - -If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, use `equals` method of `Buffer` class to assert whether or not buffers contain the same content: - -- rewrite `expect(received).toEqual(expected)` as `expect(received.equals(expected)).toBe(true)` -- rewrite `expect(received).not.toEqual(expected)` as `expect(received.equals(expected)).toBe(false)` - -### `.toMatch(regexp | string)` - -Use `.toMatch` to check that a string matches a regular expression. - -For example, you might not know what exactly `essayOnTheBestFlavor()` returns, but you know it's a really long string, and the substring `grapefruit` should be in there somewhere. You can test this with: - -```js -describe('an essay on the best flavor', () => { - test('mentions grapefruit', () => { - expect(essayOnTheBestFlavor()).toMatch(/grapefruit/); - expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit')); - }); -}); -``` - -This matcher also accepts a string, which it will try to match: - -```js -describe('grapefruits are healthy', () => { - test('grapefruits are a fruit', () => { - expect('grapefruits').toMatch('fruit'); - }); -}); -``` - -### `.toMatchObject(object)` - -Use `.toMatchObject` to check that a JavaScript object matches a subset of the properties of an object. It will match received objects with properties that are **not** in the expected object. - -You can also pass an array of objects, in which case the method will return true only if each object in the received array matches (in the `toMatchObject` sense described above) the corresponding object in the expected array. This is useful if you want to check that two arrays match in their number of elements, as opposed to `arrayContaining`, which allows for extra elements in the received array. - -You can match properties against values or against matchers. - -```js -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - }, -}; -const desiredHouse = { - bath: true, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - wallColor: expect.stringMatching(/white|yellow/), - }, -}; - -test('the house has my desired features', () => { - expect(houseForSale).toMatchObject(desiredHouse); -}); -``` - -```js -describe('toMatchObject applied to arrays', () => { - test('the number of elements must match exactly', () => { - expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]); - }); - - test('.toMatchObject is called for each elements, so extra object properties are okay', () => { - expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([ - {foo: 'bar'}, - {baz: 1}, - ]); - }); -}); -``` - -### `.toMatchSnapshot(propertyMatchers?, hint?)` - -This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -### `.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)` - -Ensures that a value matches the most recent snapshot. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. - -### `.toStrictEqual(value)` - -Use `.toStrictEqual` to test that objects have the same types as well as structure. - -Differences from `.toEqual`: - -- Keys with `undefined` properties are checked. e.g. `{a: undefined, b: 2}` does not match `{b: 2}` when using `.toStrictEqual`. -- Array sparseness is checked. e.g. `[, 1]` does not match `[undefined, 1]` when using `.toStrictEqual`. -- Object types are checked to be equal. e.g. A class instance with fields `a` and `b` will not equal a literal object with fields `a` and `b`. - -```js -class LaCroix { - constructor(flavor) { - this.flavor = flavor; - } -} - -describe('the La Croix cans on my desk', () => { - test('are not semantically the same', () => { - expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'}); - expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'}); - }); -}); -``` - -### `.toThrow(error?)` - -Also under the alias: `.toThrowError(error?)` - -Use `.toThrow` to test that a function throws when it is called. For example, if we want to test that `drinkFlavor('octopus')` throws, because octopus flavor is too disgusting to drink, we could write: - -```js -test('throws on octopus', () => { - expect(() => { - drinkFlavor('octopus'); - }).toThrow(); -}); -``` - -> Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. - -You can provide an optional argument to test that a specific error is thrown: - -- regular expression: error message **matches** the pattern -- string: error message **includes** the substring -- error object: error message is **equal to** the message property of the object -- error class: error object is **instance of** class - -For example, let's say that `drinkFlavor` is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -We could test this error gets thrown in several ways: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - // Test that the error message says "yuck" somewhere: these are equivalent - expect(drinkOctopus).toThrowError(/yuck/); - expect(drinkOctopus).toThrowError('yuck'); - - // Test the exact error message - expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/); - expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor')); - - // Test that we get a DisgustingFlavorError - expect(drinkOctopus).toThrowError(DisgustingFlavorError); -}); -``` - -### `.toThrowErrorMatchingSnapshot(hint?)` - -Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -The test for this function will look this way: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - expect(drinkOctopus).toThrowErrorMatchingSnapshot(); -}); -``` - -And it will generate the following snapshot: - -```js -exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`; -``` - -Check out [React Tree Snapshot Testing](/blog/2016/07/27/jest-14) for more information on snapshot testing. - -### `.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)` - -Use `.toThrowErrorMatchingInlineSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. diff --git a/website/versioned_docs/version-27.0/GettingStarted.md b/website/versioned_docs/version-27.0/GettingStarted.md deleted file mode 100644 index 266a05d817e3..000000000000 --- a/website/versioned_docs/version-27.0/GettingStarted.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -id: getting-started -title: Getting Started ---- - -Install Jest using [`yarn`](https://yarnpkg.com/en/package/jest): - -```bash -yarn add --dev jest -``` - -Or [`npm`](https://www.npmjs.com/package/jest): - -```bash -npm install --save-dev jest -``` - -Note: Jest documentation uses `yarn` commands, but `npm` will also work. You can compare `yarn` and `npm` commands in the [yarn docs, here](https://yarnpkg.com/en/docs/migrating-from-npm#toc-cli-commands-comparison). - -Let's get started by writing a test for a hypothetical function that adds two numbers. First, create a `sum.js` file: - -```javascript -function sum(a, b) { - return a + b; -} -module.exports = sum; -``` - -Then, create a file named `sum.test.js`. This will contain our actual test: - -```javascript -const sum = require('./sum'); - -test('adds 1 + 2 to equal 3', () => { - expect(sum(1, 2)).toBe(3); -}); -``` - -Add the following section to your `package.json`: - -```json -{ - "scripts": { - "test": "jest" - } -} -``` - -Finally, run `yarn test` or `npm run test` and Jest will print this message: - -```bash -PASS ./sum.test.js -✓ adds 1 + 2 to equal 3 (5ms) -``` - -**You just successfully wrote your first test using Jest!** - -This test used `expect` and `toBe` to test that two values were exactly identical. To learn about the other things that Jest can test, see [Using Matchers](UsingMatchers.md). - -## Running from command line - -You can run Jest directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add jest` or `npm install jest --global`) with a variety of useful options. - -Here's how to run Jest on files matching `my-test`, using `config.json` as a configuration file and display a native OS notification after the run: - -```bash -jest my-test --notify --config=config.json -``` - -If you'd like to learn more about running `jest` through the command line, take a look at the [Jest CLI Options](CLI.md) page. - -## Additional Configuration - -### Generate a basic configuration file - -Based on your project, Jest will ask you a few questions and will create a basic configuration file with a short description for each option: - -```bash -jest --init -``` - -### Using Babel - -To use [Babel](https://babeljs.io/), install required dependencies via `yarn`: - -```bash -yarn add --dev babel-jest @babel/core @babel/preset-env -``` - -Configure Babel to target your current version of Node by creating a `babel.config.js` file in the root of your project: - -```javascript title="babel.config.js" -module.exports = { - presets: [['@babel/preset-env', {targets: {node: 'current'}}]], -}; -``` - -_The ideal configuration for Babel will depend on your project._ See [Babel's docs](https://babeljs.io/docs/en/) for more details. - -
Making your Babel config jest-aware - -Jest will set `process.env.NODE_ENV` to `'test'` if it's not set to something else. You can use that in your configuration to conditionally setup only the compilation needed for Jest, e.g. - -```javascript title="babel.config.js" -module.exports = api => { - const isTest = api.env('test'); - // You can use isTest to determine what presets and plugins to use. - - return { - // ... - }; -}; -``` - -> Note: `babel-jest` is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the `transform` configuration option: - -```javascript title="jest.config.js" -module.exports = { - transform: {}, -}; -``` - -
- -### Using webpack - -Jest can be used in projects that use [webpack](https://webpack.js.org/) to manage assets, styles, and compilation. Webpack does offer some unique challenges over other tools. Refer to the [webpack guide](Webpack.md) to get started. - -### Using parcel - -Jest can be used in projects that use [parcel-bundler](https://parceljs.org/) to manage assets, styles, and compilation similar to webpack. Parcel requires zero configuration. Refer to the official [docs](https://parceljs.org/docs/) to get started. - -### Using TypeScript - -#### Via `babel` - -Jest supports TypeScript, via Babel. First, make sure you followed the instructions on [using Babel](#using-babel) above. Next, install the `@babel/preset-typescript` via `yarn`: - -```bash -yarn add --dev @babel/preset-typescript -``` - -Then add `@babel/preset-typescript` to the list of presets in your `babel.config.js`. - -```javascript title="babel.config.js" -module.exports = { - presets: [ - ['@babel/preset-env', {targets: {node: 'current'}}], - // highlight-next-line - '@babel/preset-typescript', - ], -}; -``` - -However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest) instead, or just run the TypeScript compiler [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) separately (or as part of your build process). - -#### Via `ts-jest` - -[ts-jest](https://github.com/kulshekhar/ts-jest) is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript. - -#### Type definitions - -You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. - -> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. - -```bash -yarn add --dev @types/jest -``` diff --git a/website/versioned_docs/version-27.0/JestObjectAPI.md b/website/versioned_docs/version-27.0/JestObjectAPI.md deleted file mode 100644 index de5d9a92a145..000000000000 --- a/website/versioned_docs/version-27.0/JestObjectAPI.md +++ /dev/null @@ -1,686 +0,0 @@ ---- -id: jest-object -title: The Jest Object ---- - -The `jest` object is automatically in scope within every test file. The methods in the `jest` object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via `import {jest} from '@jest/globals'`. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Mock Modules - -### `jest.disableAutomock()` - -Disables automatic mocking in the module loader. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -After this method is called, all `require()`s will return the real versions of each module (rather than a mocked version). - -Jest configuration: - -```json -{ - "automock": true -} -``` - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, -}; -``` - -```js title="__tests__/disableAutomocking.js" -import utils from '../utils'; - -jest.disableAutomock(); - -test('original implementation', () => { - // now we have the original implementation, - // even if we set the automocking in a jest configuration - expect(utils.authorize()).toBe('token'); -}); -``` - -This is usually useful when you have a scenario where the number of dependencies you want to mock is far less than the number of dependencies that you don't. For example, if you're writing a test for a module that uses a large number of dependencies that can be reasonably classified as "implementation details" of the module, then you likely do not want to mock them. - -Examples of dependencies that might be considered "implementation details" are things ranging from language built-ins (e.g. Array.prototype methods) to highly common utility methods (e.g. underscore/lo-dash, array utilities, etc) and entire libraries like React.js. - -Returns the `jest` object for chaining. - -_Note: this method was previously called `autoMockOff`. When using `babel-jest`, calls to `disableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOff` if you want to explicitly avoid this behavior._ - -### `jest.enableAutomock()` - -Enables automatic mocking in the module loader. - -Returns the `jest` object for chaining. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/enableAutomocking.js" -jest.enableAutomock(); - -import utils from '../utils'; - -test('original implementation', () => { - // now we have the mocked implementation, - expect(utils.authorize._isMockFunction).toBeTruthy(); - expect(utils.isAuthorized._isMockFunction).toBeTruthy(); -}); -``` - -_Note: this method was previously called `autoMockOn`. When using `babel-jest`, calls to `enableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOn` if you want to explicitly avoid this behavior._ - -### `jest.createMockFromModule(moduleName)` - -##### renamed in Jest **26.0.0+** - -Also under the alias: `.genMockFromModule(moduleName)` - -Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you. - -This is useful when you want to create a [manual mock](ManualMocks.md) that extends the automatic mock's behavior. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/createMockFromModule.test.js" -const utils = jest.createMockFromModule('../utils').default; -utils.isAuthorized = jest.fn(secret => secret === 'not wizard'); - -test('implementation created by jest.createMockFromModule', () => { - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized('not wizard')).toEqual(true); -}); -``` - -This is how `createMockFromModule` will mock the following data types: - -#### `Function` - -Creates a new [mock function](mock-functions). The new function has no formal parameters and when called will return `undefined`. This functionality also applies to `async` functions. - -#### `Class` - -Creates a new class. The interface of the original class is maintained, all of the class member functions and properties will be mocked. - -#### `Object` - -Creates a new deeply cloned object. The object keys are maintained and their values are mocked. - -#### `Array` - -Creates a new empty array, ignoring the original. - -#### `Primitives` - -Creates a new property with the same primitive value as the original property. - -Example: - -```js title="example.js" -module.exports = { - function: function square(a, b) { - return a * b; - }, - asyncFunction: async function asyncSquare(a, b) { - const result = (await a) * b; - return result; - }, - class: new (class Bar { - constructor() { - this.array = [1, 2, 3]; - } - foo() {} - })(), - object: { - baz: 'foo', - bar: { - fiz: 1, - buzz: [1, 2, 3], - }, - }, - array: [1, 2, 3], - number: 123, - string: 'baz', - boolean: true, - symbol: Symbol.for('a.b.c'), -}; -``` - -```js title="__tests__/example.test.js" -const example = jest.createMockFromModule('./example'); - -test('should run example code', () => { - // creates a new mocked function with no formal arguments. - expect(example.function.name).toEqual('square'); - expect(example.function.length).toEqual(0); - - // async functions get the same treatment as standard synchronous functions. - expect(example.asyncFunction.name).toEqual('asyncSquare'); - expect(example.asyncFunction.length).toEqual(0); - - // creates a new class with the same interface, member functions and properties are mocked. - expect(example.class.constructor.name).toEqual('Bar'); - expect(example.class.foo.name).toEqual('foo'); - expect(example.class.array.length).toEqual(0); - - // creates a deeply cloned version of the original object. - expect(example.object).toEqual({ - baz: 'foo', - bar: { - fiz: 1, - buzz: [], - }, - }); - - // creates a new empty array, ignoring the original array. - expect(example.array.length).toEqual(0); - - // creates a new property with the same primitive value as the original property. - expect(example.number).toEqual(123); - expect(example.string).toEqual('baz'); - expect(example.boolean).toEqual(true); - expect(example.symbol).toEqual(Symbol.for('a.b.c')); -}); -``` - -### `jest.mock(moduleName, factory, options)` - -Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example: - -```js title="banana.js" -module.exports = () => 'banana'; -``` - -```js title="__tests__/test.js" -jest.mock('../banana'); - -const banana = require('../banana'); // banana will be explicitly mocked. - -banana(); // will return 'undefined' because the function is auto-mocked. -``` - -The second argument can be used to specify an explicit module factory that is being run instead of using Jest's automocking feature: - -```js -jest.mock('../moduleName', () => { - return jest.fn(() => 42); -}); - -// This runs the function specified as second argument to `jest.mock`. -const moduleName = require('../moduleName'); -moduleName(); // Will return '42'; -``` - -When using the `factory` parameter for an ES6 module with a default export, the `__esModule: true` property needs to be specified. This property is normally generated by Babel / TypeScript, but here it needs to be set manually. When importing a default export, it's an instruction to import the property named `default` from the export object: - -```js -import moduleName, {foo} from '../moduleName'; - -jest.mock('../moduleName', () => { - return { - __esModule: true, - default: jest.fn(() => 42), - foo: jest.fn(() => 43), - }; -}); - -moduleName(); // Will return 42 -foo(); // Will return 43 -``` - -The third argument can be used to create virtual mocks – mocks of modules that don't exist anywhere in the system: - -```js -jest.mock( - '../moduleName', - () => { - /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ - }, - {virtual: true}, -); -``` - -> **Warning:** Importing a module in a setup file (as specified by `setupFilesAfterEnv`) will prevent mocking for the module in question, as well as all the modules that it imports. - -Modules that are mocked with `jest.mock` are mocked only for the file that calls `jest.mock`. Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module. - -Returns the `jest` object for chaining. - -### `jest.unmock(moduleName)` - -Indicates that the module system should never return a mocked version of the specified module from `require()` (e.g. that it should always return the real module). - -The most common use of this API is for specifying the module a given test intends to be testing (and thus doesn't want automatically mocked). - -Returns the `jest` object for chaining. - -### `jest.doMock(moduleName, factory, options)` - -When using `babel-jest`, calls to `mock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -One example when this is useful is when you want to mock a module differently within the same file: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 1); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(1); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 2); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(2); -}); -``` - -Using `jest.doMock()` with ES6 imports requires additional steps. Follow these if you don't want to use `require` in your tests: - -- We have to specify the `__esModule: true` property (see the [`jest.mock()`](#jestmockmodulename-factory-options) API for more information). -- Static ES6 module imports are hoisted to the top of the file, so instead we have to import them dynamically using `import()`. -- Finally, we need an environment which supports dynamic importing. Please see [Using Babel](GettingStarted.md#using-babel) for the initial setup. Then add the plugin [babel-plugin-dynamic-import-node](https://www.npmjs.com/package/babel-plugin-dynamic-import-node), or an equivalent, to your Babel config to enable dynamic importing in Node. - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default1', - foo: 'foo1', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default1'); - expect(moduleName.foo).toEqual('foo1'); - }); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default2', - foo: 'foo2', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default2'); - expect(moduleName.foo).toEqual('foo2'); - }); -}); -``` - -Returns the `jest` object for chaining. - -### `jest.dontMock(moduleName)` - -When using `babel-jest`, calls to `unmock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -Returns the `jest` object for chaining. - -### `jest.setMock(moduleName, moduleExports)` - -Explicitly supplies the mock object that the module system should return for the specified module. - -On occasion, there are times where the automatically generated mock the module system would normally provide you isn't adequate enough for your testing needs. Normally under those circumstances you should write a [manual mock](ManualMocks.md) that is more adequate for the module in question. However, on extremely rare occasions, even a manual mock isn't suitable for your purposes and you need to build the mock yourself inside your test. - -In these rare scenarios you can use this API to manually fill the slot in the module system's mock-module registry. - -Returns the `jest` object for chaining. - -_Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object._ - -### `jest.requireActual(moduleName)` - -Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not. - -Example: - -```js -jest.mock('../myModule', () => { - // Require the original module to not be mocked... - const originalModule = jest.requireActual('../myModule'); - - return { - __esModule: true, // Use it when dealing with esModules - ...originalModule, - getRandom: jest.fn().mockReturnValue(10), - }; -}); - -const getRandom = require('../myModule').getRandom; - -getRandom(); // Always returns 10 -``` - -### `jest.requireMock(moduleName)` - -Returns a mock module instead of the actual module, bypassing all checks on whether the module should be required normally or not. - -### `jest.resetModules()` - -Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests. - -Example: - -```js -const sum1 = require('../sum'); -jest.resetModules(); -const sum2 = require('../sum'); -sum1 === sum2; -// > false (Both sum modules are separate "instances" of the sum module.) -``` - -Example in a test: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('works', () => { - const sum = require('../sum'); -}); - -test('works too', () => { - const sum = require('../sum'); - // sum is a different copy of the sum module from the previous test. -}); -``` - -Returns the `jest` object for chaining. - -### `jest.isolateModules(fn)` - -`jest.isolateModules(fn)` goes a step further than `jest.resetModules()` and creates a sandbox registry for the modules that are loaded inside the callback function. This is useful to isolate specific modules for every test so that local module state doesn't conflict between tests. - -```js -let myModule; -jest.isolateModules(() => { - myModule = require('myModule'); -}); - -const otherCopyOfMyModule = require('myModule'); -``` - -## Mock Functions - -### `jest.fn(implementation)` - -Returns a new, unused [mock function](MockFunctionAPI.md). Optionally takes a mock implementation. - -```js -const mockFn = jest.fn(); -mockFn(); -expect(mockFn).toHaveBeenCalled(); - -// With a mock implementation: -const returnsTrue = jest.fn(() => true); -console.log(returnsTrue()); // true; -``` - -### `jest.isMockFunction(fn)` - -Determines if the given function is a mocked function. - -### `jest.spyOn(object, methodName)` - -Creates a mock function similar to `jest.fn` but also tracks calls to `object[methodName]`. Returns a Jest [mock function](MockFunctionAPI.md). - -_Note: By default, `jest.spyOn` also calls the **spied** method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use `jest.spyOn(object, methodName).mockImplementation(() => customImplementation)` or `object[methodName] = jest.fn(() => customImplementation);`_ - -Example: - -```js -const video = { - play() { - return true; - }, -}; - -module.exports = video; -``` - -Example test: - -```js -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play'); - const isPlaying = video.play(); - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); -``` - -### `jest.spyOn(object, methodName, accessType?)` - -Since Jest 22.1.0+, the `jest.spyOn` method takes an optional third argument of `accessType` that can be either `'get'` or `'set'`, which proves to be useful when you want to spy on a getter or a setter, respectively. - -Example: - -```js -const video = { - // it's a getter! - get play() { - return true; - }, -}; - -module.exports = video; - -const audio = { - _volume: false, - // it's a setter! - set volume(value) { - this._volume = value; - }, - get volume() { - return this._volume; - }, -}; - -module.exports = audio; -``` - -Example test: - -```js -const audio = require('./audio'); -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play', 'get'); // we pass 'get' - const isPlaying = video.play; - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); - -test('plays audio', () => { - const spy = jest.spyOn(audio, 'volume', 'set'); // we pass 'set' - audio.volume = 100; - - expect(spy).toHaveBeenCalled(); - expect(audio.volume).toBe(100); - - spy.mockRestore(); -}); -``` - -### `jest.clearAllMocks()` - -Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.resetAllMocks()` - -Resets the state of all mocks. Equivalent to calling [`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.restoreAllMocks()` - -Restores all mocks back to their original value. Equivalent to calling [`.mockRestore()`](MockFunctionAPI.md#mockfnmockrestore) on every mocked function. Beware that `jest.restoreAllMocks()` only works when the mock was created with `jest.spyOn`; other mocks will require you to manually restore them. - -## Mock Timers - -### `jest.useFakeTimers(implementation?: 'modern' | 'legacy')` - -Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate` as well as `Date`). - -If you pass `'legacy'` as an argument, Jest's legacy implementation will be used rather than one based on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -Returns the `jest` object for chaining. - -### `jest.useRealTimers()` - -Instructs Jest to use the real versions of the standard timer functions. - -Returns the `jest` object for chaining. - -### `jest.runAllTicks()` - -Exhausts the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending micro-tasks that have been queued via `process.nextTick` will be executed. Additionally, if those micro-tasks themselves schedule new micro-tasks, those will be continually exhausted until there are no more micro-tasks remaining in the queue. - -### `jest.runAllTimers()` - -Exhausts both the **macro**-task queue (i.e., all tasks queued by `setTimeout()`, `setInterval()`, and `setImmediate()`) and the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending macro-tasks and micro-tasks will be executed. If those tasks themselves schedule new tasks, those will be continually exhausted until there are no more tasks remaining in the queue. - -This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information. - -### `jest.runAllImmediates()` - -Exhausts all tasks queued by `setImmediate()`. - -> Note: This function is not available when using modern fake timers implementation - -### `jest.advanceTimersByTime(msToRun)` - -Executes only the macro task queue (i.e. all tasks queued by `setTimeout()` or `setInterval()` and `setImmediate()`). - -When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()`, and would be executed within this time frame will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue, that should be run within `msToRun` milliseconds. - -### `jest.runOnlyPendingTimers()` - -Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by `setTimeout()` or `setInterval()` up to this point). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call. - -This is useful for scenarios such as one where the module being tested schedules a `setTimeout()` whose callback schedules another `setTimeout()` recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time. - -### `jest.advanceTimersToNextTimer(steps)` - -Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run. - -Optionally, you can provide `steps`, so it will run `steps` amount of next timeouts/intervals. - -### `jest.clearAllTimers()` - -Removes any pending timers from the timer system. - -This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future. - -### `jest.getTimerCount()` - -Returns the number of fake timers still left to run. - -### `jest.setSystemTime(now?: number | Date)` - -Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`. - -> Note: This function is only available when using modern fake timers implementation - -### `jest.getRealSystemTime()` - -When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function. - -> Note: This function is only available when using modern fake timers implementation - -## Misc - -### `jest.setTimeout(timeout)` - -Set the default timeout interval for tests and before/after hooks in milliseconds. This only affects the test file from which this function is called. - -_Note: The default timeout interval is 5 seconds if this method is not called._ - -_Note: If you want to set the timeout for all test files, a good place to do this is in `setupFilesAfterEnv`._ - -Example: - -```js -jest.setTimeout(1000); // 1 second -``` - -### `jest.retryTimes()` - -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! This must live at the top-level of a test file or in a describe block. Retries _will not_ work if `jest.retryTimes()` is called in a `beforeEach` or a `test` block. - -Example in a test: - -```js -jest.retryTimes(3); -test('will fail', () => { - expect(true).toBe(false); -}); -``` - -Returns the `jest` object for chaining. diff --git a/website/versioned_docs/version-27.0/MockFunctionAPI.md b/website/versioned_docs/version-27.0/MockFunctionAPI.md deleted file mode 100644 index 5bb3925c81a1..000000000000 --- a/website/versioned_docs/version-27.0/MockFunctionAPI.md +++ /dev/null @@ -1,448 +0,0 @@ ---- -id: mock-function-api -title: Mock Functions ---- - -Mock functions are also known as "spies", because they let you spy on the behavior of a function that is called indirectly by some other code, rather than only testing the output. You can create a mock function with `jest.fn()`. If no implementation is given, the mock function will return `undefined` when invoked. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `mockFn.getMockName()` - -Returns the mock name string set by calling `mockFn.mockName(value)`. - -### `mockFn.mock.calls` - -An array containing the call arguments of all calls that have been made to this mock function. Each item in the array is an array of arguments that were passed during the call. - -For example: A mock function `f` that has been called twice, with the arguments `f('arg1', 'arg2')`, and then with the arguments `f('arg3', 'arg4')`, would have a `mock.calls` array that looks like this: - -```js -[ - ['arg1', 'arg2'], - ['arg3', 'arg4'], -]; -``` - -### `mockFn.mock.results` - -An array containing the results of all calls that have been made to this mock function. Each entry in this array is an object containing a `type` property, and a `value` property. `type` will be one of the following: - -- `'return'` - Indicates that the call completed by returning normally. -- `'throw'` - Indicates that the call completed by throwing a value. -- `'incomplete'` - Indicates that the call has not yet completed. This occurs if you test the result from within the mock function itself, or from within a function that was called by the mock. - -The `value` property contains the value that was thrown or returned. `value` is undefined when `type === 'incomplete'`. - -For example: A mock function `f` that has been called three times, returning `'result1'`, throwing an error, and then returning `'result2'`, would have a `mock.results` array that looks like this: - -```js -[ - { - type: 'return', - value: 'result1', - }, - { - type: 'throw', - value: { - /* Error instance */ - }, - }, - { - type: 'return', - value: 'result2', - }, -]; -``` - -### `mockFn.mock.instances` - -An array that contains all the object instances that have been instantiated from this mock function using `new`. - -For example: A mock function that has been instantiated twice would have the following `mock.instances` array: - -```js -const mockFn = jest.fn(); - -const a = new mockFn(); -const b = new mockFn(); - -mockFn.mock.instances[0] === a; // true -mockFn.mock.instances[1] === b; // true -``` - -### `mockFn.mockClear()` - -Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions. - -Beware that `mockClear` will replace `mockFn.mock`, not just these three properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data. - -The [`clearMocks`](configuration#clearmocks-boolean) configuration option is available to clear mocks automatically before each tests. - -### `mockFn.mockReset()` - -Does everything that [`mockFn.mockClear()`](#mockfnmockclear) does, and also removes any mocked return values or implementations. - -This is useful when you want to completely reset a _mock_ back to its initial state. (Note that resetting a _spy_ will result in a function with no return value). - -The [`mockReset`](configuration#resetmocks-boolean) configuration option is available to reset mocks automatically before each test. - -### `mockFn.mockRestore()` - -Does everything that [`mockFn.mockReset()`](#mockfnmockreset) does, and also restores the original (non-mocked) implementation. - -This is useful when you want to mock functions in certain test cases and restore the original implementation in others. - -Beware that `mockFn.mockRestore` only works when the mock was created with `jest.spyOn`. Thus you have to take care of restoration yourself when manually assigning `jest.fn()`. - -The [`restoreMocks`](configuration#restoremocks-boolean) configuration option is available to restore mocks automatically before each test. - -### `mockFn.mockImplementation(fn)` - -Accepts a function that should be used as the implementation of the mock. The mock itself will still record all calls that go into and instances that come from itself – the only difference is that the implementation will also be executed when the mock is called. - -_Note: `jest.fn(implementation)` is a shorthand for `jest.fn().mockImplementation(implementation)`._ - -For example: - -```js -const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar); -// or: jest.fn(scalar => 42 + scalar); - -const a = mockFn(0); -const b = mockFn(1); - -a === 42; // true -b === 43; // true - -mockFn.mock.calls[0][0] === 0; // true -mockFn.mock.calls[1][0] === 1; // true -``` - -`mockImplementation` can also be used to mock class constructors: - -```js title="SomeClass.js" -module.exports = class SomeClass { - m(a, b) {} -}; -``` - -```js title="OtherModule.test.js" -jest.mock('./SomeClass'); // this happens automatically with automocking -const SomeClass = require('./SomeClass'); -const mMock = jest.fn(); -SomeClass.mockImplementation(() => { - return { - m: mMock, - }; -}); - -const some = new SomeClass(); -some.m('a', 'b'); -console.log('Calls to m: ', mMock.mock.calls); -``` - -### `mockFn.mockImplementationOnce(fn)` - -Accepts a function that will be used as an implementation of the mock for one call to the mocked function. Can be chained so that multiple function calls produce different results. - -```js -const myMockFn = jest - .fn() - .mockImplementationOnce(cb => cb(null, true)) - .mockImplementationOnce(cb => cb(null, false)); - -myMockFn((err, val) => console.log(val)); // true - -myMockFn((err, val) => console.log(val)); // false -``` - -When the mocked function runs out of implementations defined with mockImplementationOnce, it will execute the default implementation set with `jest.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called: - -```js -const myMockFn = jest - .fn(() => 'default') - .mockImplementationOnce(() => 'first call') - .mockImplementationOnce(() => 'second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockName(value)` - -Accepts a string to use in test result output in place of "jest.fn()" to indicate which mock function is being referenced. - -For example: - -```js -const mockFn = jest.fn().mockName('mockedFunction'); -// mockFn(); -expect(mockFn).toHaveBeenCalled(); -``` - -Will result in this error: - -``` -expect(mockedFunction).toHaveBeenCalled() - -Expected mock function "mockedFunction" to have been called, but it was not called. -``` - -### `mockFn.mockReturnThis()` - -Syntactic sugar function for: - -```js -jest.fn(function () { - return this; -}); -``` - -### `mockFn.mockReturnValue(value)` - -Accepts a value that will be returned whenever the mock function is called. - -```js -const mock = jest.fn(); -mock.mockReturnValue(42); -mock(); // 42 -mock.mockReturnValue(43); -mock(); // 43 -``` - -### `mockFn.mockReturnValueOnce(value)` - -Accepts a value that will be returned for one call to the mock function. Can be chained so that successive calls to the mock function return different values. When there are no more `mockReturnValueOnce` values to use, calls will return a value specified by `mockReturnValue`. - -```js -const myMockFn = jest - .fn() - .mockReturnValue('default') - .mockReturnValueOnce('first call') - .mockReturnValueOnce('second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockResolvedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.resolve(value)); -``` - -Useful to mock async functions in async tests: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockResolvedValue(43); - - await asyncMock(); // 43 -}); -``` - -### `mockFn.mockResolvedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.resolve(value)); -``` - -Useful to resolve different values over multiple async calls: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValue('default') - .mockResolvedValueOnce('first call') - .mockResolvedValueOnce('second call'); - - await asyncMock(); // first call - await asyncMock(); // second call - await asyncMock(); // default - await asyncMock(); // default -}); -``` - -### `mockFn.mockRejectedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.reject(value)); -``` - -Useful to create async mock functions that will always reject: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); - - await asyncMock(); // throws "Async error" -}); -``` - -### `mockFn.mockRejectedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.reject(value)); -``` - -Example usage: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValueOnce('first call') - .mockRejectedValueOnce(new Error('Async error')); - - await asyncMock(); // first call - await asyncMock(); // throws "Async error" -}); -``` - -## TypeScript - -Jest itself is written in [TypeScript](https://www.typescriptlang.org). - -If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. - -Otherwise, please see our [Getting Started](GettingStarted.md#using-typescript) guide for to get setup with TypeScript. - -You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/main/examples/typescript). - -### `jest.MockedFunction` - -> `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](MockFunctions.md). - -You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. - -Example using [automatic `jest.mock`](JestObjectAPI.md#jestmockmodulename-factory-options): - -```ts -// Assume `add` is imported and used within `calculate`. -import add from './add'; -import calculate from './calc'; - -jest.mock('./add'); - -// Our mock of `add` is now fully typed -const mockAdd = add as jest.MockedFunction; - -test('calculate calls add', () => { - calculate('Add', 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -Example using [`jest.fn`](JestObjectAPI.md#jestfnimplementation): - -```ts -// Here `add` is imported for its type -import add from './add'; -import calculate from './calc'; - -test('calculate calls add', () => { - // Create a new mock that can be used in place of `add`. - const mockAdd = jest.fn() as jest.MockedFunction; - - // Note: You can use the `jest.fn` type directly like this if you want: - // const mockAdd = jest.fn, Parameters>(); - // `jest.MockedFunction` is a more friendly shortcut. - - // Now we can easily set up mock implementations. - // All the `.mock*` API can now give you proper types for `add`. - // https://jestjs.io/docs/mock-function-api - - // `.mockImplementation` can now infer that `a` and `b` are `number` - // and that the returned value is a `number`. - mockAdd.mockImplementation((a, b) => { - // Yes, this mock is still adding two numbers but imagine this - // was a complex function we are mocking. - return a + b; - }); - - // `mockAdd` is properly typed and therefore accepted by - // anything requiring `add`. - calculate(mockAdd, 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -### `jest.MockedClass` - -> `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](Es6ClassMocks.md). - -You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. - -Converting the [ES6 Class automatic mock example](Es6ClassMocks.md#automatic-mock) would look like this: - -```ts -import SoundPlayer from '../sound-player'; -import SoundPlayerConsumer from '../sound-player-consumer'; - -jest.mock('../sound-player'); // SoundPlayer is now a mock constructor - -const SoundPlayerMock = SoundPlayer as jest.MockedClass; - -beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - SoundPlayerMock.mockClear(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - // Show that mockClear() is working: - expect(SoundPlayerMock).not.toHaveBeenCalled(); - - const soundPlayerConsumer = new SoundPlayerConsumer(); - // Constructor should have been called again: - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); - - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - - // mock.instances is available with automatic mocks: - const mockSoundPlayerInstance = SoundPlayerMock.mock.instances[0]; - - // However, it will not allow access to `.mock` in TypeScript as it - // is returning `SoundPlayer`. Instead, you can check the calls to a - // method like this fully typed: - expect(SoundPlayerMock.prototype.playSoundFile.mock.calls[0][0]).toEqual( - coolSoundFileName, - ); - // Equivalent to above check: - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledWith( - coolSoundFileName, - ); - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledTimes(1); -}); -``` diff --git a/website/versioned_docs/version-27.0/MockFunctions.md b/website/versioned_docs/version-27.0/MockFunctions.md deleted file mode 100644 index 2e686a92e837..000000000000 --- a/website/versioned_docs/version-27.0/MockFunctions.md +++ /dev/null @@ -1,315 +0,0 @@ ---- -id: mock-functions -title: Mock Functions ---- - -Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with `new`, and allowing test-time configuration of return values. - -There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a [`manual mock`](ManualMocks.md) to override a module dependency. - -## Using a mock function - -Let's imagine we're testing an implementation of a function `forEach`, which invokes a callback for each item in a supplied array. - -```javascript -function forEach(items, callback) { - for (let index = 0; index < items.length; index++) { - callback(items[index]); - } -} -``` - -To test this function, we can use a mock function, and inspect the mock's state to ensure the callback is invoked as expected. - -```javascript -const mockCallback = jest.fn(x => 42 + x); -forEach([0, 1], mockCallback); - -// The mock function is called twice -expect(mockCallback.mock.calls.length).toBe(2); - -// The first argument of the first call to the function was 0 -expect(mockCallback.mock.calls[0][0]).toBe(0); - -// The first argument of the second call to the function was 1 -expect(mockCallback.mock.calls[1][0]).toBe(1); - -// The return value of the first call to the function was 42 -expect(mockCallback.mock.results[0].value).toBe(42); -``` - -## `.mock` property - -All mock functions have this special `.mock` property, which is where data about how the function has been called and what the function returned is kept. The `.mock` property also tracks the value of `this` for each call, so it is possible to inspect this as well: - -```javascript -const myMock = jest.fn(); - -const a = new myMock(); -const b = {}; -const bound = myMock.bind(b); -bound(); - -console.log(myMock.mock.instances); -// > [
, ] -``` - -These mock members are very useful in tests to assert how these functions get called, instantiated, or what they returned: - -```javascript -// The function was called exactly once -expect(someMockFunction.mock.calls.length).toBe(1); - -// The first arg of the first call to the function was 'first arg' -expect(someMockFunction.mock.calls[0][0]).toBe('first arg'); - -// The second arg of the first call to the function was 'second arg' -expect(someMockFunction.mock.calls[0][1]).toBe('second arg'); - -// The return value of the first call to the function was 'return value' -expect(someMockFunction.mock.results[0].value).toBe('return value'); - -// This function was instantiated exactly twice -expect(someMockFunction.mock.instances.length).toBe(2); - -// The object returned by the first instantiation of this function -// had a `name` property whose value was set to 'test' -expect(someMockFunction.mock.instances[0].name).toEqual('test'); -``` - -## Mock Return Values - -Mock functions can also be used to inject test values into your code during a test: - -```javascript -const myMock = jest.fn(); -console.log(myMock()); -// > undefined - -myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true); - -console.log(myMock(), myMock(), myMock(), myMock()); -// > 10, 'x', true, true -``` - -Mock functions are also very effective in code that uses a functional continuation-passing style. Code written in this style helps avoid the need for complicated stubs that recreate the behavior of the real component they're standing in for, in favor of injecting values directly into the test right before they're used. - -```javascript -const filterTestFn = jest.fn(); - -// Make the mock return `true` for the first call, -// and `false` for the second call -filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false); - -const result = [11, 12].filter(num => filterTestFn(num)); - -console.log(result); -// > [11] -console.log(filterTestFn.mock.calls[0][0]); // 11 -console.log(filterTestFn.mock.calls[1][0]); // 12 -``` - -Most real-world examples actually involve getting ahold of a mock function on a dependent component and configuring that, but the technique is the same. In these cases, try to avoid the temptation to implement logic inside of any function that's not directly being tested. - -## Mocking Modules - -Suppose we have a class that fetches users from our API. The class uses [axios](https://github.com/axios/axios) to call the API then returns the `data` attribute which contains all the users: - -```js title="users.js" -import axios from 'axios'; - -class Users { - static all() { - return axios.get('/users.json').then(resp => resp.data); - } -} - -export default Users; -``` - -Now, in order to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the `jest.mock(...)` function to automatically mock the axios module. - -Once we mock the module we can provide a `mockResolvedValue` for `.get` that returns the data we want our test to assert against. In effect, we are saying that we want `axios.get('/users.json')` to return a fake response. - -```js title="users.test.js" -import axios from 'axios'; -import Users from './users'; - -jest.mock('axios'); - -test('should fetch users', () => { - const users = [{name: 'Bob'}]; - const resp = {data: users}; - axios.get.mockResolvedValue(resp); - - // or you could use the following depending on your use case: - // axios.get.mockImplementation(() => Promise.resolve(resp)) - - return Users.all().then(data => expect(data).toEqual(users)); -}); -``` - -## Mocking Partials - -Subsets of a module can be mocked and the rest of the module can keep their actual implementation: - -```js title="foo-bar-baz.js" -export const foo = 'foo'; -export const bar = () => 'bar'; -export default () => 'baz'; -``` - -```js -//test.js -import defaultExport, {bar, foo} from '../foo-bar-baz'; - -jest.mock('../foo-bar-baz', () => { - const originalModule = jest.requireActual('../foo-bar-baz'); - - //Mock the default export and named export 'foo' - return { - __esModule: true, - ...originalModule, - default: jest.fn(() => 'mocked baz'), - foo: 'mocked foo', - }; -}); - -test('should do a partial mock', () => { - const defaultExportResult = defaultExport(); - expect(defaultExportResult).toBe('mocked baz'); - expect(defaultExport).toHaveBeenCalled(); - - expect(foo).toBe('mocked foo'); - expect(bar()).toBe('bar'); -}); -``` - -## Mock Implementations - -Still, there are cases where it's useful to go beyond the ability to specify return values and full-on replace the implementation of a mock function. This can be done with `jest.fn` or the `mockImplementationOnce` method on mock functions. - -```javascript -const myMockFn = jest.fn(cb => cb(null, true)); - -myMockFn((err, val) => console.log(val)); -// > true -``` - -The `mockImplementation` method is useful when you need to define the default implementation of a mock function that is created from another module: - -```js title="foo.js" -module.exports = function () { - // some implementation; -}; -``` - -```js title="test.js" -jest.mock('../foo'); // this happens automatically with automocking -const foo = require('../foo'); - -// foo is a mock function -foo.mockImplementation(() => 42); -foo(); -// > 42 -``` - -When you need to recreate a complex behavior of a mock function such that multiple function calls produce different results, use the `mockImplementationOnce` method: - -```javascript -const myMockFn = jest - .fn() - .mockImplementationOnce(cb => cb(null, true)) - .mockImplementationOnce(cb => cb(null, false)); - -myMockFn((err, val) => console.log(val)); -// > true - -myMockFn((err, val) => console.log(val)); -// > false -``` - -When the mocked function runs out of implementations defined with `mockImplementationOnce`, it will execute the default implementation set with `jest.fn` (if it is defined): - -```javascript -const myMockFn = jest - .fn(() => 'default') - .mockImplementationOnce(() => 'first call') - .mockImplementationOnce(() => 'second call'); - -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -// > 'first call', 'second call', 'default', 'default' -``` - -For cases where we have methods that are typically chained (and thus always need to return `this`), we have a sugary API to simplify this in the form of a `.mockReturnThis()` function that also sits on all mocks: - -```javascript -const myObj = { - myMethod: jest.fn().mockReturnThis(), -}; - -// is the same as - -const otherObj = { - myMethod: jest.fn(function () { - return this; - }), -}; -``` - -## Mock Names - -You can optionally provide a name for your mock functions, which will be displayed instead of "jest.fn()" in the test error output. Use this if you want to be able to quickly identify the mock function reporting an error in your test output. - -```javascript -const myMockFn = jest - .fn() - .mockReturnValue('default') - .mockImplementation(scalar => 42 + scalar) - .mockName('add42'); -``` - -## Custom Matchers - -Finally, in order to make it less demanding to assert how mock functions have been called, we've added some custom matcher functions for you: - -```javascript -// The mock function was called at least once -expect(mockFunc).toHaveBeenCalled(); - -// The mock function was called at least once with the specified args -expect(mockFunc).toHaveBeenCalledWith(arg1, arg2); - -// The last call to the mock function was called with the specified args -expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2); - -// All calls and the name of the mock is written as a snapshot -expect(mockFunc).toMatchSnapshot(); -``` - -These matchers are sugar for common forms of inspecting the `.mock` property. You can always do this manually yourself if that's more to your taste or if you need to do something more specific: - -```javascript -// The mock function was called at least once -expect(mockFunc.mock.calls.length).toBeGreaterThan(0); - -// The mock function was called at least once with the specified args -expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]); - -// The last call to the mock function was called with the specified args -expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([ - arg1, - arg2, -]); - -// The first arg of the last call to the mock function was `42` -// (note that there is no sugar helper for this specific of an assertion) -expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42); - -// A snapshot will check that a mock was invoked the same number of times, -// in the same order, with the same arguments. It will also assert on the name. -expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]); -expect(mockFunc.getMockName()).toBe('a mock name'); -``` - -For a complete list of matchers, check out the [reference docs](ExpectAPI.md). diff --git a/website/versioned_docs/version-27.0/TestingAsyncCode.md b/website/versioned_docs/version-27.0/TestingAsyncCode.md deleted file mode 100644 index 432db44b3944..000000000000 --- a/website/versioned_docs/version-27.0/TestingAsyncCode.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -id: asynchronous -title: Testing Asynchronous Code ---- - -It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this. - -## Callbacks - -The most common asynchronous pattern is callbacks. - -For example, let's say that you have a `fetchData(callback)` function that fetches some data and calls `callback(data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. - -By default, Jest tests complete once they reach the end of their execution. That means this test will _not_ work as intended: - -```js -// Don't do this! -test('the data is peanut butter', () => { - function callback(data) { - expect(data).toBe('peanut butter'); - } - - fetchData(callback); -}); -``` - -The problem is that the test will complete as soon as `fetchData` completes, before ever calling the callback. - -There is an alternate form of `test` that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called `done`. Jest will wait until the `done` callback is called before finishing the test. - -```js -test('the data is peanut butter', done => { - function callback(data) { - try { - expect(data).toBe('peanut butter'); - done(); - } catch (error) { - done(error); - } - } - - fetchData(callback); -}); -``` - -If `done()` is never called, the test will fail (with timeout error), which is what you want to happen. - -If the `expect` statement fails, it throws an error and `done()` is not called. If we want to see in the test log why it failed, we have to wrap `expect` in a `try` block and pass the error in the `catch` block to `done`. Otherwise, we end up with an opaque timeout error that doesn't show what value was received by `expect(data)`. - -_Note: `done()` should not be mixed with Promises as this tends to lead to memory leaks in your tests._ - -## Promises - -If your code uses promises, there is a more straightforward way to handle asynchronous tests. Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -For example, let's say that `fetchData`, instead of using a callback, returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: - -```js -test('the data is peanut butter', () => { - return fetchData().then(data => { - expect(data).toBe('peanut butter'); - }); -}); -``` - -Be sure to return the promise - if you omit this `return` statement, your test will complete before the promise returned from `fetchData` resolves and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. - -```js -test('the fetch fails with an error', () => { - expect.assertions(1); - return fetchData().catch(e => expect(e).toMatch('error')); -}); -``` - -## `.resolves` / `.rejects` - -You can also use the `.resolves` matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -```js -test('the data is peanut butter', () => { - return expect(fetchData()).resolves.toBe('peanut butter'); -}); -``` - -Be sure to return the assertion—if you omit this `return` statement, your test will complete before the promise returned from `fetchData` is resolved and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.rejects` matcher. It works analogically to the `.resolves` matcher. If the promise is fulfilled, the test will automatically fail. - -```js -test('the fetch fails with an error', () => { - return expect(fetchData()).rejects.toMatch('error'); -}); -``` - -## Async/Await - -Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: - -```js -test('the data is peanut butter', async () => { - const data = await fetchData(); - expect(data).toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - expect.assertions(1); - try { - await fetchData(); - } catch (e) { - expect(e).toMatch('error'); - } -}); -``` - -You can combine `async` and `await` with `.resolves` or `.rejects`. - -```js -test('the data is peanut butter', async () => { - await expect(fetchData()).resolves.toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - await expect(fetchData()).rejects.toMatch('error'); -}); -``` - -In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. - -None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler. diff --git a/website/versioned_docs/version-27.0/TestingFrameworks.md b/website/versioned_docs/version-27.0/TestingFrameworks.md deleted file mode 100644 index 5b085c8658a3..000000000000 --- a/website/versioned_docs/version-27.0/TestingFrameworks.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -id: testing-frameworks -title: Testing Web Frameworks ---- - -Jest is a universal testing platform, with the ability to adapt to any JavaScript library or framework. In this section, we'd like to link to community posts and articles about integrating Jest into popular JS libraries. - -## React - -- [Testing ReactJS components with Jest](https://testing-library.com/docs/react-testing-library/example-intro) by Kent C. Dodds ([@kentcdodds](https://twitter.com/kentcdodds)) - -## Vue.js - -- [Testing Vue.js components with Jest](https://alexjoverm.github.io/series/Unit-Testing-Vue-js-Components-with-the-Official-Vue-Testing-Tools-and-Jest/) by Alex Jover Morales ([@alexjoverm](https://twitter.com/alexjoverm)) -- [Jest for all: Episode 1 — Vue.js](https://medium.com/@kentaromiura_the_js_guy/jest-for-all-episode-1-vue-js-d616bccbe186#.d573vrce2) by Cristian Carlesso ([@kentaromiura](https://twitter.com/kentaromiura)) - -## AngularJS - -- [Testing an AngularJS app with Jest](https://medium.com/aya-experience/testing-an-angularjs-app-with-jest-3029a613251) by Matthieu Lux ([@Swiip](https://twitter.com/Swiip)) -- [Running AngularJS Tests with Jest](https://engineering.talentpair.com/running-angularjs-tests-with-jest-49d0cc9c6d26) by Ben Brandt ([@benjaminbrandt](https://twitter.com/benjaminbrandt)) -- [AngularJS Unit Tests with Jest Actions (Traditional Chinese)](https://dwatow.github.io/2019/08-14-angularjs/angular-jest/?fbclid=IwAR2SrqYg_o6uvCQ79FdNPeOxs86dUqB6pPKgd9BgnHt1kuIDRyRM-ch11xg) by Chris Wang ([@dwatow](https://github.com/dwatow)) - -## Angular - -- [Testing Angular faster with Jest](https://www.xfive.co/blog/testing-angular-faster-jest/) by Michał Pierzchała ([@thymikee](https://twitter.com/thymikee)) - -## MobX - -- [How to Test React and MobX with Jest](https://semaphoreci.com/community/tutorials/how-to-test-react-and-mobx-with-jest) by Will Stern ([@willsterndev](https://twitter.com/willsterndev)) - -## Redux - -- [Writing Tests](https://redux.js.org/recipes/writing-tests) by Redux docs - -## Express.js - -- [How to test Express.js with Jest and Supertest](http://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/) by Albert Gao ([@albertgao](https://twitter.com/albertgao)) - -## GatsbyJS - -- [Unit Testing](https://www.gatsbyjs.org/docs/unit-testing/) by GatsbyJS docs - -## Hapi.js - -- [Testing Hapi.js With Jest](http://niralar.com/testing-hapi-js-with-jest/) by Niralar ([Sivasankar](http://sivasankar.in/)) diff --git a/website/versioned_docs/version-27.0/TimerMocks.md b/website/versioned_docs/version-27.0/TimerMocks.md deleted file mode 100644 index 0952884e1beb..000000000000 --- a/website/versioned_docs/version-27.0/TimerMocks.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -id: timer-mocks -title: Timer Mocks ---- - -The native timer functions (i.e., `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`) are less than ideal for a testing environment since they depend on real time to elapse. Jest can swap out timers with functions that allow you to control the passage of time. [Great Scott!](https://www.youtube.com/watch?v=QZoJ2Pt27BY) - -```javascript title="timerGame.js" -'use strict'; - -function timerGame(callback) { - console.log('Ready....go!'); - setTimeout(() => { - console.log("Time's up -- stop!"); - callback && callback(); - }, 1000); -} - -module.exports = timerGame; -``` - -```javascript title="__tests__/timerGame-test.js" -'use strict'; - -jest.useFakeTimers(); // or you can set "timers": "fake" globally in configuration file -jest.spyOn(global, 'setTimeout'); - -test('waits 1 second before ending the game', () => { - const timerGame = require('../timerGame'); - timerGame(); - - expect(setTimeout).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); -}); -``` - -Here we enable fake timers by calling `jest.useFakeTimers()`. This mocks out `setTimeout` and other timer functions with mock functions. Timers can be restored to their normal behavior with `jest.useRealTimers()`. - -While you can call `jest.useFakeTimers()` or `jest.useRealTimers()` from anywhere (top level, inside an `it` block, etc.), it is a **global operation** and will affect other tests within the same file. Additionally, you need to call `jest.useFakeTimers()` to reset internal counters before each test. If you plan to not use fake timers in all your tests, you will want to clean up manually, as otherwise the faked timers will leak across tests: - -```javascript -afterEach(() => { - jest.useRealTimers(); -}); - -test('do something with fake timers', () => { - jest.useFakeTimers(); - // ... -}); - -test('do something with real timers', () => { - // ... -}); -``` - -All of the following functions need fake timers to be set, either by `jest.useFakeTimers()` or via `"timers": "fake"` in the config file. - -Currently, two implementations of the fake timers are included - `modern` and `legacy`, where `modern` is the default one. See [configuration](Configuration.md#timers-string) for how to configure it. - -## Run All Timers - -Another test we might want to write for this module is one that asserts that the callback is called after 1 second. To do this, we're going to use Jest's timer control APIs to fast-forward time right in the middle of the test: - -```javascript -jest.useFakeTimers(); -test('calls the callback after 1 second', () => { - const timerGame = require('../timerGame'); - const callback = jest.fn(); - - timerGame(callback); - - // At this point in time, the callback should not have been called yet - expect(callback).not.toBeCalled(); - - // Fast-forward until all timers have been executed - jest.runAllTimers(); - - // Now our callback should have been called! - expect(callback).toBeCalled(); - expect(callback).toHaveBeenCalledTimes(1); -}); -``` - -## Run Pending Timers - -There are also scenarios where you might have a recursive timer -- that is a timer that sets a new timer in its own callback. For these, running all the timers would be an endless loop, throwing the following error: - -``` -Ran 100000 timers, and there are still more! Assuming we've hit an infinite recursion and bailing out... -``` - -So something like `jest.runAllTimers()` is not desirable. For these cases you might use `jest.runOnlyPendingTimers()`: - -```javascript title="infiniteTimerGame.js" -'use strict'; - -function infiniteTimerGame(callback) { - console.log('Ready....go!'); - - setTimeout(() => { - console.log("Time's up! 10 seconds before the next game starts..."); - callback && callback(); - - // Schedule the next game in 10 seconds - setTimeout(() => { - infiniteTimerGame(callback); - }, 10000); - }, 1000); -} - -module.exports = infiniteTimerGame; -``` - -```javascript title="__tests__/infiniteTimerGame-test.js" -'use strict'; - -jest.useFakeTimers(); -jest.spyOn(global, 'setTimeout'); - -describe('infiniteTimerGame', () => { - test('schedules a 10-second timer after 1 second', () => { - const infiniteTimerGame = require('../infiniteTimerGame'); - const callback = jest.fn(); - - infiniteTimerGame(callback); - - // At this point in time, there should have been a single call to - // setTimeout to schedule the end of the game in 1 second. - expect(setTimeout).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); - - // Fast forward and exhaust only currently pending timers - // (but not any new timers that get created during that process) - jest.runOnlyPendingTimers(); - - // At this point, our 1-second timer should have fired its callback - expect(callback).toBeCalled(); - - // And it should have created a new timer to start the game over in - // 10 seconds - expect(setTimeout).toHaveBeenCalledTimes(2); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000); - }); -}); -``` - -## Advance Timers by Time - -Another possibility is use `jest.advanceTimersByTime(msToRun)`. When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via setTimeout() or setInterval(), and would be executed during this time frame, will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds. - -```javascript title="timerGame.js" -'use strict'; - -function timerGame(callback) { - console.log('Ready....go!'); - setTimeout(() => { - console.log("Time's up -- stop!"); - callback && callback(); - }, 1000); -} - -module.exports = timerGame; -``` - -```javascript title="__tests__/timerGame-test.js" -jest.useFakeTimers(); -it('calls the callback after 1 second via advanceTimersByTime', () => { - const timerGame = require('../timerGame'); - const callback = jest.fn(); - - timerGame(callback); - - // At this point in time, the callback should not have been called yet - expect(callback).not.toBeCalled(); - - // Fast-forward until all timers have been executed - jest.advanceTimersByTime(1000); - - // Now our callback should have been called! - expect(callback).toBeCalled(); - expect(callback).toHaveBeenCalledTimes(1); -}); -``` - -Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have `jest.clearAllTimers()`. - -The code for this example is available at [examples/timer](https://github.com/facebook/jest/tree/main/examples/timer). diff --git a/website/versioned_docs/version-27.1/CLI.md b/website/versioned_docs/version-27.1/CLI.md deleted file mode 100644 index 05952b17e30d..000000000000 --- a/website/versioned_docs/version-27.1/CLI.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -id: cli -title: Jest CLI Options ---- - -The `jest` command line runner has a number of useful options. You can run `jest --help` to view all available options. Many of the options shown below can also be used together to run tests exactly the way you want. Every one of Jest's [Configuration](Configuration.md) options can also be specified through the CLI. - -Here is a brief overview: - -## Running from the command line - -Run all tests (default): - -```bash -jest -``` - -Run only the tests that were specified with a pattern or filename: - -```bash -jest my-test #or -jest path/to/my-test.js -``` - -Run tests related to changed files based on hg/git (uncommitted files): - -```bash -jest -o -``` - -Run tests related to `path/to/fileA.js` and `path/to/fileB.js`: - -```bash -jest --findRelatedTests path/to/fileA.js path/to/fileB.js -``` - -Run tests that match this spec name (match against the name in `describe` or `test`, basically). - -```bash -jest -t name-of-spec -``` - -Run watch mode: - -```bash -jest --watch #runs jest -o by default -jest --watchAll #runs all tests -``` - -Watch mode also enables to specify the name or path to a file to focus on a specific set of tests. - -## Using with yarn - -If you run Jest via `yarn test`, you can pass the command line arguments directly as Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -yarn test -u -t="ColorPicker" -``` - -## Using with npm scripts - -If you run Jest via `npm test`, you can still use the command line arguments by inserting a `--` between `npm test` and the Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -npm test -- -u -t="ColorPicker" -``` - -## Camelcase & dashed args support - -Jest supports both camelcase and dashed arg formats. The following examples will have an equal result: - -```bash -jest --collect-coverage -jest --collectCoverage -``` - -Arguments can also be mixed: - -```bash -jest --update-snapshot --detectOpenHandles -``` - -## Options - -_Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `jest ` - -When you run `jest` with an argument, that argument is treated as a regular expression to match against files in your project. It is possible to run test suites by providing a pattern. Only the files that the pattern matches will be picked up and executed. Depending on your terminal, you may need to quote this argument: `jest "my.*(complex)?pattern"`. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--bail` - -Alias: `-b`. Exit the test suite immediately upon `n` number of failing test suite. Defaults to `1`. - -### `--cache` - -Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. _Note: the cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower._ - -If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. - -### `--changedFilesWithAncestor` - -Runs tests related to the current changes and the changes made in the last commit. Behaves similarly to `--onlyChanged`. - -### `--changedSince` - -Runs tests related to the changes since the provided branch or commit hash. If the current branch has diverged from the given branch, then only changes made locally will be tested. Behaves similarly to `--onlyChanged`. - -### `--ci` - -When this option is provided, Jest will assume it is running in a CI environment. This changes the behavior when a new snapshot is encountered. Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`. - -### `--clearCache` - -Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. _Note: clearing the cache will reduce performance._ - -### `--clearMocks` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `--collectCoverageFrom=` - -A glob pattern relative to `rootDir` matching the files that coverage info needs to be collected from. - -### `--colors` - -Forces test results output highlighting even if stdout is not a TTY. - -### `--config=` - -Alias: `-c`. The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the `rootDir` for the project. This can also be a JSON-encoded value which Jest will use as configuration. - -### `--coverage[=]` - -Alias: `--collectCoverage`. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. - -### `--coverageProvider=` - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `--debug` - -Print debugging info about your Jest config. - -### `--detectOpenHandles` - -Attempt to collect and print open handles preventing Jest from exiting cleanly. Use this in cases where you need to use `--forceExit` in order for Jest to exit to potentially track down the reason. This implies `--runInBand`, making tests run serially. Implemented using [`async_hooks`](https://nodejs.org/api/async_hooks.html). This option has a significant performance penalty and should only be used for debugging. - -### `--env=` - -The test environment used for all tests. This can point to any file or node module. Examples: `jsdom`, `node` or `path/to/my-environment.js`. - -### `--errorOnDeprecated` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `--expand` - -Alias: `-e`. Use this flag to show full diffs and errors instead of a patch. - -### `--filter=` - -Path to a module exporting a filtering function. This method receives a list of tests which can be manipulated to exclude tests from running. Especially useful when used in conjunction with a testing infrastructure to filter known broken. - -### `--findRelatedTests ` - -Find and run the tests that cover a space separated list of source files that were passed in as arguments. Useful for pre-commit hook integration to run the minimal amount of tests necessary. Can be used together with `--coverage` to include a test coverage for the source files, no duplicate `--collectCoverageFrom` arguments needed. - -### `--forceExit` - -Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. _Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down._ - -### `--help` - -Show the help information, similar to this page. - -### `--init` - -Generate a basic configuration file. Based on your project, Jest will ask you a few questions that will help to generate a `jest.config.js` file with a short description for each option. - -### `--injectGlobals` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus` test runner._ - -### `--json` - -Prints the test results in JSON. This mode will send all other test output and user messages to stderr. - -### `--outputFile=` - -Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testresultsprocessor-string). - -### `--lastCommit` - -Run all tests affected by file changes in the last commit made. Behaves similarly to `--onlyChanged`. - -### `--listTests` - -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. - -### `--logHeapUsage` - -Logs the heap usage after every test. Useful to debug memory leaks. Use together with `--runInBand` and `--expose-gc` in node. - -### `--maxConcurrency=` - -Prevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use `test.concurrent`. - -### `--maxWorkers=|` - -Alias: `-w`. Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `--maxWorkers=50%` - -### `--noStackTrace` - -Disables stack trace in test results output. - -### `--notify` - -Activates notifications for test results. Good for when you don't want your consciousness to be able to focus on anything except JavaScript testing. - -### `--onlyChanged` - -Alias: `-o`. Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a git/hg repository at the moment and requires a static dependency graph (ie. no dynamic requires). - -### `--passWithNoTests` - -Allows the test suite to pass when no files are found. - -### `--projects ... ` - -Run tests from one or more projects, found in the specified paths; also takes path globs. This option is the CLI equivalent of the [`projects`](configuration#projects-arraystring--projectconfig) configuration option. Note that if configuration files are found in the specified paths, _all_ projects specified within those configuration files will be run. - -### `--reporters` - -Run tests with specified reporters. [Reporter options](configuration#reporters-arraymodulename--modulename-options) are not available via CLI. Example with multiple reporters: - -`jest --reporters="default" --reporters="jest-junit"` - -### `--resetMocks` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `--restoreMocks` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `--roots` - -A list of paths to directories that Jest should use to search for files in. - -### `--runInBand` - -Alias: `-i`. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - -### `--runTestsByPath` - -Run only the tests that were specified with their exact paths. - -_Note: The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files_ - -### `--selectProjects ... ` - -Run only the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. - -### `--setupFilesAfterEnv ... ` - -A list of paths to modules that run some code to configure or to set up the testing framework before each test. Beware that files imported by the setup scripts will not be mocked during testing. - -### `--showConfig` - -Print your Jest config and then exits. - -### `--silent` - -Prevent tests from printing messages through the console. - -### `--testLocationInResults` - -Adds a `location` field to test results. Useful if you want to report the location of a test in a reporter. - -Note that `column` is 0-indexed while `line` is not. - -```json -{ - "column": 4, - "line": 5 -} -``` - -### `--testMatch glob1 ... globN` - -The glob patterns Jest uses to detect test files. Please refer to the [`testMatch` configuration](Configuration.md#testmatch-arraystring) for details. - -### `--testNamePattern=` - -Alias: `-t`. Run only tests with a name that matches the regex. For example, suppose you want to run only tests related to authorization which will have names like `"GET /api/posts with auth"`, then you can use `jest -t=auth`. - -_Note: The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks._ - -### `--testPathIgnorePatterns=|[array]` - -A single or array of regexp pattern strings that are tested against all tests paths before executing the test. Contrary to `--testPathPattern`, it will only run those tests with a path that does not match with the provided regexp expressions. - -To pass as an array use escaped parentheses and space delimited regexps such as `\(/node_modules/ /tests/e2e/\)`. Alternatively, you can omit parentheses by combining regexps into a single regexp like `/node_modules/|/tests/e2e/`. These two examples are equivalent. - -### `--testPathPattern=` - -A regexp pattern string that is matched against all tests paths before executing the test. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--testRunner=` - -Lets you specify a custom test runner. - -### `--testSequencer=` - -Lets you specify a custom test sequencer. Please refer to the documentation of the corresponding configuration property for details. - -### `--testTimeout=` - -Default timeout of a test in milliseconds. Default value: 5000. - -### `--updateSnapshot` - -Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots. - -### `--useStderr` - -Divert all output to stderr. - -### `--verbose` - -Display individual test results with the test suite hierarchy. - -### `--version` - -Alias: `-v`. Print the version and exit. - -### `--watch` - -Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option instead. - -### `--watchAll` - -Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. - -Use `--watchAll=false` to explicitly disable the watch mode. Note that in most CI environments, this is automatically handled for you. - -### `--watchman` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. Defaults to `true`. Disable using `--no-watchman`. diff --git a/website/versioned_docs/version-27.1/CodeTransformation.md b/website/versioned_docs/version-27.1/CodeTransformation.md deleted file mode 100644 index 3b1e0f8e3100..000000000000 --- a/website/versioned_docs/version-27.1/CodeTransformation.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -id: code-transformation -title: Code Transformation ---- - -Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node.js out of the box (such as JSX, types from TypeScript, Vue templates etc.) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers. - -Jest supports this via the [`transform` configuration option](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object). - -A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. - -Jest will cache the result of a transformation and attempt to invalidate that result based on a number of factors, such as the source of the file being transformed and changing configuration. - -## Defaults - -Jest ships with one transformer out of the box - `babel-jest`. It will automatically load your project's Babel configuration and transform any file matching the following RegEx: `/\.[jt]sx?$/` meaning any `.js`, `.jsx`, `.ts` and `.tsx` file. In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). - -If you override the `transform` configuration option `babel-jest` will no longer be active, and you'll need to add it manually if you wish to use Babel. - -## Writing custom transformers - -You can write your own transformer. The API of a transformer is as follows: - -```ts -interface SyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => SyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -interface AsyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => AsyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -type Transformer = - | SyncTransformer - | AsyncTransformer; - -interface TransformOptions { - /** - * If a transformer does module resolution and reads files, it should populate `cacheFS` so that - * Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of - * - */ - cacheFS: Map; - config: Config.ProjectConfig; - /** A stringified version of the configuration - useful in cache busting */ - configString: string; - instrument: boolean; - // names are copied from babel: https://babeljs.io/docs/en/options#caller - supportsDynamicImport: boolean; - supportsExportNamespaceFrom: boolean; - supportsStaticESM: boolean; - supportsTopLevelAwait: boolean; - /** the options passed through Jest's config by the user */ - transformerConfig: OptionType; -} - -type TransformedSource = - | {code: string; map?: RawSourceMap | string | null} - | string; - -// Config.ProjectConfig can be seen in code [here](https://github.com/facebook/jest/blob/v26.6.3/packages/jest-types/src/Config.ts#L323) -// RawSourceMap comes from [`source-map`](https://github.com/mozilla/source-map/blob/0.6.1/source-map.d.ts#L6-L12) -``` - -As can be seen, only `process` or `processAsync` is mandatory to implement, although we highly recommend implementing `getCacheKey` as well, so we don't waste resources transpiling the same source file when we can read its previous result from disk. You can use [`@jest/create-cache-key-function`](https://www.npmjs.com/package/@jest/create-cache-key-function) to help implement it. - -Note that [ECMAScript module](ECMAScriptModules.md) support is indicated by the passed in `supports*` options. Specifically `supportsDynamicImport: true` means the transformer can return `import()` expressions, which is supported by both ESM and CJS. If `supportsStaticESM: true` it means top level `import` statements are supported and the code will be interpreted as ESM and not CJS. See [Node's docs](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs) for details on the differences. - -### Examples - -### TypeScript with type checking - -While `babel-jest` by default will transpile TypeScript files, Babel will not verify the types. If you want that you can use [`ts-jest`](https://github.com/kulshekhar/ts-jest). - -#### Transforming images to their path - -Importing images is a way to include them in your browser bundle, but they are not valid JavaScript. One way of handling it in Jest is to replace the imported value with its filename. - -```js title="fileTransformer.js" -const path = require('path'); - -module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; - }, -}; -``` - -```js title="jest.config.js" -module.exports = { - transform: { - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': - '/fileTransformer.js', - }, -}; -``` diff --git a/website/versioned_docs/version-27.1/Configuration.md b/website/versioned_docs/version-27.1/Configuration.md deleted file mode 100644 index 59e1816d519c..000000000000 --- a/website/versioned_docs/version-27.1/Configuration.md +++ /dev/null @@ -1,1459 +0,0 @@ ---- -id: configuration -title: Configuring Jest ---- - -Jest's configuration can be defined in the `package.json` file of your project, or through a `jest.config.js`, or `jest.config.ts` file or through the `--config ` option. If you'd like to use your `package.json` to store Jest's config, the `"jest"` key should be used on the top level so Jest will know how to find your settings: - -```json -{ - "name": "my-project", - "jest": { - "verbose": true - } -} -``` - -Or through JavaScript: - -```js title="jest.config.js" -// Sync object -/** @type {import('@jest/types').Config.InitialOptions} */ -const config = { - verbose: true, -}; - -module.exports = config; - -// Or async function -module.exports = async () => { - return { - verbose: true, - }; -}; -``` - -Or through TypeScript (if `ts-node` is installed): - -```ts title="jest.config.ts" -import type {Config} from '@jest/types'; - -// Sync object -const config: Config.InitialOptions = { - verbose: true, -}; -export default config; - -// Or async function -export default async (): Promise => { - return { - verbose: true, - }; -}; -``` - -Please keep in mind that the resulting configuration must be JSON-serializable. - -When using the `--config` option, the JSON file must not contain a "jest" key: - -```json -{ - "bail": 1, - "verbose": true -} -``` - -## Options - -These options let you control Jest's behavior in your `package.json` file. The Jest philosophy is to work great by default, but sometimes you just need more configuration power. - -### Defaults - -You can retrieve Jest's default options to expand them if needed: - -```js title="jest.config.js" -const {defaults} = require('jest-config'); -module.exports = { - // ... - moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'], - // ... -}; -``` - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `automock` \[boolean] - -Default: `false` - -This option tells Jest that all imported modules in your tests should be mocked automatically. All modules used in your tests will have a replacement implementation, keeping the API surface. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js -//__tests__/automocking.test.js -import utils from '../utils'; - -test('if utils mocked automatically', () => { - // Public methods of `utils` are now mock functions - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized.mock).toBeTruthy(); - - // You can provide them with your own implementation - // or pass the expected return value - utils.authorize.mockReturnValue('mocked_token'); - utils.isAuthorized.mockReturnValue(true); - - expect(utils.authorize()).toBe('mocked_token'); - expect(utils.isAuthorized('not_wizard')).toBeTruthy(); -}); -``` - -_Note: Node modules are automatically mocked when you have a manual mock in place (e.g.: `__mocks__/lodash.js`). More info [here](manual-mocks#mocking-node-modules)._ - -_Note: Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`._ - -### `bail` \[number | boolean] - -Default: `0` - -By default, Jest runs all tests and produces all errors into the console upon completion. The bail config option can be used here to have Jest stop running tests after `n` failures. Setting bail to `true` is the same as setting bail to `1`. - -### `cacheDirectory` \[string] - -Default: `"/tmp/"` - -The directory where Jest should store its cached dependency information. - -Jest attempts to scan your dependency tree once (up-front) and cache it in order to ease some of the filesystem churn that needs to happen while running tests. This config option lets you customize where Jest stores that cache data on disk. - -### `clearMocks` \[boolean] - -Default: `false` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `collectCoverage` \[boolean] - -Default: `false` - -Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. - -### `collectCoverageFrom` \[array] - -Default: `undefined` - -An array of [glob patterns](https://github.com/micromatch/micromatch) indicating a set of files for which coverage information should be collected. If a file matches the specified glob pattern, coverage information will be collected for it even if no tests exist for this file and it's never required in the test suite. - -Example: - -```json -{ - "collectCoverageFrom": [ - "**/*.{js,jsx}", - "!**/node_modules/**", - "!**/vendor/**" - ] -} -``` - -This will collect coverage information for all the files inside the project's `rootDir`, except the ones that match `**/node_modules/**` or `**/vendor/**`. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__tests__/**", "**/*.js"]` will not exclude `__tests__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/*.js`.)_ - -_Note: This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`._ - -
- Help: - If you are seeing coverage output such as... - -``` -=============================== Coverage summary =============================== -Statements : Unknown% ( 0/0 ) -Branches : Unknown% ( 0/0 ) -Functions : Unknown% ( 0/0 ) -Lines : Unknown% ( 0/0 ) -================================================================================ -Jest: Coverage data for global was not found. -``` - -Most likely your glob patterns are not matching any files. Refer to the [micromatch](https://github.com/micromatch/micromatch) documentation to ensure your globs are compatible. - -
- -### `coverageDirectory` \[string] - -Default: `undefined` - -The directory where Jest should output its coverage files. - -### `coveragePathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all file paths before executing the test. If the file path matches any of the patterns, coverage information will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `coverageProvider` \[string] - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `coverageReporters` \[array<string | \[string, options]>] - -Default: `["clover", "json", "lcov", "text"]` - -A list of reporter names that Jest uses when writing coverage reports. Any [istanbul reporter](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) can be used. - -_Note: Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output._ - -Additional options can be passed using the tuple form. For example, you may hide coverage report lines for all fully-covered files: - -```json -{ - "coverageReporters": ["clover", "json", "lcov", ["text", {"skipFull": true}]] -} -``` - -For more information about the options object shape refer to `CoverageReporterWithOptions` type in the [type definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Config.ts). - -### `coverageThreshold` \[object] - -Default: `undefined` - -This will be used to configure minimum threshold enforcement for coverage results. Thresholds can be specified as `global`, as a [glob](https://github.com/isaacs/node-glob#glob-primer), and as a directory or file path. If thresholds aren't met, jest will fail. Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed. - -For example, with the following configuration jest will fail if there is less than 80% branch, line, and function coverage, or if there are more than 10 uncovered statements: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 80, - "functions": 80, - "lines": 80, - "statements": -10 - } - } - } -} -``` - -If globs or paths are specified alongside `global`, coverage data for matching paths will be subtracted from overall coverage and thresholds will be applied independently. Thresholds for globs are applied to all files matching the glob. If the file specified by path is not found, an error is returned. - -For example, with the following configuration: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 50, - "functions": 50, - "lines": 50, - "statements": 50 - }, - "./src/components/": { - "branches": 40, - "statements": 40 - }, - "./src/reducers/**/*.js": { - "statements": 90 - }, - "./src/api/very-important-module.js": { - "branches": 100, - "functions": 100, - "lines": 100, - "statements": 100 - } - } - } -} -``` - -Jest will fail if: - -- The `./src/components` directory has less than 40% branch or statement coverage. -- One of the files matching the `./src/reducers/**/*.js` glob has less than 90% statement coverage. -- The `./src/api/very-important-module.js` file has less than 100% coverage. -- Every remaining file combined has less than 50% coverage (`global`). - -### `dependencyExtractor` \[string] - -Default: `undefined` - -This option allows the use of a custom dependency extractor. It must be a node module that exports an object with an `extract` function. E.g.: - -```javascript -const crypto = require('crypto'); -const fs = require('fs'); - -module.exports = { - extract(code, filePath, defaultExtract) { - const deps = defaultExtract(code, filePath); - // Scan the file and add dependencies in `deps` (which is a `Set`) - return deps; - }, - getCacheKey() { - return crypto - .createHash('md5') - .update(fs.readFileSync(__filename)) - .digest('hex'); - }, -}; -``` - -The `extract` function should return an iterable (`Array`, `Set`, etc.) with the dependencies found in the code. - -That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded. - -### `displayName` \[string, object] - -default: `undefined` - -Allows for a label to be printed alongside a test while it is running. This becomes more useful in multi-project repositories where there can be many jest configuration files. This visually tells which project a test belongs to. Here are sample valid values. - -```js -module.exports = { - displayName: 'CLIENT', -}; -``` - -or - -```js -module.exports = { - displayName: { - name: 'CLIENT', - color: 'blue', - }, -}; -``` - -As a secondary option, an object with the properties `name` and `color` can be passed. This allows for a custom configuration of the background color of the displayName. `displayName` defaults to white when its value is a string. Jest uses [chalk](https://github.com/chalk/chalk) to provide the color. As such, all of the valid options for colors supported by chalk are also supported by jest. - -### `errorOnDeprecated` \[boolean] - -Default: `false` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `extensionsToTreatAsEsm` \[array<string>] - -Default: `[]` - -Jest will run `.mjs` and `.js` files with nearest `package.json`'s `type` field set to `module` as ECMAScript Modules. If you have any other files that should run with native ESM, you need to specify their file extension here. - -> Note: Jest's ESM support is still experimental, see [its docs for more details](ECMAScriptModules.md). - -```json -{ - ... - "jest": { - "extensionsToTreatAsEsm": [".ts"] - } -} -``` - -### `extraGlobals` \[array<string>] - -Default: `undefined` - -Test files run inside a [vm](https://nodejs.org/api/vm.html), which slows calls to global context properties (e.g. `Math`). With this option you can specify extra properties to be defined inside the vm for faster lookups. - -For example, if your tests call `Math` often, you can pass it by setting `extraGlobals`. - -```json -{ - ... - "jest": { - "extraGlobals": ["Math"] - } -} -``` - -### `forceCoverageMatch` \[array<string>] - -Default: `['']` - -Test files are normally ignored from collecting code coverage. With this option, you can overwrite this behavior and include otherwise ignored files in code coverage. - -For example, if you have tests in source files named with `.t.js` extension as following: - -```javascript title="sum.t.js" -export function sum(a, b) { - return a + b; -} - -if (process.env.NODE_ENV === 'test') { - test('sum', () => { - expect(sum(1, 2)).toBe(3); - }); -} -``` - -You can collect coverage from those files with setting `forceCoverageMatch`. - -```json -{ - ... - "jest": { - "forceCoverageMatch": ["**/*.t.js"] - } -} -``` - -### `globals` \[object] - -Default: `{}` - -A set of global variables that need to be available in all test environments. - -For example, the following would create a global `__DEV__` variable set to `true` in all test environments: - -```json -{ - ... - "jest": { - "globals": { - "__DEV__": true - } - } -} -``` - -Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will _not_ be persisted across test runs for other test files. In addition, the `globals` object must be json-serializable, so it can't be used to specify global functions. For that, you should use `setupFiles`. - -### `globalSetup` \[string] - -Default: `undefined` - -This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites._ - -_Note: While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation._ - -Example: - -```js title="setup.js" -// can be synchronous -module.exports = async () => { - // ... - // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongod; -}; -``` - -```js title="teardown.js" -module.exports = async function () { - await global.__MONGOD__.stop(); -}; -``` - -### `globalTeardown` \[string] - -Default: `undefined` - -This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: The same caveat concerning transformation of `node_modules` as for `globalSetup` applies to `globalTeardown`._ - -### `haste` \[object] - -Default: `undefined` - -This will be used to configure the behavior of `jest-haste-map`, Jest's internal file crawler/cache system. The following options are supported: - -```ts -type HasteConfig = { - /** Whether to hash files using SHA-1. */ - computeSha1?: boolean; - /** The platform to use as the default, e.g. 'ios'. */ - defaultPlatform?: string | null; - /** Force use of Node's `fs` APIs rather than shelling out to `find` */ - forceNodeFilesystemAPI?: boolean; - /** - * Whether to follow symlinks when crawling for files. - * This options cannot be used in projects which use watchman. - * Projects with `watchman` set to true will error if this option is set to true. - */ - enableSymlinks?: boolean; - /** Path to a custom implementation of Haste. */ - hasteImplModulePath?: string; - /** All platforms to target, e.g ['ios', 'android']. */ - platforms?: Array; - /** Whether to throw on error on module collision. */ - throwOnModuleCollision?: boolean; - /** Custom HasteMap module */ - hasteMapModulePath?: string; -}; -``` - -### `injectGlobals` \[boolean] - -Default: `true` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus`. test runner_ - -### `maxConcurrency` \[number] - -Default: `5` - -A number limiting the number of tests that are allowed to run at the same time when using `test.concurrent`. Any test above this limit will be queued and executed once a slot is released. - -### `maxWorkers` \[number | string] - -Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `"maxWorkers": "50%"` - -### `moduleDirectories` \[array<string>] - -Default: `["node_modules"]` - -An array of directory names to be searched recursively up from the requiring module's location. Setting this option will _override_ the default, if you wish to still search `node_modules` for packages include it along with any other options: `["node_modules", "bower_components"]` - -### `moduleFileExtensions` \[array<string>] - -Default: `["js", "jsx", "ts", "tsx", "json", "node"]` - -An array of file extensions your modules use. If you require modules without specifying a file extension, these are the extensions Jest will look for, in left-to-right order. - -We recommend placing the extensions most commonly used in your project on the left, so if you are using TypeScript, you may want to consider moving "ts" and/or "tsx" to the beginning of the array. - -### `moduleNameMapper` \[object<string, string | array<string>>] - -Default: `null` - -A map from regular expressions to module names or to arrays of module names that allow to stub out resources, like images or styles with a single module. - -Modules that are mapped to an alias are unmocked by default, regardless of whether automocking is enabled or not. - -Use `` string token to refer to [`rootDir`](#rootdir-string) value if you want to use file paths. - -Additionally, you can substitute captured regex groups using numbered backreferences. - -Example: - -```json -{ - "moduleNameMapper": { - "^image![a-zA-Z0-9$_-]+$": "GlobalImageStub", - "^[./a-zA-Z0-9$_-]+\\.png$": "/RelativeImageStub.js", - "module_name_(.*)": "/substituted_module_$1.js", - "assets/(.*)": [ - "/images/$1", - "/photos/$1", - "/recipes/$1" - ] - } -} -``` - -The order in which the mappings are defined matters. Patterns are checked one by one until one fits. The most specific rule should be listed first. This is true for arrays of module names as well. - -_Note: If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub._ - -### `modulePathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all module paths before those paths are to be considered 'visible' to the module loader. If a given module's path matches any of the patterns, it will not be `require()`-able in the test environment. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/"]`. - -### `modulePaths` \[array<string>] - -Default: `[]` - -An alternative API to setting the `NODE_PATH` env variable, `modulePaths` is an array of absolute paths to additional locations to search when resolving modules. Use the `` string token to include the path to your project's root directory. Example: `["/app/"]`. - -### `notify` \[boolean] - -Default: `false` - -Activates notifications for test results. - -**Beware:** Jest uses [node-notifier](https://github.com/mikaelbr/node-notifier) to display desktop notifications. On Windows, it creates a new start menu entry on the first use and not display the notification. Notifications will be properly displayed on subsequent runs - -### `notifyMode` \[string] - -Default: `failure-change` - -Specifies notification mode. Requires `notify: true`. - -#### Modes - -- `always`: always send a notification. -- `failure`: send a notification when tests fail. -- `success`: send a notification when tests pass. -- `change`: send a notification when the status changed. -- `success-change`: send a notification when tests pass or once when it fails. -- `failure-change`: send a notification when tests fail or once when it passes. - -### `preset` \[string] - -Default: `undefined` - -A preset that is used as a base for Jest's configuration. A preset should point to an npm module that has a `jest-preset.json`, `jest-preset.js`, `jest-preset.cjs` or `jest-preset.mjs` file at the root. - -For example, this preset `foo-bar/jest-preset.js` will be configured as follows: - -```json -{ - "preset": "foo-bar" -} -``` - -Presets may also be relative to filesystem paths. - -```json -{ - "preset": "./node_modules/foo-bar/jest-preset.js" -} -``` - -### `prettierPath` \[string] - -Default: `'prettier'` - -Sets the path to the [`prettier`](https://prettier.io/) node module used to update inline snapshots. - -### `projects` \[array<string | ProjectConfig>] - -Default: `undefined` - -When the `projects` configuration is provided with an array of paths or glob patterns, Jest will run tests in all of the specified projects at the same time. This is great for monorepos or when working on multiple projects at the same time. - -```json -{ - "projects": ["", "/examples/*"] -} -``` - -This example configuration will run Jest in the root directory as well as in every folder in the examples directory. You can have an unlimited amount of projects running in the same Jest instance. - -The projects feature can also be used to run multiple configurations or multiple [runners](#runner-string). For this purpose, you can pass an array of configuration objects. For example, to run both tests and ESLint (via [jest-runner-eslint](https://github.com/jest-community/jest-runner-eslint)) in the same invocation of Jest: - -```json -{ - "projects": [ - { - "displayName": "test" - }, - { - "displayName": "lint", - "runner": "jest-runner-eslint", - "testMatch": ["/**/*.js"] - } - ] -} -``` - -_Note: When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._ - -### `reporters` \[array<moduleName | \[moduleName, options]>] - -Default: `undefined` - -Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs. - -If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name. - -This will override default reporters: - -```json -{ - "reporters": ["/my-custom-reporter.js"] -} -``` - -This will use custom reporter in addition to default reporters that Jest provides: - -```json -{ - "reporters": ["default", "/my-custom-reporter.js"] -} -``` - -Additionally, custom reporters can be configured by passing an `options` object as a second argument: - -```json -{ - "reporters": [ - "default", - ["/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}] - ] -} -``` - -Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments: - -Example reporter: - -```js title="my-custom-reporter.js" -class MyCustomReporter { - constructor(globalConfig, options) { - this._globalConfig = globalConfig; - this._options = options; - } - - onRunComplete(contexts, results) { - console.log('Custom reporter output:'); - console.log('GlobalConfig: ', this._globalConfig); - console.log('Options: ', this._options); - } -} - -module.exports = MyCustomReporter; -// or export default MyCustomReporter; -``` - -Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods - -```js -class MyCustomReporter { - // ... - getLastError() { - if (this._shouldFail) { - return new Error('my-custom-reporter.js reported an error'); - } - } -} -``` - -For the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts) - -### `resetMocks` \[boolean] - -Default: `false` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `resetModules` \[boolean] - -Default: `false` - -By default, each test file gets its own independent module registry. Enabling `resetModules` goes a step further and resets the module registry before running each individual test. This is useful to isolate modules for every test so that the local module state doesn't conflict between tests. This can be done programmatically using [`jest.resetModules()`](JestObjectAPI.md#jestresetmodules). - -### `resolver` \[string] - -Default: `undefined` - -This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: - -```json -{ - "basedir": string, - "defaultResolver": "function(request, options)", - "extensions": [string], - "moduleDirectory": [string], - "paths": [string], - "packageFilter": "function(pkg, pkgdir)", - "rootDir": [string] -} -``` - -The function should either return a path to the module that should be resolved or throw an error if the module can't be found. - -Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)`. - -For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: - -```json -{ - ... - "jest": { - "resolver": "/resolver.js" - } -} -``` - -```js title="resolver.js" -const browserResolve = require('browser-resolve'); - -module.exports = browserResolve.sync; -``` - -By combining `defaultResolver` and `packageFilter` we can implement a `package.json` "pre-processor" that allows us to change how the default resolver will resolve modules. For example, imagine we want to use the field `"module"` if it is present, otherwise fallback to `"main"`: - -```json -{ - ... - "jest": { - "resolver": "my-module-resolve" - } -} -``` - -```js -// my-module-resolve package - -module.exports = (request, options) => { - // Call the defaultResolver, so we leverage its cache, error handling, etc. - return options.defaultResolver(request, { - ...options, - // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb) - packageFilter: pkg => { - return { - ...pkg, - // Alter the value of `main` before resolving the package - main: pkg.module || pkg.main, - }; - }, - }); -}; -``` - -### `restoreMocks` \[boolean] - -Default: `false` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `rootDir` \[string] - -Default: The root of the directory containing your Jest [config file](#) _or_ the `package.json` _or_ the [`pwd`](http://en.wikipedia.org/wiki/Pwd) if no `package.json` is found - -The root directory that Jest should scan for tests and modules within. If you put your Jest config inside your `package.json` and want the root directory to be the root of your repo, the value for this config param will default to the directory of the `package.json`. - -Oftentimes, you'll want to set this to `'src'` or `'lib'`, corresponding to where in your repository the code is stored. - -_Note that using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`._ - -### `roots` \[array<string>] - -Default: `[""]` - -A list of paths to directories that Jest should use to search for files in. - -There are times where you only want Jest to search in a single sub-directory (such as cases where you have a `src/` directory in your repo), but prevent it from accessing the rest of the repo. - -_Note: While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`)._ - -_Note: By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`._ - -### `runner` \[string] - -Default: `"jest-runner"` - -This option allows you to use a custom runner instead of Jest's default test runner. Examples of runners include: - -- [`jest-runner-eslint`](https://github.com/jest-community/jest-runner-eslint) -- [`jest-runner-mocha`](https://github.com/rogeliog/jest-runner-mocha) -- [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc) -- [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier) - -_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._ - -To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature: - -```ts -async function runTests( - tests: Array, - watcher: TestWatcher, - onStart: OnTestStart, - onResult: OnTestSuccess, - onFailure: OnTestFailure, - options: TestRunnerOptions, -): Promise; -``` - -If you need to restrict your test-runner to only run in serial rather than being executed in parallel your class should have the property `isSerial` to be set as `true`. - -### `setupFiles` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment before executing [`setupFilesAfterEnv`](#setupfilesafterenv-array) and before the test code itself. - -### `setupFilesAfterEnv` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing framework before each test file in the suite is executed. Since [`setupFiles`](#setupfiles-array) executes before the test framework is installed in the environment, this script file presents you the opportunity of running some code immediately after the test framework has been installed in the environment but before the test code itself. - -If you want a path to be [relative to the root directory of your project](#rootdir-string), please include `` inside a path's string, like `"/a-configs-folder"`. - -For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. - -Example `setupFilesAfterEnv` array in a jest.config.js: - -```js -module.exports = { - setupFilesAfterEnv: ['./jest.setup.js'], -}; -``` - -Example `jest.setup.js` file - -```js -jest.setTimeout(10000); // in milliseconds -``` - -### `slowTestThreshold` \[number] - -Default: `5` - -The number of seconds after which a test is considered as slow and reported as such in the results. - -### `snapshotFormat` \[object] - -Default: `undefined` - -Allows overriding specific snapshot formatting options documented in the [pretty-format readme](https://www.npmjs.com/package/pretty-format#usage-with-options), with the exceptions of `compareKeys` and `plugins`. For example, this config would have the snapshot formatter not print a prefix for "Object" and "Array": - -```json -{ - "jest": { - "snapshotFormat": { - "printBasicPrototype": false - } - } -} -``` - -```ts -import {expect, test} from '@jest/globals'; - -test('does not show prototypes for object and array inline', () => { - const object = { - array: [{hello: 'Danger'}], - }; - expect(object).toMatchInlineSnapshot(` -{ - "array": [ - { - "hello": "Danger", - }, - ], -} - `); -}); -``` - -### `snapshotResolver` \[string] - -Default: `undefined` - -The path to a module that can resolve test<->snapshot path. This config option lets you customize where Jest stores snapshot files on disk. - -Example snapshot resolver module: - -```js -module.exports = { - // resolves from test to snapshot path - resolveSnapshotPath: (testPath, snapshotExtension) => - testPath.replace('__tests__', '__snapshots__') + snapshotExtension, - - // resolves from snapshot to test path - resolveTestPath: (snapshotFilePath, snapshotExtension) => - snapshotFilePath - .replace('__snapshots__', '__tests__') - .slice(0, -snapshotExtension.length), - - // Example test path, used for preflight consistency check of the implementation above - testPathForConsistencyCheck: 'some/__tests__/example.test.js', -}; -``` - -### `snapshotSerializers` \[array<string>] - -Default: `[]` - -A list of paths to snapshot serializer modules Jest should use for snapshot testing. - -Jest has default serializers for built-in JavaScript types, HTML elements (Jest 20.0.0+), ImmutableJS (Jest 20.0.0+) and for React elements. See [snapshot test tutorial](TutorialReactNative.md#snapshot-test) for more information. - -Example serializer module: - -```js -// my-serializer-module -module.exports = { - serialize(val, config, indentation, depth, refs, printer) { - return `Pretty foo: ${printer(val.foo)}`; - }, - - test(val) { - return val && Object.prototype.hasOwnProperty.call(val, 'foo'); - }, -}; -``` - -`printer` is a function that serializes a value using existing plugins. - -To use `my-serializer-module` as a serializer, configuration would be as follows: - -```json -{ - ... - "jest": { - "snapshotSerializers": ["my-serializer-module"] - } -} -``` - -Finally tests would look as follows: - -```js -test(() => { - const bar = { - foo: { - x: 1, - y: 2, - }, - }; - - expect(bar).toMatchSnapshot(); -}); -``` - -Rendered snapshot: - -```json -Pretty foo: Object { - "x": 1, - "y": 2, -} -``` - -To make a dependency explicit instead of implicit, you can call [`expect.addSnapshotSerializer`](ExpectAPI.md#expectaddsnapshotserializerserializer) to add a module for an individual test file instead of adding its path to `snapshotSerializers` in Jest configuration. - -More about serializers API can be found [here](https://github.com/facebook/jest/tree/main/packages/pretty-format/README.md#serialize). - -### `testEnvironment` \[string] - -Default: `"node"` - -The test environment that will be used for testing. The default environment in Jest is a Node.js environment. If you are building a web app, you can use a browser-like environment through [`jsdom`](https://github.com/jsdom/jsdom) instead. - -By adding a `@jest-environment` docblock at the top of the file, you can specify another environment to be used for all tests in that file: - -```js -/** - * @jest-environment jsdom - */ - -test('use jsdom in this test file', () => { - const element = document.createElement('div'); - expect(element).not.toBeNull(); -}); -``` - -You can create your own module that will be used for setting up the test environment. The module must export a class with `setup`, `teardown` and `getVmContext` methods. You can also pass variables from this module to your test suites by assigning them to `this.global` object – this will make them available in your test suites as global variables. - -The class may optionally expose an asynchronous `handleTestEvent` method to bind to events fired by [`jest-circus`](https://github.com/facebook/jest/tree/main/packages/jest-circus). Normally, `jest-circus` test runner would pause until a promise returned from `handleTestEvent` gets fulfilled, **except for the next events**: `start_describe_definition`, `finish_describe_definition`, `add_hook`, `add_test` or `error` (for the up-to-date list you can look at [SyncEvent type in the types definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Circus.ts)). That is caused by backward compatibility reasons and `process.on('unhandledRejection', callback)` signature, but that usually should not be a problem for most of the use cases. - -Any docblock pragmas in test files will be passed to the environment constructor and can be used for per-test configuration. If the pragma does not have a value, it will be present in the object with its value set to an empty string. If the pragma is not present, it will not be present in the object. - -To use this class as your custom environment, refer to it by its full path within the project. For example, if your class is stored in `my-custom-environment.js` in some subfolder of your project, then the annotation might look like this: - -```js -/** - * @jest-environment ./src/test/my-custom-environment - */ -``` - -_Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ - -Example: - -```js -// my-custom-environment -const NodeEnvironment = require('jest-environment-node'); - -class CustomEnvironment extends NodeEnvironment { - constructor(config, context) { - super(config, context); - this.testPath = context.testPath; - this.docblockPragmas = context.docblockPragmas; - } - - async setup() { - await super.setup(); - await someSetupTasks(this.testPath); - this.global.someGlobalObject = createGlobalObject(); - - // Will trigger if docblock contains @my-custom-pragma my-pragma-value - if (this.docblockPragmas['my-custom-pragma'] === 'my-pragma-value') { - // ... - } - } - - async teardown() { - this.global.someGlobalObject = destroyGlobalObject(); - await someTeardownTasks(); - await super.teardown(); - } - - getVmContext() { - return super.getVmContext(); - } - - async handleTestEvent(event, state) { - if (event.name === 'test_start') { - // ... - } - } -} - -module.exports = CustomEnvironment; -``` - -```js -// my-test-suite -/** - * @jest-environment ./my-custom-environment - */ -let someGlobalObject; - -beforeAll(() => { - someGlobalObject = global.someGlobalObject; -}); -``` - -### `testEnvironmentOptions` \[Object] - -Default: `{}` - -Test environment options that will be passed to the `testEnvironment`. The relevant options depend on the environment. For example, you can override options given to [jsdom](https://github.com/jsdom/jsdom) such as `{userAgent: "Agent/007"}`. - -### `testFailureExitCode` \[number] - -Default: `1` - -The exit code Jest returns on test failure. - -_Note: This does not change the exit code in the case of Jest errors (e.g. invalid configuration)._ - -### `testMatch` \[array<string>] - -(default: `[ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]`) - -The glob patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. - -See the [micromatch](https://github.com/micromatch/micromatch) package for details of the patterns you can specify. - -See also [`testRegex` [string | array<string>]](#testregex-string--arraystring), but note that you cannot specify both options. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__fixtures__/**", "**/__tests__/**/*.js"]` will not exclude `__fixtures__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/__tests__/**/*.js`.)_ - -### `testPathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all test paths before executing the test. If the test path matches any of the patterns, it will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `testRegex` \[string | array<string>] - -Default: `(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$` - -The pattern or patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array<string>]](#testmatch-arraystring), but note that you cannot specify both options. - -The following is a visualization of the default regex: - -```bash -├── __tests__ -│ └── component.spec.js # test -│ └── anything # test -├── package.json # not test -├── foo.test.js # test -├── bar.spec.jsx # test -└── component.js # not test -``` - -_Note: `testRegex` will try to detect test files using the **absolute file path**, therefore, having a folder with a name that matches it will run all the files as tests_ - -### `testResultsProcessor` \[string] - -Default: `undefined` - -This option allows the use of a custom results processor. This processor must be a node module that exports a function expecting an object with the following structure as the first argument and return it: - -```json -{ - "success": boolean, - "startTime": epoch, - "numTotalTestSuites": number, - "numPassedTestSuites": number, - "numFailedTestSuites": number, - "numRuntimeErrorTestSuites": number, - "numTotalTests": number, - "numPassedTests": number, - "numFailedTests": number, - "numPendingTests": number, - "numTodoTests": number, - "openHandles": Array, - "testResults": [{ - "numFailingTests": number, - "numPassingTests": number, - "numPendingTests": number, - "testResults": [{ - "title": string (message in it block), - "status": "failed" | "pending" | "passed", - "ancestorTitles": [string (message in describe blocks)], - "failureMessages": [string], - "numPassingAsserts": number, - "location": { - "column": number, - "line": number - } - }, - ... - ], - "perfStats": { - "start": epoch, - "end": epoch - }, - "testFilePath": absolute path to test file, - "coverage": {} - }, - "testExecError:" (exists if there was a top-level failure) { - "message": string - "stack": string - } - ... - ] -} -``` - -`testResultsProcessor` and `reporters` are very similar to each other. One difference is that a test result processor only gets called after all tests finished. Whereas a reporter has the ability to receive test results after individual tests and/or test suites are finished. - -### `testRunner` \[string] - -Default: `jest-circus/runner` - -This option allows the use of a custom test runner. The default is `jest-circus`. A custom test runner can be provided by specifying a path to a test runner implementation. - -The test runner module must export a function with the following signature: - -```ts -function testRunner( - globalConfig: GlobalConfig, - config: ProjectConfig, - environment: Environment, - runtime: Runtime, - testPath: string, -): Promise; -``` - -An example of such function can be found in our default [jasmine2 test runner package](https://github.com/facebook/jest/blob/main/packages/jest-jasmine2/src/index.ts). - -### `testSequencer` \[string] - -Default: `@jest/test-sequencer` - -This option allows you to use a custom sequencer instead of Jest's default. `sort` may optionally return a Promise. - -Example: - -Sort test path alphabetically. - -```js title="testSequencer.js" -const Sequencer = require('@jest/test-sequencer').default; - -class CustomSequencer extends Sequencer { - sort(tests) { - // Test structure information - // https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21 - const copyTests = Array.from(tests); - return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); - } -} - -module.exports = CustomSequencer; -``` - -Use it in your Jest config file like this: - -```json -{ - "testSequencer": "path/to/testSequencer.js" -} -``` - -### `testTimeout` \[number] - -Default: `5000` - -Default timeout of a test in milliseconds. - -### `testURL` \[string] - -Default: `http://localhost` - -This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. - -### `timers` \[string] - -Default: `real` - -Setting this value to `fake` or `modern` enables fake timers for all tests by default. Fake timers are useful when a piece of code sets a long timeout that we don't want to wait for in a test. You can learn more about fake timers [here](JestObjectAPI.md#jestusefaketimersimplementation-modern--legacy). - -If the value is `legacy`, the old implementation will be used as implementation instead of one backed by [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -### `transform` \[object<string, pathToTransformer | \[pathToTransformer, object]>] - -Default: `{"\\.[jt]sx?$": "babel-jest"}` - -A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/main/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md). - -Examples of such compilers include: - -- [Babel](https://babeljs.io/) -- [TypeScript](http://www.typescriptlang.org/) -- To build your own please visit the [Custom Transformer](CodeTransformation.md#writing-custom-transformers) section - -You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}` - -_Note: a transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues)._ - -_Note: when adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or Typescript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup)_ - -A transformer must be an object with at least a `process` function, and it's also recommended to include a `getCacheKey` function. If your transformer is written in ESM you should have a default export with that object. - -If the tests are written using [native ESM](ECMAScriptModules.md) the transformer can export `processAsync` and `getCacheKeyAsync` instead or in addition to the synchronous variants. - -### `transformIgnorePatterns` \[array<string>] - -Default: `["/node_modules/", "\\.pnp\\.[^\\\/]+$"]` - -An array of regexp pattern strings that are matched against all source file paths before transformation. If the file path matches **any** of the patterns, it will not be transformed. - -Providing regexp patterns that overlap with each other may result in files not being transformed that you expected to be transformed. For example: - -```json -{ - "transformIgnorePatterns": ["/node_modules/(?!(foo|bar)/)", "/bar/"] -} -``` - -The first pattern will match (and therefore not transform) files inside `/node_modules` except for those in `/node_modules/foo/` and `/node_modules/bar/`. The second pattern will match (and therefore not transform) files inside any path with `/bar/` in it. With the two together, files in `/node_modules/bar/` will not be transformed because it does match the second pattern, even though it was excluded by the first. - -Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled code. Since all files inside `node_modules` are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use `transformIgnorePatterns` to allow transpiling such modules. You'll find a good example of this use case in [React Native Guide](/docs/tutorial-react-native#transformignorepatterns-customization). - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. - -Example: - -```json -{ - "transformIgnorePatterns": [ - "/bower_components/", - "/node_modules/" - ] -} -``` - -### `unmockedModulePathPatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them. If a module's path matches any of the patterns in this list, it will not be automatically mocked by the module loader. - -This is useful for some commonly used 'utility' modules that are almost always used as implementation details almost all the time (like underscore/lo-dash, etc). It's generally a best practice to keep this list as small as possible and always use explicit `jest.mock()`/`jest.unmock()` calls in individual tests. Explicit per-test setup is far easier for other readers of the test to reason about the environment the test will run in. - -It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. - -### `verbose` \[boolean] - -Default: `false` - -Indicates whether each individual test should be reported during the run. All errors will also still be shown on the bottom after execution. Note that if there is only one test file being run it will default to `true`. - -### `watchPathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests. - -These patterns match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/node_modules/"]`. - -Even if nothing is specified here, the watcher will ignore changes to the version control folders (.git, .hg). Other hidden files and directories, i.e. those that begin with a dot (`.`), are watched by default. Remember to escape the dot when you add them to `watchPathIgnorePatterns` as it is a special RegExp character. - -Example: - -```json -{ - "watchPathIgnorePatterns": ["/\\.tmp/", "/bar/"] -} -``` - -### `watchPlugins` \[array<string | \[string, Object]>] - -Default: `[]` - -This option allows you to use custom watch plugins. Read more about watch plugins [here](watch-plugins). - -Examples of watch plugins include: - -- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master) -- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects) -- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) -- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) -- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) - -_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._ - -### `watchman` \[boolean] - -Default: `true` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. - -### `//` \[string] - -No default - -This option allows comments in `package.json`. Include the comment text as the value of this key anywhere in `package.json`. - -Example: - -```json -{ - "name": "my-project", - "jest": { - "//": "Comment goes here", - "verbose": true - } -} -``` diff --git a/website/versioned_docs/version-27.1/DynamoDB.md b/website/versioned_docs/version-27.1/DynamoDB.md deleted file mode 100644 index a5abd9147aed..000000000000 --- a/website/versioned_docs/version-27.1/DynamoDB.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -id: dynamodb -title: Using with DynamoDB ---- - -With the [Global Setup/Teardown](Configuration.md#globalsetup-string) and [Async Test Environment](Configuration.md#testenvironment-string) APIs, Jest can work smoothly with [DynamoDB](https://aws.amazon.com/dynamodb/). - -## Use jest-dynamodb Preset - -[Jest DynamoDB](https://github.com/shelfio/jest-dynamodb) provides all required configuration to run your tests using DynamoDB. - -1. First, install `@shelf/jest-dynamodb` - -``` -yarn add @shelf/jest-dynamodb --dev -``` - -2. Specify preset in your Jest configuration: - -```json -{ - "preset": "@shelf/jest-dynamodb" -} -``` - -3. Create `jest-dynamodb-config.js` and define DynamoDB tables - -See [Create Table API](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#createTable-property) - -```js -module.exports = { - tables: [ - { - TableName: `files`, - KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}], - AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}], - ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1}, - }, - // etc - ], -}; -``` - -4. Configure DynamoDB client - -```js -const {DocumentClient} = require('aws-sdk/clients/dynamodb'); - -const isTest = process.env.JEST_WORKER_ID; -const config = { - convertEmptyValues: true, - ...(isTest && { - endpoint: 'localhost:8000', - sslEnabled: false, - region: 'local-env', - }), -}; - -const ddb = new DocumentClient(config); -``` - -5. Write tests - -```js -it('should insert item into table', async () => { - await ddb - .put({TableName: 'files', Item: {id: '1', hello: 'world'}}) - .promise(); - - const {Item} = await ddb.get({TableName: 'files', Key: {id: '1'}}).promise(); - - expect(Item).toEqual({ - id: '1', - hello: 'world', - }); -}); -``` - -There's no need to load any dependencies. - -See [documentation](https://github.com/shelfio/jest-dynamodb) for details. diff --git a/website/versioned_docs/version-27.1/ExpectAPI.md b/website/versioned_docs/version-27.1/ExpectAPI.md deleted file mode 100644 index ab3f63a70213..000000000000 --- a/website/versioned_docs/version-27.1/ExpectAPI.md +++ /dev/null @@ -1,1393 +0,0 @@ ---- -id: expect -title: Expect ---- - -When you're writing tests, you often need to check that values meet certain conditions. `expect` gives you access to a number of "matchers" that let you validate different things. - -For additional Jest matchers maintained by the Jest Community check out [`jest-extended`](https://github.com/jest-community/jest-extended). - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `expect(value)` - -The `expect` function is used every time you want to test a value. You will rarely call `expect` by itself. Instead, you will use `expect` along with a "matcher" function to assert something about a value. - -It's easier to understand this with an example. Let's say you have a method `bestLaCroixFlavor()` which is supposed to return the string `'grapefruit'`. Here's how you would test that: - -```js -test('the best flavor is grapefruit', () => { - expect(bestLaCroixFlavor()).toBe('grapefruit'); -}); -``` - -In this case, `toBe` is the matcher function. There are a lot of different matcher functions, documented below, to help you test different things. - -The argument to `expect` should be the value that your code produces, and any argument to the matcher should be the correct value. If you mix them up, your tests will still work, but the error messages on failing tests will look strange. - -### `expect.extend(matchers)` - -You can use `expect.extend` to add your own matchers to Jest. For example, let's say that you're testing a number utility library and you're frequently asserting that numbers appear within particular ranges of other numbers. You could abstract that into a `toBeWithinRange` matcher: - -```js -expect.extend({ - toBeWithinRange(received, floor, ceiling) { - const pass = received >= floor && received <= ceiling; - if (pass) { - return { - message: () => - `expected ${received} not to be within range ${floor} - ${ceiling}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be within range ${floor} - ${ceiling}`, - pass: false, - }; - } - }, -}); - -test('numeric ranges', () => { - expect(100).toBeWithinRange(90, 110); - expect(101).not.toBeWithinRange(0, 100); - expect({apples: 6, bananas: 3}).toEqual({ - apples: expect.toBeWithinRange(1, 10), - bananas: expect.not.toBeWithinRange(11, 20), - }); -}); -``` - -_Note_: In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this: - -```ts -interface CustomMatchers { - toBeWithinRange(floor: number, ceiling: number): R; -} - -declare global { - namespace jest { - interface Expect extends CustomMatchers {} - interface Matchers extends CustomMatchers {} - interface InverseAsymmetricMatchers extends CustomMatchers {} - } -} -``` - -#### Async Matchers - -`expect.extend` also supports async matchers. Async matchers return a Promise so you will need to await the returned value. Let's use an example matcher to illustrate the usage of them. We are going to implement a matcher called `toBeDivisibleByExternalValue`, where the divisible number is going to be pulled from an external source. - -```js -expect.extend({ - async toBeDivisibleByExternalValue(received) { - const externalValue = await getExternalValueFromRemoteSource(); - const pass = received % externalValue == 0; - if (pass) { - return { - message: () => - `expected ${received} not to be divisible by ${externalValue}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be divisible by ${externalValue}`, - pass: false, - }; - } - }, -}); - -test('is divisible by external value', async () => { - await expect(100).toBeDivisibleByExternalValue(); - await expect(101).not.toBeDivisibleByExternalValue(); -}); -``` - -#### Custom Matchers API - -Matchers should return an object (or a Promise of an object) with two keys. `pass` indicates whether there was a match or not, and `message` provides a function with no arguments that returns an error message in case of failure. Thus, when `pass` is false, `message` should return the error message for when `expect(x).yourMatcher()` fails. And when `pass` is true, `message` should return the error message for when `expect(x).not.yourMatcher()` fails. - -Matchers are called with the argument passed to `expect(x)` followed by the arguments passed to `.yourMatcher(y, z)`: - -```js -expect.extend({ - yourMatcher(x, y, z) { - return { - pass: true, - message: () => '', - }; - }, -}); -``` - -These helper functions and properties can be found on `this` inside a custom matcher: - -#### `this.isNot` - -A boolean to let you know this matcher was called with the negated `.not` modifier allowing you to display a clear and correct matcher hint (see example code). - -#### `this.promise` - -A string allowing you to display a clear and correct matcher hint: - -- `'rejects'` if matcher was called with the promise `.rejects` modifier -- `'resolves'` if matcher was called with the promise `.resolves` modifier -- `''` if matcher was not called with a promise modifier - -#### `this.equals(a, b)` - -This is a deep-equality function that will return `true` if two objects have the same values (recursively). - -#### `this.expand` - -A boolean to let you know this matcher was called with an `expand` option. When Jest is called with the `--expand` flag, `this.expand` can be used to determine if Jest is expected to show full diffs and errors. - -#### `this.utils` - -There are a number of helpful tools exposed on `this.utils` primarily consisting of the exports from [`jest-matcher-utils`](https://github.com/facebook/jest/tree/main/packages/jest-matcher-utils). - -The most useful ones are `matcherHint`, `printExpected` and `printReceived` to format the error messages nicely. For example, take a look at the implementation for the `toBe` matcher: - -```js -const {diff} = require('jest-diff'); -expect.extend({ - toBe(received, expected) { - const options = { - comment: 'Object.is equality', - isNot: this.isNot, - promise: this.promise, - }; - - const pass = Object.is(received, expected); - - const message = pass - ? () => - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - `Expected: not ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}` - : () => { - const diffString = diff(expected, received, { - expand: this.expand, - }); - return ( - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}`) - ); - }; - - return {actual: received, message, pass}; - }, -}); -``` - -This will print something like this: - -```bash - expect(received).toBe(expected) - - Expected value to be (using Object.is): - "banana" - Received: - "apple" -``` - -When an assertion fails, the error message should give as much signal as necessary to the user so they can resolve their issue quickly. You should craft a precise failure message to make sure users of your custom assertions have a good developer experience. - -#### Custom snapshot matchers - -To use snapshot testing inside of your custom matcher you can import `jest-snapshot` and use it from within your matcher. - -Here's a snapshot matcher that trims a string to store for a given length, `.toMatchTrimmedSnapshot(length)`: - -```js -const {toMatchSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedSnapshot(received, length) { - return toMatchSnapshot.call( - this, - received.substring(0, length), - 'toMatchTrimmedSnapshot', - ); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10); -}); - -/* -Stored snapshot will look like: - -exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`; -*/ -``` - -It's also possible to create custom matchers for inline snapshots, the snapshots will be correctly added to the custom matchers. However, inline snapshot will always try to append to the first argument or the second when the first argument is the property matcher, so it's not possible to accept custom arguments in the custom matchers. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedInlineSnapshot(received, ...rest) { - return toMatchInlineSnapshot.call(this, received.substring(0, 10), ...rest); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot( - `"extra long"` - ); - */ -}); -``` - -#### async - -If your custom inline snapshot matcher is async i.e. uses `async`-`await` you might encounter an error like "Multiple inline snapshots for the same call are not supported". Jest needs additional context information to find where the custom inline snapshot matcher was used to update the snapshots properly. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - async toMatchObservationInlineSnapshot(fn, ...rest) { - // The error (and its stacktrace) must be created before any `await` - this.error = new Error(); - - // The implementation of `observe` doesn't matter. - // It only matters that the custom snapshot matcher is async. - const observation = await observe(async () => { - await fn(); - }); - - return toMatchInlineSnapshot.call(this, recording, ...rest); - }, -}); - -it('observes something', async () => { - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(`"async action"`); - */ -}); -``` - -#### Bail out - -Usually `jest` tries to match every snapshot that is expected in a test. - -Sometimes it might not make sense to continue the test if a prior snapshot failed. For example, when you make snapshots of a state-machine after various transitions you can abort the test once one transition produced the wrong state. - -In that case you can implement a custom snapshot matcher that throws on the first mismatch instead of collecting every mismatch. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchStateInlineSnapshot(...args) { - this.dontThrow = () => {}; - - return toMatchInlineSnapshot.call(this, ...args); - }, -}); - -let state = 'initial'; - -function transition() { - // Typo in the implementation should cause the test to fail - if (state === 'INITIAL') { - state = 'pending'; - } else if (state === 'pending') { - state = 'done'; - } -} - -it('transitions as expected', () => { - expect(state).toMatchStateInlineSnapshot(`"initial"`); - - transition(); - // Already produces a mismatch. No point in continuing the test. - expect(state).toMatchStateInlineSnapshot(`"loading"`); - - transition(); - expect(state).toMatchStateInlineSnapshot(`"done"`); -}); -``` - -### `expect.anything()` - -`expect.anything()` matches anything but `null` or `undefined`. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a non-null argument: - -```js -test('map calls its argument with a non-null argument', () => { - const mock = jest.fn(); - [1].map(x => mock(x)); - expect(mock).toBeCalledWith(expect.anything()); -}); -``` - -### `expect.any(constructor)` - -`expect.any(constructor)` matches anything that was created with the given constructor or if it's a primitive that is of the passed type. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a number: - -```js -class Cat {} -function getCat(fn) { - return fn(new Cat()); -} - -test('randocall calls its callback with a class instance', () => { - const mock = jest.fn(); - getCat(mock); - expect(mock).toBeCalledWith(expect.any(Cat)); -}); - -function randocall(fn) { - return fn(Math.floor(Math.random() * 6 + 1)); -} - -test('randocall calls its callback with a number', () => { - const mock = jest.fn(); - randocall(mock); - expect(mock).toBeCalledWith(expect.any(Number)); -}); -``` - -### `expect.arrayContaining(array)` - -`expect.arrayContaining(array)` matches a received array which contains all of the elements in the expected array. That is, the expected array is a **subset** of the received array. Therefore, it matches a received array which contains elements that are **not** in the expected array. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match a property in `objectContaining` or `toMatchObject` - -```js -describe('arrayContaining', () => { - const expected = ['Alice', 'Bob']; - it('matches even if received contains additional elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected)); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected)); - }); -}); -``` - -```js -describe('Beware of a misunderstanding! A sequence of dice rolls', () => { - const expected = [1, 2, 3, 4, 5, 6]; - it('matches even with an unexpected number 7', () => { - expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match without an expected number 2', () => { - expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.assertions(number)` - -`expect.assertions(number)` verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a function `doAsync` that receives two callbacks `callback1` and `callback2`, it will asynchronously call both of them in an unknown order. We can test this with: - -```js -test('doAsync calls both callbacks', () => { - expect.assertions(2); - function callback1(data) { - expect(data).toBeTruthy(); - } - function callback2(data) { - expect(data).toBeTruthy(); - } - - doAsync(callback1, callback2); -}); -``` - -The `expect.assertions(2)` call ensures that both callbacks actually get called. - -### `expect.hasAssertions()` - -`expect.hasAssertions()` verifies that at least one assertion is called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a few functions that all deal with state. `prepareState` calls a callback with a state object, `validateState` runs on that state object, and `waitOnState` returns a promise that waits until all `prepareState` callbacks complete. We can test this with: - -```js -test('prepareState prepares a valid state', () => { - expect.hasAssertions(); - prepareState(state => { - expect(validateState(state)).toBeTruthy(); - }); - return waitOnState(); -}); -``` - -The `expect.hasAssertions()` call ensures that the `prepareState` callback actually gets called. - -### `expect.not.arrayContaining(array)` - -`expect.not.arrayContaining(array)` matches a received array which does not contain all of the elements in the expected array. That is, the expected array **is not a subset** of the received array. - -It is the inverse of `expect.arrayContaining`. - -```js -describe('not.arrayContaining', () => { - const expected = ['Samantha']; - - it('matches if the actual array does not contain the expected elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual( - expect.not.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.not.objectContaining(object)` - -`expect.not.objectContaining(object)` matches any received object that does not recursively match the expected properties. That is, the expected object **is not a subset** of the received object. Therefore, it matches a received object which contains properties that are **not** in the expected object. - -It is the inverse of `expect.objectContaining`. - -```js -describe('not.objectContaining', () => { - const expected = {foo: 'bar'}; - - it('matches if the actual object does not contain expected key: value pairs', () => { - expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected)); - }); -}); -``` - -### `expect.not.stringContaining(string)` - -`expect.not.stringContaining(string)` matches the received value if it is not a string or if it is a string that does not contain the exact expected string. - -It is the inverse of `expect.stringContaining`. - -```js -describe('not.stringContaining', () => { - const expected = 'Hello world!'; - - it('matches if the received value does not contain the expected substring', () => { - expect('How are you?').toEqual(expect.not.stringContaining(expected)); - }); -}); -``` - -### `expect.not.stringMatching(string | regexp)` - -`expect.not.stringMatching(string | regexp)` matches the received value if it is not a string or if it is a string that does not match the expected string or regular expression. - -It is the inverse of `expect.stringMatching`. - -```js -describe('not.stringMatching', () => { - const expected = /Hello world!/; - - it('matches if the received value does not match the expected regex', () => { - expect('How are you?').toEqual(expect.not.stringMatching(expected)); - }); -}); -``` - -### `expect.objectContaining(object)` - -`expect.objectContaining(object)` matches any received object that recursively matches the expected properties. That is, the expected object is a **subset** of the received object. Therefore, it matches a received object which contains properties that **are present** in the expected object. - -Instead of literal property values in the expected object, you can use matchers, `expect.anything()`, and so on. - -For example, let's say that we expect an `onPress` function to be called with an `Event` object, and all we need to verify is that the event has `event.x` and `event.y` properties. We can do that with: - -```js -test('onPress gets called with the right thing', () => { - const onPress = jest.fn(); - simulatePresses(onPress); - expect(onPress).toBeCalledWith( - expect.objectContaining({ - x: expect.any(Number), - y: expect.any(Number), - }), - ); -}); -``` - -### `expect.stringContaining(string)` - -`expect.stringContaining(string)` matches the received value if it is a string that contains the exact expected string. - -### `expect.stringMatching(string | regexp)` - -`expect.stringMatching(string | regexp)` matches the received value if it is a string that matches the expected string or regular expression. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match an element in `arrayContaining` -- to match a property in `objectContaining` or `toMatchObject` - -This example also shows how you can nest multiple asymmetric matchers, with `expect.stringMatching` inside the `expect.arrayContaining`. - -```js -describe('stringMatching in arrayContaining', () => { - const expected = [ - expect.stringMatching(/^Alic/), - expect.stringMatching(/^[BR]ob/), - ]; - it('matches even if received contains additional elements', () => { - expect(['Alicia', 'Roberto', 'Evelina']).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Roberto', 'Evelina']).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.addSnapshotSerializer(serializer)` - -You can call `expect.addSnapshotSerializer` to add a module that formats application-specific data structures. - -For an individual test file, an added module precedes any modules from `snapshotSerializers` configuration, which precede the default snapshot serializers for built-in JavaScript types and for React elements. The last module added is the first module tested. - -```js -import serializer from 'my-serializer-module'; -expect.addSnapshotSerializer(serializer); - -// affects expect(value).toMatchSnapshot() assertions in the test file -``` - -If you add a snapshot serializer in individual test files instead of adding it to `snapshotSerializers` configuration: - -- You make the dependency explicit instead of implicit. -- You avoid limits to configuration that might cause you to eject from [create-react-app](https://github.com/facebookincubator/create-react-app). - -See [configuring Jest](Configuration.md#snapshotserializers-arraystring) for more information. - -### `.not` - -If you know how to test something, `.not` lets you test its opposite. For example, this code tests that the best La Croix flavor is not coconut: - -```js -test('the best flavor is not coconut', () => { - expect(bestLaCroixFlavor()).not.toBe('coconut'); -}); -``` - -### `.resolves` - -Use `resolves` to unwrap the value of a fulfilled promise so any other matcher can be chained. If the promise is rejected the assertion fails. - -For example, this code tests that the promise resolves and that the resulting value is `'lemon'`: - -```js -test('resolves to lemon', () => { - // make sure to add a return statement - return expect(Promise.resolve('lemon')).resolves.toBe('lemon'); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.resolves`: - -```js -test('resolves to lemon', async () => { - await expect(Promise.resolve('lemon')).resolves.toBe('lemon'); - await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus'); -}); -``` - -### `.rejects` - -Use `.rejects` to unwrap the reason of a rejected promise so any other matcher can be chained. If the promise is fulfilled the assertion fails. - -For example, this code tests that the promise rejects with reason `'octopus'`: - -```js -test('rejects to octopus', () => { - // make sure to add a return statement - return expect(Promise.reject(new Error('octopus'))).rejects.toThrow( - 'octopus', - ); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.rejects`. - -```js -test('rejects to octopus', async () => { - await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); -}); -``` - -### `.toBe(value)` - -Use `.toBe` to compare primitive values or to check referential identity of object instances. It calls `Object.is` to compare values, which is even better for testing than `===` strict equality operator. - -For example, this code will validate some properties of the `can` object: - -```js -const can = { - name: 'pamplemousse', - ounces: 12, -}; - -describe('the can', () => { - test('has 12 ounces', () => { - expect(can.ounces).toBe(12); - }); - - test('has a sophisticated name', () => { - expect(can.name).toBe('pamplemousse'); - }); -}); -``` - -Don't use `.toBe` with floating-point numbers. For example, due to rounding, in JavaScript `0.2 + 0.1` is not strictly equal to `0.3`. If you have floating point numbers, try `.toBeCloseTo` instead. - -Although the `.toBe` matcher **checks** referential identity, it **reports** a deep comparison of values if the assertion fails. If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, to assert whether or not elements are the same instance: - -- rewrite `expect(received).toBe(expected)` as `expect(Object.is(received, expected)).toBe(true)` -- rewrite `expect(received).not.toBe(expected)` as `expect(Object.is(received, expected)).toBe(false)` - -### `.toHaveBeenCalled()` - -Also under the alias: `.toBeCalled()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say you have a `drinkAll(drink, flavour)` function that takes a `drink` function and applies it to all available beverages. You might want to check that `drink` gets called for `'lemon'`, but not for `'octopus'`, because `'octopus'` flavour is really weird and why would anything be octopus-flavoured? You can do that with this test suite: - -```js -function drinkAll(callback, flavour) { - if (flavour !== 'octopus') { - callback(flavour); - } -} - -describe('drinkAll', () => { - test('drinks something lemon-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'lemon'); - expect(drink).toHaveBeenCalled(); - }); - - test('does not drink something octopus-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'octopus'); - expect(drink).not.toHaveBeenCalled(); - }); -}); -``` - -### `.toHaveBeenCalledTimes(number)` - -Also under the alias: `.toBeCalledTimes(number)` - -Use `.toHaveBeenCalledTimes` to ensure that a mock function got called exact number of times. - -For example, let's say you have a `drinkEach(drink, Array)` function that takes a `drink` function and applies it to array of passed beverages. You might want to check that drink function was called exact number of times. You can do that with this test suite: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenCalledTimes(2); -}); -``` - -### `.toHaveBeenCalledWith(arg1, arg2, ...)` - -Also under the alias: `.toBeCalledWith()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say that you can register a beverage with a `register` function, and `applyToAll(f)` should apply the function `f` to all registered beverages. To make sure this works, you could write: - -```js -test('registration applies correctly to orange La Croix', () => { - const beverage = new LaCroix('orange'); - register(beverage); - const f = jest.fn(); - applyToAll(f); - expect(f).toHaveBeenCalledWith(beverage); -}); -``` - -### `.toHaveBeenLastCalledWith(arg1, arg2, ...)` - -Also under the alias: `.lastCalledWith(arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenLastCalledWith` to test what arguments it was last called with. For example, let's say you have a `applyToAllFlavors(f)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the last flavor it operates on is `'mango'`. You can write: - -```js -test('applying to all flavors does mango last', () => { - const drink = jest.fn(); - applyToAllFlavors(drink); - expect(drink).toHaveBeenLastCalledWith('mango'); -}); -``` - -### `.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)` - -Also under the alias: `.nthCalledWith(nthCall, arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenNthCalledWith` to test what arguments it was nth called with. For example, let's say you have a `drinkEach(drink, Array)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the first flavor it operates on is `'lemon'` and the second one is `'octopus'`. You can write: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenNthCalledWith(1, 'lemon'); - expect(drink).toHaveBeenNthCalledWith(2, 'octopus'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveReturned()` - -Also under the alias: `.toReturn()` - -If you have a mock function, you can use `.toHaveReturned` to test that the mock function successfully returned (i.e., did not throw an error) at least one time. For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drinks returns', () => { - const drink = jest.fn(() => true); - - drink(); - - expect(drink).toHaveReturned(); -}); -``` - -### `.toHaveReturnedTimes(number)` - -Also under the alias: `.toReturnTimes(number)` - -Use `.toHaveReturnedTimes` to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. Any calls to the mock function that throw an error are not counted toward the number of times the function returned. - -For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drink returns twice', () => { - const drink = jest.fn(() => true); - - drink(); - drink(); - - expect(drink).toHaveReturnedTimes(2); -}); -``` - -### `.toHaveReturnedWith(value)` - -Also under the alias: `.toReturnWith(value)` - -Use `.toHaveReturnedWith` to ensure that a mock function returned a specific value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix', () => { - const beverage = {name: 'La Croix'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage); - - expect(drink).toHaveReturnedWith('La Croix'); -}); -``` - -### `.toHaveLastReturnedWith(value)` - -Also under the alias: `.lastReturnedWith(value)` - -Use `.toHaveLastReturnedWith` to test the specific value that a mock function last returned. If the last call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix (Orange) last', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveLastReturnedWith('La Croix (Orange)'); -}); -``` - -### `.toHaveNthReturnedWith(nthCall, value)` - -Also under the alias: `.nthReturnedWith(nthCall, value)` - -Use `.toHaveNthReturnedWith` to test the specific value that a mock function returned for the nth call. If the nth call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns expected nth calls', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)'); - expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveLength(number)` - -Use `.toHaveLength` to check that an object has a `.length` property and it is set to a certain numeric value. - -This is especially useful for checking arrays or strings size. - -```js -expect([1, 2, 3]).toHaveLength(3); -expect('abc').toHaveLength(3); -expect('').not.toHaveLength(5); -``` - -### `.toHaveProperty(keyPath, value?)` - -Use `.toHaveProperty` to check if property at provided reference `keyPath` exists for an object. For checking deeply nested properties in an object you may use [dot notation](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors) or an array containing the keyPath for deep references. - -You can provide an optional `value` argument to compare the received property value (recursively for all properties of object instances, also known as deep equality, like the `toEqual` matcher). - -The following example contains a `houseForSale` object with nested properties. We are using `toHaveProperty` to check for the existence and values of various properties in the object. - -```js -// Object containing house features to be tested -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - 'nice.oven': true, - }, - 'ceiling.height': 2, -}; - -test('this house has my desired features', () => { - // Example Referencing - expect(houseForSale).toHaveProperty('bath'); - expect(houseForSale).toHaveProperty('bedrooms', 4); - - expect(houseForSale).not.toHaveProperty('pool'); - - // Deep referencing using dot notation - expect(houseForSale).toHaveProperty('kitchen.area', 20); - expect(houseForSale).toHaveProperty('kitchen.amenities', [ - 'oven', - 'stove', - 'washer', - ]); - - expect(houseForSale).not.toHaveProperty('kitchen.open'); - - // Deep referencing using an array containing the keyPath - expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20); - expect(houseForSale).toHaveProperty( - ['kitchen', 'amenities'], - ['oven', 'stove', 'washer'], - ); - expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven'); - expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']); - expect(houseForSale).not.toHaveProperty(['kitchen', 'open']); - - // Referencing keys with dot in the key itself - expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall'); -}); -``` - -### `.toBeCloseTo(number, numDigits?)` - -Use `toBeCloseTo` to compare floating point numbers for approximate equality. - -The optional `numDigits` argument limits the number of digits to check **after** the decimal point. For the default value `2`, the test criterion is `Math.abs(expected - received) < 0.005` (that is, `10 ** -2 / 2`). - -Intuitive equality comparisons often fail, because arithmetic on decimal (base 10) values often have rounding errors in limited precision binary (base 2) representation. For example, this test fails: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBe(0.3); // Fails! -}); -``` - -It fails because in JavaScript, `0.2 + 0.1` is actually `0.30000000000000004`. - -For example, this test passes with a precision of 5 digits: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBeCloseTo(0.3, 5); -}); -``` - -Because floating point errors are the problem that `toBeCloseTo` solves, it does not support big integer values. - -### `.toBeDefined()` - -Use `.toBeDefined` to check that a variable is not undefined. For example, if you want to check that a function `fetchNewFlavorIdea()` returns _something_, you can write: - -```js -test('there is a new flavor idea', () => { - expect(fetchNewFlavorIdea()).toBeDefined(); -}); -``` - -You could write `expect(fetchNewFlavorIdea()).not.toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeFalsy()` - -Use `.toBeFalsy` when you don't care what a value is and you want to ensure a value is false in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (!getErrors()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `getErrors` returns, specifically - it might return `false`, `null`, or `0`, and your code would still work. So if you want to test there are no errors after drinking some La Croix, you could write: - -```js -test('drinking La Croix does not lead to errors', () => { - drinkSomeLaCroix(); - expect(getErrors()).toBeFalsy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeGreaterThan(number | bigint)` - -Use `toBeGreaterThan` to compare `received > expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of more than 10 ounces: - -```js -test('ounces per can is more than 10', () => { - expect(ouncesPerCan()).toBeGreaterThan(10); -}); -``` - -### `.toBeGreaterThanOrEqual(number | bigint)` - -Use `toBeGreaterThanOrEqual` to compare `received >= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at least 12 ounces: - -```js -test('ounces per can is at least 12', () => { - expect(ouncesPerCan()).toBeGreaterThanOrEqual(12); -}); -``` - -### `.toBeLessThan(number | bigint)` - -Use `toBeLessThan` to compare `received < expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of less than 20 ounces: - -```js -test('ounces per can is less than 20', () => { - expect(ouncesPerCan()).toBeLessThan(20); -}); -``` - -### `.toBeLessThanOrEqual(number | bigint)` - -Use `toBeLessThanOrEqual` to compare `received <= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at most 12 ounces: - -```js -test('ounces per can is at most 12', () => { - expect(ouncesPerCan()).toBeLessThanOrEqual(12); -}); -``` - -### `.toBeInstanceOf(Class)` - -Use `.toBeInstanceOf(Class)` to check that an object is an instance of a class. This matcher uses `instanceof` underneath. - -```js -class A {} - -expect(new A()).toBeInstanceOf(A); -expect(() => {}).toBeInstanceOf(Function); -expect(new A()).toBeInstanceOf(Function); // throws -``` - -### `.toBeNull()` - -`.toBeNull()` is the same as `.toBe(null)` but the error messages are a bit nicer. So use `.toBeNull()` when you want to check that something is null. - -```js -function bloop() { - return null; -} - -test('bloop returns null', () => { - expect(bloop()).toBeNull(); -}); -``` - -### `.toBeTruthy()` - -Use `.toBeTruthy` when you don't care what a value is and you want to ensure a value is true in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (thirstInfo()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `thirstInfo` returns, specifically - it might return `true` or a complex object, and your code would still work. So if you want to test that `thirstInfo` will be truthy after drinking some La Croix, you could write: - -```js -test('drinking La Croix leads to having thirst info', () => { - drinkSomeLaCroix(); - expect(thirstInfo()).toBeTruthy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeUndefined()` - -Use `.toBeUndefined` to check that a variable is undefined. For example, if you want to check that a function `bestDrinkForFlavor(flavor)` returns `undefined` for the `'octopus'` flavor, because there is no good octopus-flavored drink: - -```js -test('the best drink for octopus flavor is undefined', () => { - expect(bestDrinkForFlavor('octopus')).toBeUndefined(); -}); -``` - -You could write `expect(bestDrinkForFlavor('octopus')).toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeNaN()` - -Use `.toBeNaN` when checking a value is `NaN`. - -```js -test('passes when value is NaN', () => { - expect(NaN).toBeNaN(); - expect(1).not.toBeNaN(); -}); -``` - -### `.toContain(item)` - -Use `.toContain` when you want to check that an item is in an array. For testing the items in the array, this uses `===`, a strict equality check. `.toContain` can also check whether a string is a substring of another string. - -For example, if `getAllFlavors()` returns an array of flavors and you want to be sure that `lime` is in there, you can write: - -```js -test('the flavor list contains lime', () => { - expect(getAllFlavors()).toContain('lime'); -}); -``` - -This matcher also accepts others iterables such as strings, sets, node lists and HTML collections. - -### `.toContainEqual(item)` - -Use `.toContainEqual` when you want to check that an item with a specific structure and values is contained in an array. For testing the items in the array, this matcher recursively checks the equality of all fields, rather than checking for object identity. - -```js -describe('my beverage', () => { - test('is delicious and not sour', () => { - const myBeverage = {delicious: true, sour: false}; - expect(myBeverages()).toContainEqual(myBeverage); - }); -}); -``` - -### `.toEqual(value)` - -Use `.toEqual` to compare recursively all properties of object instances (also known as "deep" equality). It calls `Object.is` to compare primitive values, which is even better for testing than `===` strict equality operator. - -For example, `.toEqual` and `.toBe` behave differently in this test suite, so all the tests pass: - -```js -const can1 = { - flavor: 'grapefruit', - ounces: 12, -}; -const can2 = { - flavor: 'grapefruit', - ounces: 12, -}; - -describe('the La Croix cans on my desk', () => { - test('have all the same properties', () => { - expect(can1).toEqual(can2); - }); - test('are not the exact same can', () => { - expect(can1).not.toBe(can2); - }); -}); -``` - -> Note: `.toEqual` won't perform a _deep equality_ check for two errors. Only the `message` property of an Error is considered for equality. It is recommended to use the `.toThrow` matcher for testing against errors. - -If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, use `equals` method of `Buffer` class to assert whether or not buffers contain the same content: - -- rewrite `expect(received).toEqual(expected)` as `expect(received.equals(expected)).toBe(true)` -- rewrite `expect(received).not.toEqual(expected)` as `expect(received.equals(expected)).toBe(false)` - -### `.toMatch(regexp | string)` - -Use `.toMatch` to check that a string matches a regular expression. - -For example, you might not know what exactly `essayOnTheBestFlavor()` returns, but you know it's a really long string, and the substring `grapefruit` should be in there somewhere. You can test this with: - -```js -describe('an essay on the best flavor', () => { - test('mentions grapefruit', () => { - expect(essayOnTheBestFlavor()).toMatch(/grapefruit/); - expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit')); - }); -}); -``` - -This matcher also accepts a string, which it will try to match: - -```js -describe('grapefruits are healthy', () => { - test('grapefruits are a fruit', () => { - expect('grapefruits').toMatch('fruit'); - }); -}); -``` - -### `.toMatchObject(object)` - -Use `.toMatchObject` to check that a JavaScript object matches a subset of the properties of an object. It will match received objects with properties that are **not** in the expected object. - -You can also pass an array of objects, in which case the method will return true only if each object in the received array matches (in the `toMatchObject` sense described above) the corresponding object in the expected array. This is useful if you want to check that two arrays match in their number of elements, as opposed to `arrayContaining`, which allows for extra elements in the received array. - -You can match properties against values or against matchers. - -```js -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - }, -}; -const desiredHouse = { - bath: true, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - wallColor: expect.stringMatching(/white|yellow/), - }, -}; - -test('the house has my desired features', () => { - expect(houseForSale).toMatchObject(desiredHouse); -}); -``` - -```js -describe('toMatchObject applied to arrays', () => { - test('the number of elements must match exactly', () => { - expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]); - }); - - test('.toMatchObject is called for each elements, so extra object properties are okay', () => { - expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([ - {foo: 'bar'}, - {baz: 1}, - ]); - }); -}); -``` - -### `.toMatchSnapshot(propertyMatchers?, hint?)` - -This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -### `.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)` - -Ensures that a value matches the most recent snapshot. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. - -### `.toStrictEqual(value)` - -Use `.toStrictEqual` to test that objects have the same types as well as structure. - -Differences from `.toEqual`: - -- Keys with `undefined` properties are checked. e.g. `{a: undefined, b: 2}` does not match `{b: 2}` when using `.toStrictEqual`. -- Array sparseness is checked. e.g. `[, 1]` does not match `[undefined, 1]` when using `.toStrictEqual`. -- Object types are checked to be equal. e.g. A class instance with fields `a` and `b` will not equal a literal object with fields `a` and `b`. - -```js -class LaCroix { - constructor(flavor) { - this.flavor = flavor; - } -} - -describe('the La Croix cans on my desk', () => { - test('are not semantically the same', () => { - expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'}); - expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'}); - }); -}); -``` - -### `.toThrow(error?)` - -Also under the alias: `.toThrowError(error?)` - -Use `.toThrow` to test that a function throws when it is called. For example, if we want to test that `drinkFlavor('octopus')` throws, because octopus flavor is too disgusting to drink, we could write: - -```js -test('throws on octopus', () => { - expect(() => { - drinkFlavor('octopus'); - }).toThrow(); -}); -``` - -> Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. - -You can provide an optional argument to test that a specific error is thrown: - -- regular expression: error message **matches** the pattern -- string: error message **includes** the substring -- error object: error message is **equal to** the message property of the object -- error class: error object is **instance of** class - -For example, let's say that `drinkFlavor` is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -We could test this error gets thrown in several ways: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - // Test that the error message says "yuck" somewhere: these are equivalent - expect(drinkOctopus).toThrowError(/yuck/); - expect(drinkOctopus).toThrowError('yuck'); - - // Test the exact error message - expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/); - expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor')); - - // Test that we get a DisgustingFlavorError - expect(drinkOctopus).toThrowError(DisgustingFlavorError); -}); -``` - -### `.toThrowErrorMatchingSnapshot(hint?)` - -Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -The test for this function will look this way: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - expect(drinkOctopus).toThrowErrorMatchingSnapshot(); -}); -``` - -And it will generate the following snapshot: - -```js -exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`; -``` - -Check out [React Tree Snapshot Testing](/blog/2016/07/27/jest-14) for more information on snapshot testing. - -### `.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)` - -Use `.toThrowErrorMatchingInlineSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. diff --git a/website/versioned_docs/version-27.1/GettingStarted.md b/website/versioned_docs/version-27.1/GettingStarted.md deleted file mode 100644 index 266a05d817e3..000000000000 --- a/website/versioned_docs/version-27.1/GettingStarted.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -id: getting-started -title: Getting Started ---- - -Install Jest using [`yarn`](https://yarnpkg.com/en/package/jest): - -```bash -yarn add --dev jest -``` - -Or [`npm`](https://www.npmjs.com/package/jest): - -```bash -npm install --save-dev jest -``` - -Note: Jest documentation uses `yarn` commands, but `npm` will also work. You can compare `yarn` and `npm` commands in the [yarn docs, here](https://yarnpkg.com/en/docs/migrating-from-npm#toc-cli-commands-comparison). - -Let's get started by writing a test for a hypothetical function that adds two numbers. First, create a `sum.js` file: - -```javascript -function sum(a, b) { - return a + b; -} -module.exports = sum; -``` - -Then, create a file named `sum.test.js`. This will contain our actual test: - -```javascript -const sum = require('./sum'); - -test('adds 1 + 2 to equal 3', () => { - expect(sum(1, 2)).toBe(3); -}); -``` - -Add the following section to your `package.json`: - -```json -{ - "scripts": { - "test": "jest" - } -} -``` - -Finally, run `yarn test` or `npm run test` and Jest will print this message: - -```bash -PASS ./sum.test.js -✓ adds 1 + 2 to equal 3 (5ms) -``` - -**You just successfully wrote your first test using Jest!** - -This test used `expect` and `toBe` to test that two values were exactly identical. To learn about the other things that Jest can test, see [Using Matchers](UsingMatchers.md). - -## Running from command line - -You can run Jest directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add jest` or `npm install jest --global`) with a variety of useful options. - -Here's how to run Jest on files matching `my-test`, using `config.json` as a configuration file and display a native OS notification after the run: - -```bash -jest my-test --notify --config=config.json -``` - -If you'd like to learn more about running `jest` through the command line, take a look at the [Jest CLI Options](CLI.md) page. - -## Additional Configuration - -### Generate a basic configuration file - -Based on your project, Jest will ask you a few questions and will create a basic configuration file with a short description for each option: - -```bash -jest --init -``` - -### Using Babel - -To use [Babel](https://babeljs.io/), install required dependencies via `yarn`: - -```bash -yarn add --dev babel-jest @babel/core @babel/preset-env -``` - -Configure Babel to target your current version of Node by creating a `babel.config.js` file in the root of your project: - -```javascript title="babel.config.js" -module.exports = { - presets: [['@babel/preset-env', {targets: {node: 'current'}}]], -}; -``` - -_The ideal configuration for Babel will depend on your project._ See [Babel's docs](https://babeljs.io/docs/en/) for more details. - -
Making your Babel config jest-aware - -Jest will set `process.env.NODE_ENV` to `'test'` if it's not set to something else. You can use that in your configuration to conditionally setup only the compilation needed for Jest, e.g. - -```javascript title="babel.config.js" -module.exports = api => { - const isTest = api.env('test'); - // You can use isTest to determine what presets and plugins to use. - - return { - // ... - }; -}; -``` - -> Note: `babel-jest` is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the `transform` configuration option: - -```javascript title="jest.config.js" -module.exports = { - transform: {}, -}; -``` - -
- -### Using webpack - -Jest can be used in projects that use [webpack](https://webpack.js.org/) to manage assets, styles, and compilation. Webpack does offer some unique challenges over other tools. Refer to the [webpack guide](Webpack.md) to get started. - -### Using parcel - -Jest can be used in projects that use [parcel-bundler](https://parceljs.org/) to manage assets, styles, and compilation similar to webpack. Parcel requires zero configuration. Refer to the official [docs](https://parceljs.org/docs/) to get started. - -### Using TypeScript - -#### Via `babel` - -Jest supports TypeScript, via Babel. First, make sure you followed the instructions on [using Babel](#using-babel) above. Next, install the `@babel/preset-typescript` via `yarn`: - -```bash -yarn add --dev @babel/preset-typescript -``` - -Then add `@babel/preset-typescript` to the list of presets in your `babel.config.js`. - -```javascript title="babel.config.js" -module.exports = { - presets: [ - ['@babel/preset-env', {targets: {node: 'current'}}], - // highlight-next-line - '@babel/preset-typescript', - ], -}; -``` - -However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest) instead, or just run the TypeScript compiler [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) separately (or as part of your build process). - -#### Via `ts-jest` - -[ts-jest](https://github.com/kulshekhar/ts-jest) is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript. - -#### Type definitions - -You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. - -> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. - -```bash -yarn add --dev @types/jest -``` diff --git a/website/versioned_docs/version-27.1/JestObjectAPI.md b/website/versioned_docs/version-27.1/JestObjectAPI.md deleted file mode 100644 index de5d9a92a145..000000000000 --- a/website/versioned_docs/version-27.1/JestObjectAPI.md +++ /dev/null @@ -1,686 +0,0 @@ ---- -id: jest-object -title: The Jest Object ---- - -The `jest` object is automatically in scope within every test file. The methods in the `jest` object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via `import {jest} from '@jest/globals'`. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Mock Modules - -### `jest.disableAutomock()` - -Disables automatic mocking in the module loader. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -After this method is called, all `require()`s will return the real versions of each module (rather than a mocked version). - -Jest configuration: - -```json -{ - "automock": true -} -``` - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, -}; -``` - -```js title="__tests__/disableAutomocking.js" -import utils from '../utils'; - -jest.disableAutomock(); - -test('original implementation', () => { - // now we have the original implementation, - // even if we set the automocking in a jest configuration - expect(utils.authorize()).toBe('token'); -}); -``` - -This is usually useful when you have a scenario where the number of dependencies you want to mock is far less than the number of dependencies that you don't. For example, if you're writing a test for a module that uses a large number of dependencies that can be reasonably classified as "implementation details" of the module, then you likely do not want to mock them. - -Examples of dependencies that might be considered "implementation details" are things ranging from language built-ins (e.g. Array.prototype methods) to highly common utility methods (e.g. underscore/lo-dash, array utilities, etc) and entire libraries like React.js. - -Returns the `jest` object for chaining. - -_Note: this method was previously called `autoMockOff`. When using `babel-jest`, calls to `disableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOff` if you want to explicitly avoid this behavior._ - -### `jest.enableAutomock()` - -Enables automatic mocking in the module loader. - -Returns the `jest` object for chaining. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/enableAutomocking.js" -jest.enableAutomock(); - -import utils from '../utils'; - -test('original implementation', () => { - // now we have the mocked implementation, - expect(utils.authorize._isMockFunction).toBeTruthy(); - expect(utils.isAuthorized._isMockFunction).toBeTruthy(); -}); -``` - -_Note: this method was previously called `autoMockOn`. When using `babel-jest`, calls to `enableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOn` if you want to explicitly avoid this behavior._ - -### `jest.createMockFromModule(moduleName)` - -##### renamed in Jest **26.0.0+** - -Also under the alias: `.genMockFromModule(moduleName)` - -Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you. - -This is useful when you want to create a [manual mock](ManualMocks.md) that extends the automatic mock's behavior. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/createMockFromModule.test.js" -const utils = jest.createMockFromModule('../utils').default; -utils.isAuthorized = jest.fn(secret => secret === 'not wizard'); - -test('implementation created by jest.createMockFromModule', () => { - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized('not wizard')).toEqual(true); -}); -``` - -This is how `createMockFromModule` will mock the following data types: - -#### `Function` - -Creates a new [mock function](mock-functions). The new function has no formal parameters and when called will return `undefined`. This functionality also applies to `async` functions. - -#### `Class` - -Creates a new class. The interface of the original class is maintained, all of the class member functions and properties will be mocked. - -#### `Object` - -Creates a new deeply cloned object. The object keys are maintained and their values are mocked. - -#### `Array` - -Creates a new empty array, ignoring the original. - -#### `Primitives` - -Creates a new property with the same primitive value as the original property. - -Example: - -```js title="example.js" -module.exports = { - function: function square(a, b) { - return a * b; - }, - asyncFunction: async function asyncSquare(a, b) { - const result = (await a) * b; - return result; - }, - class: new (class Bar { - constructor() { - this.array = [1, 2, 3]; - } - foo() {} - })(), - object: { - baz: 'foo', - bar: { - fiz: 1, - buzz: [1, 2, 3], - }, - }, - array: [1, 2, 3], - number: 123, - string: 'baz', - boolean: true, - symbol: Symbol.for('a.b.c'), -}; -``` - -```js title="__tests__/example.test.js" -const example = jest.createMockFromModule('./example'); - -test('should run example code', () => { - // creates a new mocked function with no formal arguments. - expect(example.function.name).toEqual('square'); - expect(example.function.length).toEqual(0); - - // async functions get the same treatment as standard synchronous functions. - expect(example.asyncFunction.name).toEqual('asyncSquare'); - expect(example.asyncFunction.length).toEqual(0); - - // creates a new class with the same interface, member functions and properties are mocked. - expect(example.class.constructor.name).toEqual('Bar'); - expect(example.class.foo.name).toEqual('foo'); - expect(example.class.array.length).toEqual(0); - - // creates a deeply cloned version of the original object. - expect(example.object).toEqual({ - baz: 'foo', - bar: { - fiz: 1, - buzz: [], - }, - }); - - // creates a new empty array, ignoring the original array. - expect(example.array.length).toEqual(0); - - // creates a new property with the same primitive value as the original property. - expect(example.number).toEqual(123); - expect(example.string).toEqual('baz'); - expect(example.boolean).toEqual(true); - expect(example.symbol).toEqual(Symbol.for('a.b.c')); -}); -``` - -### `jest.mock(moduleName, factory, options)` - -Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example: - -```js title="banana.js" -module.exports = () => 'banana'; -``` - -```js title="__tests__/test.js" -jest.mock('../banana'); - -const banana = require('../banana'); // banana will be explicitly mocked. - -banana(); // will return 'undefined' because the function is auto-mocked. -``` - -The second argument can be used to specify an explicit module factory that is being run instead of using Jest's automocking feature: - -```js -jest.mock('../moduleName', () => { - return jest.fn(() => 42); -}); - -// This runs the function specified as second argument to `jest.mock`. -const moduleName = require('../moduleName'); -moduleName(); // Will return '42'; -``` - -When using the `factory` parameter for an ES6 module with a default export, the `__esModule: true` property needs to be specified. This property is normally generated by Babel / TypeScript, but here it needs to be set manually. When importing a default export, it's an instruction to import the property named `default` from the export object: - -```js -import moduleName, {foo} from '../moduleName'; - -jest.mock('../moduleName', () => { - return { - __esModule: true, - default: jest.fn(() => 42), - foo: jest.fn(() => 43), - }; -}); - -moduleName(); // Will return 42 -foo(); // Will return 43 -``` - -The third argument can be used to create virtual mocks – mocks of modules that don't exist anywhere in the system: - -```js -jest.mock( - '../moduleName', - () => { - /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ - }, - {virtual: true}, -); -``` - -> **Warning:** Importing a module in a setup file (as specified by `setupFilesAfterEnv`) will prevent mocking for the module in question, as well as all the modules that it imports. - -Modules that are mocked with `jest.mock` are mocked only for the file that calls `jest.mock`. Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module. - -Returns the `jest` object for chaining. - -### `jest.unmock(moduleName)` - -Indicates that the module system should never return a mocked version of the specified module from `require()` (e.g. that it should always return the real module). - -The most common use of this API is for specifying the module a given test intends to be testing (and thus doesn't want automatically mocked). - -Returns the `jest` object for chaining. - -### `jest.doMock(moduleName, factory, options)` - -When using `babel-jest`, calls to `mock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -One example when this is useful is when you want to mock a module differently within the same file: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 1); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(1); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 2); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(2); -}); -``` - -Using `jest.doMock()` with ES6 imports requires additional steps. Follow these if you don't want to use `require` in your tests: - -- We have to specify the `__esModule: true` property (see the [`jest.mock()`](#jestmockmodulename-factory-options) API for more information). -- Static ES6 module imports are hoisted to the top of the file, so instead we have to import them dynamically using `import()`. -- Finally, we need an environment which supports dynamic importing. Please see [Using Babel](GettingStarted.md#using-babel) for the initial setup. Then add the plugin [babel-plugin-dynamic-import-node](https://www.npmjs.com/package/babel-plugin-dynamic-import-node), or an equivalent, to your Babel config to enable dynamic importing in Node. - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default1', - foo: 'foo1', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default1'); - expect(moduleName.foo).toEqual('foo1'); - }); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default2', - foo: 'foo2', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default2'); - expect(moduleName.foo).toEqual('foo2'); - }); -}); -``` - -Returns the `jest` object for chaining. - -### `jest.dontMock(moduleName)` - -When using `babel-jest`, calls to `unmock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -Returns the `jest` object for chaining. - -### `jest.setMock(moduleName, moduleExports)` - -Explicitly supplies the mock object that the module system should return for the specified module. - -On occasion, there are times where the automatically generated mock the module system would normally provide you isn't adequate enough for your testing needs. Normally under those circumstances you should write a [manual mock](ManualMocks.md) that is more adequate for the module in question. However, on extremely rare occasions, even a manual mock isn't suitable for your purposes and you need to build the mock yourself inside your test. - -In these rare scenarios you can use this API to manually fill the slot in the module system's mock-module registry. - -Returns the `jest` object for chaining. - -_Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object._ - -### `jest.requireActual(moduleName)` - -Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not. - -Example: - -```js -jest.mock('../myModule', () => { - // Require the original module to not be mocked... - const originalModule = jest.requireActual('../myModule'); - - return { - __esModule: true, // Use it when dealing with esModules - ...originalModule, - getRandom: jest.fn().mockReturnValue(10), - }; -}); - -const getRandom = require('../myModule').getRandom; - -getRandom(); // Always returns 10 -``` - -### `jest.requireMock(moduleName)` - -Returns a mock module instead of the actual module, bypassing all checks on whether the module should be required normally or not. - -### `jest.resetModules()` - -Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests. - -Example: - -```js -const sum1 = require('../sum'); -jest.resetModules(); -const sum2 = require('../sum'); -sum1 === sum2; -// > false (Both sum modules are separate "instances" of the sum module.) -``` - -Example in a test: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('works', () => { - const sum = require('../sum'); -}); - -test('works too', () => { - const sum = require('../sum'); - // sum is a different copy of the sum module from the previous test. -}); -``` - -Returns the `jest` object for chaining. - -### `jest.isolateModules(fn)` - -`jest.isolateModules(fn)` goes a step further than `jest.resetModules()` and creates a sandbox registry for the modules that are loaded inside the callback function. This is useful to isolate specific modules for every test so that local module state doesn't conflict between tests. - -```js -let myModule; -jest.isolateModules(() => { - myModule = require('myModule'); -}); - -const otherCopyOfMyModule = require('myModule'); -``` - -## Mock Functions - -### `jest.fn(implementation)` - -Returns a new, unused [mock function](MockFunctionAPI.md). Optionally takes a mock implementation. - -```js -const mockFn = jest.fn(); -mockFn(); -expect(mockFn).toHaveBeenCalled(); - -// With a mock implementation: -const returnsTrue = jest.fn(() => true); -console.log(returnsTrue()); // true; -``` - -### `jest.isMockFunction(fn)` - -Determines if the given function is a mocked function. - -### `jest.spyOn(object, methodName)` - -Creates a mock function similar to `jest.fn` but also tracks calls to `object[methodName]`. Returns a Jest [mock function](MockFunctionAPI.md). - -_Note: By default, `jest.spyOn` also calls the **spied** method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use `jest.spyOn(object, methodName).mockImplementation(() => customImplementation)` or `object[methodName] = jest.fn(() => customImplementation);`_ - -Example: - -```js -const video = { - play() { - return true; - }, -}; - -module.exports = video; -``` - -Example test: - -```js -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play'); - const isPlaying = video.play(); - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); -``` - -### `jest.spyOn(object, methodName, accessType?)` - -Since Jest 22.1.0+, the `jest.spyOn` method takes an optional third argument of `accessType` that can be either `'get'` or `'set'`, which proves to be useful when you want to spy on a getter or a setter, respectively. - -Example: - -```js -const video = { - // it's a getter! - get play() { - return true; - }, -}; - -module.exports = video; - -const audio = { - _volume: false, - // it's a setter! - set volume(value) { - this._volume = value; - }, - get volume() { - return this._volume; - }, -}; - -module.exports = audio; -``` - -Example test: - -```js -const audio = require('./audio'); -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play', 'get'); // we pass 'get' - const isPlaying = video.play; - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); - -test('plays audio', () => { - const spy = jest.spyOn(audio, 'volume', 'set'); // we pass 'set' - audio.volume = 100; - - expect(spy).toHaveBeenCalled(); - expect(audio.volume).toBe(100); - - spy.mockRestore(); -}); -``` - -### `jest.clearAllMocks()` - -Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.resetAllMocks()` - -Resets the state of all mocks. Equivalent to calling [`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.restoreAllMocks()` - -Restores all mocks back to their original value. Equivalent to calling [`.mockRestore()`](MockFunctionAPI.md#mockfnmockrestore) on every mocked function. Beware that `jest.restoreAllMocks()` only works when the mock was created with `jest.spyOn`; other mocks will require you to manually restore them. - -## Mock Timers - -### `jest.useFakeTimers(implementation?: 'modern' | 'legacy')` - -Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate` as well as `Date`). - -If you pass `'legacy'` as an argument, Jest's legacy implementation will be used rather than one based on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -Returns the `jest` object for chaining. - -### `jest.useRealTimers()` - -Instructs Jest to use the real versions of the standard timer functions. - -Returns the `jest` object for chaining. - -### `jest.runAllTicks()` - -Exhausts the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending micro-tasks that have been queued via `process.nextTick` will be executed. Additionally, if those micro-tasks themselves schedule new micro-tasks, those will be continually exhausted until there are no more micro-tasks remaining in the queue. - -### `jest.runAllTimers()` - -Exhausts both the **macro**-task queue (i.e., all tasks queued by `setTimeout()`, `setInterval()`, and `setImmediate()`) and the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending macro-tasks and micro-tasks will be executed. If those tasks themselves schedule new tasks, those will be continually exhausted until there are no more tasks remaining in the queue. - -This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information. - -### `jest.runAllImmediates()` - -Exhausts all tasks queued by `setImmediate()`. - -> Note: This function is not available when using modern fake timers implementation - -### `jest.advanceTimersByTime(msToRun)` - -Executes only the macro task queue (i.e. all tasks queued by `setTimeout()` or `setInterval()` and `setImmediate()`). - -When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()`, and would be executed within this time frame will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue, that should be run within `msToRun` milliseconds. - -### `jest.runOnlyPendingTimers()` - -Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by `setTimeout()` or `setInterval()` up to this point). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call. - -This is useful for scenarios such as one where the module being tested schedules a `setTimeout()` whose callback schedules another `setTimeout()` recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time. - -### `jest.advanceTimersToNextTimer(steps)` - -Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run. - -Optionally, you can provide `steps`, so it will run `steps` amount of next timeouts/intervals. - -### `jest.clearAllTimers()` - -Removes any pending timers from the timer system. - -This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future. - -### `jest.getTimerCount()` - -Returns the number of fake timers still left to run. - -### `jest.setSystemTime(now?: number | Date)` - -Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`. - -> Note: This function is only available when using modern fake timers implementation - -### `jest.getRealSystemTime()` - -When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function. - -> Note: This function is only available when using modern fake timers implementation - -## Misc - -### `jest.setTimeout(timeout)` - -Set the default timeout interval for tests and before/after hooks in milliseconds. This only affects the test file from which this function is called. - -_Note: The default timeout interval is 5 seconds if this method is not called._ - -_Note: If you want to set the timeout for all test files, a good place to do this is in `setupFilesAfterEnv`._ - -Example: - -```js -jest.setTimeout(1000); // 1 second -``` - -### `jest.retryTimes()` - -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! This must live at the top-level of a test file or in a describe block. Retries _will not_ work if `jest.retryTimes()` is called in a `beforeEach` or a `test` block. - -Example in a test: - -```js -jest.retryTimes(3); -test('will fail', () => { - expect(true).toBe(false); -}); -``` - -Returns the `jest` object for chaining. diff --git a/website/versioned_docs/version-27.1/MockFunctionAPI.md b/website/versioned_docs/version-27.1/MockFunctionAPI.md deleted file mode 100644 index 5bb3925c81a1..000000000000 --- a/website/versioned_docs/version-27.1/MockFunctionAPI.md +++ /dev/null @@ -1,448 +0,0 @@ ---- -id: mock-function-api -title: Mock Functions ---- - -Mock functions are also known as "spies", because they let you spy on the behavior of a function that is called indirectly by some other code, rather than only testing the output. You can create a mock function with `jest.fn()`. If no implementation is given, the mock function will return `undefined` when invoked. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `mockFn.getMockName()` - -Returns the mock name string set by calling `mockFn.mockName(value)`. - -### `mockFn.mock.calls` - -An array containing the call arguments of all calls that have been made to this mock function. Each item in the array is an array of arguments that were passed during the call. - -For example: A mock function `f` that has been called twice, with the arguments `f('arg1', 'arg2')`, and then with the arguments `f('arg3', 'arg4')`, would have a `mock.calls` array that looks like this: - -```js -[ - ['arg1', 'arg2'], - ['arg3', 'arg4'], -]; -``` - -### `mockFn.mock.results` - -An array containing the results of all calls that have been made to this mock function. Each entry in this array is an object containing a `type` property, and a `value` property. `type` will be one of the following: - -- `'return'` - Indicates that the call completed by returning normally. -- `'throw'` - Indicates that the call completed by throwing a value. -- `'incomplete'` - Indicates that the call has not yet completed. This occurs if you test the result from within the mock function itself, or from within a function that was called by the mock. - -The `value` property contains the value that was thrown or returned. `value` is undefined when `type === 'incomplete'`. - -For example: A mock function `f` that has been called three times, returning `'result1'`, throwing an error, and then returning `'result2'`, would have a `mock.results` array that looks like this: - -```js -[ - { - type: 'return', - value: 'result1', - }, - { - type: 'throw', - value: { - /* Error instance */ - }, - }, - { - type: 'return', - value: 'result2', - }, -]; -``` - -### `mockFn.mock.instances` - -An array that contains all the object instances that have been instantiated from this mock function using `new`. - -For example: A mock function that has been instantiated twice would have the following `mock.instances` array: - -```js -const mockFn = jest.fn(); - -const a = new mockFn(); -const b = new mockFn(); - -mockFn.mock.instances[0] === a; // true -mockFn.mock.instances[1] === b; // true -``` - -### `mockFn.mockClear()` - -Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions. - -Beware that `mockClear` will replace `mockFn.mock`, not just these three properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data. - -The [`clearMocks`](configuration#clearmocks-boolean) configuration option is available to clear mocks automatically before each tests. - -### `mockFn.mockReset()` - -Does everything that [`mockFn.mockClear()`](#mockfnmockclear) does, and also removes any mocked return values or implementations. - -This is useful when you want to completely reset a _mock_ back to its initial state. (Note that resetting a _spy_ will result in a function with no return value). - -The [`mockReset`](configuration#resetmocks-boolean) configuration option is available to reset mocks automatically before each test. - -### `mockFn.mockRestore()` - -Does everything that [`mockFn.mockReset()`](#mockfnmockreset) does, and also restores the original (non-mocked) implementation. - -This is useful when you want to mock functions in certain test cases and restore the original implementation in others. - -Beware that `mockFn.mockRestore` only works when the mock was created with `jest.spyOn`. Thus you have to take care of restoration yourself when manually assigning `jest.fn()`. - -The [`restoreMocks`](configuration#restoremocks-boolean) configuration option is available to restore mocks automatically before each test. - -### `mockFn.mockImplementation(fn)` - -Accepts a function that should be used as the implementation of the mock. The mock itself will still record all calls that go into and instances that come from itself – the only difference is that the implementation will also be executed when the mock is called. - -_Note: `jest.fn(implementation)` is a shorthand for `jest.fn().mockImplementation(implementation)`._ - -For example: - -```js -const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar); -// or: jest.fn(scalar => 42 + scalar); - -const a = mockFn(0); -const b = mockFn(1); - -a === 42; // true -b === 43; // true - -mockFn.mock.calls[0][0] === 0; // true -mockFn.mock.calls[1][0] === 1; // true -``` - -`mockImplementation` can also be used to mock class constructors: - -```js title="SomeClass.js" -module.exports = class SomeClass { - m(a, b) {} -}; -``` - -```js title="OtherModule.test.js" -jest.mock('./SomeClass'); // this happens automatically with automocking -const SomeClass = require('./SomeClass'); -const mMock = jest.fn(); -SomeClass.mockImplementation(() => { - return { - m: mMock, - }; -}); - -const some = new SomeClass(); -some.m('a', 'b'); -console.log('Calls to m: ', mMock.mock.calls); -``` - -### `mockFn.mockImplementationOnce(fn)` - -Accepts a function that will be used as an implementation of the mock for one call to the mocked function. Can be chained so that multiple function calls produce different results. - -```js -const myMockFn = jest - .fn() - .mockImplementationOnce(cb => cb(null, true)) - .mockImplementationOnce(cb => cb(null, false)); - -myMockFn((err, val) => console.log(val)); // true - -myMockFn((err, val) => console.log(val)); // false -``` - -When the mocked function runs out of implementations defined with mockImplementationOnce, it will execute the default implementation set with `jest.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called: - -```js -const myMockFn = jest - .fn(() => 'default') - .mockImplementationOnce(() => 'first call') - .mockImplementationOnce(() => 'second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockName(value)` - -Accepts a string to use in test result output in place of "jest.fn()" to indicate which mock function is being referenced. - -For example: - -```js -const mockFn = jest.fn().mockName('mockedFunction'); -// mockFn(); -expect(mockFn).toHaveBeenCalled(); -``` - -Will result in this error: - -``` -expect(mockedFunction).toHaveBeenCalled() - -Expected mock function "mockedFunction" to have been called, but it was not called. -``` - -### `mockFn.mockReturnThis()` - -Syntactic sugar function for: - -```js -jest.fn(function () { - return this; -}); -``` - -### `mockFn.mockReturnValue(value)` - -Accepts a value that will be returned whenever the mock function is called. - -```js -const mock = jest.fn(); -mock.mockReturnValue(42); -mock(); // 42 -mock.mockReturnValue(43); -mock(); // 43 -``` - -### `mockFn.mockReturnValueOnce(value)` - -Accepts a value that will be returned for one call to the mock function. Can be chained so that successive calls to the mock function return different values. When there are no more `mockReturnValueOnce` values to use, calls will return a value specified by `mockReturnValue`. - -```js -const myMockFn = jest - .fn() - .mockReturnValue('default') - .mockReturnValueOnce('first call') - .mockReturnValueOnce('second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockResolvedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.resolve(value)); -``` - -Useful to mock async functions in async tests: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockResolvedValue(43); - - await asyncMock(); // 43 -}); -``` - -### `mockFn.mockResolvedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.resolve(value)); -``` - -Useful to resolve different values over multiple async calls: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValue('default') - .mockResolvedValueOnce('first call') - .mockResolvedValueOnce('second call'); - - await asyncMock(); // first call - await asyncMock(); // second call - await asyncMock(); // default - await asyncMock(); // default -}); -``` - -### `mockFn.mockRejectedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.reject(value)); -``` - -Useful to create async mock functions that will always reject: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); - - await asyncMock(); // throws "Async error" -}); -``` - -### `mockFn.mockRejectedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.reject(value)); -``` - -Example usage: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValueOnce('first call') - .mockRejectedValueOnce(new Error('Async error')); - - await asyncMock(); // first call - await asyncMock(); // throws "Async error" -}); -``` - -## TypeScript - -Jest itself is written in [TypeScript](https://www.typescriptlang.org). - -If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. - -Otherwise, please see our [Getting Started](GettingStarted.md#using-typescript) guide for to get setup with TypeScript. - -You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/main/examples/typescript). - -### `jest.MockedFunction` - -> `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](MockFunctions.md). - -You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. - -Example using [automatic `jest.mock`](JestObjectAPI.md#jestmockmodulename-factory-options): - -```ts -// Assume `add` is imported and used within `calculate`. -import add from './add'; -import calculate from './calc'; - -jest.mock('./add'); - -// Our mock of `add` is now fully typed -const mockAdd = add as jest.MockedFunction; - -test('calculate calls add', () => { - calculate('Add', 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -Example using [`jest.fn`](JestObjectAPI.md#jestfnimplementation): - -```ts -// Here `add` is imported for its type -import add from './add'; -import calculate from './calc'; - -test('calculate calls add', () => { - // Create a new mock that can be used in place of `add`. - const mockAdd = jest.fn() as jest.MockedFunction; - - // Note: You can use the `jest.fn` type directly like this if you want: - // const mockAdd = jest.fn, Parameters>(); - // `jest.MockedFunction` is a more friendly shortcut. - - // Now we can easily set up mock implementations. - // All the `.mock*` API can now give you proper types for `add`. - // https://jestjs.io/docs/mock-function-api - - // `.mockImplementation` can now infer that `a` and `b` are `number` - // and that the returned value is a `number`. - mockAdd.mockImplementation((a, b) => { - // Yes, this mock is still adding two numbers but imagine this - // was a complex function we are mocking. - return a + b; - }); - - // `mockAdd` is properly typed and therefore accepted by - // anything requiring `add`. - calculate(mockAdd, 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -### `jest.MockedClass` - -> `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](Es6ClassMocks.md). - -You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. - -Converting the [ES6 Class automatic mock example](Es6ClassMocks.md#automatic-mock) would look like this: - -```ts -import SoundPlayer from '../sound-player'; -import SoundPlayerConsumer from '../sound-player-consumer'; - -jest.mock('../sound-player'); // SoundPlayer is now a mock constructor - -const SoundPlayerMock = SoundPlayer as jest.MockedClass; - -beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - SoundPlayerMock.mockClear(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - // Show that mockClear() is working: - expect(SoundPlayerMock).not.toHaveBeenCalled(); - - const soundPlayerConsumer = new SoundPlayerConsumer(); - // Constructor should have been called again: - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); - - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - - // mock.instances is available with automatic mocks: - const mockSoundPlayerInstance = SoundPlayerMock.mock.instances[0]; - - // However, it will not allow access to `.mock` in TypeScript as it - // is returning `SoundPlayer`. Instead, you can check the calls to a - // method like this fully typed: - expect(SoundPlayerMock.prototype.playSoundFile.mock.calls[0][0]).toEqual( - coolSoundFileName, - ); - // Equivalent to above check: - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledWith( - coolSoundFileName, - ); - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledTimes(1); -}); -``` diff --git a/website/versioned_docs/version-27.1/MockFunctions.md b/website/versioned_docs/version-27.1/MockFunctions.md deleted file mode 100644 index 2e686a92e837..000000000000 --- a/website/versioned_docs/version-27.1/MockFunctions.md +++ /dev/null @@ -1,315 +0,0 @@ ---- -id: mock-functions -title: Mock Functions ---- - -Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with `new`, and allowing test-time configuration of return values. - -There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a [`manual mock`](ManualMocks.md) to override a module dependency. - -## Using a mock function - -Let's imagine we're testing an implementation of a function `forEach`, which invokes a callback for each item in a supplied array. - -```javascript -function forEach(items, callback) { - for (let index = 0; index < items.length; index++) { - callback(items[index]); - } -} -``` - -To test this function, we can use a mock function, and inspect the mock's state to ensure the callback is invoked as expected. - -```javascript -const mockCallback = jest.fn(x => 42 + x); -forEach([0, 1], mockCallback); - -// The mock function is called twice -expect(mockCallback.mock.calls.length).toBe(2); - -// The first argument of the first call to the function was 0 -expect(mockCallback.mock.calls[0][0]).toBe(0); - -// The first argument of the second call to the function was 1 -expect(mockCallback.mock.calls[1][0]).toBe(1); - -// The return value of the first call to the function was 42 -expect(mockCallback.mock.results[0].value).toBe(42); -``` - -## `.mock` property - -All mock functions have this special `.mock` property, which is where data about how the function has been called and what the function returned is kept. The `.mock` property also tracks the value of `this` for each call, so it is possible to inspect this as well: - -```javascript -const myMock = jest.fn(); - -const a = new myMock(); -const b = {}; -const bound = myMock.bind(b); -bound(); - -console.log(myMock.mock.instances); -// > [
, ] -``` - -These mock members are very useful in tests to assert how these functions get called, instantiated, or what they returned: - -```javascript -// The function was called exactly once -expect(someMockFunction.mock.calls.length).toBe(1); - -// The first arg of the first call to the function was 'first arg' -expect(someMockFunction.mock.calls[0][0]).toBe('first arg'); - -// The second arg of the first call to the function was 'second arg' -expect(someMockFunction.mock.calls[0][1]).toBe('second arg'); - -// The return value of the first call to the function was 'return value' -expect(someMockFunction.mock.results[0].value).toBe('return value'); - -// This function was instantiated exactly twice -expect(someMockFunction.mock.instances.length).toBe(2); - -// The object returned by the first instantiation of this function -// had a `name` property whose value was set to 'test' -expect(someMockFunction.mock.instances[0].name).toEqual('test'); -``` - -## Mock Return Values - -Mock functions can also be used to inject test values into your code during a test: - -```javascript -const myMock = jest.fn(); -console.log(myMock()); -// > undefined - -myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true); - -console.log(myMock(), myMock(), myMock(), myMock()); -// > 10, 'x', true, true -``` - -Mock functions are also very effective in code that uses a functional continuation-passing style. Code written in this style helps avoid the need for complicated stubs that recreate the behavior of the real component they're standing in for, in favor of injecting values directly into the test right before they're used. - -```javascript -const filterTestFn = jest.fn(); - -// Make the mock return `true` for the first call, -// and `false` for the second call -filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false); - -const result = [11, 12].filter(num => filterTestFn(num)); - -console.log(result); -// > [11] -console.log(filterTestFn.mock.calls[0][0]); // 11 -console.log(filterTestFn.mock.calls[1][0]); // 12 -``` - -Most real-world examples actually involve getting ahold of a mock function on a dependent component and configuring that, but the technique is the same. In these cases, try to avoid the temptation to implement logic inside of any function that's not directly being tested. - -## Mocking Modules - -Suppose we have a class that fetches users from our API. The class uses [axios](https://github.com/axios/axios) to call the API then returns the `data` attribute which contains all the users: - -```js title="users.js" -import axios from 'axios'; - -class Users { - static all() { - return axios.get('/users.json').then(resp => resp.data); - } -} - -export default Users; -``` - -Now, in order to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the `jest.mock(...)` function to automatically mock the axios module. - -Once we mock the module we can provide a `mockResolvedValue` for `.get` that returns the data we want our test to assert against. In effect, we are saying that we want `axios.get('/users.json')` to return a fake response. - -```js title="users.test.js" -import axios from 'axios'; -import Users from './users'; - -jest.mock('axios'); - -test('should fetch users', () => { - const users = [{name: 'Bob'}]; - const resp = {data: users}; - axios.get.mockResolvedValue(resp); - - // or you could use the following depending on your use case: - // axios.get.mockImplementation(() => Promise.resolve(resp)) - - return Users.all().then(data => expect(data).toEqual(users)); -}); -``` - -## Mocking Partials - -Subsets of a module can be mocked and the rest of the module can keep their actual implementation: - -```js title="foo-bar-baz.js" -export const foo = 'foo'; -export const bar = () => 'bar'; -export default () => 'baz'; -``` - -```js -//test.js -import defaultExport, {bar, foo} from '../foo-bar-baz'; - -jest.mock('../foo-bar-baz', () => { - const originalModule = jest.requireActual('../foo-bar-baz'); - - //Mock the default export and named export 'foo' - return { - __esModule: true, - ...originalModule, - default: jest.fn(() => 'mocked baz'), - foo: 'mocked foo', - }; -}); - -test('should do a partial mock', () => { - const defaultExportResult = defaultExport(); - expect(defaultExportResult).toBe('mocked baz'); - expect(defaultExport).toHaveBeenCalled(); - - expect(foo).toBe('mocked foo'); - expect(bar()).toBe('bar'); -}); -``` - -## Mock Implementations - -Still, there are cases where it's useful to go beyond the ability to specify return values and full-on replace the implementation of a mock function. This can be done with `jest.fn` or the `mockImplementationOnce` method on mock functions. - -```javascript -const myMockFn = jest.fn(cb => cb(null, true)); - -myMockFn((err, val) => console.log(val)); -// > true -``` - -The `mockImplementation` method is useful when you need to define the default implementation of a mock function that is created from another module: - -```js title="foo.js" -module.exports = function () { - // some implementation; -}; -``` - -```js title="test.js" -jest.mock('../foo'); // this happens automatically with automocking -const foo = require('../foo'); - -// foo is a mock function -foo.mockImplementation(() => 42); -foo(); -// > 42 -``` - -When you need to recreate a complex behavior of a mock function such that multiple function calls produce different results, use the `mockImplementationOnce` method: - -```javascript -const myMockFn = jest - .fn() - .mockImplementationOnce(cb => cb(null, true)) - .mockImplementationOnce(cb => cb(null, false)); - -myMockFn((err, val) => console.log(val)); -// > true - -myMockFn((err, val) => console.log(val)); -// > false -``` - -When the mocked function runs out of implementations defined with `mockImplementationOnce`, it will execute the default implementation set with `jest.fn` (if it is defined): - -```javascript -const myMockFn = jest - .fn(() => 'default') - .mockImplementationOnce(() => 'first call') - .mockImplementationOnce(() => 'second call'); - -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -// > 'first call', 'second call', 'default', 'default' -``` - -For cases where we have methods that are typically chained (and thus always need to return `this`), we have a sugary API to simplify this in the form of a `.mockReturnThis()` function that also sits on all mocks: - -```javascript -const myObj = { - myMethod: jest.fn().mockReturnThis(), -}; - -// is the same as - -const otherObj = { - myMethod: jest.fn(function () { - return this; - }), -}; -``` - -## Mock Names - -You can optionally provide a name for your mock functions, which will be displayed instead of "jest.fn()" in the test error output. Use this if you want to be able to quickly identify the mock function reporting an error in your test output. - -```javascript -const myMockFn = jest - .fn() - .mockReturnValue('default') - .mockImplementation(scalar => 42 + scalar) - .mockName('add42'); -``` - -## Custom Matchers - -Finally, in order to make it less demanding to assert how mock functions have been called, we've added some custom matcher functions for you: - -```javascript -// The mock function was called at least once -expect(mockFunc).toHaveBeenCalled(); - -// The mock function was called at least once with the specified args -expect(mockFunc).toHaveBeenCalledWith(arg1, arg2); - -// The last call to the mock function was called with the specified args -expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2); - -// All calls and the name of the mock is written as a snapshot -expect(mockFunc).toMatchSnapshot(); -``` - -These matchers are sugar for common forms of inspecting the `.mock` property. You can always do this manually yourself if that's more to your taste or if you need to do something more specific: - -```javascript -// The mock function was called at least once -expect(mockFunc.mock.calls.length).toBeGreaterThan(0); - -// The mock function was called at least once with the specified args -expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]); - -// The last call to the mock function was called with the specified args -expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([ - arg1, - arg2, -]); - -// The first arg of the last call to the mock function was `42` -// (note that there is no sugar helper for this specific of an assertion) -expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42); - -// A snapshot will check that a mock was invoked the same number of times, -// in the same order, with the same arguments. It will also assert on the name. -expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]); -expect(mockFunc.getMockName()).toBe('a mock name'); -``` - -For a complete list of matchers, check out the [reference docs](ExpectAPI.md). diff --git a/website/versioned_docs/version-27.1/MongoDB.md b/website/versioned_docs/version-27.1/MongoDB.md deleted file mode 100644 index a67d365fd9a9..000000000000 --- a/website/versioned_docs/version-27.1/MongoDB.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -id: mongodb -title: Using with MongoDB ---- - -With the [Global Setup/Teardown](Configuration.md#globalsetup-string) and [Async Test Environment](Configuration.md#testenvironment-string) APIs, Jest can work smoothly with [MongoDB](https://www.mongodb.com/). - -## Use jest-mongodb Preset - -[Jest MongoDB](https://github.com/shelfio/jest-mongodb) provides all required configuration to run your tests using MongoDB. - -1. First install `@shelf/jest-mongodb` - -``` -yarn add @shelf/jest-mongodb --dev -``` - -2. Specify preset in your Jest configuration: - -```json -{ - "preset": "@shelf/jest-mongodb" -} -``` - -3. Write your test - -```js -const {MongoClient} = require('mongodb'); - -describe('insert', () => { - let connection; - let db; - - beforeAll(async () => { - connection = await MongoClient.connect(global.__MONGO_URI__, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - db = await connection.db(global.__MONGO_DB_NAME__); - }); - - afterAll(async () => { - await connection.close(); - }); - - it('should insert a doc into collection', async () => { - const users = db.collection('users'); - - const mockUser = {_id: 'some-user-id', name: 'John'}; - await users.insertOne(mockUser); - - const insertedUser = await users.findOne({_id: 'some-user-id'}); - expect(insertedUser).toEqual(mockUser); - }); -}); -``` - -There's no need to load any dependencies. - -See [documentation](https://github.com/shelfio/jest-mongodb) for details (configuring MongoDB version, etc). diff --git a/website/versioned_docs/version-27.1/Puppeteer.md b/website/versioned_docs/version-27.1/Puppeteer.md deleted file mode 100644 index 2c96464e21e9..000000000000 --- a/website/versioned_docs/version-27.1/Puppeteer.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -id: puppeteer -title: Using with puppeteer ---- - -With the [Global Setup/Teardown](Configuration.md#globalsetup-string) and [Async Test Environment](Configuration.md#testenvironment-string) APIs, Jest can work smoothly with [puppeteer](https://github.com/GoogleChrome/puppeteer). - -> Generating code coverage for test files using Puppeteer is currently not possible if your test uses `page.$eval`, `page.$$eval` or `page.evaluate` as the passed function is executed outside of Jest's scope. Check out [issue #7962](https://github.com/facebook/jest/issues/7962#issuecomment-495272339) on GitHub for a workaround. - -## Use jest-puppeteer Preset - -[Jest Puppeteer](https://github.com/smooth-code/jest-puppeteer) provides all required configuration to run your tests using Puppeteer. - -1. First, install `jest-puppeteer` - -``` -yarn add --dev jest-puppeteer -``` - -2. Specify preset in your [Jest configuration](Configuration.md): - -```json -{ - "preset": "jest-puppeteer" -} -``` - -3. Write your test - -```js -describe('Google', () => { - beforeAll(async () => { - await page.goto('https://google.com'); - }); - - it('should be titled "Google"', async () => { - await expect(page.title()).resolves.toMatch('Google'); - }); -}); -``` - -There's no need to load any dependencies. Puppeteer's `page` and `browser` classes will automatically be exposed - -See [documentation](https://github.com/smooth-code/jest-puppeteer). - -## Custom example without jest-puppeteer preset - -You can also hook up puppeteer from scratch. The basic idea is to: - -1. launch & file the websocket endpoint of puppeteer with Global Setup -2. connect to puppeteer from each Test Environment -3. close puppeteer with Global Teardown - -Here's an example of the GlobalSetup script - -```js title="setup.js" -const {mkdir, writeFile} = require('fs').promises; -const os = require('os'); -const path = require('path'); -const puppeteer = require('puppeteer'); - -const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup'); - -module.exports = async function () { - const browser = await puppeteer.launch(); - // store the browser instance so we can teardown it later - // this global is only available in the teardown but not in TestEnvironments - global.__BROWSER_GLOBAL__ = browser; - - // use the file system to expose the wsEndpoint for TestEnvironments - await mkdir(DIR, {recursive: true}); - await writeFile(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint()); -}; -``` - -Then we need a custom Test Environment for puppeteer - -```js title="puppeteer_environment.js" -const {readFile} = require('fs').promises; -const os = require('os'); -const path = require('path'); -const puppeteer = require('puppeteer'); -const NodeEnvironment = require('jest-environment-node'); - -const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup'); - -class PuppeteerEnvironment extends NodeEnvironment { - constructor(config) { - super(config); - } - - async setup() { - await super.setup(); - // get the wsEndpoint - const wsEndpoint = await readFile(path.join(DIR, 'wsEndpoint'), 'utf8'); - if (!wsEndpoint) { - throw new Error('wsEndpoint not found'); - } - - // connect to puppeteer - this.global.__BROWSER_GLOBAL__ = await puppeteer.connect({ - browserWSEndpoint: wsEndpoint, - }); - } - - async teardown() { - await super.teardown(); - } - - getVmContext() { - return super.getVmContext(); - } -} - -module.exports = PuppeteerEnvironment; -``` - -Finally, we can close the puppeteer instance and clean-up the file - -```js title="teardown.js" -const fs = require('fs').promises; -const os = require('os'); -const path = require('path'); - -const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup'); -module.exports = async function () { - // close the browser instance - await global.__BROWSER_GLOBAL__.close(); - - // clean-up the wsEndpoint file - await fs.rm(DIR, {recursive: true, force: true}); -}; -``` - -With all the things set up, we can now write our tests like this: - -```js title="test.js" -const timeout = 5000; - -describe( - '/ (Home Page)', - () => { - let page; - beforeAll(async () => { - page = await global.__BROWSER_GLOBAL__.newPage(); - await page.goto('https://google.com'); - }, timeout); - - it('should load without error', async () => { - const text = await page.evaluate(() => document.body.textContent); - expect(text).toContain('google'); - }); - }, - timeout, -); -``` - -Finally, set `jest.config.js` to read from these files. (The `jest-puppeteer` preset does something like this under the hood.) - -```js -module.exports = { - globalSetup: './setup.js', - globalTeardown: './teardown.js', - testEnvironment: './puppeteer_environment.js', -}; -``` - -Here's the code of [full working example](https://github.com/xfumihiro/jest-puppeteer-example). diff --git a/website/versioned_docs/version-27.1/TestingFrameworks.md b/website/versioned_docs/version-27.1/TestingFrameworks.md deleted file mode 100644 index 5b085c8658a3..000000000000 --- a/website/versioned_docs/version-27.1/TestingFrameworks.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -id: testing-frameworks -title: Testing Web Frameworks ---- - -Jest is a universal testing platform, with the ability to adapt to any JavaScript library or framework. In this section, we'd like to link to community posts and articles about integrating Jest into popular JS libraries. - -## React - -- [Testing ReactJS components with Jest](https://testing-library.com/docs/react-testing-library/example-intro) by Kent C. Dodds ([@kentcdodds](https://twitter.com/kentcdodds)) - -## Vue.js - -- [Testing Vue.js components with Jest](https://alexjoverm.github.io/series/Unit-Testing-Vue-js-Components-with-the-Official-Vue-Testing-Tools-and-Jest/) by Alex Jover Morales ([@alexjoverm](https://twitter.com/alexjoverm)) -- [Jest for all: Episode 1 — Vue.js](https://medium.com/@kentaromiura_the_js_guy/jest-for-all-episode-1-vue-js-d616bccbe186#.d573vrce2) by Cristian Carlesso ([@kentaromiura](https://twitter.com/kentaromiura)) - -## AngularJS - -- [Testing an AngularJS app with Jest](https://medium.com/aya-experience/testing-an-angularjs-app-with-jest-3029a613251) by Matthieu Lux ([@Swiip](https://twitter.com/Swiip)) -- [Running AngularJS Tests with Jest](https://engineering.talentpair.com/running-angularjs-tests-with-jest-49d0cc9c6d26) by Ben Brandt ([@benjaminbrandt](https://twitter.com/benjaminbrandt)) -- [AngularJS Unit Tests with Jest Actions (Traditional Chinese)](https://dwatow.github.io/2019/08-14-angularjs/angular-jest/?fbclid=IwAR2SrqYg_o6uvCQ79FdNPeOxs86dUqB6pPKgd9BgnHt1kuIDRyRM-ch11xg) by Chris Wang ([@dwatow](https://github.com/dwatow)) - -## Angular - -- [Testing Angular faster with Jest](https://www.xfive.co/blog/testing-angular-faster-jest/) by Michał Pierzchała ([@thymikee](https://twitter.com/thymikee)) - -## MobX - -- [How to Test React and MobX with Jest](https://semaphoreci.com/community/tutorials/how-to-test-react-and-mobx-with-jest) by Will Stern ([@willsterndev](https://twitter.com/willsterndev)) - -## Redux - -- [Writing Tests](https://redux.js.org/recipes/writing-tests) by Redux docs - -## Express.js - -- [How to test Express.js with Jest and Supertest](http://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/) by Albert Gao ([@albertgao](https://twitter.com/albertgao)) - -## GatsbyJS - -- [Unit Testing](https://www.gatsbyjs.org/docs/unit-testing/) by GatsbyJS docs - -## Hapi.js - -- [Testing Hapi.js With Jest](http://niralar.com/testing-hapi-js-with-jest/) by Niralar ([Sivasankar](http://sivasankar.in/)) diff --git a/website/versioned_docs/version-27.1/Troubleshooting.md b/website/versioned_docs/version-27.1/Troubleshooting.md deleted file mode 100644 index 28cea902472d..000000000000 --- a/website/versioned_docs/version-27.1/Troubleshooting.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -id: troubleshooting -title: Troubleshooting ---- - -Uh oh, something went wrong? Use this guide to resolve issues with Jest. - -## Tests are Failing and You Don't Know Why - -Try using the debugging support built into Node. Note: This will only work in Node.js 8+. - -Place a `debugger;` statement in any of your tests, and then, in your project's directory, run: - -```bash -node --inspect-brk node_modules/.bin/jest --runInBand [any other arguments here] -or on Windows -node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand [any other arguments here] -``` - -This will run Jest in a Node process that an external debugger can connect to. Note that the process will pause until the debugger has connected to it. - -To debug in Google Chrome (or any Chromium-based browser), open your browser and go to `chrome://inspect` and click on "Open Dedicated DevTools for Node", which will give you a list of available node instances you can connect to. Click on the address displayed in the terminal (usually something like `localhost:9229`) after running the above command, and you will be able to debug Jest using Chrome's DevTools. - -The Chrome Developer Tools will be displayed, and a breakpoint will be set at the first line of the Jest CLI script (this is done to give you time to open the developer tools and to prevent Jest from executing before you have time to do so). Click the button that looks like a "play" button in the upper right hand side of the screen to continue execution. When Jest executes the test that contains the `debugger` statement, execution will pause and you can examine the current scope and call stack. - -> Note: the `--runInBand` cli option makes sure Jest runs the test in the same process rather than spawning processes for individual tests. Normally Jest parallelizes test runs across processes but it is hard to debug many processes at the same time. - -## Debugging in VS Code - -There are multiple ways to debug Jest tests with [Visual Studio Code's](https://code.visualstudio.com) built-in [debugger](https://code.visualstudio.com/docs/nodejs/nodejs-debugging). - -To attach the built-in debugger, run your tests as aforementioned: - -```bash -node --inspect-brk node_modules/.bin/jest --runInBand [any other arguments here] -or on Windows -node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand [any other arguments here] -``` - -Then attach VS Code's debugger using the following `launch.json` config: - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "attach", - "name": "Attach", - "port": 9229 - } - ] -} -``` - -To automatically launch and attach to a process running your tests, use the following configuration: - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Jest Tests", - "type": "node", - "request": "launch", - "runtimeArgs": [ - "--inspect-brk", - "${workspaceRoot}/node_modules/.bin/jest", - "--runInBand" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 - } - ] -} -``` - -or the following for Windows: - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Jest Tests", - "type": "node", - "request": "launch", - "runtimeArgs": [ - "--inspect-brk", - "${workspaceRoot}/node_modules/jest/bin/jest.js", - "--runInBand" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 - } - ] -} -``` - -If you are using Facebook's [`create-react-app`](https://github.com/facebookincubator/create-react-app), you can debug your Jest tests with the following configuration: - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug CRA Tests", - "type": "node", - "request": "launch", - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts", - "args": ["test", "--runInBand", "--no-cache", "--env=jsdom"], - "cwd": "${workspaceRoot}", - "protocol": "inspector", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen" - } - ] -} -``` - -More information on Node debugging can be found [here](https://nodejs.org/api/debugger.html). - -## Debugging in WebStorm - -[WebStorm](https://www.jetbrains.com/webstorm/) has built-in support for Jest. Read [Testing With Jest in WebStorm](https://blog.jetbrains.com/webstorm/2018/10/testing-with-jest-in-webstorm/) to learn more. - -## Caching Issues - -The transform script was changed or Babel was updated and the changes aren't being recognized by Jest? - -Retry with [`--no-cache`](CLI.md#--cache). Jest caches transformed module files to speed up test execution. If you are using your own custom transformer, consider adding a `getCacheKey` function to it: [getCacheKey in Relay](https://github.com/facebook/relay/blob/58cf36c73769690f0bbf90562707eadb062b029d/scripts/jest/preprocessor.js#L56-L61). - -## Unresolved Promises - -If a promise doesn't resolve at all, this error might be thrown: - -```bash -- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.` -``` - -Most commonly this is being caused by conflicting Promise implementations. Consider replacing the global promise implementation with your own, for example `global.Promise = jest.requireActual('promise');` and/or consolidate the used Promise libraries to a single one. - -If your test is long running, you may want to consider to increase the timeout by calling `jest.setTimeout` - -```js -jest.setTimeout(10000); // 10 second timeout -``` - -## Watchman Issues - -Try running Jest with [`--no-watchman`](CLI.md#--watchman) or set the `watchman` configuration option to `false`. - -Also see [watchman troubleshooting](https://facebook.github.io/watchman/docs/troubleshooting). - -## Tests are Extremely Slow on Docker and/or Continuous Integration (CI) server. - -While Jest is most of the time extremely fast on modern multi-core computers with fast SSDs, it may be slow on certain setups as our users [have](https://github.com/facebook/jest/issues/1395) [discovered](https://github.com/facebook/jest/issues/1524#issuecomment-260246008). - -Based on the [findings](https://github.com/facebook/jest/issues/1524#issuecomment-262366820), one way to mitigate this issue and improve the speed by up to 50% is to run tests sequentially. - -In order to do this you can run tests in the same thread using [`--runInBand`](CLI.md#--runinband): - -```bash -# Using Jest CLI -jest --runInBand - -# Using yarn test (e.g. with create-react-app) -yarn test --runInBand -``` - -Another alternative to expediting test execution time on Continuous Integration Servers such as Travis-CI is to set the max worker pool to ~_4_. Specifically on Travis-CI, this can reduce test execution time in half. Note: The Travis CI _free_ plan available for open source projects only includes 2 CPU cores. - -```bash -# Using Jest CLI -jest --maxWorkers=4 - -# Using yarn test (e.g. with create-react-app) -yarn test --maxWorkers=4 -``` - -## `coveragePathIgnorePatterns` seems to not have any effect. - -Make sure you are not using the `babel-plugin-istanbul` plugin. Jest wraps Istanbul, and therefore also tells Istanbul what files to instrument with coverage collection. When using `babel-plugin-istanbul`, every file that is processed by Babel will have coverage collection code, hence it is not being ignored by `coveragePathIgnorePatterns`. - -## Defining Tests - -Tests must be defined synchronously for Jest to be able to collect your tests. - -As an example to show why this is the case, imagine we wrote a test like so: - -```js -// Don't do this it will not work -setTimeout(() => { - it('passes', () => expect(1).toBe(1)); -}, 0); -``` - -When Jest runs your test to collect the `test`s it will not find any because we have set the definition to happen asynchronously on the next tick of the event loop. - -_Note:_ This means when you are using `test.each` you cannot set the table asynchronously within a `beforeEach` / `beforeAll`. - -## Still unresolved? - -See [Help](/help). diff --git a/website/versioned_docs/version-27.1/TutorialReact.md b/website/versioned_docs/version-27.1/TutorialReact.md deleted file mode 100644 index 8fa282fff661..000000000000 --- a/website/versioned_docs/version-27.1/TutorialReact.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -id: tutorial-react -title: Testing React Apps ---- - -At Facebook, we use Jest to test [React](https://reactjs.org/) applications. - -## Setup - -### Setup with Create React App - -If you are new to React, we recommend using [Create React App](https://create-react-app.dev/). It is ready to use and [ships with Jest](https://create-react-app.dev/docs/running-tests/#docsNav)! You will only need to add `react-test-renderer` for rendering snapshots. - -Run - -```bash -yarn add --dev react-test-renderer -``` - -### Setup without Create React App - -If you have an existing application you'll need to install a few packages to make everything work well together. We are using the `babel-jest` package and the `react` babel preset to transform our code inside of the test environment. Also see [using babel](GettingStarted.md#using-babel). - -Run - -```bash -yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer -``` - -Your `package.json` should look something like this (where `` is the actual latest version number for the package). Please add the scripts and jest configuration entries: - -```json - "dependencies": { - "react": "", - "react-dom": "" - }, - "devDependencies": { - "@babel/preset-env": "", - "@babel/preset-react": "", - "babel-jest": "", - "jest": "", - "react-test-renderer": "" - }, - "scripts": { - "test": "jest" - } -``` - -```js title="babel.config.js" -module.exports = { - presets: ['@babel/preset-env', '@babel/preset-react'], -}; -``` - -**And you're good to go!** - -### Snapshot Testing - -Let's create a [snapshot test](SnapshotTesting.md) for a Link component that renders hyperlinks: - -```tsx title="Link.js" -import React, {useState} from 'react'; - -const STATUS = { - HOVERED: 'hovered', - NORMAL: 'normal', -}; - -const Link = ({page, children}) => { - const [status, setStatus] = useState(STATUS.NORMAL); - - const onMouseEnter = () => { - setStatus(STATUS.HOVERED); - }; - - const onMouseLeave = () => { - setStatus(STATUS.NORMAL); - }; - - return ( - - {children} - - ); -}; - -export default Link; -``` - -> Note: Examples are using Function components, but Class components can be tested in the same way. See [React: Function and Class Components](https://reactjs.org/docs/components-and-props.html#function-and-class-components). **Reminders** that with Class components, we expect Jest to be used to test props and not methods directly. - -Now let's use React's test renderer and Jest's snapshot feature to interact with the component and capture the rendered output and create a snapshot file: - -```tsx title="Link.test.js" -import React from 'react'; -import renderer from 'react-test-renderer'; -import Link from '../Link'; - -test('Link changes the class when hovered', () => { - const component = renderer.create( - Facebook, - ); - let tree = component.toJSON(); - expect(tree).toMatchSnapshot(); - - // manually trigger the callback - tree.props.onMouseEnter(); - // re-rendering - tree = component.toJSON(); - expect(tree).toMatchSnapshot(); - - // manually trigger the callback - tree.props.onMouseLeave(); - // re-rendering - tree = component.toJSON(); - expect(tree).toMatchSnapshot(); -}); -``` - -When you run `yarn test` or `jest`, this will produce an output file like this: - -```javascript title="__tests__/__snapshots__/Link.test.js.snap" -exports[`Link changes the class when hovered 1`] = ` - - Facebook - -`; - -exports[`Link changes the class when hovered 2`] = ` - - Facebook - -`; - -exports[`Link changes the class when hovered 3`] = ` - - Facebook - -`; -``` - -The next time you run the tests, the rendered output will be compared to the previously created snapshot. The snapshot should be committed along with code changes. When a snapshot test fails, you need to inspect whether it is an intended or unintended change. If the change is expected you can invoke Jest with `jest -u` to overwrite the existing snapshot. - -The code for this example is available at [examples/snapshot](https://github.com/facebook/jest/tree/main/examples/snapshot). - -#### Snapshot Testing with Mocks, Enzyme and React 16 - -There's a caveat around snapshot testing when using Enzyme and React 16+. If you mock out a module using the following style: - -```js -jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent'); -``` - -Then you will see warnings in the console: - -```bash -Warning: is using uppercase HTML. Always use lowercase HTML tags in React. - -# Or: -Warning: The tag is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter. -``` - -React 16 triggers these warnings due to how it checks element types, and the mocked module fails these checks. Your options are: - -1. Render as text. This way you won't see the props passed to the mock component in the snapshot, but it's straightforward: - ```js - jest.mock('./SomeComponent', () => () => 'SomeComponent'); - ``` -2. Render as a custom element. DOM "custom elements" aren't checked for anything and shouldn't fire warnings. They are lowercase and have a dash in the name. - ```tsx - jest.mock('./Widget', () => () => ); - ``` -3. Use `react-test-renderer`. The test renderer doesn't care about element types and will happily accept e.g. `SomeComponent`. You could check snapshots using the test renderer, and check component behavior separately using Enzyme. -4. Disable warnings all together (should be done in your jest setup file): - ```js - jest.mock('fbjs/lib/warning', () => require('fbjs/lib/emptyFunction')); - ``` - This shouldn't normally be your option of choice as useful warnings could be lost. However, in some cases, for example when testing react-native's components we are rendering react-native tags into the DOM and many warnings are irrelevant. Another option is to swizzle the console.warn and suppress specific warnings. - -### DOM Testing - -If you'd like to assert, and manipulate your rendered components you can use [react-testing-library](https://github.com/kentcdodds/react-testing-library), [Enzyme](http://airbnb.io/enzyme/), or React's [TestUtils](https://reactjs.org/docs/test-utils.html). The following two examples use react-testing-library and Enzyme. - -#### react-testing-library - -You have to run `yarn add --dev @testing-library/react` to use react-testing-library. - -Let's implement a checkbox which swaps between two labels: - -```tsx title="CheckboxWithLabel.js" -import React, {useState} from 'react'; - -const CheckboxWithLabel = ({labelOn, labelOff}) => { - const [isChecked, setIsChecked] = useState(false); - - const onChange = () => { - setIsChecked(!isChecked); - }; - - return ( - - ); -}; - -export default CheckboxWithLabel; -``` - -```tsx title="__tests__/CheckboxWithLabel-test.js" -import React from 'react'; -import {cleanup, fireEvent, render} from '@testing-library/react'; -import CheckboxWithLabel from '../CheckboxWithLabel'; - -// Note: running cleanup afterEach is done automatically for you in @testing-library/react@9.0.0 or higher -// unmount and cleanup DOM after the test is finished. -afterEach(cleanup); - -it('CheckboxWithLabel changes the text after click', () => { - const {queryByLabelText, getByLabelText} = render( - , - ); - - expect(queryByLabelText(/off/i)).toBeTruthy(); - - fireEvent.click(getByLabelText(/off/i)); - - expect(queryByLabelText(/on/i)).toBeTruthy(); -}); -``` - -The code for this example is available at [examples/react-testing-library](https://github.com/facebook/jest/tree/main/examples/react-testing-library). - -#### Enzyme - -You have to run `yarn add --dev enzyme` to use Enzyme. If you are using a React version below 15.5.0, you will also need to install `react-addons-test-utils`. - -Let's rewrite the test from above using Enzyme instead of react-testing-library. We use Enzyme's [shallow renderer](http://airbnb.io/enzyme/docs/api/shallow.html) in this example. - -```tsx title="__tests__/CheckboxWithLabel-test.js" -import React from 'react'; -import {shallow} from 'enzyme'; -import CheckboxWithLabel from '../CheckboxWithLabel'; - -test('CheckboxWithLabel changes the text after click', () => { - // Render a checkbox with label in the document - const checkbox = shallow(); - - expect(checkbox.text()).toEqual('Off'); - - checkbox.find('input').simulate('change'); - - expect(checkbox.text()).toEqual('On'); -}); -``` - -The code for this example is available at [examples/enzyme](https://github.com/facebook/jest/tree/main/examples/enzyme). - -### Custom transformers - -If you need more advanced functionality, you can also build your own transformer. Instead of using `babel-jest`, here is an example of using `@babel/core`: - -```javascript title="custom-transformer.js" -'use strict'; - -const {transform} = require('@babel/core'); -const jestPreset = require('babel-preset-jest'); - -module.exports = { - process(src, filename) { - const result = transform(src, { - filename, - presets: [jestPreset], - }); - - return result || src; - }, -}; -``` - -Don't forget to install the `@babel/core` and `babel-preset-jest` packages for this example to work. - -To make this work with Jest you need to update your Jest configuration with this: `"transform": {"\\.js$": "path/to/custom-transformer.js"}`. - -If you'd like to build a transformer with babel support, you can also use `babel-jest` to compose one and pass in your custom configuration options: - -```javascript -const babelJest = require('babel-jest'); - -module.exports = babelJest.createTransformer({ - presets: ['my-custom-preset'], -}); -``` - -See [dedicated docs](CodeTransformation.md#writing-custom-transformers) for more details. diff --git a/website/versioned_docs/version-27.1/Webpack.md b/website/versioned_docs/version-27.1/Webpack.md deleted file mode 100644 index 1838ce89b222..000000000000 --- a/website/versioned_docs/version-27.1/Webpack.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -id: webpack -title: Using with webpack ---- - -Jest can be used in projects that use [webpack](https://webpack.js.org/) to manage assets, styles, and compilation. webpack _does_ offer some unique challenges over other tools because it integrates directly with your application to allow managing stylesheets, assets like images and fonts, along with the expansive ecosystem of compile-to-JavaScript languages and tools. - -## A webpack example - -Let's start with a common sort of webpack config file and translate it to a Jest setup. - -```js title="webpack.config.js" -module.exports = { - module: { - loaders: [ - {exclude: ['node_modules'], loader: 'babel', test: /\.jsx?$/}, - {loader: 'style-loader!css-loader', test: /\.css$/}, - {loader: 'url-loader', test: /\.gif$/}, - {loader: 'file-loader', test: /\.(ttf|eot|svg)$/}, - ], - }, - resolve: { - alias: { - config$: './configs/app-config.js', - react: './vendor/react-master', - }, - extensions: ['', 'js', 'jsx'], - modules: [ - 'node_modules', - 'bower_components', - 'shared', - '/shared/vendor/modules', - ], - }, -}; -``` - -If you have JavaScript files that are transformed by Babel, you can [enable support for Babel](GettingStarted.md#using-babel) by installing the `babel-jest` plugin. Non-Babel JavaScript transformations can be handled with Jest's [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) config option. - -### Handling Static Assets - -Next, let's configure Jest to gracefully handle asset files such as stylesheets and images. Usually, these files aren't particularly useful in tests so we can safely mock them out. However, if you are using CSS Modules then it's better to mock a proxy for your className lookups. - -```json title="package.json" -{ - "jest": { - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(css|less)$": "/__mocks__/styleMock.js" - } - } -} -``` - -And the mock files themselves: - -```js title="__mocks__/styleMock.js" -module.exports = {}; -``` - -```js title="__mocks__/fileMock.js" -module.exports = 'test-file-stub'; -``` - -### Mocking CSS Modules - -You can use an [ES6 Proxy](https://github.com/keyanzhang/identity-obj-proxy) to mock [CSS Modules](https://github.com/css-modules/css-modules): - -```bash -yarn add --dev identity-obj-proxy -``` - -Then all your className lookups on the styles object will be returned as-is (e.g., `styles.foobar === 'foobar'`). This is pretty handy for React [Snapshot Testing](SnapshotTesting.md). - -```json title="package.json (for CSS Modules)" -{ - "jest": { - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", - "\\.(css|less)$": "identity-obj-proxy" - } - } -} -``` - -> Notice that Proxy is enabled in Node 6 by default. If you are not on Node 6 yet, make sure you invoke Jest using `node --harmony_proxies node_modules/.bin/jest`. - -If `moduleNameMapper` cannot fulfill your requirements, you can use Jest's [`transform`](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object) config option to specify how assets are transformed. For example, a transformer that returns the basename of a file (such that `require('logo.jpg');` returns `'logo'`) can be written as: - -```js title="fileTransformer.js" -const path = require('path'); - -module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; - }, -}; -``` - -```json title="package.json (for custom transformers and CSS Modules)" -{ - "jest": { - "moduleNameMapper": { - "\\.(css|less)$": "identity-obj-proxy" - }, - "transform": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/fileTransformer.js" - } - } -} -``` - -We've told Jest to ignore files matching a stylesheet or image extension, and instead, require our mock files. You can adjust the regular expression to match the file types your webpack config handles. - -_Note: if you are using babel-jest with additional code preprocessors, you have to explicitly define babel-jest as a transformer for your JavaScript code to map `.js` files to the babel-jest module._ - -```json -"transform": { - "\\.js$": "babel-jest", - "\\.css$": "custom-transformer", - ... -} -``` - -### Configuring Jest to find our files - -Now that Jest knows how to process our files, we need to tell it how to _find_ them. For webpack's `modulesDirectories`, and `extensions` options there are direct analogs in Jest's `moduleDirectories` and `moduleFileExtensions` options. - -```json title="package.json" -{ - "jest": { - "moduleFileExtensions": ["js", "jsx"], - "moduleDirectories": ["node_modules", "bower_components", "shared"], - - "moduleNameMapper": { - "\\.(css|less)$": "/__mocks__/styleMock.js", - "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js" - } - } -} -``` - -> Note: `` is a special token that gets replaced by Jest with the root of your project. Most of the time this will be the folder where your `package.json` is located unless you specify a custom `rootDir` option in your configuration. - -Similarly, webpack's `resolve.root` option functions like setting the `NODE_PATH` env variable, which you can set, or make use of the `modulePaths` option. - -```json title="package.json" -{ - "jest": { - "modulePaths": ["/shared/vendor/modules"], - "moduleFileExtensions": ["js", "jsx"], - "moduleDirectories": ["node_modules", "bower_components", "shared"], - "moduleNameMapper": { - "\\.(css|less)$": "/__mocks__/styleMock.js", - "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js" - } - } -} -``` - -And finally, we have to handle the webpack `alias`. For that, we can make use of the `moduleNameMapper` option again. - -```json title="package.json" -{ - "jest": { - "modulePaths": ["/shared/vendor/modules"], - "moduleFileExtensions": ["js", "jsx"], - "moduleDirectories": ["node_modules", "bower_components", "shared"], - - "moduleNameMapper": { - "\\.(css|less)$": "/__mocks__/styleMock.js", - "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js", - - "^react(.*)$": "/vendor/react-master$1", - "^config$": "/configs/app-config.js" - } - } -} -``` - -That's it! webpack is a complex and flexible tool, so you may have to make some adjustments to handle your specific application's needs. Luckily for most projects, Jest should be more than flexible enough to handle your webpack config. - -> Note: For more complex webpack configurations, you may also want to investigate projects such as: [babel-plugin-webpack-loaders](https://github.com/istarkov/babel-plugin-webpack-loaders). - -## Using with webpack 2 - -webpack 2 offers native support for ES modules. However, Jest runs in Node, and thus requires ES modules to be transpiled to CommonJS modules. As such, if you are using webpack 2, you most likely will want to configure Babel to transpile ES modules to CommonJS modules only in the `test` environment. - -```json -// .babelrc -{ - "presets": [["env", {"modules": false}]], - - "env": { - "test": { - "plugins": ["transform-es2015-modules-commonjs"] - } - } -} -``` - -> Note: Jest caches files to speed up test execution. If you updated .babelrc and Jest is still not working, try running Jest with `--no-cache`. - -If you use dynamic imports (`import('some-file.js').then(module => ...)`), you need to enable the `dynamic-import-node` plugin. - -```json -// .babelrc -{ - "presets": [["env", {"modules": false}]], - - "plugins": ["syntax-dynamic-import"], - - "env": { - "test": { - "plugins": ["dynamic-import-node"] - } - } -} -``` - -For an example of how to use Jest with Webpack with React, Redux, and Node, you can view one [here](https://github.com/jenniferabowd/jest_react_redux_node_webpack_complex_example). diff --git a/website/versioned_docs/version-27.2/Architecture.md b/website/versioned_docs/version-27.2/Architecture.md deleted file mode 100644 index 300abd75d9f0..000000000000 --- a/website/versioned_docs/version-27.2/Architecture.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -id: architecture -title: Architecture ---- - -If you are interested in learning more about how Jest works, understand its architecture, and how Jest is split up into individual reusable packages, check out this video: - - - -If you'd like to learn how to build a testing framework like Jest from scratch, check out this video: - - - -There is also a [written guide you can follow](https://cpojer.net/posts/building-a-javascript-testing-framework). It teaches the fundamental concepts of Jest and explains how various parts of Jest can be used to compose a custom testing framework. diff --git a/website/versioned_docs/version-27.2/BypassingModuleMocks.md b/website/versioned_docs/version-27.2/BypassingModuleMocks.md deleted file mode 100644 index 96dfa7462016..000000000000 --- a/website/versioned_docs/version-27.2/BypassingModuleMocks.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -id: bypassing-module-mocks -title: Bypassing module mocks ---- - -Jest allows you to mock out whole modules in your tests, which can be useful for testing if your code is calling functions from that module correctly. However, sometimes you may want to use parts of a mocked module in your _test file_, in which case you want to access the original implementation, rather than a mocked version. - -Consider writing a test case for this `createUser` function: - -```javascript title="createUser.js" -import fetch from 'node-fetch'; - -export const createUser = async () => { - const response = await fetch('http://website.com/users', {method: 'POST'}); - const userId = await response.text(); - return userId; -}; -``` - -Your test will want to mock the `fetch` function so that we can be sure that it gets called without actually making the network request. However, you'll also need to mock the return value of `fetch` with a `Response` (wrapped in a `Promise`), as our function uses it to grab the created user's ID. So you might initially try writing a test like this: - -```javascript -jest.mock('node-fetch'); - -import fetch, {Response} from 'node-fetch'; -import {createUser} from './createUser'; - -test('createUser calls fetch with the right args and returns the user id', async () => { - fetch.mockReturnValue(Promise.resolve(new Response('4'))); - - const userId = await createUser(); - - expect(fetch).toHaveBeenCalledTimes(1); - expect(fetch).toHaveBeenCalledWith('http://website.com/users', { - method: 'POST', - }); - expect(userId).toBe('4'); -}); -``` - -However, if you ran that test you would find that the `createUser` function would fail, throwing the error: `TypeError: response.text is not a function`. This is because the `Response` class you've imported from `node-fetch` has been mocked (due to the `jest.mock` call at the top of the test file) so it no longer behaves the way it should. - -To get around problems like this, Jest provides the `jest.requireActual` helper. To make the above test work, make the following change to the imports in the test file: - -```javascript -// BEFORE -jest.mock('node-fetch'); -import fetch, {Response} from 'node-fetch'; -``` - -```javascript -// AFTER -jest.mock('node-fetch'); -import fetch from 'node-fetch'; -const {Response} = jest.requireActual('node-fetch'); -``` - -This allows your test file to import the actual `Response` object from `node-fetch`, rather than a mocked version. This means the test will now pass correctly. diff --git a/website/versioned_docs/version-27.2/CLI.md b/website/versioned_docs/version-27.2/CLI.md deleted file mode 100644 index 251741cddd3f..000000000000 --- a/website/versioned_docs/version-27.2/CLI.md +++ /dev/null @@ -1,394 +0,0 @@ ---- -id: cli -title: Jest CLI Options ---- - -The `jest` command line runner has a number of useful options. You can run `jest --help` to view all available options. Many of the options shown below can also be used together to run tests exactly the way you want. Every one of Jest's [Configuration](Configuration.md) options can also be specified through the CLI. - -Here is a brief overview: - -## Running from the command line - -Run all tests (default): - -```bash -jest -``` - -Run only the tests that were specified with a pattern or filename: - -```bash -jest my-test #or -jest path/to/my-test.js -``` - -Run tests related to changed files based on hg/git (uncommitted files): - -```bash -jest -o -``` - -Run tests related to `path/to/fileA.js` and `path/to/fileB.js`: - -```bash -jest --findRelatedTests path/to/fileA.js path/to/fileB.js -``` - -Run tests that match this spec name (match against the name in `describe` or `test`, basically). - -```bash -jest -t name-of-spec -``` - -Run watch mode: - -```bash -jest --watch #runs jest -o by default -jest --watchAll #runs all tests -``` - -Watch mode also enables to specify the name or path to a file to focus on a specific set of tests. - -## Using with yarn - -If you run Jest via `yarn test`, you can pass the command line arguments directly as Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -yarn test -u -t="ColorPicker" -``` - -## Using with npm scripts - -If you run Jest via `npm test`, you can still use the command line arguments by inserting a `--` between `npm test` and the Jest arguments. - -Instead of: - -```bash -jest -u -t="ColorPicker" -``` - -you can use: - -```bash -npm test -- -u -t="ColorPicker" -``` - -## Camelcase & dashed args support - -Jest supports both camelcase and dashed arg formats. The following examples will have an equal result: - -```bash -jest --collect-coverage -jest --collectCoverage -``` - -Arguments can also be mixed: - -```bash -jest --update-snapshot --detectOpenHandles -``` - -## Options - -_Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `jest ` - -When you run `jest` with an argument, that argument is treated as a regular expression to match against files in your project. It is possible to run test suites by providing a pattern. Only the files that the pattern matches will be picked up and executed. Depending on your terminal, you may need to quote this argument: `jest "my.*(complex)?pattern"`. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--bail` - -Alias: `-b`. Exit the test suite immediately upon `n` number of failing test suite. Defaults to `1`. - -### `--cache` - -Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. _Note: the cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower._ - -If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. - -### `--changedFilesWithAncestor` - -Runs tests related to the current changes and the changes made in the last commit. Behaves similarly to `--onlyChanged`. - -### `--changedSince` - -Runs tests related to the changes since the provided branch or commit hash. If the current branch has diverged from the given branch, then only changes made locally will be tested. Behaves similarly to `--onlyChanged`. - -### `--ci` - -When this option is provided, Jest will assume it is running in a CI environment. This changes the behavior when a new snapshot is encountered. Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`. - -### `--clearCache` - -Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. _Note: clearing the cache will reduce performance._ - -### `--clearMocks` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `--collectCoverageFrom=` - -A glob pattern relative to `rootDir` matching the files that coverage info needs to be collected from. - -### `--colors` - -Forces test results output highlighting even if stdout is not a TTY. - -### `--config=` - -Alias: `-c`. The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the `rootDir` for the project. This can also be a JSON-encoded value which Jest will use as configuration. - -### `--coverage[=]` - -Alias: `--collectCoverage`. Indicates that test coverage information should be collected and reported in the output. Optionally pass `` to override option set in configuration. - -### `--coverageProvider=` - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `--debug` - -Print debugging info about your Jest config. - -### `--detectOpenHandles` - -Attempt to collect and print open handles preventing Jest from exiting cleanly. Use this in cases where you need to use `--forceExit` in order for Jest to exit to potentially track down the reason. This implies `--runInBand`, making tests run serially. Implemented using [`async_hooks`](https://nodejs.org/api/async_hooks.html). This option has a significant performance penalty and should only be used for debugging. - -### `--env=` - -The test environment used for all tests. This can point to any file or node module. Examples: `jsdom`, `node` or `path/to/my-environment.js`. - -### `--errorOnDeprecated` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `--expand` - -Alias: `-e`. Use this flag to show full diffs and errors instead of a patch. - -### `--filter=` - -Path to a module exporting a filtering function. This method receives a list of tests which can be manipulated to exclude tests from running. Especially useful when used in conjunction with a testing infrastructure to filter known broken. - -### `--findRelatedTests ` - -Find and run the tests that cover a space separated list of source files that were passed in as arguments. Useful for pre-commit hook integration to run the minimal amount of tests necessary. Can be used together with `--coverage` to include a test coverage for the source files, no duplicate `--collectCoverageFrom` arguments needed. - -### `--forceExit` - -Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. _Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down._ - -### `--help` - -Show the help information, similar to this page. - -### `--init` - -Generate a basic configuration file. Based on your project, Jest will ask you a few questions that will help to generate a `jest.config.js` file with a short description for each option. - -### `--injectGlobals` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus` test runner._ - -### `--json` - -Prints the test results in JSON. This mode will send all other test output and user messages to stderr. - -### `--outputFile=` - -Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testresultsprocessor-string). - -### `--lastCommit` - -Run all tests affected by file changes in the last commit made. Behaves similarly to `--onlyChanged`. - -### `--listTests` - -Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. - -### `--logHeapUsage` - -Logs the heap usage after every test. Useful to debug memory leaks. Use together with `--runInBand` and `--expose-gc` in node. - -### `--maxConcurrency=` - -Prevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use `test.concurrent`. - -### `--maxWorkers=|` - -Alias: `-w`. Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `--maxWorkers=50%` - -### `--noStackTrace` - -Disables stack trace in test results output. - -### `--notify` - -Activates notifications for test results. Good for when you don't want your consciousness to be able to focus on anything except JavaScript testing. - -### `--onlyChanged` - -Alias: `-o`. Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a git/hg repository at the moment and requires a static dependency graph (ie. no dynamic requires). - -### `--passWithNoTests` - -Allows the test suite to pass when no files are found. - -### `--projects ... ` - -Run tests from one or more projects, found in the specified paths; also takes path globs. This option is the CLI equivalent of the [`projects`](configuration#projects-arraystring--projectconfig) configuration option. Note that if configuration files are found in the specified paths, _all_ projects specified within those configuration files will be run. - -### `--reporters` - -Run tests with specified reporters. [Reporter options](configuration#reporters-arraymodulename--modulename-options) are not available via CLI. Example with multiple reporters: - -`jest --reporters="default" --reporters="jest-junit"` - -### `--resetMocks` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `--restoreMocks` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `--roots` - -A list of paths to directories that Jest should use to search for files in. - -### `--runInBand` - -Alias: `-i`. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - -### `--runTestsByPath` - -Run only the tests that were specified with their exact paths. - -_Note: The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files_ - -### `--selectProjects ... ` - -Run only the tests of the specified projects. Jest uses the attribute `displayName` in the configuration to identify each project. If you use this option, you should provide a `displayName` to all your projects. - -### `--setupFilesAfterEnv ... ` - -A list of paths to modules that run some code to configure or to set up the testing framework before each test. Beware that files imported by the setup scripts will not be mocked during testing. - -### `--showConfig` - -Print your Jest config and then exits. - -### `--silent` - -Prevent tests from printing messages through the console. - -### `--testEnvironmentOptions=` - -A JSON string with options that will be passed to the `testEnvironment`. The relevant options depend on the environment. - -### `--testLocationInResults` - -Adds a `location` field to test results. Useful if you want to report the location of a test in a reporter. - -Note that `column` is 0-indexed while `line` is not. - -```json -{ - "column": 4, - "line": 5 -} -``` - -### `--testMatch glob1 ... globN` - -The glob patterns Jest uses to detect test files. Please refer to the [`testMatch` configuration](Configuration.md#testmatch-arraystring) for details. - -### `--testNamePattern=` - -Alias: `-t`. Run only tests with a name that matches the regex. For example, suppose you want to run only tests related to authorization which will have names like `"GET /api/posts with auth"`, then you can use `jest -t=auth`. - -_Note: The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks._ - -### `--testPathIgnorePatterns=|[array]` - -A single or array of regexp pattern strings that are tested against all tests paths before executing the test. Contrary to `--testPathPattern`, it will only run those tests with a path that does not match with the provided regexp expressions. - -To pass as an array use escaped parentheses and space delimited regexps such as `\(/node_modules/ /tests/e2e/\)`. Alternatively, you can omit parentheses by combining regexps into a single regexp like `/node_modules/|/tests/e2e/`. These two examples are equivalent. - -### `--testPathPattern=` - -A regexp pattern string that is matched against all tests paths before executing the test. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. - -### `--testRunner=` - -Lets you specify a custom test runner. - -### `--testSequencer=` - -Lets you specify a custom test sequencer. Please refer to the documentation of the corresponding configuration property for details. - -### `--testTimeout=` - -Default timeout of a test in milliseconds. Default value: 5000. - -### `--updateSnapshot` - -Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots. - -### `--useStderr` - -Divert all output to stderr. - -### `--verbose` - -Display individual test results with the test suite hierarchy. - -### `--version` - -Alias: `-v`. Print the version and exit. - -### `--watch` - -Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option instead. - -### `--watchAll` - -Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. - -Use `--watchAll=false` to explicitly disable the watch mode. Note that in most CI environments, this is automatically handled for you. - -### `--watchman` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. Defaults to `true`. Disable using `--no-watchman`. diff --git a/website/versioned_docs/version-27.2/CodeTransformation.md b/website/versioned_docs/version-27.2/CodeTransformation.md deleted file mode 100644 index 3b1e0f8e3100..000000000000 --- a/website/versioned_docs/version-27.2/CodeTransformation.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -id: code-transformation -title: Code Transformation ---- - -Jest runs the code in your project as JavaScript, but if you use some syntax not supported by Node.js out of the box (such as JSX, types from TypeScript, Vue templates etc.) then you'll need to transform that code into plain JavaScript, similar to what you would do when building for browsers. - -Jest supports this via the [`transform` configuration option](Configuration.md#transform-objectstring-pathtotransformer--pathtotransformer-object). - -A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by Node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. - -Jest will cache the result of a transformation and attempt to invalidate that result based on a number of factors, such as the source of the file being transformed and changing configuration. - -## Defaults - -Jest ships with one transformer out of the box - `babel-jest`. It will automatically load your project's Babel configuration and transform any file matching the following RegEx: `/\.[jt]sx?$/` meaning any `.js`, `.jsx`, `.ts` and `.tsx` file. In addition, `babel-jest` will inject the Babel plugin necessary for mock hoisting talked about in [ES Module mocking](ManualMocks.md#using-with-es-module-imports). - -If you override the `transform` configuration option `babel-jest` will no longer be active, and you'll need to add it manually if you wish to use Babel. - -## Writing custom transformers - -You can write your own transformer. The API of a transformer is as follows: - -```ts -interface SyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => SyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -interface AsyncTransformer { - /** - * Indicates if the transformer is capable of instrumenting the code for code coverage. - * - * If V8 coverage is _not_ active, and this is `true`, Jest will assume the code is instrumented. - * If V8 coverage is _not_ active, and this is `false`. Jest will instrument the code returned by this transformer using Babel. - */ - canInstrument?: boolean; - createTransformer?: (options?: OptionType) => AsyncTransformer; - - getCacheKey?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => string; - - getCacheKeyAsync?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; - - process?: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => TransformedSource; - - processAsync: ( - sourceText: string, - sourcePath: Config.Path, - options: TransformOptions, - ) => Promise; -} - -type Transformer = - | SyncTransformer - | AsyncTransformer; - -interface TransformOptions { - /** - * If a transformer does module resolution and reads files, it should populate `cacheFS` so that - * Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of - * - */ - cacheFS: Map; - config: Config.ProjectConfig; - /** A stringified version of the configuration - useful in cache busting */ - configString: string; - instrument: boolean; - // names are copied from babel: https://babeljs.io/docs/en/options#caller - supportsDynamicImport: boolean; - supportsExportNamespaceFrom: boolean; - supportsStaticESM: boolean; - supportsTopLevelAwait: boolean; - /** the options passed through Jest's config by the user */ - transformerConfig: OptionType; -} - -type TransformedSource = - | {code: string; map?: RawSourceMap | string | null} - | string; - -// Config.ProjectConfig can be seen in code [here](https://github.com/facebook/jest/blob/v26.6.3/packages/jest-types/src/Config.ts#L323) -// RawSourceMap comes from [`source-map`](https://github.com/mozilla/source-map/blob/0.6.1/source-map.d.ts#L6-L12) -``` - -As can be seen, only `process` or `processAsync` is mandatory to implement, although we highly recommend implementing `getCacheKey` as well, so we don't waste resources transpiling the same source file when we can read its previous result from disk. You can use [`@jest/create-cache-key-function`](https://www.npmjs.com/package/@jest/create-cache-key-function) to help implement it. - -Note that [ECMAScript module](ECMAScriptModules.md) support is indicated by the passed in `supports*` options. Specifically `supportsDynamicImport: true` means the transformer can return `import()` expressions, which is supported by both ESM and CJS. If `supportsStaticESM: true` it means top level `import` statements are supported and the code will be interpreted as ESM and not CJS. See [Node's docs](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs) for details on the differences. - -### Examples - -### TypeScript with type checking - -While `babel-jest` by default will transpile TypeScript files, Babel will not verify the types. If you want that you can use [`ts-jest`](https://github.com/kulshekhar/ts-jest). - -#### Transforming images to their path - -Importing images is a way to include them in your browser bundle, but they are not valid JavaScript. One way of handling it in Jest is to replace the imported value with its filename. - -```js title="fileTransformer.js" -const path = require('path'); - -module.exports = { - process(src, filename, config, options) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; - }, -}; -``` - -```js title="jest.config.js" -module.exports = { - transform: { - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': - '/fileTransformer.js', - }, -}; -``` diff --git a/website/versioned_docs/version-27.2/Configuration.md b/website/versioned_docs/version-27.2/Configuration.md deleted file mode 100644 index 82773cfbeaf6..000000000000 --- a/website/versioned_docs/version-27.2/Configuration.md +++ /dev/null @@ -1,1463 +0,0 @@ ---- -id: configuration -title: Configuring Jest ---- - -Jest's configuration can be defined in the `package.json` file of your project, or through a `jest.config.js`, or `jest.config.ts` file or through the `--config ` option. If you'd like to use your `package.json` to store Jest's config, the `"jest"` key should be used on the top level so Jest will know how to find your settings: - -```json -{ - "name": "my-project", - "jest": { - "verbose": true - } -} -``` - -Or through JavaScript: - -```js title="jest.config.js" -// Sync object -/** @type {import('@jest/types').Config.InitialOptions} */ -const config = { - verbose: true, -}; - -module.exports = config; - -// Or async function -module.exports = async () => { - return { - verbose: true, - }; -}; -``` - -Or through TypeScript (if `ts-node` is installed): - -```ts title="jest.config.ts" -import type {Config} from '@jest/types'; - -// Sync object -const config: Config.InitialOptions = { - verbose: true, -}; -export default config; - -// Or async function -export default async (): Promise => { - return { - verbose: true, - }; -}; -``` - -Please keep in mind that the resulting configuration must be JSON-serializable. - -When using the `--config` option, the JSON file must not contain a "jest" key: - -```json -{ - "bail": 1, - "verbose": true -} -``` - -## Options - -These options let you control Jest's behavior in your `package.json` file. The Jest philosophy is to work great by default, but sometimes you just need more configuration power. - -### Defaults - -You can retrieve Jest's default options to expand them if needed: - -```js title="jest.config.js" -const {defaults} = require('jest-config'); -module.exports = { - // ... - moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'], - // ... -}; -``` - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `automock` \[boolean] - -Default: `false` - -This option tells Jest that all imported modules in your tests should be mocked automatically. All modules used in your tests will have a replacement implementation, keeping the API surface. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js -//__tests__/automocking.test.js -import utils from '../utils'; - -test('if utils mocked automatically', () => { - // Public methods of `utils` are now mock functions - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized.mock).toBeTruthy(); - - // You can provide them with your own implementation - // or pass the expected return value - utils.authorize.mockReturnValue('mocked_token'); - utils.isAuthorized.mockReturnValue(true); - - expect(utils.authorize()).toBe('mocked_token'); - expect(utils.isAuthorized('not_wizard')).toBeTruthy(); -}); -``` - -_Note: Node modules are automatically mocked when you have a manual mock in place (e.g.: `__mocks__/lodash.js`). More info [here](manual-mocks#mocking-node-modules)._ - -_Note: Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`._ - -### `bail` \[number | boolean] - -Default: `0` - -By default, Jest runs all tests and produces all errors into the console upon completion. The bail config option can be used here to have Jest stop running tests after `n` failures. Setting bail to `true` is the same as setting bail to `1`. - -### `cacheDirectory` \[string] - -Default: `"/tmp/"` - -The directory where Jest should store its cached dependency information. - -Jest attempts to scan your dependency tree once (up-front) and cache it in order to ease some of the filesystem churn that needs to happen while running tests. This config option lets you customize where Jest stores that cache data on disk. - -### `clearMocks` \[boolean] - -Default: `false` - -Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided. - -### `collectCoverage` \[boolean] - -Default: `false` - -Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. - -### `collectCoverageFrom` \[array] - -Default: `undefined` - -An array of [glob patterns](https://github.com/micromatch/micromatch) indicating a set of files for which coverage information should be collected. If a file matches the specified glob pattern, coverage information will be collected for it even if no tests exist for this file and it's never required in the test suite. - -Example: - -```json -{ - "collectCoverageFrom": [ - "**/*.{js,jsx}", - "!**/node_modules/**", - "!**/vendor/**" - ] -} -``` - -This will collect coverage information for all the files inside the project's `rootDir`, except the ones that match `**/node_modules/**` or `**/vendor/**`. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__tests__/**", "**/*.js"]` will not exclude `__tests__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/*.js`.)_ - -_Note: This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`._ - -
- Help: - If you are seeing coverage output such as... - -``` -=============================== Coverage summary =============================== -Statements : Unknown% ( 0/0 ) -Branches : Unknown% ( 0/0 ) -Functions : Unknown% ( 0/0 ) -Lines : Unknown% ( 0/0 ) -================================================================================ -Jest: Coverage data for global was not found. -``` - -Most likely your glob patterns are not matching any files. Refer to the [micromatch](https://github.com/micromatch/micromatch) documentation to ensure your globs are compatible. - -
- -### `coverageDirectory` \[string] - -Default: `undefined` - -The directory where Jest should output its coverage files. - -### `coveragePathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all file paths before executing the test. If the file path matches any of the patterns, coverage information will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `coverageProvider` \[string] - -Indicates which provider should be used to instrument code for coverage. Allowed values are `babel` (default) or `v8`. - -Note that using `v8` is considered experimental. This uses V8's builtin code coverage rather than one based on Babel. It is not as well tested, and it has also improved in the last few releases of Node. Using the latest versions of node (v14 at the time of this writing) will yield better results. - -### `coverageReporters` \[array<string | \[string, options]>] - -Default: `["clover", "json", "lcov", "text"]` - -A list of reporter names that Jest uses when writing coverage reports. Any [istanbul reporter](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) can be used. - -_Note: Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output._ - -Additional options can be passed using the tuple form. For example, you may hide coverage report lines for all fully-covered files: - -```json -{ - "coverageReporters": ["clover", "json", "lcov", ["text", {"skipFull": true}]] -} -``` - -For more information about the options object shape refer to `CoverageReporterWithOptions` type in the [type definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Config.ts). - -### `coverageThreshold` \[object] - -Default: `undefined` - -This will be used to configure minimum threshold enforcement for coverage results. Thresholds can be specified as `global`, as a [glob](https://github.com/isaacs/node-glob#glob-primer), and as a directory or file path. If thresholds aren't met, jest will fail. Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed. - -For example, with the following configuration jest will fail if there is less than 80% branch, line, and function coverage, or if there are more than 10 uncovered statements: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 80, - "functions": 80, - "lines": 80, - "statements": -10 - } - } - } -} -``` - -If globs or paths are specified alongside `global`, coverage data for matching paths will be subtracted from overall coverage and thresholds will be applied independently. Thresholds for globs are applied to all files matching the glob. If the file specified by path is not found, an error is returned. - -For example, with the following configuration: - -```json -{ - ... - "jest": { - "coverageThreshold": { - "global": { - "branches": 50, - "functions": 50, - "lines": 50, - "statements": 50 - }, - "./src/components/": { - "branches": 40, - "statements": 40 - }, - "./src/reducers/**/*.js": { - "statements": 90 - }, - "./src/api/very-important-module.js": { - "branches": 100, - "functions": 100, - "lines": 100, - "statements": 100 - } - } - } -} -``` - -Jest will fail if: - -- The `./src/components` directory has less than 40% branch or statement coverage. -- One of the files matching the `./src/reducers/**/*.js` glob has less than 90% statement coverage. -- The `./src/api/very-important-module.js` file has less than 100% coverage. -- Every remaining file combined has less than 50% coverage (`global`). - -### `dependencyExtractor` \[string] - -Default: `undefined` - -This option allows the use of a custom dependency extractor. It must be a node module that exports an object with an `extract` function. E.g.: - -```javascript -const crypto = require('crypto'); -const fs = require('fs'); - -module.exports = { - extract(code, filePath, defaultExtract) { - const deps = defaultExtract(code, filePath); - // Scan the file and add dependencies in `deps` (which is a `Set`) - return deps; - }, - getCacheKey() { - return crypto - .createHash('md5') - .update(fs.readFileSync(__filename)) - .digest('hex'); - }, -}; -``` - -The `extract` function should return an iterable (`Array`, `Set`, etc.) with the dependencies found in the code. - -That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded. - -### `displayName` \[string, object] - -default: `undefined` - -Allows for a label to be printed alongside a test while it is running. This becomes more useful in multi-project repositories where there can be many jest configuration files. This visually tells which project a test belongs to. Here are sample valid values. - -```js -module.exports = { - displayName: 'CLIENT', -}; -``` - -or - -```js -module.exports = { - displayName: { - name: 'CLIENT', - color: 'blue', - }, -}; -``` - -As a secondary option, an object with the properties `name` and `color` can be passed. This allows for a custom configuration of the background color of the displayName. `displayName` defaults to white when its value is a string. Jest uses [chalk](https://github.com/chalk/chalk) to provide the color. As such, all of the valid options for colors supported by chalk are also supported by jest. - -### `errorOnDeprecated` \[boolean] - -Default: `false` - -Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. - -### `extensionsToTreatAsEsm` \[array<string>] - -Default: `[]` - -Jest will run `.mjs` and `.js` files with nearest `package.json`'s `type` field set to `module` as ECMAScript Modules. If you have any other files that should run with native ESM, you need to specify their file extension here. - -> Note: Jest's ESM support is still experimental, see [its docs for more details](ECMAScriptModules.md). - -```json -{ - ... - "jest": { - "extensionsToTreatAsEsm": [".ts"] - } -} -``` - -### `extraGlobals` \[array<string>] - -Default: `undefined` - -Test files run inside a [vm](https://nodejs.org/api/vm.html), which slows calls to global context properties (e.g. `Math`). With this option you can specify extra properties to be defined inside the vm for faster lookups. - -For example, if your tests call `Math` often, you can pass it by setting `extraGlobals`. - -```json -{ - ... - "jest": { - "extraGlobals": ["Math"] - } -} -``` - -### `forceCoverageMatch` \[array<string>] - -Default: `['']` - -Test files are normally ignored from collecting code coverage. With this option, you can overwrite this behavior and include otherwise ignored files in code coverage. - -For example, if you have tests in source files named with `.t.js` extension as following: - -```javascript title="sum.t.js" -export function sum(a, b) { - return a + b; -} - -if (process.env.NODE_ENV === 'test') { - test('sum', () => { - expect(sum(1, 2)).toBe(3); - }); -} -``` - -You can collect coverage from those files with setting `forceCoverageMatch`. - -```json -{ - ... - "jest": { - "forceCoverageMatch": ["**/*.t.js"] - } -} -``` - -### `globals` \[object] - -Default: `{}` - -A set of global variables that need to be available in all test environments. - -For example, the following would create a global `__DEV__` variable set to `true` in all test environments: - -```json -{ - ... - "jest": { - "globals": { - "__DEV__": true - } - } -} -``` - -Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will _not_ be persisted across test runs for other test files. In addition, the `globals` object must be json-serializable, so it can't be used to specify global functions. For that, you should use `setupFiles`. - -### `globalSetup` \[string] - -Default: `undefined` - -This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites._ - -_Note: While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation._ - -Example: - -```js title="setup.js" -// can be synchronous -module.exports = async () => { - // ... - // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongod; -}; -``` - -```js title="teardown.js" -module.exports = async function () { - await global.__MONGOD__.stop(); -}; -``` - -### `globalTeardown` \[string] - -Default: `undefined` - -This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. This function gets Jest's `globalConfig` object as a parameter. - -_Note: A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ - -_Note: The same caveat concerning transformation of `node_modules` as for `globalSetup` applies to `globalTeardown`._ - -### `haste` \[object] - -Default: `undefined` - -This will be used to configure the behavior of `jest-haste-map`, Jest's internal file crawler/cache system. The following options are supported: - -```ts -type HasteConfig = { - /** Whether to hash files using SHA-1. */ - computeSha1?: boolean; - /** The platform to use as the default, e.g. 'ios'. */ - defaultPlatform?: string | null; - /** Force use of Node's `fs` APIs rather than shelling out to `find` */ - forceNodeFilesystemAPI?: boolean; - /** - * Whether to follow symlinks when crawling for files. - * This options cannot be used in projects which use watchman. - * Projects with `watchman` set to true will error if this option is set to true. - */ - enableSymlinks?: boolean; - /** Path to a custom implementation of Haste. */ - hasteImplModulePath?: string; - /** All platforms to target, e.g ['ios', 'android']. */ - platforms?: Array; - /** Whether to throw on error on module collision. */ - throwOnModuleCollision?: boolean; - /** Custom HasteMap module */ - hasteMapModulePath?: string; -}; -``` - -### `injectGlobals` \[boolean] - -Default: `true` - -Insert Jest's globals (`expect`, `test`, `describe`, `beforeEach` etc.) into the global environment. If you set this to `false`, you should import from `@jest/globals`, e.g. - -```ts -import {expect, jest, test} from '@jest/globals'; - -jest.useFakeTimers(); - -test('some test', () => { - expect(Date.now()).toBe(0); -}); -``` - -_Note: This option is only supported using the default `jest-circus`. test runner_ - -### `maxConcurrency` \[number] - -Default: `5` - -A number limiting the number of tests that are allowed to run at the same time when using `test.concurrent`. Any test above this limit will be queued and executed once a slot is released. - -### `maxWorkers` \[number | string] - -Specifies the maximum number of workers the worker-pool will spawn for running tests. In single run mode, this defaults to the number of the cores available on your machine minus one for the main thread. In watch mode, this defaults to half of the available cores on your machine to ensure Jest is unobtrusive and does not grind your machine to a halt. It may be useful to adjust this in resource limited environments like CIs but the defaults should be adequate for most use-cases. - -For environments with variable CPUs available, you can use percentage based configuration: `"maxWorkers": "50%"` - -### `moduleDirectories` \[array<string>] - -Default: `["node_modules"]` - -An array of directory names to be searched recursively up from the requiring module's location. Setting this option will _override_ the default, if you wish to still search `node_modules` for packages include it along with any other options: `["node_modules", "bower_components"]` - -### `moduleFileExtensions` \[array<string>] - -Default: `["js", "jsx", "ts", "tsx", "json", "node"]` - -An array of file extensions your modules use. If you require modules without specifying a file extension, these are the extensions Jest will look for, in left-to-right order. - -We recommend placing the extensions most commonly used in your project on the left, so if you are using TypeScript, you may want to consider moving "ts" and/or "tsx" to the beginning of the array. - -### `moduleNameMapper` \[object<string, string | array<string>>] - -Default: `null` - -A map from regular expressions to module names or to arrays of module names that allow to stub out resources, like images or styles with a single module. - -Modules that are mapped to an alias are unmocked by default, regardless of whether automocking is enabled or not. - -Use `` string token to refer to [`rootDir`](#rootdir-string) value if you want to use file paths. - -Additionally, you can substitute captured regex groups using numbered backreferences. - -Example: - -```json -{ - "moduleNameMapper": { - "^image![a-zA-Z0-9$_-]+$": "GlobalImageStub", - "^[./a-zA-Z0-9$_-]+\\.png$": "/RelativeImageStub.js", - "module_name_(.*)": "/substituted_module_$1.js", - "assets/(.*)": [ - "/images/$1", - "/photos/$1", - "/recipes/$1" - ] - } -} -``` - -The order in which the mappings are defined matters. Patterns are checked one by one until one fits. The most specific rule should be listed first. This is true for arrays of module names as well. - -_Note: If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub._ - -### `modulePathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all module paths before those paths are to be considered 'visible' to the module loader. If a given module's path matches any of the patterns, it will not be `require()`-able in the test environment. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/"]`. - -### `modulePaths` \[array<string>] - -Default: `[]` - -An alternative API to setting the `NODE_PATH` env variable, `modulePaths` is an array of absolute paths to additional locations to search when resolving modules. Use the `` string token to include the path to your project's root directory. Example: `["/app/"]`. - -### `notify` \[boolean] - -Default: `false` - -Activates notifications for test results. - -**Beware:** Jest uses [node-notifier](https://github.com/mikaelbr/node-notifier) to display desktop notifications. On Windows, it creates a new start menu entry on the first use and not display the notification. Notifications will be properly displayed on subsequent runs - -### `notifyMode` \[string] - -Default: `failure-change` - -Specifies notification mode. Requires `notify: true`. - -#### Modes - -- `always`: always send a notification. -- `failure`: send a notification when tests fail. -- `success`: send a notification when tests pass. -- `change`: send a notification when the status changed. -- `success-change`: send a notification when tests pass or once when it fails. -- `failure-change`: send a notification when tests fail or once when it passes. - -### `preset` \[string] - -Default: `undefined` - -A preset that is used as a base for Jest's configuration. A preset should point to an npm module that has a `jest-preset.json`, `jest-preset.js`, `jest-preset.cjs` or `jest-preset.mjs` file at the root. - -For example, this preset `foo-bar/jest-preset.js` will be configured as follows: - -```json -{ - "preset": "foo-bar" -} -``` - -Presets may also be relative to filesystem paths. - -```json -{ - "preset": "./node_modules/foo-bar/jest-preset.js" -} -``` - -### `prettierPath` \[string] - -Default: `'prettier'` - -Sets the path to the [`prettier`](https://prettier.io/) node module used to update inline snapshots. - -### `projects` \[array<string | ProjectConfig>] - -Default: `undefined` - -When the `projects` configuration is provided with an array of paths or glob patterns, Jest will run tests in all of the specified projects at the same time. This is great for monorepos or when working on multiple projects at the same time. - -```json -{ - "projects": ["", "/examples/*"] -} -``` - -This example configuration will run Jest in the root directory as well as in every folder in the examples directory. You can have an unlimited amount of projects running in the same Jest instance. - -The projects feature can also be used to run multiple configurations or multiple [runners](#runner-string). For this purpose, you can pass an array of configuration objects. For example, to run both tests and ESLint (via [jest-runner-eslint](https://github.com/jest-community/jest-runner-eslint)) in the same invocation of Jest: - -```json -{ - "projects": [ - { - "displayName": "test" - }, - { - "displayName": "lint", - "runner": "jest-runner-eslint", - "testMatch": ["/**/*.js"] - } - ] -} -``` - -_Note: When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._ - -### `reporters` \[array<moduleName | \[moduleName, options]>] - -Default: `undefined` - -Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs. - -If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name. - -This will override default reporters: - -```json -{ - "reporters": ["/my-custom-reporter.js"] -} -``` - -This will use custom reporter in addition to default reporters that Jest provides: - -```json -{ - "reporters": ["default", "/my-custom-reporter.js"] -} -``` - -Additionally, custom reporters can be configured by passing an `options` object as a second argument: - -```json -{ - "reporters": [ - "default", - ["/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}] - ] -} -``` - -Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments: - -Example reporter: - -```js title="my-custom-reporter.js" -class MyCustomReporter { - constructor(globalConfig, options) { - this._globalConfig = globalConfig; - this._options = options; - } - - onRunComplete(contexts, results) { - console.log('Custom reporter output:'); - console.log('GlobalConfig: ', this._globalConfig); - console.log('Options: ', this._options); - } -} - -module.exports = MyCustomReporter; -// or export default MyCustomReporter; -``` - -Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods - -```js -class MyCustomReporter { - // ... - getLastError() { - if (this._shouldFail) { - return new Error('my-custom-reporter.js reported an error'); - } - } -} -``` - -For the full list of methods and argument types see `Reporter` interface in [packages/jest-reporters/src/types.ts](https://github.com/facebook/jest/blob/main/packages/jest-reporters/src/types.ts) - -### `resetMocks` \[boolean] - -Default: `false` - -Automatically reset mock state before every test. Equivalent to calling [`jest.resetAllMocks()`](JestObjectAPI.md#jestresetallmocks) before each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. - -### `resetModules` \[boolean] - -Default: `false` - -By default, each test file gets its own independent module registry. Enabling `resetModules` goes a step further and resets the module registry before running each individual test. This is useful to isolate modules for every test so that the local module state doesn't conflict between tests. This can be done programmatically using [`jest.resetModules()`](JestObjectAPI.md#jestresetmodules). - -### `resolver` \[string] - -Default: `undefined` - -This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: - -```json -{ - "basedir": string, - "conditions": [string], - "defaultResolver": "function(request, options)", - "extensions": [string], - "moduleDirectory": [string], - "paths": [string], - "packageFilter": "function(pkg, pkgdir)", - "pathFilter": "function(pkg, path, relativePath)", - "rootDir": [string] -} -``` - -The function should either return a path to the module that should be resolved or throw an error if the module can't be found. - -Note: the defaultResolver passed as an option is the Jest default resolver which might be useful when you write your custom one. It takes the same arguments as your custom one, e.g. `(request, options)`. - -For example, if you want to respect Browserify's [`"browser"` field](https://github.com/browserify/browserify-handbook/blob/master/readme.markdown#browser-field), you can use the following configuration: - -```json -{ - ... - "jest": { - "resolver": "/resolver.js" - } -} -``` - -```js title="resolver.js" -const browserResolve = require('browser-resolve'); - -module.exports = browserResolve.sync; -``` - -By combining `defaultResolver` and `packageFilter` we can implement a `package.json` "pre-processor" that allows us to change how the default resolver will resolve modules. For example, imagine we want to use the field `"module"` if it is present, otherwise fallback to `"main"`: - -```json -{ - ... - "jest": { - "resolver": "my-module-resolve" - } -} -``` - -```js -// my-module-resolve package - -module.exports = (request, options) => { - // Call the defaultResolver, so we leverage its cache, error handling, etc. - return options.defaultResolver(request, { - ...options, - // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb) - packageFilter: pkg => { - return { - ...pkg, - // Alter the value of `main` before resolving the package - main: pkg.module || pkg.main, - }; - }, - }); -}; -``` - -While Jest does not support [package `exports`](https://nodejs.org/api/packages.html#packages_package_entry_points) (beyond `main`), Jest will provide `conditions` as an option when calling custom resolvers, which can be used to implement support for `exports` in userland. Jest will pass `['import', 'default']` when running a test in ESM mode, and `['require', 'default']` when running with CJS. Additionally, custom test environments can specify an `exportConditions` method which returns an array of conditions that will be passed along with Jest's defaults. - -### `restoreMocks` \[boolean] - -Default: `false` - -Automatically restore mock state and implementation before every test. Equivalent to calling [`jest.restoreAllMocks()`](JestObjectAPI.md#jestrestoreallmocks) before each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. - -### `rootDir` \[string] - -Default: The root of the directory containing your Jest [config file](#) _or_ the `package.json` _or_ the [`pwd`](http://en.wikipedia.org/wiki/Pwd) if no `package.json` is found - -The root directory that Jest should scan for tests and modules within. If you put your Jest config inside your `package.json` and want the root directory to be the root of your repo, the value for this config param will default to the directory of the `package.json`. - -Oftentimes, you'll want to set this to `'src'` or `'lib'`, corresponding to where in your repository the code is stored. - -_Note that using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`._ - -### `roots` \[array<string>] - -Default: `[""]` - -A list of paths to directories that Jest should use to search for files in. - -There are times where you only want Jest to search in a single sub-directory (such as cases where you have a `src/` directory in your repo), but prevent it from accessing the rest of the repo. - -_Note: While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`)._ - -_Note: By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`._ - -### `runner` \[string] - -Default: `"jest-runner"` - -This option allows you to use a custom runner instead of Jest's default test runner. Examples of runners include: - -- [`jest-runner-eslint`](https://github.com/jest-community/jest-runner-eslint) -- [`jest-runner-mocha`](https://github.com/rogeliog/jest-runner-mocha) -- [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc) -- [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier) - -_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._ - -To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature: - -```ts -async function runTests( - tests: Array, - watcher: TestWatcher, - onStart: OnTestStart, - onResult: OnTestSuccess, - onFailure: OnTestFailure, - options: TestRunnerOptions, -): Promise; -``` - -If you need to restrict your test-runner to only run in serial rather than being executed in parallel your class should have the property `isSerial` to be set as `true`. - -### `setupFiles` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment before executing [`setupFilesAfterEnv`](#setupfilesafterenv-array) and before the test code itself. - -### `setupFilesAfterEnv` \[array] - -Default: `[]` - -A list of paths to modules that run some code to configure or set up the testing framework before each test file in the suite is executed. Since [`setupFiles`](#setupfiles-array) executes before the test framework is installed in the environment, this script file presents you the opportunity of running some code immediately after the test framework has been installed in the environment but before the test code itself. - -If you want a path to be [relative to the root directory of your project](#rootdir-string), please include `` inside a path's string, like `"/a-configs-folder"`. - -For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. - -Example `setupFilesAfterEnv` array in a jest.config.js: - -```js -module.exports = { - setupFilesAfterEnv: ['./jest.setup.js'], -}; -``` - -Example `jest.setup.js` file - -```js -jest.setTimeout(10000); // in milliseconds -``` - -### `slowTestThreshold` \[number] - -Default: `5` - -The number of seconds after which a test is considered as slow and reported as such in the results. - -### `snapshotFormat` \[object] - -Default: `undefined` - -Allows overriding specific snapshot formatting options documented in the [pretty-format readme](https://www.npmjs.com/package/pretty-format#usage-with-options), with the exceptions of `compareKeys` and `plugins`. For example, this config would have the snapshot formatter not print a prefix for "Object" and "Array": - -```json -{ - "jest": { - "snapshotFormat": { - "printBasicPrototype": false - } - } -} -``` - -```ts -import {expect, test} from '@jest/globals'; - -test('does not show prototypes for object and array inline', () => { - const object = { - array: [{hello: 'Danger'}], - }; - expect(object).toMatchInlineSnapshot(` -{ - "array": [ - { - "hello": "Danger", - }, - ], -} - `); -}); -``` - -### `snapshotResolver` \[string] - -Default: `undefined` - -The path to a module that can resolve test<->snapshot path. This config option lets you customize where Jest stores snapshot files on disk. - -Example snapshot resolver module: - -```js -module.exports = { - // resolves from test to snapshot path - resolveSnapshotPath: (testPath, snapshotExtension) => - testPath.replace('__tests__', '__snapshots__') + snapshotExtension, - - // resolves from snapshot to test path - resolveTestPath: (snapshotFilePath, snapshotExtension) => - snapshotFilePath - .replace('__snapshots__', '__tests__') - .slice(0, -snapshotExtension.length), - - // Example test path, used for preflight consistency check of the implementation above - testPathForConsistencyCheck: 'some/__tests__/example.test.js', -}; -``` - -### `snapshotSerializers` \[array<string>] - -Default: `[]` - -A list of paths to snapshot serializer modules Jest should use for snapshot testing. - -Jest has default serializers for built-in JavaScript types, HTML elements (Jest 20.0.0+), ImmutableJS (Jest 20.0.0+) and for React elements. See [snapshot test tutorial](TutorialReactNative.md#snapshot-test) for more information. - -Example serializer module: - -```js -// my-serializer-module -module.exports = { - serialize(val, config, indentation, depth, refs, printer) { - return `Pretty foo: ${printer(val.foo)}`; - }, - - test(val) { - return val && Object.prototype.hasOwnProperty.call(val, 'foo'); - }, -}; -``` - -`printer` is a function that serializes a value using existing plugins. - -To use `my-serializer-module` as a serializer, configuration would be as follows: - -```json -{ - ... - "jest": { - "snapshotSerializers": ["my-serializer-module"] - } -} -``` - -Finally tests would look as follows: - -```js -test(() => { - const bar = { - foo: { - x: 1, - y: 2, - }, - }; - - expect(bar).toMatchSnapshot(); -}); -``` - -Rendered snapshot: - -```json -Pretty foo: Object { - "x": 1, - "y": 2, -} -``` - -To make a dependency explicit instead of implicit, you can call [`expect.addSnapshotSerializer`](ExpectAPI.md#expectaddsnapshotserializerserializer) to add a module for an individual test file instead of adding its path to `snapshotSerializers` in Jest configuration. - -More about serializers API can be found [here](https://github.com/facebook/jest/tree/main/packages/pretty-format/README.md#serialize). - -### `testEnvironment` \[string] - -Default: `"node"` - -The test environment that will be used for testing. The default environment in Jest is a Node.js environment. If you are building a web app, you can use a browser-like environment through [`jsdom`](https://github.com/jsdom/jsdom) instead. - -By adding a `@jest-environment` docblock at the top of the file, you can specify another environment to be used for all tests in that file: - -```js -/** - * @jest-environment jsdom - */ - -test('use jsdom in this test file', () => { - const element = document.createElement('div'); - expect(element).not.toBeNull(); -}); -``` - -You can create your own module that will be used for setting up the test environment. The module must export a class with `setup`, `teardown` and `getVmContext` methods. You can also pass variables from this module to your test suites by assigning them to `this.global` object – this will make them available in your test suites as global variables. - -The class may optionally expose an asynchronous `handleTestEvent` method to bind to events fired by [`jest-circus`](https://github.com/facebook/jest/tree/main/packages/jest-circus). Normally, `jest-circus` test runner would pause until a promise returned from `handleTestEvent` gets fulfilled, **except for the next events**: `start_describe_definition`, `finish_describe_definition`, `add_hook`, `add_test` or `error` (for the up-to-date list you can look at [SyncEvent type in the types definitions](https://github.com/facebook/jest/tree/main/packages/jest-types/src/Circus.ts)). That is caused by backward compatibility reasons and `process.on('unhandledRejection', callback)` signature, but that usually should not be a problem for most of the use cases. - -Any docblock pragmas in test files will be passed to the environment constructor and can be used for per-test configuration. If the pragma does not have a value, it will be present in the object with its value set to an empty string. If the pragma is not present, it will not be present in the object. - -To use this class as your custom environment, refer to it by its full path within the project. For example, if your class is stored in `my-custom-environment.js` in some subfolder of your project, then the annotation might look like this: - -```js -/** - * @jest-environment ./src/test/my-custom-environment - */ -``` - -_Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ - -Example: - -```js -// my-custom-environment -const NodeEnvironment = require('jest-environment-node'); - -class CustomEnvironment extends NodeEnvironment { - constructor(config, context) { - super(config, context); - this.testPath = context.testPath; - this.docblockPragmas = context.docblockPragmas; - } - - async setup() { - await super.setup(); - await someSetupTasks(this.testPath); - this.global.someGlobalObject = createGlobalObject(); - - // Will trigger if docblock contains @my-custom-pragma my-pragma-value - if (this.docblockPragmas['my-custom-pragma'] === 'my-pragma-value') { - // ... - } - } - - async teardown() { - this.global.someGlobalObject = destroyGlobalObject(); - await someTeardownTasks(); - await super.teardown(); - } - - getVmContext() { - return super.getVmContext(); - } - - async handleTestEvent(event, state) { - if (event.name === 'test_start') { - // ... - } - } -} - -module.exports = CustomEnvironment; -``` - -```js -// my-test-suite -/** - * @jest-environment ./my-custom-environment - */ -let someGlobalObject; - -beforeAll(() => { - someGlobalObject = global.someGlobalObject; -}); -``` - -### `testEnvironmentOptions` \[Object] - -Default: `{}` - -Test environment options that will be passed to the `testEnvironment`. The relevant options depend on the environment. For example, you can override options given to [jsdom](https://github.com/jsdom/jsdom) such as `{userAgent: "Agent/007"}`. - -### `testFailureExitCode` \[number] - -Default: `1` - -The exit code Jest returns on test failure. - -_Note: This does not change the exit code in the case of Jest errors (e.g. invalid configuration)._ - -### `testMatch` \[array<string>] - -(default: `[ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]`) - -The glob patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. - -See the [micromatch](https://github.com/micromatch/micromatch) package for details of the patterns you can specify. - -See also [`testRegex` [string | array<string>]](#testregex-string--arraystring), but note that you cannot specify both options. - -_Note: Each glob pattern is applied in the order they are specified in the config. (For example `["!**/__fixtures__/**", "**/__tests__/**/*.js"]` will not exclude `__fixtures__` because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after `**/__tests__/**/*.js`.)_ - -### `testPathIgnorePatterns` \[array<string>] - -Default: `["/node_modules/"]` - -An array of regexp pattern strings that are matched against all test paths before executing the test. If the test path matches any of the patterns, it will be skipped. - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/build/", "/node_modules/"]`. - -### `testRegex` \[string | array<string>] - -Default: `(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$` - -The pattern or patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array<string>]](#testmatch-arraystring), but note that you cannot specify both options. - -The following is a visualization of the default regex: - -```bash -├── __tests__ -│ └── component.spec.js # test -│ └── anything # test -├── package.json # not test -├── foo.test.js # test -├── bar.spec.jsx # test -└── component.js # not test -``` - -_Note: `testRegex` will try to detect test files using the **absolute file path**, therefore, having a folder with a name that matches it will run all the files as tests_ - -### `testResultsProcessor` \[string] - -Default: `undefined` - -This option allows the use of a custom results processor. This processor must be a node module that exports a function expecting an object with the following structure as the first argument and return it: - -```json -{ - "success": boolean, - "startTime": epoch, - "numTotalTestSuites": number, - "numPassedTestSuites": number, - "numFailedTestSuites": number, - "numRuntimeErrorTestSuites": number, - "numTotalTests": number, - "numPassedTests": number, - "numFailedTests": number, - "numPendingTests": number, - "numTodoTests": number, - "openHandles": Array, - "testResults": [{ - "numFailingTests": number, - "numPassingTests": number, - "numPendingTests": number, - "testResults": [{ - "title": string (message in it block), - "status": "failed" | "pending" | "passed", - "ancestorTitles": [string (message in describe blocks)], - "failureMessages": [string], - "numPassingAsserts": number, - "location": { - "column": number, - "line": number - } - }, - ... - ], - "perfStats": { - "start": epoch, - "end": epoch - }, - "testFilePath": absolute path to test file, - "coverage": {} - }, - "testExecError:" (exists if there was a top-level failure) { - "message": string - "stack": string - } - ... - ] -} -``` - -`testResultsProcessor` and `reporters` are very similar to each other. One difference is that a test result processor only gets called after all tests finished. Whereas a reporter has the ability to receive test results after individual tests and/or test suites are finished. - -### `testRunner` \[string] - -Default: `jest-circus/runner` - -This option allows the use of a custom test runner. The default is `jest-circus`. A custom test runner can be provided by specifying a path to a test runner implementation. - -The test runner module must export a function with the following signature: - -```ts -function testRunner( - globalConfig: GlobalConfig, - config: ProjectConfig, - environment: Environment, - runtime: Runtime, - testPath: string, -): Promise; -``` - -An example of such function can be found in our default [jasmine2 test runner package](https://github.com/facebook/jest/blob/main/packages/jest-jasmine2/src/index.ts). - -### `testSequencer` \[string] - -Default: `@jest/test-sequencer` - -This option allows you to use a custom sequencer instead of Jest's default. `sort` may optionally return a Promise. - -Example: - -Sort test path alphabetically. - -```js title="testSequencer.js" -const Sequencer = require('@jest/test-sequencer').default; - -class CustomSequencer extends Sequencer { - sort(tests) { - // Test structure information - // https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21 - const copyTests = Array.from(tests); - return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); - } -} - -module.exports = CustomSequencer; -``` - -Use it in your Jest config file like this: - -```json -{ - "testSequencer": "path/to/testSequencer.js" -} -``` - -### `testTimeout` \[number] - -Default: `5000` - -Default timeout of a test in milliseconds. - -### `testURL` \[string] - -Default: `http://localhost` - -This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. - -### `timers` \[string] - -Default: `real` - -Setting this value to `fake` or `modern` enables fake timers for all tests by default. Fake timers are useful when a piece of code sets a long timeout that we don't want to wait for in a test. You can learn more about fake timers [here](JestObjectAPI.md#jestusefaketimersimplementation-modern--legacy). - -If the value is `legacy`, the old implementation will be used as implementation instead of one backed by [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -### `transform` \[object<string, pathToTransformer | \[pathToTransformer, object]>] - -Default: `{"\\.[jt]sx?$": "babel-jest"}` - -A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that aren't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/main/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md). - -Examples of such compilers include: - -- [Babel](https://babeljs.io/) -- [TypeScript](http://www.typescriptlang.org/) -- To build your own please visit the [Custom Transformer](CodeTransformation.md#writing-custom-transformers) section - -You can pass configuration to a transformer like `{filePattern: ['path-to-transformer', {options}]}` For example, to configure babel-jest for non-default behavior, `{"\\.js$": ['babel-jest', {rootMode: "upward"}]}` - -_Note: a transformer is only run once per file unless the file has changed. During the development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues)._ - -_Note: when adding additional code transformers, this will overwrite the default config and `babel-jest` is no longer automatically loaded. If you want to use it to compile JavaScript or Typescript, it has to be explicitly defined by adding `{"\\.[jt]sx?$": "babel-jest"}` to the transform property. See [babel-jest plugin](https://github.com/facebook/jest/tree/main/packages/babel-jest#setup)_ - -A transformer must be an object with at least a `process` function, and it's also recommended to include a `getCacheKey` function. If your transformer is written in ESM you should have a default export with that object. - -If the tests are written using [native ESM](ECMAScriptModules.md) the transformer can export `processAsync` and `getCacheKeyAsync` instead or in addition to the synchronous variants. - -### `transformIgnorePatterns` \[array<string>] - -Default: `["/node_modules/", "\\.pnp\\.[^\\\/]+$"]` - -An array of regexp pattern strings that are matched against all source file paths before transformation. If the file path matches **any** of the patterns, it will not be transformed. - -Providing regexp patterns that overlap with each other may result in files not being transformed that you expected to be transformed. For example: - -```json -{ - "transformIgnorePatterns": ["/node_modules/(?!(foo|bar)/)", "/bar/"] -} -``` - -The first pattern will match (and therefore not transform) files inside `/node_modules` except for those in `/node_modules/foo/` and `/node_modules/bar/`. The second pattern will match (and therefore not transform) files inside any path with `/bar/` in it. With the two together, files in `/node_modules/bar/` will not be transformed because it does match the second pattern, even though it was excluded by the first. - -Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled code. Since all files inside `node_modules` are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use `transformIgnorePatterns` to allow transpiling such modules. You'll find a good example of this use case in [React Native Guide](/docs/tutorial-react-native#transformignorepatterns-customization). - -These pattern strings match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. - -Example: - -```json -{ - "transformIgnorePatterns": [ - "/bower_components/", - "/node_modules/" - ] -} -``` - -### `unmockedModulePathPatterns` \[array<string>] - -Default: `[]` - -An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them. If a module's path matches any of the patterns in this list, it will not be automatically mocked by the module loader. - -This is useful for some commonly used 'utility' modules that are almost always used as implementation details almost all the time (like underscore/lo-dash, etc). It's generally a best practice to keep this list as small as possible and always use explicit `jest.mock()`/`jest.unmock()` calls in individual tests. Explicit per-test setup is far easier for other readers of the test to reason about the environment the test will run in. - -It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. - -### `verbose` \[boolean] - -Default: `false` - -Indicates whether each individual test should be reported during the run. All errors will also still be shown on the bottom after execution. Note that if there is only one test file being run it will default to `true`. - -### `watchPathIgnorePatterns` \[array<string>] - -Default: `[]` - -An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests. - -These patterns match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/node_modules/"]`. - -Even if nothing is specified here, the watcher will ignore changes to the version control folders (.git, .hg). Other hidden files and directories, i.e. those that begin with a dot (`.`), are watched by default. Remember to escape the dot when you add them to `watchPathIgnorePatterns` as it is a special RegExp character. - -Example: - -```json -{ - "watchPathIgnorePatterns": ["/\\.tmp/", "/bar/"] -} -``` - -### `watchPlugins` \[array<string | \[string, Object]>] - -Default: `[]` - -This option allows you to use custom watch plugins. Read more about watch plugins [here](watch-plugins). - -Examples of watch plugins include: - -- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master) -- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects) -- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) -- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) -- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) - -_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._ - -### `watchman` \[boolean] - -Default: `true` - -Whether to use [`watchman`](https://facebook.github.io/watchman/) for file crawling. - -### `//` \[string] - -No default - -This option allows comments in `package.json`. Include the comment text as the value of this key anywhere in `package.json`. - -Example: - -```json -{ - "name": "my-project", - "jest": { - "//": "Comment goes here", - "verbose": true - } -} -``` diff --git a/website/versioned_docs/version-27.2/EnvironmentVariables.md b/website/versioned_docs/version-27.2/EnvironmentVariables.md deleted file mode 100644 index fb50c3e72d08..000000000000 --- a/website/versioned_docs/version-27.2/EnvironmentVariables.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -id: environment-variables -title: Environment Variables ---- - -Jest sets the following environment variables: - -### `NODE_ENV` - -Set to `'test'` if it's not already set to something else. - -### `JEST_WORKER_ID` - -Each worker process is assigned a unique id (index-based that starts with `1`). This is set to `1` for all tests when [`runInBand`](CLI.md#--runinband) is set to true. diff --git a/website/versioned_docs/version-27.2/Es6ClassMocks.md b/website/versioned_docs/version-27.2/Es6ClassMocks.md deleted file mode 100644 index ad2fe2103018..000000000000 --- a/website/versioned_docs/version-27.2/Es6ClassMocks.md +++ /dev/null @@ -1,443 +0,0 @@ ---- -id: es6-class-mocks -title: ES6 Class Mocks ---- - -Jest can be used to mock ES6 classes that are imported into files you want to test. - -ES6 classes are constructor functions with some syntactic sugar. Therefore, any mock for an ES6 class must be a function or an actual ES6 class (which is, again, another function). So you can mock them using [mock functions](MockFunctions.md). - -## An ES6 Class Example - -We'll use a contrived example of a class that plays sound files, `SoundPlayer`, and a consumer class which uses that class, `SoundPlayerConsumer`. We'll mock `SoundPlayer` in our tests for `SoundPlayerConsumer`. - -```javascript title="sound-player.js" -export default class SoundPlayer { - constructor() { - this.foo = 'bar'; - } - - playSoundFile(fileName) { - console.log('Playing sound file ' + fileName); - } -} -``` - -```javascript title="sound-player-consumer.js" -import SoundPlayer from './sound-player'; - -export default class SoundPlayerConsumer { - constructor() { - this.soundPlayer = new SoundPlayer(); - } - - playSomethingCool() { - const coolSoundFileName = 'song.mp3'; - this.soundPlayer.playSoundFile(coolSoundFileName); - } -} -``` - -## The 4 ways to create an ES6 class mock - -### Automatic mock - -Calling `jest.mock('./sound-player')` returns a useful "automatic mock" you can use to spy on calls to the class constructor and all of its methods. It replaces the ES6 class with a mock constructor, and replaces all of its methods with [mock functions](MockFunctions.md) that always return `undefined`. Method calls are saved in `theAutomaticMock.mock.instances[index].methodName.mock.calls`. - -Please note that if you use arrow functions in your classes, they will _not_ be part of the mock. The reason for that is that arrow functions are not present on the object's prototype, they are merely properties holding a reference to a function. - -If you don't need to replace the implementation of the class, this is the easiest option to set up. For example: - -```javascript -import SoundPlayer from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; -jest.mock('./sound-player'); // SoundPlayer is now a mock constructor - -beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - SoundPlayer.mockClear(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayer).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - // Show that mockClear() is working: - expect(SoundPlayer).not.toHaveBeenCalled(); - - const soundPlayerConsumer = new SoundPlayerConsumer(); - // Constructor should have been called again: - expect(SoundPlayer).toHaveBeenCalledTimes(1); - - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - - // mock.instances is available with automatic mocks: - const mockSoundPlayerInstance = SoundPlayer.mock.instances[0]; - const mockPlaySoundFile = mockSoundPlayerInstance.playSoundFile; - expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName); - // Equivalent to above check: - expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName); - expect(mockPlaySoundFile).toHaveBeenCalledTimes(1); -}); -``` - -### Manual mock - -Create a [manual mock](ManualMocks.md) by saving a mock implementation in the `__mocks__` folder. This allows you to specify the implementation, and it can be used across test files. - -```javascript title="__mocks__/sound-player.js" -// Import this named export into your test file: -export const mockPlaySoundFile = jest.fn(); -const mock = jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; -}); - -export default mock; -``` - -Import the mock and the mock method shared by all instances: - -```javascript title="sound-player-consumer.test.js" -import SoundPlayer, {mockPlaySoundFile} from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; -jest.mock('./sound-player'); // SoundPlayer is now a mock constructor - -beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - SoundPlayer.mockClear(); - mockPlaySoundFile.mockClear(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayer).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - expect(mockPlaySoundFile).toHaveBeenCalledWith(coolSoundFileName); -}); -``` - -### Calling [`jest.mock()`](JestObjectAPI.md#jestmockmodulename-factory-options) with the module factory parameter - -`jest.mock(path, moduleFactory)` takes a **module factory** argument. A module factory is a function that returns the mock. - -In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF). - -```javascript -import SoundPlayer from './sound-player'; -const mockPlaySoundFile = jest.fn(); -jest.mock('./sound-player', () => { - return jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; - }); -}); -``` - -:::caution - -Since calls to `jest.mock()` are hoisted to the top of the file, Jest prevents access to out-of-scope variables. By default, you cannot first define a variable and then use it in the factory. Jest will disable this check for variables that start with the word `mock`. However, it is still up to you to guarantee that they will be initialized on time. Be aware of [Temporal Dead Zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz). - -::: - -For example, the following will throw an out-of-scope error due to the use of `fake` instead of `mock` in the variable declaration. - -```javascript -// Note: this will fail -import SoundPlayer from './sound-player'; -const fakePlaySoundFile = jest.fn(); -jest.mock('./sound-player', () => { - return jest.fn().mockImplementation(() => { - return {playSoundFile: fakePlaySoundFile}; - }); -}); -``` - -The following will throw a `ReferenceError` despite using `mock` in the variable declaration, as the `mockSoundPlayer` is not wrapped in an arrow function and thus accessed before initialization after hoisting. - -```javascript -import SoundPlayer from './sound-player'; -const mockSoundPlayer = jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; -}); -// results in a ReferenceError -jest.mock('./sound-player', () => { - return mockSoundPlayer; -}); -``` - -### Replacing the mock using [`mockImplementation()`](MockFunctionAPI.md#mockfnmockimplementationfn) or [`mockImplementationOnce()`](MockFunctionAPI.md#mockfnmockimplementationoncefn) - -You can replace all of the above mocks in order to change the implementation, for a single test or all tests, by calling `mockImplementation()` on the existing mock. - -Calls to jest.mock are hoisted to the top of the code. You can specify a mock later, e.g. in `beforeAll()`, by calling `mockImplementation()` (or `mockImplementationOnce()`) on the existing mock instead of using the factory parameter. This also allows you to change the mock between tests, if needed: - -```javascript -import SoundPlayer from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; - -jest.mock('./sound-player'); - -describe('When SoundPlayer throws an error', () => { - beforeAll(() => { - SoundPlayer.mockImplementation(() => { - return { - playSoundFile: () => { - throw new Error('Test error'); - }, - }; - }); - }); - - it('Should throw an error when calling playSomethingCool', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(() => soundPlayerConsumer.playSomethingCool()).toThrow(); - }); -}); -``` - -## In depth: Understanding mock constructor functions - -Building your constructor function mock using `jest.fn().mockImplementation()` makes mocks appear more complicated than they really are. This section shows how you can create your own mocks to illustrate how mocking works. - -### Manual mock that is another ES6 class - -If you define an ES6 class using the same filename as the mocked class in the `__mocks__` folder, it will serve as the mock. This class will be used in place of the real class. This allows you to inject a test implementation for the class, but does not provide a way to spy on calls. - -For the contrived example, the mock might look like this: - -```javascript title="__mocks__/sound-player.js" -export default class SoundPlayer { - constructor() { - console.log('Mock SoundPlayer: constructor was called'); - } - - playSoundFile() { - console.log('Mock SoundPlayer: playSoundFile was called'); - } -} -``` - -### Mock using module factory parameter - -The module factory function passed to `jest.mock(path, moduleFactory)` can be a HOF that returns a function\*. This will allow calling `new` on the mock. Again, this allows you to inject different behavior for testing, but does not provide a way to spy on calls. - -#### \* Module factory function must return a function - -In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF). - -```javascript -jest.mock('./sound-player', () => { - return function () { - return {playSoundFile: () => {}}; - }; -}); -``` - -**_Note: Arrow functions won't work_** - -Note that the mock can't be an arrow function because calling `new` on an arrow function is not allowed in JavaScript. So this won't work: - -```javascript -jest.mock('./sound-player', () => { - return () => { - // Does not work; arrow functions can't be called with new - return {playSoundFile: () => {}}; - }; -}); -``` - -This will throw **_TypeError: \_soundPlayer2.default is not a constructor_**, unless the code is transpiled to ES5, e.g. by `@babel/preset-env`. (ES5 doesn't have arrow functions nor classes, so both will be transpiled to plain functions.) - -## Mocking a specific method of a class - -Lets say that you want to mock or spy the method `playSoundFile` within the class `SoundPlayer`. A simple example: - -```javascript -// your jest test file below -import SoundPlayer from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; - -const playSoundFileMock = jest - .spyOn(SoundPlayer.prototype, 'playSoundFile') - .mockImplementation(() => { - console.log('mocked function'); - }); // comment this line if just want to "spy" - -it('player consumer plays music', () => { - const player = new SoundPlayerConsumer(); - player.playSomethingCool(); - expect(playSoundFileMock).toHaveBeenCalled(); -}); -``` - -### Static, getter and setter methods - -Lets imagine our class `SoundPlayer` has a getter method `foo` and a static method `brand` - -```javascript -export default class SoundPlayer { - constructor() { - this.foo = 'bar'; - } - - playSoundFile(fileName) { - console.log('Playing sound file ' + fileName); - } - - get foo() { - return 'bar'; - } - static brand() { - return 'player-brand'; - } -} -``` - -You can mock/spy them easily, here is an example: - -```javascript -// your jest test file below -import SoundPlayer from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; - -const staticMethodMock = jest - .spyOn(SoundPlayer, 'brand') - .mockImplementation(() => 'some-mocked-brand'); - -const getterMethodMock = jest - .spyOn(SoundPlayer.prototype, 'foo', 'get') - .mockImplementation(() => 'some-mocked-result'); - -it('custom methods are called', () => { - const player = new SoundPlayer(); - const foo = player.foo; - const brand = SoundPlayer.brand(); - - expect(staticMethodMock).toHaveBeenCalled(); - expect(getterMethodMock).toHaveBeenCalled(); -}); -``` - -## Keeping track of usage (spying on the mock) - -Injecting a test implementation is helpful, but you will probably also want to test whether the class constructor and methods are called with the correct parameters. - -### Spying on the constructor - -In order to track calls to the constructor, replace the function returned by the HOF with a Jest mock function. Create it with [`jest.fn()`](JestObjectAPI.md#jestfnimplementation), and then specify its implementation with `mockImplementation()`. - -```javascript -import SoundPlayer from './sound-player'; -jest.mock('./sound-player', () => { - // Works and lets you check for constructor calls: - return jest.fn().mockImplementation(() => { - return {playSoundFile: () => {}}; - }); -}); -``` - -This will let us inspect usage of our mocked class, using `SoundPlayer.mock.calls`: `expect(SoundPlayer).toHaveBeenCalled();` or near-equivalent: `expect(SoundPlayer.mock.calls.length).toEqual(1);` - -### Mocking non-default class exports - -If the class is **not** the default export from the module then you need to return an object with the key that is the same as the class export name. - -```javascript -import {SoundPlayer} from './sound-player'; -jest.mock('./sound-player', () => { - // Works and lets you check for constructor calls: - return { - SoundPlayer: jest.fn().mockImplementation(() => { - return {playSoundFile: () => {}}; - }), - }; -}); -``` - -### Spying on methods of our class - -Our mocked class will need to provide any member functions (`playSoundFile` in the example) that will be called during our tests, or else we'll get an error for calling a function that doesn't exist. But we'll probably want to also spy on calls to those methods, to ensure that they were called with the expected parameters. - -A new object will be created each time the mock constructor function is called during tests. To spy on method calls in all of these objects, we populate `playSoundFile` with another mock function, and store a reference to that same mock function in our test file, so it's available during tests. - -```javascript -import SoundPlayer from './sound-player'; -const mockPlaySoundFile = jest.fn(); -jest.mock('./sound-player', () => { - return jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; - // Now we can track calls to playSoundFile - }); -}); -``` - -The manual mock equivalent of this would be: - -```javascript title="__mocks__/sound-player.js" -// Import this named export into your test file -export const mockPlaySoundFile = jest.fn(); -const mock = jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; -}); - -export default mock; -``` - -Usage is similar to the module factory function, except that you can omit the second argument from `jest.mock()`, and you must import the mocked method into your test file, since it is no longer defined there. Use the original module path for this; don't include `__mocks__`. - -### Cleaning up between tests - -To clear the record of calls to the mock constructor function and its methods, we call [`mockClear()`](MockFunctionAPI.md#mockfnmockclear) in the `beforeEach()` function: - -```javascript -beforeEach(() => { - SoundPlayer.mockClear(); - mockPlaySoundFile.mockClear(); -}); -``` - -## Complete example - -Here's a complete test file which uses the module factory parameter to `jest.mock`: - -```javascript title="sound-player-consumer.test.js" -import SoundPlayer from './sound-player'; -import SoundPlayerConsumer from './sound-player-consumer'; - -const mockPlaySoundFile = jest.fn(); -jest.mock('./sound-player', () => { - return jest.fn().mockImplementation(() => { - return {playSoundFile: mockPlaySoundFile}; - }); -}); - -beforeEach(() => { - SoundPlayer.mockClear(); - mockPlaySoundFile.mockClear(); -}); - -it('The consumer should be able to call new() on SoundPlayer', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - // Ensure constructor created the object: - expect(soundPlayerConsumer).toBeTruthy(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayer).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName); -}); -``` diff --git a/website/versioned_docs/version-27.2/ExpectAPI.md b/website/versioned_docs/version-27.2/ExpectAPI.md deleted file mode 100644 index ab3f63a70213..000000000000 --- a/website/versioned_docs/version-27.2/ExpectAPI.md +++ /dev/null @@ -1,1393 +0,0 @@ ---- -id: expect -title: Expect ---- - -When you're writing tests, you often need to check that values meet certain conditions. `expect` gives you access to a number of "matchers" that let you validate different things. - -For additional Jest matchers maintained by the Jest Community check out [`jest-extended`](https://github.com/jest-community/jest-extended). - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `expect(value)` - -The `expect` function is used every time you want to test a value. You will rarely call `expect` by itself. Instead, you will use `expect` along with a "matcher" function to assert something about a value. - -It's easier to understand this with an example. Let's say you have a method `bestLaCroixFlavor()` which is supposed to return the string `'grapefruit'`. Here's how you would test that: - -```js -test('the best flavor is grapefruit', () => { - expect(bestLaCroixFlavor()).toBe('grapefruit'); -}); -``` - -In this case, `toBe` is the matcher function. There are a lot of different matcher functions, documented below, to help you test different things. - -The argument to `expect` should be the value that your code produces, and any argument to the matcher should be the correct value. If you mix them up, your tests will still work, but the error messages on failing tests will look strange. - -### `expect.extend(matchers)` - -You can use `expect.extend` to add your own matchers to Jest. For example, let's say that you're testing a number utility library and you're frequently asserting that numbers appear within particular ranges of other numbers. You could abstract that into a `toBeWithinRange` matcher: - -```js -expect.extend({ - toBeWithinRange(received, floor, ceiling) { - const pass = received >= floor && received <= ceiling; - if (pass) { - return { - message: () => - `expected ${received} not to be within range ${floor} - ${ceiling}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be within range ${floor} - ${ceiling}`, - pass: false, - }; - } - }, -}); - -test('numeric ranges', () => { - expect(100).toBeWithinRange(90, 110); - expect(101).not.toBeWithinRange(0, 100); - expect({apples: 6, bananas: 3}).toEqual({ - apples: expect.toBeWithinRange(1, 10), - bananas: expect.not.toBeWithinRange(11, 20), - }); -}); -``` - -_Note_: In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this: - -```ts -interface CustomMatchers { - toBeWithinRange(floor: number, ceiling: number): R; -} - -declare global { - namespace jest { - interface Expect extends CustomMatchers {} - interface Matchers extends CustomMatchers {} - interface InverseAsymmetricMatchers extends CustomMatchers {} - } -} -``` - -#### Async Matchers - -`expect.extend` also supports async matchers. Async matchers return a Promise so you will need to await the returned value. Let's use an example matcher to illustrate the usage of them. We are going to implement a matcher called `toBeDivisibleByExternalValue`, where the divisible number is going to be pulled from an external source. - -```js -expect.extend({ - async toBeDivisibleByExternalValue(received) { - const externalValue = await getExternalValueFromRemoteSource(); - const pass = received % externalValue == 0; - if (pass) { - return { - message: () => - `expected ${received} not to be divisible by ${externalValue}`, - pass: true, - }; - } else { - return { - message: () => - `expected ${received} to be divisible by ${externalValue}`, - pass: false, - }; - } - }, -}); - -test('is divisible by external value', async () => { - await expect(100).toBeDivisibleByExternalValue(); - await expect(101).not.toBeDivisibleByExternalValue(); -}); -``` - -#### Custom Matchers API - -Matchers should return an object (or a Promise of an object) with two keys. `pass` indicates whether there was a match or not, and `message` provides a function with no arguments that returns an error message in case of failure. Thus, when `pass` is false, `message` should return the error message for when `expect(x).yourMatcher()` fails. And when `pass` is true, `message` should return the error message for when `expect(x).not.yourMatcher()` fails. - -Matchers are called with the argument passed to `expect(x)` followed by the arguments passed to `.yourMatcher(y, z)`: - -```js -expect.extend({ - yourMatcher(x, y, z) { - return { - pass: true, - message: () => '', - }; - }, -}); -``` - -These helper functions and properties can be found on `this` inside a custom matcher: - -#### `this.isNot` - -A boolean to let you know this matcher was called with the negated `.not` modifier allowing you to display a clear and correct matcher hint (see example code). - -#### `this.promise` - -A string allowing you to display a clear and correct matcher hint: - -- `'rejects'` if matcher was called with the promise `.rejects` modifier -- `'resolves'` if matcher was called with the promise `.resolves` modifier -- `''` if matcher was not called with a promise modifier - -#### `this.equals(a, b)` - -This is a deep-equality function that will return `true` if two objects have the same values (recursively). - -#### `this.expand` - -A boolean to let you know this matcher was called with an `expand` option. When Jest is called with the `--expand` flag, `this.expand` can be used to determine if Jest is expected to show full diffs and errors. - -#### `this.utils` - -There are a number of helpful tools exposed on `this.utils` primarily consisting of the exports from [`jest-matcher-utils`](https://github.com/facebook/jest/tree/main/packages/jest-matcher-utils). - -The most useful ones are `matcherHint`, `printExpected` and `printReceived` to format the error messages nicely. For example, take a look at the implementation for the `toBe` matcher: - -```js -const {diff} = require('jest-diff'); -expect.extend({ - toBe(received, expected) { - const options = { - comment: 'Object.is equality', - isNot: this.isNot, - promise: this.promise, - }; - - const pass = Object.is(received, expected); - - const message = pass - ? () => - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - `Expected: not ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}` - : () => { - const diffString = diff(expected, received, { - expand: this.expand, - }); - return ( - // eslint-disable-next-line prefer-template - this.utils.matcherHint('toBe', undefined, undefined, options) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(received)}`) - ); - }; - - return {actual: received, message, pass}; - }, -}); -``` - -This will print something like this: - -```bash - expect(received).toBe(expected) - - Expected value to be (using Object.is): - "banana" - Received: - "apple" -``` - -When an assertion fails, the error message should give as much signal as necessary to the user so they can resolve their issue quickly. You should craft a precise failure message to make sure users of your custom assertions have a good developer experience. - -#### Custom snapshot matchers - -To use snapshot testing inside of your custom matcher you can import `jest-snapshot` and use it from within your matcher. - -Here's a snapshot matcher that trims a string to store for a given length, `.toMatchTrimmedSnapshot(length)`: - -```js -const {toMatchSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedSnapshot(received, length) { - return toMatchSnapshot.call( - this, - received.substring(0, length), - 'toMatchTrimmedSnapshot', - ); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10); -}); - -/* -Stored snapshot will look like: - -exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`; -*/ -``` - -It's also possible to create custom matchers for inline snapshots, the snapshots will be correctly added to the custom matchers. However, inline snapshot will always try to append to the first argument or the second when the first argument is the property matcher, so it's not possible to accept custom arguments in the custom matchers. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchTrimmedInlineSnapshot(received, ...rest) { - return toMatchInlineSnapshot.call(this, received.substring(0, 10), ...rest); - }, -}); - -it('stores only 10 characters', () => { - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot( - `"extra long"` - ); - */ -}); -``` - -#### async - -If your custom inline snapshot matcher is async i.e. uses `async`-`await` you might encounter an error like "Multiple inline snapshots for the same call are not supported". Jest needs additional context information to find where the custom inline snapshot matcher was used to update the snapshots properly. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - async toMatchObservationInlineSnapshot(fn, ...rest) { - // The error (and its stacktrace) must be created before any `await` - this.error = new Error(); - - // The implementation of `observe` doesn't matter. - // It only matters that the custom snapshot matcher is async. - const observation = await observe(async () => { - await fn(); - }); - - return toMatchInlineSnapshot.call(this, recording, ...rest); - }, -}); - -it('observes something', async () => { - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(); - /* - The snapshot will be added inline like - await expect(async () => { - return 'async action'; - }).toMatchTrimmedInlineSnapshot(`"async action"`); - */ -}); -``` - -#### Bail out - -Usually `jest` tries to match every snapshot that is expected in a test. - -Sometimes it might not make sense to continue the test if a prior snapshot failed. For example, when you make snapshots of a state-machine after various transitions you can abort the test once one transition produced the wrong state. - -In that case you can implement a custom snapshot matcher that throws on the first mismatch instead of collecting every mismatch. - -```js -const {toMatchInlineSnapshot} = require('jest-snapshot'); - -expect.extend({ - toMatchStateInlineSnapshot(...args) { - this.dontThrow = () => {}; - - return toMatchInlineSnapshot.call(this, ...args); - }, -}); - -let state = 'initial'; - -function transition() { - // Typo in the implementation should cause the test to fail - if (state === 'INITIAL') { - state = 'pending'; - } else if (state === 'pending') { - state = 'done'; - } -} - -it('transitions as expected', () => { - expect(state).toMatchStateInlineSnapshot(`"initial"`); - - transition(); - // Already produces a mismatch. No point in continuing the test. - expect(state).toMatchStateInlineSnapshot(`"loading"`); - - transition(); - expect(state).toMatchStateInlineSnapshot(`"done"`); -}); -``` - -### `expect.anything()` - -`expect.anything()` matches anything but `null` or `undefined`. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a non-null argument: - -```js -test('map calls its argument with a non-null argument', () => { - const mock = jest.fn(); - [1].map(x => mock(x)); - expect(mock).toBeCalledWith(expect.anything()); -}); -``` - -### `expect.any(constructor)` - -`expect.any(constructor)` matches anything that was created with the given constructor or if it's a primitive that is of the passed type. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, if you want to check that a mock function is called with a number: - -```js -class Cat {} -function getCat(fn) { - return fn(new Cat()); -} - -test('randocall calls its callback with a class instance', () => { - const mock = jest.fn(); - getCat(mock); - expect(mock).toBeCalledWith(expect.any(Cat)); -}); - -function randocall(fn) { - return fn(Math.floor(Math.random() * 6 + 1)); -} - -test('randocall calls its callback with a number', () => { - const mock = jest.fn(); - randocall(mock); - expect(mock).toBeCalledWith(expect.any(Number)); -}); -``` - -### `expect.arrayContaining(array)` - -`expect.arrayContaining(array)` matches a received array which contains all of the elements in the expected array. That is, the expected array is a **subset** of the received array. Therefore, it matches a received array which contains elements that are **not** in the expected array. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match a property in `objectContaining` or `toMatchObject` - -```js -describe('arrayContaining', () => { - const expected = ['Alice', 'Bob']; - it('matches even if received contains additional elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected)); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected)); - }); -}); -``` - -```js -describe('Beware of a misunderstanding! A sequence of dice rolls', () => { - const expected = [1, 2, 3, 4, 5, 6]; - it('matches even with an unexpected number 7', () => { - expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match without an expected number 2', () => { - expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.assertions(number)` - -`expect.assertions(number)` verifies that a certain number of assertions are called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a function `doAsync` that receives two callbacks `callback1` and `callback2`, it will asynchronously call both of them in an unknown order. We can test this with: - -```js -test('doAsync calls both callbacks', () => { - expect.assertions(2); - function callback1(data) { - expect(data).toBeTruthy(); - } - function callback2(data) { - expect(data).toBeTruthy(); - } - - doAsync(callback1, callback2); -}); -``` - -The `expect.assertions(2)` call ensures that both callbacks actually get called. - -### `expect.hasAssertions()` - -`expect.hasAssertions()` verifies that at least one assertion is called during a test. This is often useful when testing asynchronous code, in order to make sure that assertions in a callback actually got called. - -For example, let's say that we have a few functions that all deal with state. `prepareState` calls a callback with a state object, `validateState` runs on that state object, and `waitOnState` returns a promise that waits until all `prepareState` callbacks complete. We can test this with: - -```js -test('prepareState prepares a valid state', () => { - expect.hasAssertions(); - prepareState(state => { - expect(validateState(state)).toBeTruthy(); - }); - return waitOnState(); -}); -``` - -The `expect.hasAssertions()` call ensures that the `prepareState` callback actually gets called. - -### `expect.not.arrayContaining(array)` - -`expect.not.arrayContaining(array)` matches a received array which does not contain all of the elements in the expected array. That is, the expected array **is not a subset** of the received array. - -It is the inverse of `expect.arrayContaining`. - -```js -describe('not.arrayContaining', () => { - const expected = ['Samantha']; - - it('matches if the actual array does not contain the expected elements', () => { - expect(['Alice', 'Bob', 'Eve']).toEqual( - expect.not.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.not.objectContaining(object)` - -`expect.not.objectContaining(object)` matches any received object that does not recursively match the expected properties. That is, the expected object **is not a subset** of the received object. Therefore, it matches a received object which contains properties that are **not** in the expected object. - -It is the inverse of `expect.objectContaining`. - -```js -describe('not.objectContaining', () => { - const expected = {foo: 'bar'}; - - it('matches if the actual object does not contain expected key: value pairs', () => { - expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected)); - }); -}); -``` - -### `expect.not.stringContaining(string)` - -`expect.not.stringContaining(string)` matches the received value if it is not a string or if it is a string that does not contain the exact expected string. - -It is the inverse of `expect.stringContaining`. - -```js -describe('not.stringContaining', () => { - const expected = 'Hello world!'; - - it('matches if the received value does not contain the expected substring', () => { - expect('How are you?').toEqual(expect.not.stringContaining(expected)); - }); -}); -``` - -### `expect.not.stringMatching(string | regexp)` - -`expect.not.stringMatching(string | regexp)` matches the received value if it is not a string or if it is a string that does not match the expected string or regular expression. - -It is the inverse of `expect.stringMatching`. - -```js -describe('not.stringMatching', () => { - const expected = /Hello world!/; - - it('matches if the received value does not match the expected regex', () => { - expect('How are you?').toEqual(expect.not.stringMatching(expected)); - }); -}); -``` - -### `expect.objectContaining(object)` - -`expect.objectContaining(object)` matches any received object that recursively matches the expected properties. That is, the expected object is a **subset** of the received object. Therefore, it matches a received object which contains properties that **are present** in the expected object. - -Instead of literal property values in the expected object, you can use matchers, `expect.anything()`, and so on. - -For example, let's say that we expect an `onPress` function to be called with an `Event` object, and all we need to verify is that the event has `event.x` and `event.y` properties. We can do that with: - -```js -test('onPress gets called with the right thing', () => { - const onPress = jest.fn(); - simulatePresses(onPress); - expect(onPress).toBeCalledWith( - expect.objectContaining({ - x: expect.any(Number), - y: expect.any(Number), - }), - ); -}); -``` - -### `expect.stringContaining(string)` - -`expect.stringContaining(string)` matches the received value if it is a string that contains the exact expected string. - -### `expect.stringMatching(string | regexp)` - -`expect.stringMatching(string | regexp)` matches the received value if it is a string that matches the expected string or regular expression. - -You can use it instead of a literal value: - -- in `toEqual` or `toBeCalledWith` -- to match an element in `arrayContaining` -- to match a property in `objectContaining` or `toMatchObject` - -This example also shows how you can nest multiple asymmetric matchers, with `expect.stringMatching` inside the `expect.arrayContaining`. - -```js -describe('stringMatching in arrayContaining', () => { - const expected = [ - expect.stringMatching(/^Alic/), - expect.stringMatching(/^[BR]ob/), - ]; - it('matches even if received contains additional elements', () => { - expect(['Alicia', 'Roberto', 'Evelina']).toEqual( - expect.arrayContaining(expected), - ); - }); - it('does not match if received does not contain expected elements', () => { - expect(['Roberto', 'Evelina']).not.toEqual( - expect.arrayContaining(expected), - ); - }); -}); -``` - -### `expect.addSnapshotSerializer(serializer)` - -You can call `expect.addSnapshotSerializer` to add a module that formats application-specific data structures. - -For an individual test file, an added module precedes any modules from `snapshotSerializers` configuration, which precede the default snapshot serializers for built-in JavaScript types and for React elements. The last module added is the first module tested. - -```js -import serializer from 'my-serializer-module'; -expect.addSnapshotSerializer(serializer); - -// affects expect(value).toMatchSnapshot() assertions in the test file -``` - -If you add a snapshot serializer in individual test files instead of adding it to `snapshotSerializers` configuration: - -- You make the dependency explicit instead of implicit. -- You avoid limits to configuration that might cause you to eject from [create-react-app](https://github.com/facebookincubator/create-react-app). - -See [configuring Jest](Configuration.md#snapshotserializers-arraystring) for more information. - -### `.not` - -If you know how to test something, `.not` lets you test its opposite. For example, this code tests that the best La Croix flavor is not coconut: - -```js -test('the best flavor is not coconut', () => { - expect(bestLaCroixFlavor()).not.toBe('coconut'); -}); -``` - -### `.resolves` - -Use `resolves` to unwrap the value of a fulfilled promise so any other matcher can be chained. If the promise is rejected the assertion fails. - -For example, this code tests that the promise resolves and that the resulting value is `'lemon'`: - -```js -test('resolves to lemon', () => { - // make sure to add a return statement - return expect(Promise.resolve('lemon')).resolves.toBe('lemon'); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.resolves`: - -```js -test('resolves to lemon', async () => { - await expect(Promise.resolve('lemon')).resolves.toBe('lemon'); - await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus'); -}); -``` - -### `.rejects` - -Use `.rejects` to unwrap the reason of a rejected promise so any other matcher can be chained. If the promise is fulfilled the assertion fails. - -For example, this code tests that the promise rejects with reason `'octopus'`: - -```js -test('rejects to octopus', () => { - // make sure to add a return statement - return expect(Promise.reject(new Error('octopus'))).rejects.toThrow( - 'octopus', - ); -}); -``` - -Note that, since you are still testing promises, the test is still asynchronous. Hence, you will need to [tell Jest to wait](TestingAsyncCode.md#promises) by returning the unwrapped assertion. - -Alternatively, you can use `async/await` in combination with `.rejects`. - -```js -test('rejects to octopus', async () => { - await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); -}); -``` - -### `.toBe(value)` - -Use `.toBe` to compare primitive values or to check referential identity of object instances. It calls `Object.is` to compare values, which is even better for testing than `===` strict equality operator. - -For example, this code will validate some properties of the `can` object: - -```js -const can = { - name: 'pamplemousse', - ounces: 12, -}; - -describe('the can', () => { - test('has 12 ounces', () => { - expect(can.ounces).toBe(12); - }); - - test('has a sophisticated name', () => { - expect(can.name).toBe('pamplemousse'); - }); -}); -``` - -Don't use `.toBe` with floating-point numbers. For example, due to rounding, in JavaScript `0.2 + 0.1` is not strictly equal to `0.3`. If you have floating point numbers, try `.toBeCloseTo` instead. - -Although the `.toBe` matcher **checks** referential identity, it **reports** a deep comparison of values if the assertion fails. If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, to assert whether or not elements are the same instance: - -- rewrite `expect(received).toBe(expected)` as `expect(Object.is(received, expected)).toBe(true)` -- rewrite `expect(received).not.toBe(expected)` as `expect(Object.is(received, expected)).toBe(false)` - -### `.toHaveBeenCalled()` - -Also under the alias: `.toBeCalled()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say you have a `drinkAll(drink, flavour)` function that takes a `drink` function and applies it to all available beverages. You might want to check that `drink` gets called for `'lemon'`, but not for `'octopus'`, because `'octopus'` flavour is really weird and why would anything be octopus-flavoured? You can do that with this test suite: - -```js -function drinkAll(callback, flavour) { - if (flavour !== 'octopus') { - callback(flavour); - } -} - -describe('drinkAll', () => { - test('drinks something lemon-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'lemon'); - expect(drink).toHaveBeenCalled(); - }); - - test('does not drink something octopus-flavoured', () => { - const drink = jest.fn(); - drinkAll(drink, 'octopus'); - expect(drink).not.toHaveBeenCalled(); - }); -}); -``` - -### `.toHaveBeenCalledTimes(number)` - -Also under the alias: `.toBeCalledTimes(number)` - -Use `.toHaveBeenCalledTimes` to ensure that a mock function got called exact number of times. - -For example, let's say you have a `drinkEach(drink, Array)` function that takes a `drink` function and applies it to array of passed beverages. You might want to check that drink function was called exact number of times. You can do that with this test suite: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenCalledTimes(2); -}); -``` - -### `.toHaveBeenCalledWith(arg1, arg2, ...)` - -Also under the alias: `.toBeCalledWith()` - -Use `.toHaveBeenCalledWith` to ensure that a mock function was called with specific arguments. The arguments are checked with the same algorithm that `.toEqual` uses. - -For example, let's say that you can register a beverage with a `register` function, and `applyToAll(f)` should apply the function `f` to all registered beverages. To make sure this works, you could write: - -```js -test('registration applies correctly to orange La Croix', () => { - const beverage = new LaCroix('orange'); - register(beverage); - const f = jest.fn(); - applyToAll(f); - expect(f).toHaveBeenCalledWith(beverage); -}); -``` - -### `.toHaveBeenLastCalledWith(arg1, arg2, ...)` - -Also under the alias: `.lastCalledWith(arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenLastCalledWith` to test what arguments it was last called with. For example, let's say you have a `applyToAllFlavors(f)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the last flavor it operates on is `'mango'`. You can write: - -```js -test('applying to all flavors does mango last', () => { - const drink = jest.fn(); - applyToAllFlavors(drink); - expect(drink).toHaveBeenLastCalledWith('mango'); -}); -``` - -### `.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)` - -Also under the alias: `.nthCalledWith(nthCall, arg1, arg2, ...)` - -If you have a mock function, you can use `.toHaveBeenNthCalledWith` to test what arguments it was nth called with. For example, let's say you have a `drinkEach(drink, Array)` function that applies `f` to a bunch of flavors, and you want to ensure that when you call it, the first flavor it operates on is `'lemon'` and the second one is `'octopus'`. You can write: - -```js -test('drinkEach drinks each drink', () => { - const drink = jest.fn(); - drinkEach(drink, ['lemon', 'octopus']); - expect(drink).toHaveBeenNthCalledWith(1, 'lemon'); - expect(drink).toHaveBeenNthCalledWith(2, 'octopus'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveReturned()` - -Also under the alias: `.toReturn()` - -If you have a mock function, you can use `.toHaveReturned` to test that the mock function successfully returned (i.e., did not throw an error) at least one time. For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drinks returns', () => { - const drink = jest.fn(() => true); - - drink(); - - expect(drink).toHaveReturned(); -}); -``` - -### `.toHaveReturnedTimes(number)` - -Also under the alias: `.toReturnTimes(number)` - -Use `.toHaveReturnedTimes` to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times. Any calls to the mock function that throw an error are not counted toward the number of times the function returned. - -For example, let's say you have a mock `drink` that returns `true`. You can write: - -```js -test('drink returns twice', () => { - const drink = jest.fn(() => true); - - drink(); - drink(); - - expect(drink).toHaveReturnedTimes(2); -}); -``` - -### `.toHaveReturnedWith(value)` - -Also under the alias: `.toReturnWith(value)` - -Use `.toHaveReturnedWith` to ensure that a mock function returned a specific value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix', () => { - const beverage = {name: 'La Croix'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage); - - expect(drink).toHaveReturnedWith('La Croix'); -}); -``` - -### `.toHaveLastReturnedWith(value)` - -Also under the alias: `.lastReturnedWith(value)` - -Use `.toHaveLastReturnedWith` to test the specific value that a mock function last returned. If the last call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns La Croix (Orange) last', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveLastReturnedWith('La Croix (Orange)'); -}); -``` - -### `.toHaveNthReturnedWith(nthCall, value)` - -Also under the alias: `.nthReturnedWith(nthCall, value)` - -Use `.toHaveNthReturnedWith` to test the specific value that a mock function returned for the nth call. If the nth call to the mock function threw an error, then this matcher will fail no matter what value you provided as the expected return value. - -For example, let's say you have a mock `drink` that returns the name of the beverage that was consumed. You can write: - -```js -test('drink returns expected nth calls', () => { - const beverage1 = {name: 'La Croix (Lemon)'}; - const beverage2 = {name: 'La Croix (Orange)'}; - const drink = jest.fn(beverage => beverage.name); - - drink(beverage1); - drink(beverage2); - - expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)'); - expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)'); -}); -``` - -Note: the nth argument must be positive integer starting from 1. - -### `.toHaveLength(number)` - -Use `.toHaveLength` to check that an object has a `.length` property and it is set to a certain numeric value. - -This is especially useful for checking arrays or strings size. - -```js -expect([1, 2, 3]).toHaveLength(3); -expect('abc').toHaveLength(3); -expect('').not.toHaveLength(5); -``` - -### `.toHaveProperty(keyPath, value?)` - -Use `.toHaveProperty` to check if property at provided reference `keyPath` exists for an object. For checking deeply nested properties in an object you may use [dot notation](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors) or an array containing the keyPath for deep references. - -You can provide an optional `value` argument to compare the received property value (recursively for all properties of object instances, also known as deep equality, like the `toEqual` matcher). - -The following example contains a `houseForSale` object with nested properties. We are using `toHaveProperty` to check for the existence and values of various properties in the object. - -```js -// Object containing house features to be tested -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - 'nice.oven': true, - }, - 'ceiling.height': 2, -}; - -test('this house has my desired features', () => { - // Example Referencing - expect(houseForSale).toHaveProperty('bath'); - expect(houseForSale).toHaveProperty('bedrooms', 4); - - expect(houseForSale).not.toHaveProperty('pool'); - - // Deep referencing using dot notation - expect(houseForSale).toHaveProperty('kitchen.area', 20); - expect(houseForSale).toHaveProperty('kitchen.amenities', [ - 'oven', - 'stove', - 'washer', - ]); - - expect(houseForSale).not.toHaveProperty('kitchen.open'); - - // Deep referencing using an array containing the keyPath - expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20); - expect(houseForSale).toHaveProperty( - ['kitchen', 'amenities'], - ['oven', 'stove', 'washer'], - ); - expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven'); - expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']); - expect(houseForSale).not.toHaveProperty(['kitchen', 'open']); - - // Referencing keys with dot in the key itself - expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall'); -}); -``` - -### `.toBeCloseTo(number, numDigits?)` - -Use `toBeCloseTo` to compare floating point numbers for approximate equality. - -The optional `numDigits` argument limits the number of digits to check **after** the decimal point. For the default value `2`, the test criterion is `Math.abs(expected - received) < 0.005` (that is, `10 ** -2 / 2`). - -Intuitive equality comparisons often fail, because arithmetic on decimal (base 10) values often have rounding errors in limited precision binary (base 2) representation. For example, this test fails: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBe(0.3); // Fails! -}); -``` - -It fails because in JavaScript, `0.2 + 0.1` is actually `0.30000000000000004`. - -For example, this test passes with a precision of 5 digits: - -```js -test('adding works sanely with decimals', () => { - expect(0.2 + 0.1).toBeCloseTo(0.3, 5); -}); -``` - -Because floating point errors are the problem that `toBeCloseTo` solves, it does not support big integer values. - -### `.toBeDefined()` - -Use `.toBeDefined` to check that a variable is not undefined. For example, if you want to check that a function `fetchNewFlavorIdea()` returns _something_, you can write: - -```js -test('there is a new flavor idea', () => { - expect(fetchNewFlavorIdea()).toBeDefined(); -}); -``` - -You could write `expect(fetchNewFlavorIdea()).not.toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeFalsy()` - -Use `.toBeFalsy` when you don't care what a value is and you want to ensure a value is false in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (!getErrors()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `getErrors` returns, specifically - it might return `false`, `null`, or `0`, and your code would still work. So if you want to test there are no errors after drinking some La Croix, you could write: - -```js -test('drinking La Croix does not lead to errors', () => { - drinkSomeLaCroix(); - expect(getErrors()).toBeFalsy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeGreaterThan(number | bigint)` - -Use `toBeGreaterThan` to compare `received > expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of more than 10 ounces: - -```js -test('ounces per can is more than 10', () => { - expect(ouncesPerCan()).toBeGreaterThan(10); -}); -``` - -### `.toBeGreaterThanOrEqual(number | bigint)` - -Use `toBeGreaterThanOrEqual` to compare `received >= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at least 12 ounces: - -```js -test('ounces per can is at least 12', () => { - expect(ouncesPerCan()).toBeGreaterThanOrEqual(12); -}); -``` - -### `.toBeLessThan(number | bigint)` - -Use `toBeLessThan` to compare `received < expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of less than 20 ounces: - -```js -test('ounces per can is less than 20', () => { - expect(ouncesPerCan()).toBeLessThan(20); -}); -``` - -### `.toBeLessThanOrEqual(number | bigint)` - -Use `toBeLessThanOrEqual` to compare `received <= expected` for number or big integer values. For example, test that `ouncesPerCan()` returns a value of at most 12 ounces: - -```js -test('ounces per can is at most 12', () => { - expect(ouncesPerCan()).toBeLessThanOrEqual(12); -}); -``` - -### `.toBeInstanceOf(Class)` - -Use `.toBeInstanceOf(Class)` to check that an object is an instance of a class. This matcher uses `instanceof` underneath. - -```js -class A {} - -expect(new A()).toBeInstanceOf(A); -expect(() => {}).toBeInstanceOf(Function); -expect(new A()).toBeInstanceOf(Function); // throws -``` - -### `.toBeNull()` - -`.toBeNull()` is the same as `.toBe(null)` but the error messages are a bit nicer. So use `.toBeNull()` when you want to check that something is null. - -```js -function bloop() { - return null; -} - -test('bloop returns null', () => { - expect(bloop()).toBeNull(); -}); -``` - -### `.toBeTruthy()` - -Use `.toBeTruthy` when you don't care what a value is and you want to ensure a value is true in a boolean context. For example, let's say you have some application code that looks like: - -```js -drinkSomeLaCroix(); -if (thirstInfo()) { - drinkMoreLaCroix(); -} -``` - -You may not care what `thirstInfo` returns, specifically - it might return `true` or a complex object, and your code would still work. So if you want to test that `thirstInfo` will be truthy after drinking some La Croix, you could write: - -```js -test('drinking La Croix leads to having thirst info', () => { - drinkSomeLaCroix(); - expect(thirstInfo()).toBeTruthy(); -}); -``` - -In JavaScript, there are six falsy values: `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy. - -### `.toBeUndefined()` - -Use `.toBeUndefined` to check that a variable is undefined. For example, if you want to check that a function `bestDrinkForFlavor(flavor)` returns `undefined` for the `'octopus'` flavor, because there is no good octopus-flavored drink: - -```js -test('the best drink for octopus flavor is undefined', () => { - expect(bestDrinkForFlavor('octopus')).toBeUndefined(); -}); -``` - -You could write `expect(bestDrinkForFlavor('octopus')).toBe(undefined)`, but it's better practice to avoid referring to `undefined` directly in your code. - -### `.toBeNaN()` - -Use `.toBeNaN` when checking a value is `NaN`. - -```js -test('passes when value is NaN', () => { - expect(NaN).toBeNaN(); - expect(1).not.toBeNaN(); -}); -``` - -### `.toContain(item)` - -Use `.toContain` when you want to check that an item is in an array. For testing the items in the array, this uses `===`, a strict equality check. `.toContain` can also check whether a string is a substring of another string. - -For example, if `getAllFlavors()` returns an array of flavors and you want to be sure that `lime` is in there, you can write: - -```js -test('the flavor list contains lime', () => { - expect(getAllFlavors()).toContain('lime'); -}); -``` - -This matcher also accepts others iterables such as strings, sets, node lists and HTML collections. - -### `.toContainEqual(item)` - -Use `.toContainEqual` when you want to check that an item with a specific structure and values is contained in an array. For testing the items in the array, this matcher recursively checks the equality of all fields, rather than checking for object identity. - -```js -describe('my beverage', () => { - test('is delicious and not sour', () => { - const myBeverage = {delicious: true, sour: false}; - expect(myBeverages()).toContainEqual(myBeverage); - }); -}); -``` - -### `.toEqual(value)` - -Use `.toEqual` to compare recursively all properties of object instances (also known as "deep" equality). It calls `Object.is` to compare primitive values, which is even better for testing than `===` strict equality operator. - -For example, `.toEqual` and `.toBe` behave differently in this test suite, so all the tests pass: - -```js -const can1 = { - flavor: 'grapefruit', - ounces: 12, -}; -const can2 = { - flavor: 'grapefruit', - ounces: 12, -}; - -describe('the La Croix cans on my desk', () => { - test('have all the same properties', () => { - expect(can1).toEqual(can2); - }); - test('are not the exact same can', () => { - expect(can1).not.toBe(can2); - }); -}); -``` - -> Note: `.toEqual` won't perform a _deep equality_ check for two errors. Only the `message` property of an Error is considered for equality. It is recommended to use the `.toThrow` matcher for testing against errors. - -If differences between properties do not help you to understand why a test fails, especially if the report is large, then you might move the comparison into the `expect` function. For example, use `equals` method of `Buffer` class to assert whether or not buffers contain the same content: - -- rewrite `expect(received).toEqual(expected)` as `expect(received.equals(expected)).toBe(true)` -- rewrite `expect(received).not.toEqual(expected)` as `expect(received.equals(expected)).toBe(false)` - -### `.toMatch(regexp | string)` - -Use `.toMatch` to check that a string matches a regular expression. - -For example, you might not know what exactly `essayOnTheBestFlavor()` returns, but you know it's a really long string, and the substring `grapefruit` should be in there somewhere. You can test this with: - -```js -describe('an essay on the best flavor', () => { - test('mentions grapefruit', () => { - expect(essayOnTheBestFlavor()).toMatch(/grapefruit/); - expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit')); - }); -}); -``` - -This matcher also accepts a string, which it will try to match: - -```js -describe('grapefruits are healthy', () => { - test('grapefruits are a fruit', () => { - expect('grapefruits').toMatch('fruit'); - }); -}); -``` - -### `.toMatchObject(object)` - -Use `.toMatchObject` to check that a JavaScript object matches a subset of the properties of an object. It will match received objects with properties that are **not** in the expected object. - -You can also pass an array of objects, in which case the method will return true only if each object in the received array matches (in the `toMatchObject` sense described above) the corresponding object in the expected array. This is useful if you want to check that two arrays match in their number of elements, as opposed to `arrayContaining`, which allows for extra elements in the received array. - -You can match properties against values or against matchers. - -```js -const houseForSale = { - bath: true, - bedrooms: 4, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - area: 20, - wallColor: 'white', - }, -}; -const desiredHouse = { - bath: true, - kitchen: { - amenities: ['oven', 'stove', 'washer'], - wallColor: expect.stringMatching(/white|yellow/), - }, -}; - -test('the house has my desired features', () => { - expect(houseForSale).toMatchObject(desiredHouse); -}); -``` - -```js -describe('toMatchObject applied to arrays', () => { - test('the number of elements must match exactly', () => { - expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]); - }); - - test('.toMatchObject is called for each elements, so extra object properties are okay', () => { - expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([ - {foo: 'bar'}, - {baz: 1}, - ]); - }); -}); -``` - -### `.toMatchSnapshot(propertyMatchers?, hint?)` - -This ensures that a value matches the most recent snapshot. Check out [the Snapshot Testing guide](SnapshotTesting.md) for more information. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -### `.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)` - -Ensures that a value matches the most recent snapshot. - -You can provide an optional `propertyMatchers` object argument, which has asymmetric matchers as values of a subset of expected properties, **if** the received value will be an **object** instance. It is like `toMatchObject` with flexible criteria for a subset of properties, followed by a snapshot test as exact criteria for the rest of the properties. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. - -### `.toStrictEqual(value)` - -Use `.toStrictEqual` to test that objects have the same types as well as structure. - -Differences from `.toEqual`: - -- Keys with `undefined` properties are checked. e.g. `{a: undefined, b: 2}` does not match `{b: 2}` when using `.toStrictEqual`. -- Array sparseness is checked. e.g. `[, 1]` does not match `[undefined, 1]` when using `.toStrictEqual`. -- Object types are checked to be equal. e.g. A class instance with fields `a` and `b` will not equal a literal object with fields `a` and `b`. - -```js -class LaCroix { - constructor(flavor) { - this.flavor = flavor; - } -} - -describe('the La Croix cans on my desk', () => { - test('are not semantically the same', () => { - expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'}); - expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'}); - }); -}); -``` - -### `.toThrow(error?)` - -Also under the alias: `.toThrowError(error?)` - -Use `.toThrow` to test that a function throws when it is called. For example, if we want to test that `drinkFlavor('octopus')` throws, because octopus flavor is too disgusting to drink, we could write: - -```js -test('throws on octopus', () => { - expect(() => { - drinkFlavor('octopus'); - }).toThrow(); -}); -``` - -> Note: You must wrap the code in a function, otherwise the error will not be caught and the assertion will fail. - -You can provide an optional argument to test that a specific error is thrown: - -- regular expression: error message **matches** the pattern -- string: error message **includes** the substring -- error object: error message is **equal to** the message property of the object -- error class: error object is **instance of** class - -For example, let's say that `drinkFlavor` is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -We could test this error gets thrown in several ways: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - // Test that the error message says "yuck" somewhere: these are equivalent - expect(drinkOctopus).toThrowError(/yuck/); - expect(drinkOctopus).toThrowError('yuck'); - - // Test the exact error message - expect(drinkOctopus).toThrowError(/^yuck, octopus flavor$/); - expect(drinkOctopus).toThrowError(new Error('yuck, octopus flavor')); - - // Test that we get a DisgustingFlavorError - expect(drinkOctopus).toThrowError(DisgustingFlavorError); -}); -``` - -### `.toThrowErrorMatchingSnapshot(hint?)` - -Use `.toThrowErrorMatchingSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -You can provide an optional `hint` string argument that is appended to the test name. Although Jest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate **multiple** snapshots in a **single** `it` or `test` block. Jest sorts snapshots by name in the corresponding `.snap` file. - -For example, let's say you have a `drinkFlavor` function that throws whenever the flavor is `'octopus'`, and is coded like this: - -```js -function drinkFlavor(flavor) { - if (flavor == 'octopus') { - throw new DisgustingFlavorError('yuck, octopus flavor'); - } - // Do some other stuff -} -``` - -The test for this function will look this way: - -```js -test('throws on octopus', () => { - function drinkOctopus() { - drinkFlavor('octopus'); - } - - expect(drinkOctopus).toThrowErrorMatchingSnapshot(); -}); -``` - -And it will generate the following snapshot: - -```js -exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`; -``` - -Check out [React Tree Snapshot Testing](/blog/2016/07/27/jest-14) for more information on snapshot testing. - -### `.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)` - -Use `.toThrowErrorMatchingInlineSnapshot` to test that a function throws an error matching the most recent snapshot when it is called. - -Jest adds the `inlineSnapshot` string argument to the matcher in the test file (instead of an external `.snap` file) the first time that the test runs. - -Check out the section on [Inline Snapshots](SnapshotTesting.md#inline-snapshots) for more info. diff --git a/website/versioned_docs/version-27.2/GlobalAPI.md b/website/versioned_docs/version-27.2/GlobalAPI.md deleted file mode 100644 index 26185d0d9885..000000000000 --- a/website/versioned_docs/version-27.2/GlobalAPI.md +++ /dev/null @@ -1,880 +0,0 @@ ---- -id: api -title: Globals ---- - -In your test files, Jest puts each of these methods and objects into the global environment. You don't have to require or import anything to use them. However, if you prefer explicit imports, you can do `import {describe, expect, test} from '@jest/globals'`. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `afterAll(fn, timeout)` - -Runs a function after all the tests in this file have completed. If the function returns a promise or is a generator, Jest waits for that promise to resolve before continuing. - -Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -This is often useful if you want to clean up some global setup state that is shared across tests. - -For example: - -```js -const globalDatabase = makeGlobalDatabase(); - -function cleanUpDatabase(db) { - db.cleanUp(); -} - -afterAll(() => { - cleanUpDatabase(globalDatabase); -}); - -test('can find things', () => { - return globalDatabase.find('thing', {}, results => { - expect(results.length).toBeGreaterThan(0); - }); -}); - -test('can insert a thing', () => { - return globalDatabase.insert('thing', makeThing(), response => { - expect(response.success).toBeTruthy(); - }); -}); -``` - -Here the `afterAll` ensures that `cleanUpDatabase` is called after all tests run. - -If `afterAll` is inside a `describe` block, it runs at the end of the describe block. - -If you want to run some cleanup after every test instead of after all tests, use `afterEach` instead. - -### `afterEach(fn, timeout)` - -Runs a function after each one of the tests in this file completes. If the function returns a promise or is a generator, Jest waits for that promise to resolve before continuing. - -Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -This is often useful if you want to clean up some temporary state that is created by each test. - -For example: - -```js -const globalDatabase = makeGlobalDatabase(); - -function cleanUpDatabase(db) { - db.cleanUp(); -} - -afterEach(() => { - cleanUpDatabase(globalDatabase); -}); - -test('can find things', () => { - return globalDatabase.find('thing', {}, results => { - expect(results.length).toBeGreaterThan(0); - }); -}); - -test('can insert a thing', () => { - return globalDatabase.insert('thing', makeThing(), response => { - expect(response.success).toBeTruthy(); - }); -}); -``` - -Here the `afterEach` ensures that `cleanUpDatabase` is called after each test runs. - -If `afterEach` is inside a `describe` block, it only runs after the tests that are inside this describe block. - -If you want to run some cleanup just once, after all of the tests run, use `afterAll` instead. - -### `beforeAll(fn, timeout)` - -Runs a function before any of the tests in this file run. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running tests. - -Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -This is often useful if you want to set up some global state that will be used by many tests. - -For example: - -```js -const globalDatabase = makeGlobalDatabase(); - -beforeAll(() => { - // Clears the database and adds some testing data. - // Jest will wait for this promise to resolve before running tests. - return globalDatabase.clear().then(() => { - return globalDatabase.insert({testData: 'foo'}); - }); -}); - -// Since we only set up the database once in this example, it's important -// that our tests don't modify it. -test('can find things', () => { - return globalDatabase.find('thing', {}, results => { - expect(results.length).toBeGreaterThan(0); - }); -}); -``` - -Here the `beforeAll` ensures that the database is set up before tests run. If setup was synchronous, you could do this without `beforeAll`. The key is that Jest will wait for a promise to resolve, so you can have asynchronous setup as well. - -If `beforeAll` is inside a `describe` block, it runs at the beginning of the describe block. - -If you want to run something before every test instead of before any test runs, use `beforeEach` instead. - -### `beforeEach(fn, timeout)` - -Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test. - -Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -This is often useful if you want to reset some global state that will be used by many tests. - -For example: - -```js -const globalDatabase = makeGlobalDatabase(); - -beforeEach(() => { - // Clears the database and adds some testing data. - // Jest will wait for this promise to resolve before running tests. - return globalDatabase.clear().then(() => { - return globalDatabase.insert({testData: 'foo'}); - }); -}); - -test('can find things', () => { - return globalDatabase.find('thing', {}, results => { - expect(results.length).toBeGreaterThan(0); - }); -}); - -test('can insert a thing', () => { - return globalDatabase.insert('thing', makeThing(), response => { - expect(response.success).toBeTruthy(); - }); -}); -``` - -Here the `beforeEach` ensures that the database is reset for each test. - -If `beforeEach` is inside a `describe` block, it runs for each test in the describe block. - -If you only need to run some setup code once, before any tests run, use `beforeAll` instead. - -### `describe(name, fn)` - -`describe(name, fn)` creates a block that groups together several related tests. For example, if you have a `myBeverage` object that is supposed to be delicious but not sour, you could test it with: - -```js -const myBeverage = { - delicious: true, - sour: false, -}; - -describe('my beverage', () => { - test('is delicious', () => { - expect(myBeverage.delicious).toBeTruthy(); - }); - - test('is not sour', () => { - expect(myBeverage.sour).toBeFalsy(); - }); -}); -``` - -This isn't required - you can write the `test` blocks directly at the top level. But this can be handy if you prefer your tests to be organized into groups. - -You can also nest `describe` blocks if you have a hierarchy of tests: - -```js -const binaryStringToNumber = binString => { - if (!/^[01]+$/.test(binString)) { - throw new CustomError('Not a binary number.'); - } - - return parseInt(binString, 2); -}; - -describe('binaryStringToNumber', () => { - describe('given an invalid binary string', () => { - test('composed of non-numbers throws CustomError', () => { - expect(() => binaryStringToNumber('abc')).toThrowError(CustomError); - }); - - test('with extra whitespace throws CustomError', () => { - expect(() => binaryStringToNumber(' 100')).toThrowError(CustomError); - }); - }); - - describe('given a valid binary string', () => { - test('returns the correct number', () => { - expect(binaryStringToNumber('100')).toBe(4); - }); - }); -}); -``` - -### `describe.each(table)(name, fn, timeout)` - -Use `describe.each` if you keep duplicating the same test suites with different data. `describe.each` allows you to write the test suite once and pass data in. - -`describe.each` is available with two APIs: - -#### 1. `describe.each(table)(name, fn, timeout)` - -- `table`: `Array` of Arrays with the arguments that are passed into the `fn` for each row. - - _Note_ If you pass in a 1D array of primitives, internally it will be mapped to a table i.e. `[1, 2, 3] -> [[1], [2], [3]]` -- `name`: `String` the title of the test suite. - - Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args): - - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format). - - `%s`- String. - - `%d`- Number. - - `%i` - Integer. - - `%f` - Floating point value. - - `%j` - JSON. - - `%o` - Object. - - `%#` - Index of the test case. - - `%%` - single percent sign ('%'). This does not consume an argument. - - Or generate unique test titles by injecting properties of test case object with `$variable` - - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value` - - You can use `$#` to inject the index of the test case - - You cannot use `$variable` with the `printf` formatting except for `%%` -- `fn`: `Function` the suite of tests to be ran, this is the function that will receive the parameters in each row as function arguments. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -describe.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); - }); - - test(`returned value not be greater than ${expected}`, () => { - expect(a + b).not.toBeGreaterThan(expected); - }); - - test(`returned value not be less than ${expected}`, () => { - expect(a + b).not.toBeLessThan(expected); - }); -}); -``` - -```js -describe.each([ - {a: 1, b: 1, expected: 2}, - {a: 1, b: 2, expected: 3}, - {a: 2, b: 1, expected: 3}, -])('.add($a, $b)', ({a, b, expected}) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); - }); - - test(`returned value not be greater than ${expected}`, () => { - expect(a + b).not.toBeGreaterThan(expected); - }); - - test(`returned value not be less than ${expected}`, () => { - expect(a + b).not.toBeLessThan(expected); - }); -}); -``` - -#### 2. `` describe.each`table`(name, fn, timeout) `` - -- `table`: `Tagged Template Literal` - - First row of variable name column headings separated with `|` - - One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. -- `name`: `String` the title of the test suite, use `$variable` to inject test data into the suite title from the tagged template expressions, and `$#` for the index of the row. - - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value` -- `fn`: `Function` the suite of tests to be ran, this is the function that will receive the test data object. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -describe.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('$a + $b', ({a, b, expected}) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); - }); - - test(`returned value not be greater than ${expected}`, () => { - expect(a + b).not.toBeGreaterThan(expected); - }); - - test(`returned value not be less than ${expected}`, () => { - expect(a + b).not.toBeLessThan(expected); - }); -}); -``` - -### `describe.only(name, fn)` - -Also under the alias: `fdescribe(name, fn)` - -You can use `describe.skip` if you do not want to run the tests of a particular `describe` block: - -```js -describe.only('my beverage', () => { - test('is delicious', () => { - expect(myBeverage.delicious).toBeTruthy(); - }); - - test('is not sour', () => { - expect(myBeverage.sour).toBeFalsy(); - }); -}); - -describe('my other beverage', () => { - // ... will be skipped -}); -``` - -### `describe.only.each(table)(name, fn)` - -Also under the aliases: `fdescribe.each(table)(name, fn)` and `` fdescribe.each`table`(name, fn) `` - -Use `describe.only.each` if you want to only run specific tests suites of data driven tests. - -`describe.only.each` is available with two APIs: - -#### `describe.only.each(table)(name, fn)` - -```js -describe.only.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); - }); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` describe.only.each`table`(name, fn) `` - -```js -describe.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', ({a, b, expected}) => { - test('passes', () => { - expect(a + b).toBe(expected); - }); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `describe.skip(name, fn)` - -Also under the alias: `xdescribe(name, fn)` - -You can use `describe.skip` if you do not want to run a particular describe block: - -```js -describe('my beverage', () => { - test('is delicious', () => { - expect(myBeverage.delicious).toBeTruthy(); - }); - - test('is not sour', () => { - expect(myBeverage.sour).toBeFalsy(); - }); -}); - -describe.skip('my other beverage', () => { - // ... will be skipped -}); -``` - -Using `describe.skip` is often a cleaner alternative to temporarily commenting out a chunk of tests. Beware that the `describe` block will still run. If you have some setup that also should be skipped, do it in a `beforeAll` or `beforeEach` block. - -### `describe.skip.each(table)(name, fn)` - -Also under the aliases: `xdescribe.each(table)(name, fn)` and `` xdescribe.each`table`(name, fn) `` - -Use `describe.skip.each` if you want to stop running a suite of data driven tests. - -`describe.skip.each` is available with two APIs: - -#### `describe.skip.each(table)(name, fn)` - -```js -describe.skip.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); // will not be ran - }); -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` describe.skip.each`table`(name, fn) `` - -```js -describe.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', ({a, b, expected}) => { - test('will not be ran', () => { - expect(a + b).toBe(expected); // will not be ran - }); -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `test(name, fn, timeout)` - -Also under the alias: `it(name, fn, timeout)` - -All you need in a test file is the `test` method which runs a test. For example, let's say there's a function `inchesOfRain()` that should be zero. Your whole test could be: - -```js -test('did not rain', () => { - expect(inchesOfRain()).toBe(0); -}); -``` - -The first argument is the test name; the second argument is a function that contains the expectations to test. The third argument (optional) is `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -> Note: If a **promise is returned** from `test`, Jest will wait for the promise to resolve before letting the test complete. Jest will also wait if you **provide an argument to the test function**, usually called `done`. This could be handy when you want to test callbacks. See how to test async code [here](TestingAsyncCode.md#callbacks). - -For example, let's say `fetchBeverageList()` returns a promise that is supposed to resolve to a list that has `lemon` in it. You can test this with: - -```js -test('has lemon in it', () => { - return fetchBeverageList().then(list => { - expect(list).toContain('lemon'); - }); -}); -``` - -Even though the call to `test` will return right away, the test doesn't complete until the promise resolves as well. - -### `test.concurrent(name, fn, timeout)` - -Also under the alias: `it.concurrent(name, fn, timeout)` - -Use `test.concurrent` if you want the test to run concurrently. - -> Note: `test.concurrent` is considered experimental - see [here](https://github.com/facebook/jest/labels/Area%3A%20Concurrent) for details on missing features and other issues - -The first argument is the test name; the second argument is an asynchronous function that contains the expectations to test. The third argument (optional) is `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -``` -test.concurrent('addition of 2 numbers', async () => { - expect(5 + 3).toBe(8); -}); - -test.concurrent('subtraction 2 numbers', async () => { - expect(5 - 3).toBe(2); -}); -``` - -> Note: Use `maxConcurrency` in configuration to prevents Jest from executing more than the specified amount of tests at the same time - -### `test.concurrent.each(table)(name, fn, timeout)` - -Also under the alias: `it.concurrent.each(table)(name, fn, timeout)` - -Use `test.concurrent.each` if you keep duplicating the same test with different data. `test.each` allows you to write the test once and pass data in, the tests are all run asynchronously. - -`test.concurrent.each` is available with two APIs: - -#### 1. `test.concurrent.each(table)(name, fn, timeout)` - -- `table`: `Array` of Arrays with the arguments that are passed into the test `fn` for each row. - - _Note_ If you pass in a 1D array of primitives, internally it will be mapped to a table i.e. `[1, 2, 3] -> [[1], [2], [3]]` -- `name`: `String` the title of the test block. - - Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args): - - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format). - - `%s`- String. - - `%d`- Number. - - `%i` - Integer. - - `%f` - Floating point value. - - `%j` - JSON. - - `%o` - Object. - - `%#` - Index of the test case. - - `%%` - single percent sign ('%'). This does not consume an argument. -- `fn`: `Function` the test to be ran, this is the function that will receive the parameters in each row as function arguments, **this will have to be an asynchronous function**. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -test.concurrent.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', async (a, b, expected) => { - expect(a + b).toBe(expected); -}); -``` - -#### 2. `` test.concurrent.each`table`(name, fn, timeout) `` - -- `table`: `Tagged Template Literal` - - First row of variable name column headings separated with `|` - - One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. -- `name`: `String` the title of the test, use `$variable` to inject test data into the test title from the tagged template expressions. - - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value` -- `fn`: `Function` the test to be ran, this is the function that will receive the test data object, **this will have to be an asynchronous function**. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -test.concurrent.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', async ({a, b, expected}) => { - expect(a + b).toBe(expected); -}); -``` - -### `test.concurrent.only.each(table)(name, fn)` - -Also under the alias: `it.concurrent.only.each(table)(name, fn)` - -Use `test.concurrent.only.each` if you want to only run specific tests with different test data concurrently. - -`test.concurrent.only.each` is available with two APIs: - -#### `test.concurrent.only.each(table)(name, fn)` - -```js -test.concurrent.only.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', async (a, b, expected) => { - expect(a + b).toBe(expected); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` test.only.each`table`(name, fn) `` - -```js -test.concurrent.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', async ({a, b, expected}) => { - expect(a + b).toBe(expected); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `test.concurrent.skip.each(table)(name, fn)` - -Also under the alias: `it.concurrent.skip.each(table)(name, fn)` - -Use `test.concurrent.skip.each` if you want to stop running a collection of asynchronous data driven tests. - -`test.concurrent.skip.each` is available with two APIs: - -#### `test.concurrent.skip.each(table)(name, fn)` - -```js -test.concurrent.skip.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', async (a, b, expected) => { - expect(a + b).toBe(expected); // will not be ran -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` test.concurrent.skip.each`table`(name, fn) `` - -```js -test.concurrent.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', async ({a, b, expected}) => { - expect(a + b).toBe(expected); // will not be ran -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `test.each(table)(name, fn, timeout)` - -Also under the alias: `it.each(table)(name, fn)` and `` it.each`table`(name, fn) `` - -Use `test.each` if you keep duplicating the same test with different data. `test.each` allows you to write the test once and pass data in. - -`test.each` is available with two APIs: - -#### 1. `test.each(table)(name, fn, timeout)` - -- `table`: `Array` of Arrays with the arguments that are passed into the test `fn` for each row. - - _Note_ If you pass in a 1D array of primitives, internally it will be mapped to a table i.e. `[1, 2, 3] -> [[1], [2], [3]]` -- `name`: `String` the title of the test block. - - Generate unique test titles by positionally injecting parameters with [`printf` formatting](https://nodejs.org/api/util.html#util_util_format_format_args): - - `%p` - [pretty-format](https://www.npmjs.com/package/pretty-format). - - `%s`- String. - - `%d`- Number. - - `%i` - Integer. - - `%f` - Floating point value. - - `%j` - JSON. - - `%o` - Object. - - `%#` - Index of the test case. - - `%%` - single percent sign ('%'). This does not consume an argument. - - Or generate unique test titles by injecting properties of test case object with `$variable` - - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value` - - You can use `$#` to inject the index of the test case - - You cannot use `$variable` with the `printf` formatting except for `%%` -- `fn`: `Function` the test to be ran, this is the function that will receive the parameters in each row as function arguments. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -test.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - expect(a + b).toBe(expected); -}); -``` - -```js -test.each([ - {a: 1, b: 1, expected: 2}, - {a: 1, b: 2, expected: 3}, - {a: 2, b: 1, expected: 3}, -])('.add($a, $b)', ({a, b, expected}) => { - expect(a + b).toBe(expected); -}); -``` - -#### 2. `` test.each`table`(name, fn, timeout) `` - -- `table`: `Tagged Template Literal` - - First row of variable name column headings separated with `|` - - One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. -- `name`: `String` the title of the test, use `$variable` to inject test data into the test title from the tagged template expressions. - - To inject nested object values use you can supply a keyPath i.e. `$variable.path.to.value` -- `fn`: `Function` the test to be ran, this is the function that will receive the test data object. -- Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait for each row before aborting. _Note: The default timeout is 5 seconds._ - -Example: - -```js -test.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', ({a, b, expected}) => { - expect(a + b).toBe(expected); -}); -``` - -### `test.only(name, fn, timeout)` - -Also under the aliases: `it.only(name, fn, timeout)`, and `fit(name, fn, timeout)` - -When you are debugging a large test file, you will often only want to run a subset of tests. You can use `.only` to specify which tests are the only ones you want to run in that test file. - -Optionally, you can provide a `timeout` (in milliseconds) for specifying how long to wait before aborting. _Note: The default timeout is 5 seconds._ - -For example, let's say you had these tests: - -```js -test.only('it is raining', () => { - expect(inchesOfRain()).toBeGreaterThan(0); -}); - -test('it is not snowing', () => { - expect(inchesOfSnow()).toBe(0); -}); -``` - -Only the "it is raining" test will run in that test file, since it is run with `test.only`. - -Usually you wouldn't check code using `test.only` into source control - you would use it for debugging, and remove it once you have fixed the broken tests. - -### `test.only.each(table)(name, fn)` - -Also under the aliases: `it.only.each(table)(name, fn)`, `fit.each(table)(name, fn)`, `` it.only.each`table`(name, fn) `` and `` fit.each`table`(name, fn) `` - -Use `test.only.each` if you want to only run specific tests with different test data. - -`test.only.each` is available with two APIs: - -#### `test.only.each(table)(name, fn)` - -```js -test.only.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - expect(a + b).toBe(expected); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` test.only.each`table`(name, fn) `` - -```js -test.only.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', ({a, b, expected}) => { - expect(a + b).toBe(expected); -}); - -test('will not be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `test.skip(name, fn)` - -Also under the aliases: `it.skip(name, fn)`, `xit(name, fn)`, and `xtest(name, fn)` - -When you are maintaining a large codebase, you may sometimes find a test that is temporarily broken for some reason. If you want to skip running this test, but you don't want to delete this code, you can use `test.skip` to specify some tests to skip. - -For example, let's say you had these tests: - -```js -test('it is raining', () => { - expect(inchesOfRain()).toBeGreaterThan(0); -}); - -test.skip('it is not snowing', () => { - expect(inchesOfSnow()).toBe(0); -}); -``` - -Only the "it is raining" test will run, since the other test is run with `test.skip`. - -You could comment the test out, but it's often a bit nicer to use `test.skip` because it will maintain indentation and syntax highlighting. - -### `test.skip.each(table)(name, fn)` - -Also under the aliases: `it.skip.each(table)(name, fn)`, `xit.each(table)(name, fn)`, `xtest.each(table)(name, fn)`, `` it.skip.each`table`(name, fn) ``, `` xit.each`table`(name, fn) `` and `` xtest.each`table`(name, fn) `` - -Use `test.skip.each` if you want to stop running a collection of data driven tests. - -`test.skip.each` is available with two APIs: - -#### `test.skip.each(table)(name, fn)` - -```js -test.skip.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('.add(%i, %i)', (a, b, expected) => { - expect(a + b).toBe(expected); // will not be ran -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -#### `` test.skip.each`table`(name, fn) `` - -```js -test.skip.each` - a | b | expected - ${1} | ${1} | ${2} - ${1} | ${2} | ${3} - ${2} | ${1} | ${3} -`('returns $expected when $a is added $b', ({a, b, expected}) => { - expect(a + b).toBe(expected); // will not be ran -}); - -test('will be ran', () => { - expect(1 / 0).toBe(Infinity); -}); -``` - -### `test.todo(name)` - -Also under the alias: `it.todo(name)` - -Use `test.todo` when you are planning on writing tests. These tests will be highlighted in the summary output at the end so you know how many tests you still need todo. - -_Note_: If you supply a test callback function then the `test.todo` will throw an error. If you have already implemented the test and it is broken and you do not want it to run, then use `test.skip` instead. - -#### API - -- `name`: `String` the title of the test plan. - -Example: - -```js -const add = (a, b) => a + b; - -test.todo('add should be associative'); -``` diff --git a/website/versioned_docs/version-27.2/JestCommunity.md b/website/versioned_docs/version-27.2/JestCommunity.md deleted file mode 100644 index af71f2e575ae..000000000000 --- a/website/versioned_docs/version-27.2/JestCommunity.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Jest Community -id: jest-community ---- - -The community around Jest is working hard to make the testing experience even greater. - -[jest-community](https://github.com/jest-community) is a new GitHub organization for high quality Jest additions curated by Jest maintainers and collaborators. It already features some of our favorite projects, to name a few: - -- [vscode-jest](https://github.com/jest-community/vscode-jest) -- [jest-extended](https://github.com/jest-community/jest-extended) -- [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) -- [awesome-jest](https://github.com/jest-community/awesome-jest) - -Community projects under one organisation are a great way for Jest to experiment with new ideas/techniques and approaches. Encourage contributions from the community and publish contributions independently at a faster pace. - -## Awesome Jest - -The jest-community org maintains an [awesome-jest](https://github.com/jest-community/awesome-jest) list of great projects and resources related to Jest. - -If you have something awesome to share, feel free to reach out to us! We'd love to share your project on the awesome-jest list ([send a PR here](https://github.com/jest-community/awesome-jest/pulls)) or if you would like to transfer your project to the jest-community org reach out to one of the owners of the org. diff --git a/website/versioned_docs/version-27.2/JestObjectAPI.md b/website/versioned_docs/version-27.2/JestObjectAPI.md deleted file mode 100644 index de5d9a92a145..000000000000 --- a/website/versioned_docs/version-27.2/JestObjectAPI.md +++ /dev/null @@ -1,686 +0,0 @@ ---- -id: jest-object -title: The Jest Object ---- - -The `jest` object is automatically in scope within every test file. The methods in the `jest` object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via `import {jest} from '@jest/globals'`. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Mock Modules - -### `jest.disableAutomock()` - -Disables automatic mocking in the module loader. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -After this method is called, all `require()`s will return the real versions of each module (rather than a mocked version). - -Jest configuration: - -```json -{ - "automock": true -} -``` - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, -}; -``` - -```js title="__tests__/disableAutomocking.js" -import utils from '../utils'; - -jest.disableAutomock(); - -test('original implementation', () => { - // now we have the original implementation, - // even if we set the automocking in a jest configuration - expect(utils.authorize()).toBe('token'); -}); -``` - -This is usually useful when you have a scenario where the number of dependencies you want to mock is far less than the number of dependencies that you don't. For example, if you're writing a test for a module that uses a large number of dependencies that can be reasonably classified as "implementation details" of the module, then you likely do not want to mock them. - -Examples of dependencies that might be considered "implementation details" are things ranging from language built-ins (e.g. Array.prototype methods) to highly common utility methods (e.g. underscore/lo-dash, array utilities, etc) and entire libraries like React.js. - -Returns the `jest` object for chaining. - -_Note: this method was previously called `autoMockOff`. When using `babel-jest`, calls to `disableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOff` if you want to explicitly avoid this behavior._ - -### `jest.enableAutomock()` - -Enables automatic mocking in the module loader. - -Returns the `jest` object for chaining. - -> See `automock` section of [configuration](Configuration.md#automock-boolean) for more information - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/enableAutomocking.js" -jest.enableAutomock(); - -import utils from '../utils'; - -test('original implementation', () => { - // now we have the mocked implementation, - expect(utils.authorize._isMockFunction).toBeTruthy(); - expect(utils.isAuthorized._isMockFunction).toBeTruthy(); -}); -``` - -_Note: this method was previously called `autoMockOn`. When using `babel-jest`, calls to `enableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOn` if you want to explicitly avoid this behavior._ - -### `jest.createMockFromModule(moduleName)` - -##### renamed in Jest **26.0.0+** - -Also under the alias: `.genMockFromModule(moduleName)` - -Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you. - -This is useful when you want to create a [manual mock](ManualMocks.md) that extends the automatic mock's behavior. - -Example: - -```js title="utils.js" -export default { - authorize: () => { - return 'token'; - }, - isAuthorized: secret => secret === 'wizard', -}; -``` - -```js title="__tests__/createMockFromModule.test.js" -const utils = jest.createMockFromModule('../utils').default; -utils.isAuthorized = jest.fn(secret => secret === 'not wizard'); - -test('implementation created by jest.createMockFromModule', () => { - expect(utils.authorize.mock).toBeTruthy(); - expect(utils.isAuthorized('not wizard')).toEqual(true); -}); -``` - -This is how `createMockFromModule` will mock the following data types: - -#### `Function` - -Creates a new [mock function](mock-functions). The new function has no formal parameters and when called will return `undefined`. This functionality also applies to `async` functions. - -#### `Class` - -Creates a new class. The interface of the original class is maintained, all of the class member functions and properties will be mocked. - -#### `Object` - -Creates a new deeply cloned object. The object keys are maintained and their values are mocked. - -#### `Array` - -Creates a new empty array, ignoring the original. - -#### `Primitives` - -Creates a new property with the same primitive value as the original property. - -Example: - -```js title="example.js" -module.exports = { - function: function square(a, b) { - return a * b; - }, - asyncFunction: async function asyncSquare(a, b) { - const result = (await a) * b; - return result; - }, - class: new (class Bar { - constructor() { - this.array = [1, 2, 3]; - } - foo() {} - })(), - object: { - baz: 'foo', - bar: { - fiz: 1, - buzz: [1, 2, 3], - }, - }, - array: [1, 2, 3], - number: 123, - string: 'baz', - boolean: true, - symbol: Symbol.for('a.b.c'), -}; -``` - -```js title="__tests__/example.test.js" -const example = jest.createMockFromModule('./example'); - -test('should run example code', () => { - // creates a new mocked function with no formal arguments. - expect(example.function.name).toEqual('square'); - expect(example.function.length).toEqual(0); - - // async functions get the same treatment as standard synchronous functions. - expect(example.asyncFunction.name).toEqual('asyncSquare'); - expect(example.asyncFunction.length).toEqual(0); - - // creates a new class with the same interface, member functions and properties are mocked. - expect(example.class.constructor.name).toEqual('Bar'); - expect(example.class.foo.name).toEqual('foo'); - expect(example.class.array.length).toEqual(0); - - // creates a deeply cloned version of the original object. - expect(example.object).toEqual({ - baz: 'foo', - bar: { - fiz: 1, - buzz: [], - }, - }); - - // creates a new empty array, ignoring the original array. - expect(example.array.length).toEqual(0); - - // creates a new property with the same primitive value as the original property. - expect(example.number).toEqual(123); - expect(example.string).toEqual('baz'); - expect(example.boolean).toEqual(true); - expect(example.symbol).toEqual(Symbol.for('a.b.c')); -}); -``` - -### `jest.mock(moduleName, factory, options)` - -Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example: - -```js title="banana.js" -module.exports = () => 'banana'; -``` - -```js title="__tests__/test.js" -jest.mock('../banana'); - -const banana = require('../banana'); // banana will be explicitly mocked. - -banana(); // will return 'undefined' because the function is auto-mocked. -``` - -The second argument can be used to specify an explicit module factory that is being run instead of using Jest's automocking feature: - -```js -jest.mock('../moduleName', () => { - return jest.fn(() => 42); -}); - -// This runs the function specified as second argument to `jest.mock`. -const moduleName = require('../moduleName'); -moduleName(); // Will return '42'; -``` - -When using the `factory` parameter for an ES6 module with a default export, the `__esModule: true` property needs to be specified. This property is normally generated by Babel / TypeScript, but here it needs to be set manually. When importing a default export, it's an instruction to import the property named `default` from the export object: - -```js -import moduleName, {foo} from '../moduleName'; - -jest.mock('../moduleName', () => { - return { - __esModule: true, - default: jest.fn(() => 42), - foo: jest.fn(() => 43), - }; -}); - -moduleName(); // Will return 42 -foo(); // Will return 43 -``` - -The third argument can be used to create virtual mocks – mocks of modules that don't exist anywhere in the system: - -```js -jest.mock( - '../moduleName', - () => { - /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ - }, - {virtual: true}, -); -``` - -> **Warning:** Importing a module in a setup file (as specified by `setupFilesAfterEnv`) will prevent mocking for the module in question, as well as all the modules that it imports. - -Modules that are mocked with `jest.mock` are mocked only for the file that calls `jest.mock`. Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module. - -Returns the `jest` object for chaining. - -### `jest.unmock(moduleName)` - -Indicates that the module system should never return a mocked version of the specified module from `require()` (e.g. that it should always return the real module). - -The most common use of this API is for specifying the module a given test intends to be testing (and thus doesn't want automatically mocked). - -Returns the `jest` object for chaining. - -### `jest.doMock(moduleName, factory, options)` - -When using `babel-jest`, calls to `mock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -One example when this is useful is when you want to mock a module differently within the same file: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 1); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(1); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return jest.fn(() => 2); - }); - const moduleName = require('../moduleName'); - expect(moduleName()).toEqual(2); -}); -``` - -Using `jest.doMock()` with ES6 imports requires additional steps. Follow these if you don't want to use `require` in your tests: - -- We have to specify the `__esModule: true` property (see the [`jest.mock()`](#jestmockmodulename-factory-options) API for more information). -- Static ES6 module imports are hoisted to the top of the file, so instead we have to import them dynamically using `import()`. -- Finally, we need an environment which supports dynamic importing. Please see [Using Babel](GettingStarted.md#using-babel) for the initial setup. Then add the plugin [babel-plugin-dynamic-import-node](https://www.npmjs.com/package/babel-plugin-dynamic-import-node), or an equivalent, to your Babel config to enable dynamic importing in Node. - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('moduleName 1', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default1', - foo: 'foo1', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default1'); - expect(moduleName.foo).toEqual('foo1'); - }); -}); - -test('moduleName 2', () => { - jest.doMock('../moduleName', () => { - return { - __esModule: true, - default: 'default2', - foo: 'foo2', - }; - }); - return import('../moduleName').then(moduleName => { - expect(moduleName.default).toEqual('default2'); - expect(moduleName.foo).toEqual('foo2'); - }); -}); -``` - -Returns the `jest` object for chaining. - -### `jest.dontMock(moduleName)` - -When using `babel-jest`, calls to `unmock` will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior. - -Returns the `jest` object for chaining. - -### `jest.setMock(moduleName, moduleExports)` - -Explicitly supplies the mock object that the module system should return for the specified module. - -On occasion, there are times where the automatically generated mock the module system would normally provide you isn't adequate enough for your testing needs. Normally under those circumstances you should write a [manual mock](ManualMocks.md) that is more adequate for the module in question. However, on extremely rare occasions, even a manual mock isn't suitable for your purposes and you need to build the mock yourself inside your test. - -In these rare scenarios you can use this API to manually fill the slot in the module system's mock-module registry. - -Returns the `jest` object for chaining. - -_Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object._ - -### `jest.requireActual(moduleName)` - -Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not. - -Example: - -```js -jest.mock('../myModule', () => { - // Require the original module to not be mocked... - const originalModule = jest.requireActual('../myModule'); - - return { - __esModule: true, // Use it when dealing with esModules - ...originalModule, - getRandom: jest.fn().mockReturnValue(10), - }; -}); - -const getRandom = require('../myModule').getRandom; - -getRandom(); // Always returns 10 -``` - -### `jest.requireMock(moduleName)` - -Returns a mock module instead of the actual module, bypassing all checks on whether the module should be required normally or not. - -### `jest.resetModules()` - -Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests. - -Example: - -```js -const sum1 = require('../sum'); -jest.resetModules(); -const sum2 = require('../sum'); -sum1 === sum2; -// > false (Both sum modules are separate "instances" of the sum module.) -``` - -Example in a test: - -```js -beforeEach(() => { - jest.resetModules(); -}); - -test('works', () => { - const sum = require('../sum'); -}); - -test('works too', () => { - const sum = require('../sum'); - // sum is a different copy of the sum module from the previous test. -}); -``` - -Returns the `jest` object for chaining. - -### `jest.isolateModules(fn)` - -`jest.isolateModules(fn)` goes a step further than `jest.resetModules()` and creates a sandbox registry for the modules that are loaded inside the callback function. This is useful to isolate specific modules for every test so that local module state doesn't conflict between tests. - -```js -let myModule; -jest.isolateModules(() => { - myModule = require('myModule'); -}); - -const otherCopyOfMyModule = require('myModule'); -``` - -## Mock Functions - -### `jest.fn(implementation)` - -Returns a new, unused [mock function](MockFunctionAPI.md). Optionally takes a mock implementation. - -```js -const mockFn = jest.fn(); -mockFn(); -expect(mockFn).toHaveBeenCalled(); - -// With a mock implementation: -const returnsTrue = jest.fn(() => true); -console.log(returnsTrue()); // true; -``` - -### `jest.isMockFunction(fn)` - -Determines if the given function is a mocked function. - -### `jest.spyOn(object, methodName)` - -Creates a mock function similar to `jest.fn` but also tracks calls to `object[methodName]`. Returns a Jest [mock function](MockFunctionAPI.md). - -_Note: By default, `jest.spyOn` also calls the **spied** method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use `jest.spyOn(object, methodName).mockImplementation(() => customImplementation)` or `object[methodName] = jest.fn(() => customImplementation);`_ - -Example: - -```js -const video = { - play() { - return true; - }, -}; - -module.exports = video; -``` - -Example test: - -```js -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play'); - const isPlaying = video.play(); - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); -``` - -### `jest.spyOn(object, methodName, accessType?)` - -Since Jest 22.1.0+, the `jest.spyOn` method takes an optional third argument of `accessType` that can be either `'get'` or `'set'`, which proves to be useful when you want to spy on a getter or a setter, respectively. - -Example: - -```js -const video = { - // it's a getter! - get play() { - return true; - }, -}; - -module.exports = video; - -const audio = { - _volume: false, - // it's a setter! - set volume(value) { - this._volume = value; - }, - get volume() { - return this._volume; - }, -}; - -module.exports = audio; -``` - -Example test: - -```js -const audio = require('./audio'); -const video = require('./video'); - -test('plays video', () => { - const spy = jest.spyOn(video, 'play', 'get'); // we pass 'get' - const isPlaying = video.play; - - expect(spy).toHaveBeenCalled(); - expect(isPlaying).toBe(true); - - spy.mockRestore(); -}); - -test('plays audio', () => { - const spy = jest.spyOn(audio, 'volume', 'set'); // we pass 'set' - audio.volume = 100; - - expect(spy).toHaveBeenCalled(); - expect(audio.volume).toBe(100); - - spy.mockRestore(); -}); -``` - -### `jest.clearAllMocks()` - -Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.resetAllMocks()` - -Resets the state of all mocks. Equivalent to calling [`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. - -Returns the `jest` object for chaining. - -### `jest.restoreAllMocks()` - -Restores all mocks back to their original value. Equivalent to calling [`.mockRestore()`](MockFunctionAPI.md#mockfnmockrestore) on every mocked function. Beware that `jest.restoreAllMocks()` only works when the mock was created with `jest.spyOn`; other mocks will require you to manually restore them. - -## Mock Timers - -### `jest.useFakeTimers(implementation?: 'modern' | 'legacy')` - -Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate` as well as `Date`). - -If you pass `'legacy'` as an argument, Jest's legacy implementation will be used rather than one based on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). - -Returns the `jest` object for chaining. - -### `jest.useRealTimers()` - -Instructs Jest to use the real versions of the standard timer functions. - -Returns the `jest` object for chaining. - -### `jest.runAllTicks()` - -Exhausts the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending micro-tasks that have been queued via `process.nextTick` will be executed. Additionally, if those micro-tasks themselves schedule new micro-tasks, those will be continually exhausted until there are no more micro-tasks remaining in the queue. - -### `jest.runAllTimers()` - -Exhausts both the **macro**-task queue (i.e., all tasks queued by `setTimeout()`, `setInterval()`, and `setImmediate()`) and the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending macro-tasks and micro-tasks will be executed. If those tasks themselves schedule new tasks, those will be continually exhausted until there are no more tasks remaining in the queue. - -This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information. - -### `jest.runAllImmediates()` - -Exhausts all tasks queued by `setImmediate()`. - -> Note: This function is not available when using modern fake timers implementation - -### `jest.advanceTimersByTime(msToRun)` - -Executes only the macro task queue (i.e. all tasks queued by `setTimeout()` or `setInterval()` and `setImmediate()`). - -When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()`, and would be executed within this time frame will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue, that should be run within `msToRun` milliseconds. - -### `jest.runOnlyPendingTimers()` - -Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by `setTimeout()` or `setInterval()` up to this point). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call. - -This is useful for scenarios such as one where the module being tested schedules a `setTimeout()` whose callback schedules another `setTimeout()` recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time. - -### `jest.advanceTimersToNextTimer(steps)` - -Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run. - -Optionally, you can provide `steps`, so it will run `steps` amount of next timeouts/intervals. - -### `jest.clearAllTimers()` - -Removes any pending timers from the timer system. - -This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future. - -### `jest.getTimerCount()` - -Returns the number of fake timers still left to run. - -### `jest.setSystemTime(now?: number | Date)` - -Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`. - -> Note: This function is only available when using modern fake timers implementation - -### `jest.getRealSystemTime()` - -When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function. - -> Note: This function is only available when using modern fake timers implementation - -## Misc - -### `jest.setTimeout(timeout)` - -Set the default timeout interval for tests and before/after hooks in milliseconds. This only affects the test file from which this function is called. - -_Note: The default timeout interval is 5 seconds if this method is not called._ - -_Note: If you want to set the timeout for all test files, a good place to do this is in `setupFilesAfterEnv`._ - -Example: - -```js -jest.setTimeout(1000); // 1 second -``` - -### `jest.retryTimes()` - -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! This must live at the top-level of a test file or in a describe block. Retries _will not_ work if `jest.retryTimes()` is called in a `beforeEach` or a `test` block. - -Example in a test: - -```js -jest.retryTimes(3); -test('will fail', () => { - expect(true).toBe(false); -}); -``` - -Returns the `jest` object for chaining. diff --git a/website/versioned_docs/version-27.2/JestPlatform.md b/website/versioned_docs/version-27.2/JestPlatform.md deleted file mode 100644 index 7e638e040b63..000000000000 --- a/website/versioned_docs/version-27.2/JestPlatform.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -id: jest-platform -title: Jest Platform ---- - -You can cherry pick specific features of Jest and use them as standalone packages. Here's a list of the available packages: - -## jest-changed-files - -Tool for identifying modified files in a git/hg repository. Exports two functions: - -- `getChangedFilesForRoots` returns a promise that resolves to an object with the changed files and repos. -- `findRepos` returns a promise that resolves to a set of repositories contained in the specified path. - -### Example - -```javascript -const {getChangedFilesForRoots} = require('jest-changed-files'); - -// print the set of modified files since last commit in the current repo -getChangedFilesForRoots(['./'], { - lastCommit: true, -}).then(result => console.log(result.changedFiles)); -``` - -You can read more about `jest-changed-files` in the [readme file](https://github.com/facebook/jest/blob/main/packages/jest-changed-files/README.md). - -## jest-diff - -Tool for visualizing changes in data. Exports a function that compares two values of any type and returns a "pretty-printed" string illustrating the difference between the two arguments. - -### Example - -```javascript -const {diff} = require('jest-diff'); - -const a = {a: {b: {c: 5}}}; -const b = {a: {b: {c: 6}}}; - -const result = diff(a, b); - -// print diff -console.log(result); -``` - -## jest-docblock - -Tool for extracting and parsing the comments at the top of a JavaScript file. Exports various functions to manipulate the data inside the comment block. - -### Example - -```javascript -const {parseWithComments} = require('jest-docblock'); - -const code = ` -/** - * This is a sample - * - * @flow - */ - - console.log('Hello World!'); -`; - -const parsed = parseWithComments(code); - -// prints an object with two attributes: comments and pragmas. -console.log(parsed); -``` - -You can read more about `jest-docblock` in the [readme file](https://github.com/facebook/jest/blob/main/packages/jest-docblock/README.md). - -## jest-get-type - -Module that identifies the primitive type of any JavaScript value. Exports a function that returns a string with the type of the value passed as argument. - -### Example - -```javascript -const {getType} = require('jest-get-type'); - -const array = [1, 2, 3]; -const nullValue = null; -const undefinedValue = undefined; - -// prints 'array' -console.log(getType(array)); -// prints 'null' -console.log(getType(nullValue)); -// prints 'undefined' -console.log(getType(undefinedValue)); -``` - -## jest-validate - -Tool for validating configurations submitted by users. Exports a function that takes two arguments: the user's configuration and an object containing an example configuration and other options. The return value is an object with two attributes: - -- `hasDeprecationWarnings`, a boolean indicating whether the submitted configuration has deprecation warnings, -- `isValid`, a boolean indicating whether the configuration is correct or not. - -### Example - -```javascript -const {validate} = require('jest-validate'); - -const configByUser = { - transform: '/node_modules/my-custom-transform', -}; - -const result = validate(configByUser, { - comment: ' Documentation: http://custom-docs.com', - exampleConfig: {transform: '/node_modules/babel-jest'}, -}); - -console.log(result); -``` - -You can read more about `jest-validate` in the [readme file](https://github.com/facebook/jest/blob/main/packages/jest-validate/README.md). - -## jest-worker - -Module used for parallelization of tasks. Exports a class `JestWorker` that takes the path of Node.js module and lets you call the module's exported methods as if they were class methods, returning a promise that resolves when the specified method finishes its execution in a forked process. - -### Example - -```javascript title="heavy-task.js" -module.exports = { - myHeavyTask: args => { - // long running CPU intensive task. - }, -}; -``` - -```javascript title="main.js" -async function main() { - const worker = new Worker(require.resolve('./heavy-task.js')); - - // run 2 tasks in parallel with different arguments - const results = await Promise.all([ - worker.myHeavyTask({foo: 'bar'}), - worker.myHeavyTask({bar: 'foo'}), - ]); - - console.log(results); -} - -main(); -``` - -You can read more about `jest-worker` in the [readme file](https://github.com/facebook/jest/blob/main/packages/jest-worker/README.md). - -## pretty-format - -Exports a function that converts any JavaScript value into a human-readable string. Supports all built-in JavaScript types out of the box and allows extension for application-specific types via user-defined plugins. - -### Example - -```javascript -const {format: prettyFormat} = require('pretty-format'); - -const val = {object: {}}; -val.circularReference = val; -val[Symbol('foo')] = 'foo'; -val.map = new Map([['prop', 'value']]); -val.array = [-0, Infinity, NaN]; - -console.log(prettyFormat(val)); -``` - -You can read more about `pretty-format` in the [readme file](https://github.com/facebook/jest/blob/main/packages/pretty-format/README.md). diff --git a/website/versioned_docs/version-27.2/ManualMocks.md b/website/versioned_docs/version-27.2/ManualMocks.md deleted file mode 100644 index 178226d727f9..000000000000 --- a/website/versioned_docs/version-27.2/ManualMocks.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -id: manual-mocks -title: Manual Mocks ---- - -Manual mocks are used to stub out functionality with mock data. For example, instead of accessing a remote resource like a website or a database, you might want to create a manual mock that allows you to use fake data. This ensures your tests will be fast and not flaky. - -## Mocking user modules - -Manual mocks are defined by writing a module in a `__mocks__/` subdirectory immediately adjacent to the module. For example, to mock a module called `user` in the `models` directory, create a file called `user.js` and put it in the `models/__mocks__` directory. Note that the `__mocks__` folder is case-sensitive, so naming the directory `__MOCKS__` will break on some systems. - -> When we require that module in our tests (meaning we want to use the manual mock instead of the real implementation), explicitly calling `jest.mock('./moduleName')` is **required**. - -## Mocking Node modules - -If the module you are mocking is a Node module (e.g.: `lodash`), the mock should be placed in the `__mocks__` directory adjacent to `node_modules` (unless you configured [`roots`](Configuration.md#roots-arraystring) to point to a folder other than the project root) and will be **automatically** mocked. There's no need to explicitly call `jest.mock('module_name')`. - -Scoped modules (also known as [scoped packages](https://docs.npmjs.com/cli/v6/using-npm/scope)) can be mocked by creating a file in a directory structure that matches the name of the scoped module. For example, to mock a scoped module called `@scope/project-name`, create a file at `__mocks__/@scope/project-name.js`, creating the `@scope/` directory accordingly. - -> Warning: If we want to mock Node's core modules (e.g.: `fs` or `path`), then explicitly calling e.g. `jest.mock('path')` is **required**, because core Node modules are not mocked by default. - -## Examples - -```bash -. -├── config -├── __mocks__ -│   └── fs.js -├── models -│   ├── __mocks__ -│   │   └── user.js -│   └── user.js -├── node_modules -└── views -``` - -When a manual mock exists for a given module, Jest's module system will use that module when explicitly calling `jest.mock('moduleName')`. However, when `automock` is set to `true`, the manual mock implementation will be used instead of the automatically created mock, even if `jest.mock('moduleName')` is not called. To opt out of this behavior you will need to explicitly call `jest.unmock('moduleName')` in tests that should use the actual module implementation. - -> Note: In order to mock properly, Jest needs `jest.mock('moduleName')` to be in the same scope as the `require/import` statement. - -Here's a contrived example where we have a module that provides a summary of all the files in a given directory. In this case, we use the core (built in) `fs` module. - -```javascript title="FileSummarizer.js" -'use strict'; - -const fs = require('fs'); - -function summarizeFilesInDirectorySync(directory) { - return fs.readdirSync(directory).map(fileName => ({ - directory, - fileName, - })); -} - -exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync; -``` - -Since we'd like our tests to avoid actually hitting the disk (that's pretty slow and fragile), we create a manual mock for the `fs` module by extending an automatic mock. Our manual mock will implement custom versions of the `fs` APIs that we can build on for our tests: - -```javascript title="__mocks__/fs.js" -'use strict'; - -const path = require('path'); - -const fs = jest.createMockFromModule('fs'); - -// This is a custom function that our tests can use during setup to specify -// what the files on the "mock" filesystem should look like when any of the -// `fs` APIs are used. -let mockFiles = Object.create(null); -function __setMockFiles(newMockFiles) { - mockFiles = Object.create(null); - for (const file in newMockFiles) { - const dir = path.dirname(file); - - if (!mockFiles[dir]) { - mockFiles[dir] = []; - } - mockFiles[dir].push(path.basename(file)); - } -} - -// A custom version of `readdirSync` that reads from the special mocked out -// file list set via __setMockFiles -function readdirSync(directoryPath) { - return mockFiles[directoryPath] || []; -} - -fs.__setMockFiles = __setMockFiles; -fs.readdirSync = readdirSync; - -module.exports = fs; -``` - -Now we write our test. Note that we need to explicitly tell that we want to mock the `fs` module because it’s a core Node module: - -```javascript title="__tests__/FileSummarizer-test.js" -'use strict'; - -jest.mock('fs'); - -describe('listFilesInDirectorySync', () => { - const MOCK_FILE_INFO = { - '/path/to/file1.js': 'console.log("file1 contents");', - '/path/to/file2.txt': 'file2 contents', - }; - - beforeEach(() => { - // Set up some mocked out file info before each test - require('fs').__setMockFiles(MOCK_FILE_INFO); - }); - - test('includes all files in the directory in the summary', () => { - const FileSummarizer = require('../FileSummarizer'); - const fileSummary = - FileSummarizer.summarizeFilesInDirectorySync('/path/to'); - - expect(fileSummary.length).toBe(2); - }); -}); -``` - -The example mock shown here uses [`jest.createMockFromModule`](JestObjectAPI.md#jestcreatemockfrommodulemodulename) to generate an automatic mock, and overrides its default behavior. This is the recommended approach, but is completely optional. If you do not want to use the automatic mock at all, you can export your own functions from the mock file. One downside to fully manual mocks is that they're manual – meaning you have to manually update them any time the module they are mocking changes. Because of this, it's best to use or extend the automatic mock when it works for your needs. - -To ensure that a manual mock and its real implementation stay in sync, it might be useful to require the real module using [`jest.requireActual(moduleName)`](JestObjectAPI.md#jestrequireactualmodulename) in your manual mock and amending it with mock functions before exporting it. - -The code for this example is available at [examples/manual-mocks](https://github.com/facebook/jest/tree/main/examples/manual-mocks). - -## Using with ES module imports - -If you're using [ES module imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) then you'll normally be inclined to put your `import` statements at the top of the test file. But often you need to instruct Jest to use a mock before modules use it. For this reason, Jest will automatically hoist `jest.mock` calls to the top of the module (before any imports). To learn more about this and see it in action, see [this repo](https://github.com/kentcdodds/how-jest-mocking-works). - -## Mocking methods which are not implemented in JSDOM - -If some code uses a method which JSDOM (the DOM implementation used by Jest) hasn't implemented yet, testing it is not easily possible. This is e.g. the case with `window.matchMedia()`. Jest returns `TypeError: window.matchMedia is not a function` and doesn't properly execute the test. - -In this case, mocking `matchMedia` in the test file should solve the issue: - -```js -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // deprecated - removeListener: jest.fn(), // deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); -``` - -This works if `window.matchMedia()` is used in a function (or method) which is invoked in the test. If `window.matchMedia()` is executed directly in the tested file, Jest reports the same error. In this case, the solution is to move the manual mock into a separate file and include this one in the test **before** the tested file: - -```js -import './matchMedia.mock'; // Must be imported before the tested file -import {myMethod} from './file-to-test'; - -describe('myMethod()', () => { - // Test the method here... -}); -``` diff --git a/website/versioned_docs/version-27.2/MigrationGuide.md b/website/versioned_docs/version-27.2/MigrationGuide.md deleted file mode 100644 index 13d0bed5674d..000000000000 --- a/website/versioned_docs/version-27.2/MigrationGuide.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: migration-guide -title: Migrating to Jest ---- - -If you'd like to try out Jest with an existing codebase, there are a number of ways to convert to Jest: - -- If you are using Jasmine, or a Jasmine like API (for example [Mocha](https://mochajs.org)), Jest should be mostly compatible, which makes it less complicated to migrate to. -- If you are using AVA, Expect.js (by Automattic), Jasmine, Mocha, proxyquire, Should.js or Tape you can automatically migrate with Jest Codemods (see below). -- If you like [chai](http://chaijs.com/), you can upgrade to Jest and continue using chai. However, we recommend trying out Jest's assertions and their failure messages. Jest Codemods can migrate from chai (see below). - -## jest-codemods - -If you are using [AVA](https://github.com/avajs/ava), [Chai](https://github.com/chaijs/chai), [Expect.js (by Automattic)](https://github.com/Automattic/expect.js), [Jasmine](https://github.com/jasmine/jasmine), [Mocha](https://github.com/mochajs/mocha), [proxyquire](https://github.com/thlorenz/proxyquire), [Should.js](https://github.com/shouldjs/should.js) or [Tape](https://github.com/substack/tape) you can use the third-party [jest-codemods](https://github.com/skovhus/jest-codemods) to do most of the dirty migration work. It runs a code transformation on your codebase using [jscodeshift](https://github.com/facebook/jscodeshift). - -To transform your existing tests, navigate to the project containing the tests and run: - -```bash -npx jest-codemods -``` - -More information can be found at [https://github.com/skovhus/jest-codemods](https://github.com/skovhus/jest-codemods). diff --git a/website/versioned_docs/version-27.2/MockFunctionAPI.md b/website/versioned_docs/version-27.2/MockFunctionAPI.md deleted file mode 100644 index 5bb3925c81a1..000000000000 --- a/website/versioned_docs/version-27.2/MockFunctionAPI.md +++ /dev/null @@ -1,448 +0,0 @@ ---- -id: mock-function-api -title: Mock Functions ---- - -Mock functions are also known as "spies", because they let you spy on the behavior of a function that is called indirectly by some other code, rather than only testing the output. You can create a mock function with `jest.fn()`. If no implementation is given, the mock function will return `undefined` when invoked. - -## Methods - -import TOCInline from '@theme/TOCInline'; - - - ---- - -## Reference - -### `mockFn.getMockName()` - -Returns the mock name string set by calling `mockFn.mockName(value)`. - -### `mockFn.mock.calls` - -An array containing the call arguments of all calls that have been made to this mock function. Each item in the array is an array of arguments that were passed during the call. - -For example: A mock function `f` that has been called twice, with the arguments `f('arg1', 'arg2')`, and then with the arguments `f('arg3', 'arg4')`, would have a `mock.calls` array that looks like this: - -```js -[ - ['arg1', 'arg2'], - ['arg3', 'arg4'], -]; -``` - -### `mockFn.mock.results` - -An array containing the results of all calls that have been made to this mock function. Each entry in this array is an object containing a `type` property, and a `value` property. `type` will be one of the following: - -- `'return'` - Indicates that the call completed by returning normally. -- `'throw'` - Indicates that the call completed by throwing a value. -- `'incomplete'` - Indicates that the call has not yet completed. This occurs if you test the result from within the mock function itself, or from within a function that was called by the mock. - -The `value` property contains the value that was thrown or returned. `value` is undefined when `type === 'incomplete'`. - -For example: A mock function `f` that has been called three times, returning `'result1'`, throwing an error, and then returning `'result2'`, would have a `mock.results` array that looks like this: - -```js -[ - { - type: 'return', - value: 'result1', - }, - { - type: 'throw', - value: { - /* Error instance */ - }, - }, - { - type: 'return', - value: 'result2', - }, -]; -``` - -### `mockFn.mock.instances` - -An array that contains all the object instances that have been instantiated from this mock function using `new`. - -For example: A mock function that has been instantiated twice would have the following `mock.instances` array: - -```js -const mockFn = jest.fn(); - -const a = new mockFn(); -const b = new mockFn(); - -mockFn.mock.instances[0] === a; // true -mockFn.mock.instances[1] === b; // true -``` - -### `mockFn.mockClear()` - -Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions. - -Beware that `mockClear` will replace `mockFn.mock`, not just these three properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data. - -The [`clearMocks`](configuration#clearmocks-boolean) configuration option is available to clear mocks automatically before each tests. - -### `mockFn.mockReset()` - -Does everything that [`mockFn.mockClear()`](#mockfnmockclear) does, and also removes any mocked return values or implementations. - -This is useful when you want to completely reset a _mock_ back to its initial state. (Note that resetting a _spy_ will result in a function with no return value). - -The [`mockReset`](configuration#resetmocks-boolean) configuration option is available to reset mocks automatically before each test. - -### `mockFn.mockRestore()` - -Does everything that [`mockFn.mockReset()`](#mockfnmockreset) does, and also restores the original (non-mocked) implementation. - -This is useful when you want to mock functions in certain test cases and restore the original implementation in others. - -Beware that `mockFn.mockRestore` only works when the mock was created with `jest.spyOn`. Thus you have to take care of restoration yourself when manually assigning `jest.fn()`. - -The [`restoreMocks`](configuration#restoremocks-boolean) configuration option is available to restore mocks automatically before each test. - -### `mockFn.mockImplementation(fn)` - -Accepts a function that should be used as the implementation of the mock. The mock itself will still record all calls that go into and instances that come from itself – the only difference is that the implementation will also be executed when the mock is called. - -_Note: `jest.fn(implementation)` is a shorthand for `jest.fn().mockImplementation(implementation)`._ - -For example: - -```js -const mockFn = jest.fn().mockImplementation(scalar => 42 + scalar); -// or: jest.fn(scalar => 42 + scalar); - -const a = mockFn(0); -const b = mockFn(1); - -a === 42; // true -b === 43; // true - -mockFn.mock.calls[0][0] === 0; // true -mockFn.mock.calls[1][0] === 1; // true -``` - -`mockImplementation` can also be used to mock class constructors: - -```js title="SomeClass.js" -module.exports = class SomeClass { - m(a, b) {} -}; -``` - -```js title="OtherModule.test.js" -jest.mock('./SomeClass'); // this happens automatically with automocking -const SomeClass = require('./SomeClass'); -const mMock = jest.fn(); -SomeClass.mockImplementation(() => { - return { - m: mMock, - }; -}); - -const some = new SomeClass(); -some.m('a', 'b'); -console.log('Calls to m: ', mMock.mock.calls); -``` - -### `mockFn.mockImplementationOnce(fn)` - -Accepts a function that will be used as an implementation of the mock for one call to the mocked function. Can be chained so that multiple function calls produce different results. - -```js -const myMockFn = jest - .fn() - .mockImplementationOnce(cb => cb(null, true)) - .mockImplementationOnce(cb => cb(null, false)); - -myMockFn((err, val) => console.log(val)); // true - -myMockFn((err, val) => console.log(val)); // false -``` - -When the mocked function runs out of implementations defined with mockImplementationOnce, it will execute the default implementation set with `jest.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called: - -```js -const myMockFn = jest - .fn(() => 'default') - .mockImplementationOnce(() => 'first call') - .mockImplementationOnce(() => 'second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockName(value)` - -Accepts a string to use in test result output in place of "jest.fn()" to indicate which mock function is being referenced. - -For example: - -```js -const mockFn = jest.fn().mockName('mockedFunction'); -// mockFn(); -expect(mockFn).toHaveBeenCalled(); -``` - -Will result in this error: - -``` -expect(mockedFunction).toHaveBeenCalled() - -Expected mock function "mockedFunction" to have been called, but it was not called. -``` - -### `mockFn.mockReturnThis()` - -Syntactic sugar function for: - -```js -jest.fn(function () { - return this; -}); -``` - -### `mockFn.mockReturnValue(value)` - -Accepts a value that will be returned whenever the mock function is called. - -```js -const mock = jest.fn(); -mock.mockReturnValue(42); -mock(); // 42 -mock.mockReturnValue(43); -mock(); // 43 -``` - -### `mockFn.mockReturnValueOnce(value)` - -Accepts a value that will be returned for one call to the mock function. Can be chained so that successive calls to the mock function return different values. When there are no more `mockReturnValueOnce` values to use, calls will return a value specified by `mockReturnValue`. - -```js -const myMockFn = jest - .fn() - .mockReturnValue('default') - .mockReturnValueOnce('first call') - .mockReturnValueOnce('second call'); - -// 'first call', 'second call', 'default', 'default' -console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); -``` - -### `mockFn.mockResolvedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.resolve(value)); -``` - -Useful to mock async functions in async tests: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockResolvedValue(43); - - await asyncMock(); // 43 -}); -``` - -### `mockFn.mockResolvedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.resolve(value)); -``` - -Useful to resolve different values over multiple async calls: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValue('default') - .mockResolvedValueOnce('first call') - .mockResolvedValueOnce('second call'); - - await asyncMock(); // first call - await asyncMock(); // second call - await asyncMock(); // default - await asyncMock(); // default -}); -``` - -### `mockFn.mockRejectedValue(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementation(() => Promise.reject(value)); -``` - -Useful to create async mock functions that will always reject: - -```js -test('async test', async () => { - const asyncMock = jest.fn().mockRejectedValue(new Error('Async error')); - - await asyncMock(); // throws "Async error" -}); -``` - -### `mockFn.mockRejectedValueOnce(value)` - -Syntactic sugar function for: - -```js -jest.fn().mockImplementationOnce(() => Promise.reject(value)); -``` - -Example usage: - -```js -test('async test', async () => { - const asyncMock = jest - .fn() - .mockResolvedValueOnce('first call') - .mockRejectedValueOnce(new Error('Async error')); - - await asyncMock(); // first call - await asyncMock(); // throws "Async error" -}); -``` - -## TypeScript - -Jest itself is written in [TypeScript](https://www.typescriptlang.org). - -If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. - -Otherwise, please see our [Getting Started](GettingStarted.md#using-typescript) guide for to get setup with TypeScript. - -You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/main/examples/typescript). - -### `jest.MockedFunction` - -> `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](MockFunctions.md). - -You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. - -Example using [automatic `jest.mock`](JestObjectAPI.md#jestmockmodulename-factory-options): - -```ts -// Assume `add` is imported and used within `calculate`. -import add from './add'; -import calculate from './calc'; - -jest.mock('./add'); - -// Our mock of `add` is now fully typed -const mockAdd = add as jest.MockedFunction; - -test('calculate calls add', () => { - calculate('Add', 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -Example using [`jest.fn`](JestObjectAPI.md#jestfnimplementation): - -```ts -// Here `add` is imported for its type -import add from './add'; -import calculate from './calc'; - -test('calculate calls add', () => { - // Create a new mock that can be used in place of `add`. - const mockAdd = jest.fn() as jest.MockedFunction; - - // Note: You can use the `jest.fn` type directly like this if you want: - // const mockAdd = jest.fn, Parameters>(); - // `jest.MockedFunction` is a more friendly shortcut. - - // Now we can easily set up mock implementations. - // All the `.mock*` API can now give you proper types for `add`. - // https://jestjs.io/docs/mock-function-api - - // `.mockImplementation` can now infer that `a` and `b` are `number` - // and that the returned value is a `number`. - mockAdd.mockImplementation((a, b) => { - // Yes, this mock is still adding two numbers but imagine this - // was a complex function we are mocking. - return a + b; - }); - - // `mockAdd` is properly typed and therefore accepted by - // anything requiring `add`. - calculate(mockAdd, 1, 2); - - expect(mockAdd).toBeCalledTimes(1); - expect(mockAdd).toBeCalledWith(1, 2); -}); -``` - -### `jest.MockedClass` - -> `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. - -The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](Es6ClassMocks.md). - -You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. - -Converting the [ES6 Class automatic mock example](Es6ClassMocks.md#automatic-mock) would look like this: - -```ts -import SoundPlayer from '../sound-player'; -import SoundPlayerConsumer from '../sound-player-consumer'; - -jest.mock('../sound-player'); // SoundPlayer is now a mock constructor - -const SoundPlayerMock = SoundPlayer as jest.MockedClass; - -beforeEach(() => { - // Clear all instances and calls to constructor and all methods: - SoundPlayerMock.mockClear(); -}); - -it('We can check if the consumer called the class constructor', () => { - const soundPlayerConsumer = new SoundPlayerConsumer(); - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); -}); - -it('We can check if the consumer called a method on the class instance', () => { - // Show that mockClear() is working: - expect(SoundPlayerMock).not.toHaveBeenCalled(); - - const soundPlayerConsumer = new SoundPlayerConsumer(); - // Constructor should have been called again: - expect(SoundPlayerMock).toHaveBeenCalledTimes(1); - - const coolSoundFileName = 'song.mp3'; - soundPlayerConsumer.playSomethingCool(); - - // mock.instances is available with automatic mocks: - const mockSoundPlayerInstance = SoundPlayerMock.mock.instances[0]; - - // However, it will not allow access to `.mock` in TypeScript as it - // is returning `SoundPlayer`. Instead, you can check the calls to a - // method like this fully typed: - expect(SoundPlayerMock.prototype.playSoundFile.mock.calls[0][0]).toEqual( - coolSoundFileName, - ); - // Equivalent to above check: - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledWith( - coolSoundFileName, - ); - expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledTimes(1); -}); -``` diff --git a/website/versioned_docs/version-27.2/MoreResources.md b/website/versioned_docs/version-27.2/MoreResources.md deleted file mode 100644 index e4509d53281f..000000000000 --- a/website/versioned_docs/version-27.2/MoreResources.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: more-resources -title: More Resources ---- - -By now you should have a good idea of how Jest can help you test your applications. If you're interested in learning more, here's some related stuff you might want to check out. - -## Browse the docs - -- Learn about [Snapshot Testing](SnapshotTesting.md), [Mock Functions](MockFunctions.md), and more in our in-depth guides. -- Migrate your existing tests to Jest by following our [migration guide](MigrationGuide.md). -- Learn how to [configure Jest](Configuration.md). -- Look at the full [API Reference](GlobalAPI.md). -- [Troubleshoot](Troubleshooting.md) problems with Jest. - -## Learn by example - -You will find a number of example test cases in the [`examples`](https://github.com/facebook/jest/tree/main/examples) folder on GitHub. You can also learn from the excellent tests used by the [React](https://github.com/facebook/react/tree/main/packages/react/src/__tests__), [Relay](https://github.com/facebook/relay/tree/main/packages/react-relay/__tests__), and [React Native](https://github.com/facebook/react-native/tree/main/Libraries/Animated/src/__tests__) projects. - -## Join the community - -Ask questions and find answers from other Jest users like you. [Reactiflux](https://discord.gg/j6FKKQQrW9) is a Discord chat where a lot of Jest discussion happens. Check out the `#testing` channel. - -Follow the [Jest Twitter account](https://twitter.com/fbjest) and [blog](/blog/) to find out what's happening in the world of Jest. diff --git a/website/versioned_docs/version-27.2/SetupAndTeardown.md b/website/versioned_docs/version-27.2/SetupAndTeardown.md deleted file mode 100644 index 131376d0de6f..000000000000 --- a/website/versioned_docs/version-27.2/SetupAndTeardown.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -id: setup-teardown -title: Setup and Teardown ---- - -Often while writing tests you have some setup work that needs to happen before tests run, and you have some finishing work that needs to happen after tests run. Jest provides helper functions to handle this. - -## Repeating Setup For Many Tests - -If you have some work you need to do repeatedly for many tests, you can use `beforeEach` and `afterEach`. - -For example, let's say that several tests interact with a database of cities. You have a method `initializeCityDatabase()` that must be called before each of these tests, and a method `clearCityDatabase()` that must be called after each of these tests. You can do this with: - -```js -beforeEach(() => { - initializeCityDatabase(); -}); - -afterEach(() => { - clearCityDatabase(); -}); - -test('city database has Vienna', () => { - expect(isCity('Vienna')).toBeTruthy(); -}); - -test('city database has San Juan', () => { - expect(isCity('San Juan')).toBeTruthy(); -}); -``` - -`beforeEach` and `afterEach` can handle asynchronous code in the same ways that [tests can handle asynchronous code](TestingAsyncCode.md) - they can either take a `done` parameter or return a promise. For example, if `initializeCityDatabase()` returned a promise that resolved when the database was initialized, we would want to return that promise: - -```js -beforeEach(() => { - return initializeCityDatabase(); -}); -``` - -## One-Time Setup - -In some cases, you only need to do setup once, at the beginning of a file. This can be especially bothersome when the setup is asynchronous, so you can't do it inline. Jest provides `beforeAll` and `afterAll` to handle this situation. - -For example, if both `initializeCityDatabase` and `clearCityDatabase` returned promises, and the city database could be reused between tests, we could change our test code to: - -```js -beforeAll(() => { - return initializeCityDatabase(); -}); - -afterAll(() => { - return clearCityDatabase(); -}); - -test('city database has Vienna', () => { - expect(isCity('Vienna')).toBeTruthy(); -}); - -test('city database has San Juan', () => { - expect(isCity('San Juan')).toBeTruthy(); -}); -``` - -## Scoping - -By default, the `beforeAll` and `afterAll` blocks apply to every test in a file. You can also group tests together using a `describe` block. When they are inside a `describe` block, the `beforeAll` and `afterAll` blocks only apply to the tests within that `describe` block. - -For example, let's say we had not just a city database, but also a food database. We could do different setup for different tests: - -```js -// Applies to all tests in this file -beforeEach(() => { - return initializeCityDatabase(); -}); - -test('city database has Vienna', () => { - expect(isCity('Vienna')).toBeTruthy(); -}); - -test('city database has San Juan', () => { - expect(isCity('San Juan')).toBeTruthy(); -}); - -describe('matching cities to foods', () => { - // Applies only to tests in this describe block - beforeEach(() => { - return initializeFoodDatabase(); - }); - - test('Vienna <3 veal', () => { - expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true); - }); - - test('San Juan <3 plantains', () => { - expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true); - }); -}); -``` - -Note that the top-level `beforeEach` is executed before the `beforeEach` inside the `describe` block. It may help to illustrate the order of execution of all hooks. - -```js -beforeAll(() => console.log('1 - beforeAll')); -afterAll(() => console.log('1 - afterAll')); -beforeEach(() => console.log('1 - beforeEach')); -afterEach(() => console.log('1 - afterEach')); -test('', () => console.log('1 - test')); -describe('Scoped / Nested block', () => { - beforeAll(() => console.log('2 - beforeAll')); - afterAll(() => console.log('2 - afterAll')); - beforeEach(() => console.log('2 - beforeEach')); - afterEach(() => console.log('2 - afterEach')); - test('', () => console.log('2 - test')); -}); - -// 1 - beforeAll -// 1 - beforeEach -// 1 - test -// 1 - afterEach -// 2 - beforeAll -// 1 - beforeEach -// 2 - beforeEach -// 2 - test -// 2 - afterEach -// 1 - afterEach -// 2 - afterAll -// 1 - afterAll -``` - -## Order of execution of describe and test blocks - -Jest executes all describe handlers in a test file _before_ it executes any of the actual tests. This is another reason to do setup and teardown inside `before*` and `after*` handlers rather than inside the describe blocks. Once the describe blocks are complete, by default Jest runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on. - -Consider the following illustrative test file and output: - -```js -describe('outer', () => { - console.log('describe outer-a'); - - describe('describe inner 1', () => { - console.log('describe inner 1'); - test('test 1', () => { - console.log('test for describe inner 1'); - expect(true).toEqual(true); - }); - }); - - console.log('describe outer-b'); - - test('test 1', () => { - console.log('test for describe outer'); - expect(true).toEqual(true); - }); - - describe('describe inner 2', () => { - console.log('describe inner 2'); - test('test for describe inner 2', () => { - console.log('test for describe inner 2'); - expect(false).toEqual(false); - }); - }); - - console.log('describe outer-c'); -}); - -// describe outer-a -// describe inner 1 -// describe outer-b -// describe inner 2 -// describe outer-c -// test for describe inner 1 -// test for describe outer -// test for describe inner 2 -``` - -## General Advice - -If a test is failing, one of the first things to check should be whether the test is failing when it's the only test that runs. To run only one test with Jest, temporarily change that `test` command to a `test.only`: - -```js -test.only('this will be the only test that runs', () => { - expect(true).toBe(false); -}); - -test('this test will not run', () => { - expect('A').toBe('A'); -}); -``` - -If you have a test that often fails when it's run as part of a larger suite, but doesn't fail when you run it alone, it's a good bet that something from a different test is interfering with this one. You can often fix this by clearing some shared state with `beforeEach`. If you're not sure whether some shared state is being modified, you can also try a `beforeEach` that logs data. diff --git a/website/versioned_docs/version-27.2/SnapshotTesting.md b/website/versioned_docs/version-27.2/SnapshotTesting.md deleted file mode 100644 index 385b29abae3f..000000000000 --- a/website/versioned_docs/version-27.2/SnapshotTesting.md +++ /dev/null @@ -1,316 +0,0 @@ ---- -id: snapshot-testing -title: Snapshot Testing ---- - -Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly. - -A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component. - -## Snapshot Testing with Jest - -A similar approach can be taken when it comes to testing your React components. Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your React tree. Consider this [example test](https://github.com/facebook/jest/blob/main/examples/snapshot/__tests__/link.test.js) for a [Link component](https://github.com/facebook/jest/blob/main/examples/snapshot/Link.js): - -```tsx -import React from 'react'; -import renderer from 'react-test-renderer'; -import Link from '../Link'; - -it('renders correctly', () => { - const tree = renderer - .create(Facebook) - .toJSON(); - expect(tree).toMatchSnapshot(); -}); -``` - -The first time this test is run, Jest creates a [snapshot file](https://github.com/facebook/jest/blob/main/examples/snapshot/__tests__/__snapshots__/link.test.js.snap) that looks like this: - -```javascript -exports[`renders correctly 1`] = ` - - Facebook - -`; -``` - -The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses [pretty-format](https://github.com/facebook/jest/tree/main/packages/pretty-format) to make snapshots human-readable during code review. On subsequent test runs, Jest will compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code (in the `` component in this case) that should be fixed, or the implementation has changed and the snapshot needs to be updated. - -> Note: The snapshot is directly scoped to the data you render – in our example the `` component with `page` prop passed to it. This implies that even if any other file has missing props (Say, `App.js`) in the `` component, it will still pass the test as the test doesn't know the usage of `` component and it's scoped only to the `Link.js`. Also, rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other. - -More information on how snapshot testing works and why we built it can be found on the [release blog post](/blog/2016/07/27/jest-14). We recommend reading [this blog post](http://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/) to get a good sense of when you should use snapshot testing. We also recommend watching this [egghead video](https://egghead.io/lessons/javascript-use-jest-s-snapshot-testing-feature?pl=testing-javascript-with-jest-a36c4074) on Snapshot Testing with Jest. - -### Updating Snapshots - -It's straightforward to spot when a snapshot test fails after a bug has been introduced. When that happens, go ahead and fix the issue and make sure your snapshot tests are passing again. Now, let's talk about the case when a snapshot test is failing due to an intentional implementation change. - -One such situation can arise if we intentionally change the address the Link component in our example is pointing to. - -```tsx -// Updated test case with a Link to a different address -it('renders correctly', () => { - const tree = renderer - .create(Instagram) - .toJSON(); - expect(tree).toMatchSnapshot(); -}); -``` - -In that case, Jest will print this output: - -![](/img/content/failedSnapshotTest.png) - -Since we just updated our component to point to a different address, it's reasonable to expect changes in the snapshot for this component. Our snapshot test case is failing because the snapshot for our updated component no longer matches the snapshot artifact for this test case. - -To resolve this, we will need to update our snapshot artifacts. You can run Jest with a flag that will tell it to re-generate snapshots: - -```bash -jest --updateSnapshot -``` - -Go ahead and accept the changes by running the above command. You may also use the equivalent single-character `-u` flag to re-generate snapshots if you prefer. This will re-generate snapshot artifacts for all failing snapshot tests. If we had any additional failing snapshot tests due to an unintentional bug, we would need to fix the bug before re-generating snapshots to avoid recording snapshots of the buggy behavior. - -If you'd like to limit which snapshot test cases get re-generated, you can pass an additional `--testNamePattern` flag to re-record snapshots only for those tests that match the pattern. - -You can try out this functionality by cloning the [snapshot example](https://github.com/facebook/jest/tree/main/examples/snapshot), modifying the `Link` component, and running Jest. - -### Interactive Snapshot Mode - -Failed snapshots can also be updated interactively in watch mode: - -![](/img/content/interactiveSnapshot.png) - -Once you enter Interactive Snapshot Mode, Jest will step you through the failed snapshots one test at a time and give you the opportunity to review the failed output. - -From here you can choose to update that snapshot or skip to the next: - -![](/img/content/interactiveSnapshotUpdate.gif) - -Once you're finished, Jest will give you a summary before returning back to watch mode: - -![](/img/content/interactiveSnapshotDone.png) - -### Inline Snapshots - -Inline snapshots behave identically to external snapshots (`.snap` files), except the snapshot values are written automatically back into the source code. This means you can get the benefits of automatically generated snapshots without having to switch to an external file to make sure the correct value was written. - -**Example:** - -First, you write a test, calling `.toMatchInlineSnapshot()` with no arguments: - -```tsx -it('renders correctly', () => { - const tree = renderer - .create(Example Site) - .toJSON(); - expect(tree).toMatchInlineSnapshot(); -}); -``` - -The next time you run Jest, `tree` will be evaluated, and a snapshot will be written as an argument to `toMatchInlineSnapshot`: - -```tsx -it('renders correctly', () => { - const tree = renderer - .create(Example Site) - .toJSON(); - expect(tree).toMatchInlineSnapshot(` - - Example Site - -`); -}); -``` - -That's all there is to it! You can even update the snapshots with `--updateSnapshot` or using the `u` key in `--watch` mode. - -By default, Jest handles the writing of snapshots into your source code. However, if you're using [prettier](https://www.npmjs.com/package/prettier) in your project, Jest will detect this and delegate the work to prettier instead (including honoring your configuration). - -### Property Matchers - -Often there are fields in the object you want to snapshot which are generated (like IDs and Dates). If you try to snapshot these objects, they will force the snapshot to fail on every run: - -```javascript -it('will fail every time', () => { - const user = { - createdAt: new Date(), - id: Math.floor(Math.random() * 20), - name: 'LeBron James', - }; - - expect(user).toMatchSnapshot(); -}); - -// Snapshot -exports[`will fail every time 1`] = ` -Object { - "createdAt": 2018-05-19T23:36:09.816Z, - "id": 3, - "name": "LeBron James", -} -`; -``` - -For these cases, Jest allows providing an asymmetric matcher for any property. These matchers are checked before the snapshot is written or tested, and then saved to the snapshot file instead of the received value: - -```javascript -it('will check the matchers and pass', () => { - const user = { - createdAt: new Date(), - id: Math.floor(Math.random() * 20), - name: 'LeBron James', - }; - - expect(user).toMatchSnapshot({ - createdAt: expect.any(Date), - id: expect.any(Number), - }); -}); - -// Snapshot -exports[`will check the matchers and pass 1`] = ` -Object { - "createdAt": Any, - "id": Any, - "name": "LeBron James", -} -`; -``` - -Any given value that is not a matcher will be checked exactly and saved to the snapshot: - -```javascript -it('will check the values and pass', () => { - const user = { - createdAt: new Date(), - name: 'Bond... James Bond', - }; - - expect(user).toMatchSnapshot({ - createdAt: expect.any(Date), - name: 'Bond... James Bond', - }); -}); - -// Snapshot -exports[`will check the values and pass 1`] = ` -Object { - "createdAt": Any, - "name": 'Bond... James Bond', -} -`; -``` - -## Best Practices - -Snapshots are a fantastic tool for identifying unexpected interface changes within your application – whether that interface is an API response, UI, logs, or error messages. As with any testing strategy, there are some best-practices you should be aware of, and guidelines you should follow, in order to use them effectively. - -### 1. Treat snapshots as code - -Commit snapshots and review them as part of your regular code review process. This means treating snapshots as you would any other type of test or code in your project. - -Ensure that your snapshots are readable by keeping them focused, short, and by using tools that enforce these stylistic conventions. - -As mentioned previously, Jest uses [`pretty-format`](https://yarnpkg.com/en/package/pretty-format) to make snapshots human-readable, but you may find it useful to introduce additional tools, like [`eslint-plugin-jest`](https://yarnpkg.com/en/package/eslint-plugin-jest) with its [`no-large-snapshots`](https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-large-snapshots.md) option, or [`snapshot-diff`](https://yarnpkg.com/en/package/snapshot-diff) with its component snapshot comparison feature, to promote committing short, focused assertions. - -The goal is to make it easy to review snapshots in pull requests, and fight against the habit of regenerating snapshots when test suites fail instead of examining the root causes of their failure. - -### 2. Tests should be deterministic - -Your tests should be deterministic. Running the same tests multiple times on a component that has not changed should produce the same results every time. You're responsible for making sure your generated snapshots do not include platform specific or other non-deterministic data. - -For example, if you have a [Clock](https://github.com/facebook/jest/blob/main/examples/snapshot/Clock.js) component that uses `Date.now()`, the snapshot generated from this component will be different every time the test case is run. In this case we can [mock the Date.now() method](MockFunctions.md) to return a consistent value every time the test is run: - -```js -Date.now = jest.fn(() => 1482363367071); -``` - -Now, every time the snapshot test case runs, `Date.now()` will return `1482363367071` consistently. This will result in the same snapshot being generated for this component regardless of when the test is run. - -### 3. Use descriptive snapshot names - -Always strive to use descriptive test and/or snapshot names for snapshots. The best names describe the expected snapshot content. This makes it easier for reviewers to verify the snapshots during review, and for anyone to know whether or not an outdated snapshot is the correct behavior before updating. - -For example, compare: - -```js -exports[` should handle some test case`] = `null`; - -exports[` should handle some other test case`] = ` -
- Alan Turing -
-`; -``` - -To: - -```js -exports[` should render null`] = `null`; - -exports[` should render Alan Turing`] = ` -
- Alan Turing -
-`; -``` - -Since the later describes exactly what's expected in the output, it's more clear to see when it's wrong: - -```js -exports[` should render null`] = ` -
- Alan Turing -
-`; - -exports[` should render Alan Turing`] = `null`; -``` - -## Frequently Asked Questions - -### Are snapshots written automatically on Continuous Integration (CI) systems? - -No, as of Jest 20, snapshots in Jest are not automatically written when Jest is run in a CI system without explicitly passing `--updateSnapshot`. It is expected that all snapshots are part of the code that is run on CI and since new snapshots automatically pass, they should not pass a test run on a CI system. It is recommended to always commit all snapshots and to keep them in version control. - -### Should snapshot files be committed? - -Yes, all snapshot files should be committed alongside the modules they are covering and their tests. They should be considered part of a test, similar to the value of any other assertion in Jest. In fact, snapshots represent the state of the source modules at any given point in time. In this way, when the source modules are modified, Jest can tell what changed from the previous version. It can also provide a lot of additional context during code review in which reviewers can study your changes better. - -### Does snapshot testing only work with React components? - -[React](TutorialReact.md) and [React Native](TutorialReactNative.md) components are a good use case for snapshot testing. However, snapshots can capture any serializable value and should be used anytime the goal is testing whether the output is correct. The Jest repository contains many examples of testing the output of Jest itself, the output of Jest's assertion library as well as log messages from various parts of the Jest codebase. See an example of [snapshotting CLI output](https://github.com/facebook/jest/blob/main/e2e/__tests__/console.test.ts) in the Jest repo. - -### What's the difference between snapshot testing and visual regression testing? - -Snapshot testing and visual regression testing are two distinct ways of testing UIs, and they serve different purposes. Visual regression testing tools take screenshots of web pages and compare the resulting images pixel by pixel. With Snapshot testing values are serialized, stored within text files, and compared using a diff algorithm. There are different trade-offs to consider and we listed the reasons why snapshot testing was built in the [Jest blog](/blog/2016/07/27/jest-14#why-snapshot-testing). - -### Does snapshot testing replace unit testing? - -Snapshot testing is only one of more than 20 assertions that ship with Jest. The aim of snapshot testing is not to replace existing unit tests, but to provide additional value and make testing painless. In some scenarios, snapshot testing can potentially remove the need for unit testing for a particular set of functionalities (e.g. React components), but they can work together as well. - -### What is the performance of snapshot testing regarding speed and size of the generated files? - -Jest has been rewritten with performance in mind, and snapshot testing is not an exception. Since snapshots are stored within text files, this way of testing is fast and reliable. Jest generates a new file for each test file that invokes the `toMatchSnapshot` matcher. The size of the snapshots is pretty small: For reference, the size of all snapshot files in the Jest codebase itself is less than 300 KB. - -### How do I resolve conflicts within snapshot files? - -Snapshot files must always represent the current state of the modules they are covering. Therefore, if you are merging two branches and encounter a conflict in the snapshot files, you can either resolve the conflict manually or update the snapshot file by running Jest and inspecting the result. - -### Is it possible to apply test-driven development principles with snapshot testing? - -Although it is possible to write snapshot files manually, that is usually not approachable. Snapshots help to figure out whether the output of the modules covered by tests is changed, rather than giving guidance to design the code in the first place. - -### Does code coverage work with snapshot testing? - -Yes, as well as with any other test. diff --git a/website/versioned_docs/version-27.2/TestingAsyncCode.md b/website/versioned_docs/version-27.2/TestingAsyncCode.md deleted file mode 100644 index 432db44b3944..000000000000 --- a/website/versioned_docs/version-27.2/TestingAsyncCode.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -id: asynchronous -title: Testing Asynchronous Code ---- - -It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test. Jest has several ways to handle this. - -## Callbacks - -The most common asynchronous pattern is callbacks. - -For example, let's say that you have a `fetchData(callback)` function that fetches some data and calls `callback(data)` when it is complete. You want to test that this returned data is the string `'peanut butter'`. - -By default, Jest tests complete once they reach the end of their execution. That means this test will _not_ work as intended: - -```js -// Don't do this! -test('the data is peanut butter', () => { - function callback(data) { - expect(data).toBe('peanut butter'); - } - - fetchData(callback); -}); -``` - -The problem is that the test will complete as soon as `fetchData` completes, before ever calling the callback. - -There is an alternate form of `test` that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called `done`. Jest will wait until the `done` callback is called before finishing the test. - -```js -test('the data is peanut butter', done => { - function callback(data) { - try { - expect(data).toBe('peanut butter'); - done(); - } catch (error) { - done(error); - } - } - - fetchData(callback); -}); -``` - -If `done()` is never called, the test will fail (with timeout error), which is what you want to happen. - -If the `expect` statement fails, it throws an error and `done()` is not called. If we want to see in the test log why it failed, we have to wrap `expect` in a `try` block and pass the error in the `catch` block to `done`. Otherwise, we end up with an opaque timeout error that doesn't show what value was received by `expect(data)`. - -_Note: `done()` should not be mixed with Promises as this tends to lead to memory leaks in your tests._ - -## Promises - -If your code uses promises, there is a more straightforward way to handle asynchronous tests. Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -For example, let's say that `fetchData`, instead of using a callback, returns a promise that is supposed to resolve to the string `'peanut butter'`. We could test it with: - -```js -test('the data is peanut butter', () => { - return fetchData().then(data => { - expect(data).toBe('peanut butter'); - }); -}); -``` - -Be sure to return the promise - if you omit this `return` statement, your test will complete before the promise returned from `fetchData` resolves and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test. - -```js -test('the fetch fails with an error', () => { - expect.assertions(1); - return fetchData().catch(e => expect(e).toMatch('error')); -}); -``` - -## `.resolves` / `.rejects` - -You can also use the `.resolves` matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. - -```js -test('the data is peanut butter', () => { - return expect(fetchData()).resolves.toBe('peanut butter'); -}); -``` - -Be sure to return the assertion—if you omit this `return` statement, your test will complete before the promise returned from `fetchData` is resolved and then() has a chance to execute the callback. - -If you expect a promise to be rejected, use the `.rejects` matcher. It works analogically to the `.resolves` matcher. If the promise is fulfilled, the test will automatically fail. - -```js -test('the fetch fails with an error', () => { - return expect(fetchData()).rejects.toMatch('error'); -}); -``` - -## Async/Await - -Alternatively, you can use `async` and `await` in your tests. To write an async test, use the `async` keyword in front of the function passed to `test`. For example, the same `fetchData` scenario can be tested with: - -```js -test('the data is peanut butter', async () => { - const data = await fetchData(); - expect(data).toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - expect.assertions(1); - try { - await fetchData(); - } catch (e) { - expect(e).toMatch('error'); - } -}); -``` - -You can combine `async` and `await` with `.resolves` or `.rejects`. - -```js -test('the data is peanut butter', async () => { - await expect(fetchData()).resolves.toBe('peanut butter'); -}); - -test('the fetch fails with an error', async () => { - await expect(fetchData()).rejects.toMatch('error'); -}); -``` - -In these cases, `async` and `await` are effectively syntactic sugar for the same logic as the promises example uses. - -None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler. diff --git a/website/versioned_docs/version-27.2/TimerMocks.md b/website/versioned_docs/version-27.2/TimerMocks.md deleted file mode 100644 index 0952884e1beb..000000000000 --- a/website/versioned_docs/version-27.2/TimerMocks.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -id: timer-mocks -title: Timer Mocks ---- - -The native timer functions (i.e., `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`) are less than ideal for a testing environment since they depend on real time to elapse. Jest can swap out timers with functions that allow you to control the passage of time. [Great Scott!](https://www.youtube.com/watch?v=QZoJ2Pt27BY) - -```javascript title="timerGame.js" -'use strict'; - -function timerGame(callback) { - console.log('Ready....go!'); - setTimeout(() => { - console.log("Time's up -- stop!"); - callback && callback(); - }, 1000); -} - -module.exports = timerGame; -``` - -```javascript title="__tests__/timerGame-test.js" -'use strict'; - -jest.useFakeTimers(); // or you can set "timers": "fake" globally in configuration file -jest.spyOn(global, 'setTimeout'); - -test('waits 1 second before ending the game', () => { - const timerGame = require('../timerGame'); - timerGame(); - - expect(setTimeout).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); -}); -``` - -Here we enable fake timers by calling `jest.useFakeTimers()`. This mocks out `setTimeout` and other timer functions with mock functions. Timers can be restored to their normal behavior with `jest.useRealTimers()`. - -While you can call `jest.useFakeTimers()` or `jest.useRealTimers()` from anywhere (top level, inside an `it` block, etc.), it is a **global operation** and will affect other tests within the same file. Additionally, you need to call `jest.useFakeTimers()` to reset internal counters before each test. If you plan to not use fake timers in all your tests, you will want to clean up manually, as otherwise the faked timers will leak across tests: - -```javascript -afterEach(() => { - jest.useRealTimers(); -}); - -test('do something with fake timers', () => { - jest.useFakeTimers(); - // ... -}); - -test('do something with real timers', () => { - // ... -}); -``` - -All of the following functions need fake timers to be set, either by `jest.useFakeTimers()` or via `"timers": "fake"` in the config file. - -Currently, two implementations of the fake timers are included - `modern` and `legacy`, where `modern` is the default one. See [configuration](Configuration.md#timers-string) for how to configure it. - -## Run All Timers - -Another test we might want to write for this module is one that asserts that the callback is called after 1 second. To do this, we're going to use Jest's timer control APIs to fast-forward time right in the middle of the test: - -```javascript -jest.useFakeTimers(); -test('calls the callback after 1 second', () => { - const timerGame = require('../timerGame'); - const callback = jest.fn(); - - timerGame(callback); - - // At this point in time, the callback should not have been called yet - expect(callback).not.toBeCalled(); - - // Fast-forward until all timers have been executed - jest.runAllTimers(); - - // Now our callback should have been called! - expect(callback).toBeCalled(); - expect(callback).toHaveBeenCalledTimes(1); -}); -``` - -## Run Pending Timers - -There are also scenarios where you might have a recursive timer -- that is a timer that sets a new timer in its own callback. For these, running all the timers would be an endless loop, throwing the following error: - -``` -Ran 100000 timers, and there are still more! Assuming we've hit an infinite recursion and bailing out... -``` - -So something like `jest.runAllTimers()` is not desirable. For these cases you might use `jest.runOnlyPendingTimers()`: - -```javascript title="infiniteTimerGame.js" -'use strict'; - -function infiniteTimerGame(callback) { - console.log('Ready....go!'); - - setTimeout(() => { - console.log("Time's up! 10 seconds before the next game starts..."); - callback && callback(); - - // Schedule the next game in 10 seconds - setTimeout(() => { - infiniteTimerGame(callback); - }, 10000); - }, 1000); -} - -module.exports = infiniteTimerGame; -``` - -```javascript title="__tests__/infiniteTimerGame-test.js" -'use strict'; - -jest.useFakeTimers(); -jest.spyOn(global, 'setTimeout'); - -describe('infiniteTimerGame', () => { - test('schedules a 10-second timer after 1 second', () => { - const infiniteTimerGame = require('../infiniteTimerGame'); - const callback = jest.fn(); - - infiniteTimerGame(callback); - - // At this point in time, there should have been a single call to - // setTimeout to schedule the end of the game in 1 second. - expect(setTimeout).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); - - // Fast forward and exhaust only currently pending timers - // (but not any new timers that get created during that process) - jest.runOnlyPendingTimers(); - - // At this point, our 1-second timer should have fired its callback - expect(callback).toBeCalled(); - - // And it should have created a new timer to start the game over in - // 10 seconds - expect(setTimeout).toHaveBeenCalledTimes(2); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000); - }); -}); -``` - -## Advance Timers by Time - -Another possibility is use `jest.advanceTimersByTime(msToRun)`. When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via setTimeout() or setInterval(), and would be executed during this time frame, will be executed. Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds. - -```javascript title="timerGame.js" -'use strict'; - -function timerGame(callback) { - console.log('Ready....go!'); - setTimeout(() => { - console.log("Time's up -- stop!"); - callback && callback(); - }, 1000); -} - -module.exports = timerGame; -``` - -```javascript title="__tests__/timerGame-test.js" -jest.useFakeTimers(); -it('calls the callback after 1 second via advanceTimersByTime', () => { - const timerGame = require('../timerGame'); - const callback = jest.fn(); - - timerGame(callback); - - // At this point in time, the callback should not have been called yet - expect(callback).not.toBeCalled(); - - // Fast-forward until all timers have been executed - jest.advanceTimersByTime(1000); - - // Now our callback should have been called! - expect(callback).toBeCalled(); - expect(callback).toHaveBeenCalledTimes(1); -}); -``` - -Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have `jest.clearAllTimers()`. - -The code for this example is available at [examples/timer](https://github.com/facebook/jest/tree/main/examples/timer). diff --git a/website/versioned_docs/version-27.2/TutorialAsync.md b/website/versioned_docs/version-27.2/TutorialAsync.md deleted file mode 100644 index e4c7117c16dc..000000000000 --- a/website/versioned_docs/version-27.2/TutorialAsync.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -id: tutorial-async -title: An Async Example ---- - -First, enable Babel support in Jest as documented in the [Getting Started](GettingStarted.md#using-babel) guide. - -Let's implement a module that fetches user data from an API and returns the user name. - -```js title="user.js" -import request from './request'; - -export function getUserName(userID) { - return request(`/users/${userID}`).then(user => user.name); -} -``` - -In the above implementation, we expect the `request.js` module to return a promise. We chain a call to `then` to receive the user name. - -Now imagine an implementation of `request.js` that goes to the network and fetches some user data: - -```js title="request.js" -const http = require('http'); - -export default function request(url) { - return new Promise(resolve => { - // This is an example of an http request, for example to fetch - // user data from an API. - // This module is being mocked in __mocks__/request.js - http.get({path: url}, response => { - let data = ''; - response.on('data', _data => (data += _data)); - response.on('end', () => resolve(data)); - }); - }); -} -``` - -Because we don't want to go to the network in our test, we are going to create a manual mock for our `request.js` module in the `__mocks__` folder (the folder is case-sensitive, `__MOCKS__` will not work). It could look something like this: - -```js title="__mocks__/request.js" -const users = { - 4: {name: 'Mark'}, - 5: {name: 'Paul'}, -}; - -export default function request(url) { - return new Promise((resolve, reject) => { - const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick(() => - users[userID] - ? resolve(users[userID]) - : reject({ - error: `User with ${userID} not found.`, - }), - ); - }); -} -``` - -Now let's write a test for our async functionality. - -```js title="__tests__/user-test.js" -jest.mock('../request'); - -import * as user from '../user'; - -// The assertion for a promise must be returned. -it('works with promises', () => { - expect.assertions(1); - return user.getUserName(4).then(data => expect(data).toEqual('Mark')); -}); -``` - -We call `jest.mock('../request')` to tell Jest to use our manual mock. `it` expects the return value to be a Promise that is going to be resolved. You can chain as many Promises as you like and call `expect` at any time, as long as you return a Promise at the end. - -## `.resolves` - -There is a less verbose way using `resolves` to unwrap the value of a fulfilled promise together with any other matcher. If the promise is rejected, the assertion will fail. - -```js -it('works with resolves', () => { - expect.assertions(1); - return expect(user.getUserName(5)).resolves.toEqual('Paul'); -}); -``` - -## `async`/`await` - -Writing tests using the `async`/`await` syntax is also possible. Here is how you'd write the same examples from before: - -```js -// async/await can be used. -it('works with async/await', async () => { - expect.assertions(1); - const data = await user.getUserName(4); - expect(data).toEqual('Mark'); -}); - -// async/await can also be used with `.resolves`. -it('works with async/await and resolves', async () => { - expect.assertions(1); - await expect(user.getUserName(5)).resolves.toEqual('Paul'); -}); -``` - -To enable async/await in your project, install [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env) and enable the feature in your `babel.config.js` file. - -## Error handling - -Errors can be handled using the `.catch` method. Make sure to add `expect.assertions` to verify that a certain number of assertions are called. Otherwise a fulfilled promise would not fail the test: - -```js -// Testing for async errors using Promise.catch. -it('tests error with promises', () => { - expect.assertions(1); - return user.getUserName(2).catch(e => - expect(e).toEqual({ - error: 'User with 2 not found.', - }), - ); -}); - -// Or using async/await. -it('tests error with async/await', async () => { - expect.assertions(1); - try { - await user.getUserName(1); - } catch (e) { - expect(e).toEqual({ - error: 'User with 1 not found.', - }); - } -}); -``` - -## `.rejects` - -The`.rejects` helper works like the `.resolves` helper. If the promise is fulfilled, the test will automatically fail. `expect.assertions(number)` is not required but recommended to verify that a certain number of [assertions](expect#expectassertionsnumber) are called during a test. It is otherwise easy to forget to `return`/`await` the `.resolves` assertions. - -```js -// Testing for async errors using `.rejects`. -it('tests error with rejects', () => { - expect.assertions(1); - return expect(user.getUserName(3)).rejects.toEqual({ - error: 'User with 3 not found.', - }); -}); - -// Or using async/await with `.rejects`. -it('tests error with async/await and rejects', async () => { - expect.assertions(1); - await expect(user.getUserName(3)).rejects.toEqual({ - error: 'User with 3 not found.', - }); -}); -``` - -The code for this example is available at [examples/async](https://github.com/facebook/jest/tree/main/examples/async). - -If you'd like to test timers, like `setTimeout`, take a look at the [Timer mocks](TimerMocks.md) documentation. diff --git a/website/versioned_docs/version-27.2/TutorialReact.md b/website/versioned_docs/version-27.2/TutorialReact.md deleted file mode 100644 index 8fa282fff661..000000000000 --- a/website/versioned_docs/version-27.2/TutorialReact.md +++ /dev/null @@ -1,313 +0,0 @@ ---- -id: tutorial-react -title: Testing React Apps ---- - -At Facebook, we use Jest to test [React](https://reactjs.org/) applications. - -## Setup - -### Setup with Create React App - -If you are new to React, we recommend using [Create React App](https://create-react-app.dev/). It is ready to use and [ships with Jest](https://create-react-app.dev/docs/running-tests/#docsNav)! You will only need to add `react-test-renderer` for rendering snapshots. - -Run - -```bash -yarn add --dev react-test-renderer -``` - -### Setup without Create React App - -If you have an existing application you'll need to install a few packages to make everything work well together. We are using the `babel-jest` package and the `react` babel preset to transform our code inside of the test environment. Also see [using babel](GettingStarted.md#using-babel). - -Run - -```bash -yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer -``` - -Your `package.json` should look something like this (where `` is the actual latest version number for the package). Please add the scripts and jest configuration entries: - -```json - "dependencies": { - "react": "", - "react-dom": "" - }, - "devDependencies": { - "@babel/preset-env": "", - "@babel/preset-react": "", - "babel-jest": "", - "jest": "", - "react-test-renderer": "" - }, - "scripts": { - "test": "jest" - } -``` - -```js title="babel.config.js" -module.exports = { - presets: ['@babel/preset-env', '@babel/preset-react'], -}; -``` - -**And you're good to go!** - -### Snapshot Testing - -Let's create a [snapshot test](SnapshotTesting.md) for a Link component that renders hyperlinks: - -```tsx title="Link.js" -import React, {useState} from 'react'; - -const STATUS = { - HOVERED: 'hovered', - NORMAL: 'normal', -}; - -const Link = ({page, children}) => { - const [status, setStatus] = useState(STATUS.NORMAL); - - const onMouseEnter = () => { - setStatus(STATUS.HOVERED); - }; - - const onMouseLeave = () => { - setStatus(STATUS.NORMAL); - }; - - return ( - - {children} - - ); -}; - -export default Link; -``` - -> Note: Examples are using Function components, but Class components can be tested in the same way. See [React: Function and Class Components](https://reactjs.org/docs/components-and-props.html#function-and-class-components). **Reminders** that with Class components, we expect Jest to be used to test props and not methods directly. - -Now let's use React's test renderer and Jest's snapshot feature to interact with the component and capture the rendered output and create a snapshot file: - -```tsx title="Link.test.js" -import React from 'react'; -import renderer from 'react-test-renderer'; -import Link from '../Link'; - -test('Link changes the class when hovered', () => { - const component = renderer.create( - Facebook, - ); - let tree = component.toJSON(); - expect(tree).toMatchSnapshot(); - - // manually trigger the callback - tree.props.onMouseEnter(); - // re-rendering - tree = component.toJSON(); - expect(tree).toMatchSnapshot(); - - // manually trigger the callback - tree.props.onMouseLeave(); - // re-rendering - tree = component.toJSON(); - expect(tree).toMatchSnapshot(); -}); -``` - -When you run `yarn test` or `jest`, this will produce an output file like this: - -```javascript title="__tests__/__snapshots__/Link.test.js.snap" -exports[`Link changes the class when hovered 1`] = ` - - Facebook - -`; - -exports[`Link changes the class when hovered 2`] = ` - - Facebook - -`; - -exports[`Link changes the class when hovered 3`] = ` - - Facebook - -`; -``` - -The next time you run the tests, the rendered output will be compared to the previously created snapshot. The snapshot should be committed along with code changes. When a snapshot test fails, you need to inspect whether it is an intended or unintended change. If the change is expected you can invoke Jest with `jest -u` to overwrite the existing snapshot. - -The code for this example is available at [examples/snapshot](https://github.com/facebook/jest/tree/main/examples/snapshot). - -#### Snapshot Testing with Mocks, Enzyme and React 16 - -There's a caveat around snapshot testing when using Enzyme and React 16+. If you mock out a module using the following style: - -```js -jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent'); -``` - -Then you will see warnings in the console: - -```bash -Warning: is using uppercase HTML. Always use lowercase HTML tags in React. - -# Or: -Warning: The tag is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter. -``` - -React 16 triggers these warnings due to how it checks element types, and the mocked module fails these checks. Your options are: - -1. Render as text. This way you won't see the props passed to the mock component in the snapshot, but it's straightforward: - ```js - jest.mock('./SomeComponent', () => () => 'SomeComponent'); - ``` -2. Render as a custom element. DOM "custom elements" aren't checked for anything and shouldn't fire warnings. They are lowercase and have a dash in the name. - ```tsx - jest.mock('./Widget', () => () => ); - ``` -3. Use `react-test-renderer`. The test renderer doesn't care about element types and will happily accept e.g. `SomeComponent`. You could check snapshots using the test renderer, and check component behavior separately using Enzyme. -4. Disable warnings all together (should be done in your jest setup file): - ```js - jest.mock('fbjs/lib/warning', () => require('fbjs/lib/emptyFunction')); - ``` - This shouldn't normally be your option of choice as useful warnings could be lost. However, in some cases, for example when testing react-native's components we are rendering react-native tags into the DOM and many warnings are irrelevant. Another option is to swizzle the console.warn and suppress specific warnings. - -### DOM Testing - -If you'd like to assert, and manipulate your rendered components you can use [react-testing-library](https://github.com/kentcdodds/react-testing-library), [Enzyme](http://airbnb.io/enzyme/), or React's [TestUtils](https://reactjs.org/docs/test-utils.html). The following two examples use react-testing-library and Enzyme. - -#### react-testing-library - -You have to run `yarn add --dev @testing-library/react` to use react-testing-library. - -Let's implement a checkbox which swaps between two labels: - -```tsx title="CheckboxWithLabel.js" -import React, {useState} from 'react'; - -const CheckboxWithLabel = ({labelOn, labelOff}) => { - const [isChecked, setIsChecked] = useState(false); - - const onChange = () => { - setIsChecked(!isChecked); - }; - - return ( - - ); -}; - -export default CheckboxWithLabel; -``` - -```tsx title="__tests__/CheckboxWithLabel-test.js" -import React from 'react'; -import {cleanup, fireEvent, render} from '@testing-library/react'; -import CheckboxWithLabel from '../CheckboxWithLabel'; - -// Note: running cleanup afterEach is done automatically for you in @testing-library/react@9.0.0 or higher -// unmount and cleanup DOM after the test is finished. -afterEach(cleanup); - -it('CheckboxWithLabel changes the text after click', () => { - const {queryByLabelText, getByLabelText} = render( - , - ); - - expect(queryByLabelText(/off/i)).toBeTruthy(); - - fireEvent.click(getByLabelText(/off/i)); - - expect(queryByLabelText(/on/i)).toBeTruthy(); -}); -``` - -The code for this example is available at [examples/react-testing-library](https://github.com/facebook/jest/tree/main/examples/react-testing-library). - -#### Enzyme - -You have to run `yarn add --dev enzyme` to use Enzyme. If you are using a React version below 15.5.0, you will also need to install `react-addons-test-utils`. - -Let's rewrite the test from above using Enzyme instead of react-testing-library. We use Enzyme's [shallow renderer](http://airbnb.io/enzyme/docs/api/shallow.html) in this example. - -```tsx title="__tests__/CheckboxWithLabel-test.js" -import React from 'react'; -import {shallow} from 'enzyme'; -import CheckboxWithLabel from '../CheckboxWithLabel'; - -test('CheckboxWithLabel changes the text after click', () => { - // Render a checkbox with label in the document - const checkbox = shallow(); - - expect(checkbox.text()).toEqual('Off'); - - checkbox.find('input').simulate('change'); - - expect(checkbox.text()).toEqual('On'); -}); -``` - -The code for this example is available at [examples/enzyme](https://github.com/facebook/jest/tree/main/examples/enzyme). - -### Custom transformers - -If you need more advanced functionality, you can also build your own transformer. Instead of using `babel-jest`, here is an example of using `@babel/core`: - -```javascript title="custom-transformer.js" -'use strict'; - -const {transform} = require('@babel/core'); -const jestPreset = require('babel-preset-jest'); - -module.exports = { - process(src, filename) { - const result = transform(src, { - filename, - presets: [jestPreset], - }); - - return result || src; - }, -}; -``` - -Don't forget to install the `@babel/core` and `babel-preset-jest` packages for this example to work. - -To make this work with Jest you need to update your Jest configuration with this: `"transform": {"\\.js$": "path/to/custom-transformer.js"}`. - -If you'd like to build a transformer with babel support, you can also use `babel-jest` to compose one and pass in your custom configuration options: - -```javascript -const babelJest = require('babel-jest'); - -module.exports = babelJest.createTransformer({ - presets: ['my-custom-preset'], -}); -``` - -See [dedicated docs](CodeTransformation.md#writing-custom-transformers) for more details. diff --git a/website/versioned_docs/version-27.2/TutorialReactNative.md b/website/versioned_docs/version-27.2/TutorialReactNative.md deleted file mode 100644 index ddb60b230c31..000000000000 --- a/website/versioned_docs/version-27.2/TutorialReactNative.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -id: tutorial-react-native -title: Testing React Native Apps ---- - -At Facebook, we use Jest to test [React Native](https://reactnative.dev/) applications. - -Get a deeper insight into testing a working React Native app example by reading the following series: [Part 1: Jest – Snapshot come into play](https://callstack.com/blog/testing-react-native-with-the-new-jest-part-1-snapshots-come-into-play/) and [Part 2: Jest – Redux Snapshots for your Actions and Reducers](https://callstack.com/blog/testing-react-native-with-the-new-jest-part-2-redux-snapshots-for-your-actions-and-reducers/). - -## Setup - -Starting from react-native version 0.38, a Jest setup is included by default when running `react-native init`. The following configuration should be automatically added to your package.json file: - -```json -{ - "scripts": { - "test": "jest" - }, - "jest": { - "preset": "react-native" - } -} -``` - -_Note: If you are upgrading your react-native application and previously used the `jest-react-native` preset, remove the dependency from your `package.json` file and change the preset to `react-native` instead._ - -Run `yarn test` to run tests with Jest. - -## Snapshot Test - -Let's create a [snapshot test](SnapshotTesting.md) for a small intro component with a few views and text components and some styles: - -```tsx title="Intro.js" -import React, {Component} from 'react'; -import {StyleSheet, Text, View} from 'react-native'; - -class Intro extends Component { - render() { - return ( - - Welcome to React Native! - - This is a React Native snapshot test. - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - alignItems: 'center', - backgroundColor: '#F5FCFF', - flex: 1, - justifyContent: 'center', - }, - instructions: { - color: '#333333', - marginBottom: 5, - textAlign: 'center', - }, - welcome: { - fontSize: 20, - margin: 10, - textAlign: 'center', - }, -}); - -export default Intro; -``` - -Now let's use React's test renderer and Jest's snapshot feature to interact with the component and capture the rendered output and create a snapshot file: - -```tsx title="__tests__/Intro-test.js" -import React from 'react'; -import renderer from 'react-test-renderer'; -import Intro from '../Intro'; - -test('renders correctly', () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); -}); -``` - -When you run `yarn test` or `jest`, this will produce an output file like this: - -```javascript title="__tests__/__snapshots__/Intro-test.js.snap" -exports[`Intro renders correctly 1`] = ` - - - Welcome to React Native! - - - This is a React Native snapshot test. - - -`; -``` - -The next time you run the tests, the rendered output will be compared to the previously created snapshot. The snapshot should be committed along with code changes. When a snapshot test fails, you need to inspect whether it is an intended or unintended change. If the change is expected you can invoke Jest with `jest -u` to overwrite the existing snapshot. - -The code for this example is available at [examples/react-native](https://github.com/facebook/jest/tree/main/examples/react-native). - -## Preset configuration - -The preset sets up the environment and is very opinionated and based on what we found to be useful at Facebook. All of the configuration options can be overwritten just as they can be customized when no preset is used. - -### Environment - -`react-native` ships with a Jest preset, so the `jest.preset` field of your `package.json` should point to `react-native`. The preset is a node environment that mimics the environment of a React Native app. Because it doesn't load any DOM or browser APIs, it greatly improves Jest's startup time. - -### transformIgnorePatterns customization - -The [`transformIgnorePatterns`](configuration#transformignorepatterns-arraystring) option can be used to specify which files shall be transformed by Babel. Many `react-native` npm modules unfortunately don't pre-compile their source code before publishing. - -By default the `jest-react-native` preset only processes the project's own source files and `react-native`. If you have npm dependencies that have to be transformed you can customize this configuration option by including modules other than `react-native` by grouping them and separating them with the `|` operator: - -```json -{ - "transformIgnorePatterns": [ - "node_modules/(?!(react-native|my-project|react-native-button)/)" - ] -} -``` - -You can test which paths would match (and thus be excluded from transformation) with a tool [like this](https://regex101.com/r/JsLIDM/1). - -`transformIgnorePatterns` will exclude a file from transformation if the path matches against **any** pattern provided. Splitting into multiple patterns could therefore have unintended results if you are not careful. In the example below, the exclusion (also known as a negative lookahead assertion) for `foo` and `bar` cancel each other out: - -```json -{ - "transformIgnorePatterns": ["node_modules/(?!foo/)", "node_modules/(?!bar/)"] // not what you want -} -``` - -### setupFiles - -If you'd like to provide additional configuration for every test file, the [`setupFiles` configuration option](configuration#setupfiles-array) can be used to specify setup scripts. - -### moduleNameMapper - -The [`moduleNameMapper`](configuration#modulenamemapper-objectstring-string--arraystring) can be used to map a module path to a different module. By default the preset maps all images to an image stub module but if a module cannot be found this configuration option can help: - -```json -{ - "moduleNameMapper": { - "my-module.js": "/path/to/my-module.js" - } -} -``` - -## Tips - -### Mock native modules using jest.mock - -The Jest preset built into `react-native` comes with a few default mocks that are applied on a react-native repository. However, some react-native components or third party components rely on native code to be rendered. In such cases, Jest's manual mocking system can help to mock out the underlying implementation. - -For example, if your code depends on a third party native video component called `react-native-video` you might want to stub it out with a manual mock like this: - -```js -jest.mock('react-native-video', () => 'Video'); -``` - -This will render the component as `