1"use strict";(globalThis.webpackChunkjest_website||=[]).push([[727],{35059(e,n,t){t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>u});const s=JSON.parse('{"id":"test-environment","title":"Test Environment","description":"Jest provides environment option to run code inside a specific environment. You can modify how environment behaves with testEnvironmentOptions option.","source":"@site/versioned_docs/version-30.5/TestEnvironment.md","sourceDirName":".","slug":"/test-environment","permalink":"/docs/test-environment","draft":false,"unlisted":false,"editUrl":"https://github.com/jestjs/jest/edit/main/website/versioned_docs/version-30.5/TestEnvironment.md","tags":[],"version":"30.5","lastUpdatedBy":"Simen Bekkhus","lastUpdatedAt":1787905187000,"frontMatter":{"id":"test-environment","title":"Test Environment"},"sidebar":"docs","previous":{"title":"Snapshot Testing","permalink":"/docs/snapshot-testing"},"next":{"title":"An Async Example","permalink":"/docs/tutorial-async"}}');var o=t(62540),r=t(43023),i=t(49479),a=t(22491);const l={id:"test-environment",title:"Test Environment"},c=void 0,d={},u=[{value:"Environments for Specific Files",id:"environments-for-specific-files",level:2},{value:"Extending built-in Environments",id:"extending-built-in-environments",level:2},{value:"Custom Environment",id:"custom-environment",level:2},{value:"See Also",id:"see-also",level:2}];function m(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(n.p,{children:["Jest provides environment option to run code inside a specific environment. You can modify how environment behaves with ",(0,o.jsx)(n.code,{children:"testEnvironmentOptions"})," option."]}),"\n",(0,o.jsx)(n.p,{children:"By default, you can use these environments:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"node"})," is default environment"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"jsdom"})," emulates browser environment by providing Browser API, uses ",(0,o.jsx)(n.a,{href:"https://github.com/jsdom/jsdom",children:(0,o.jsx)(n.code,{children:"jsdom"})})," package"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"environments-for-specific-files",children:"Environments for Specific Files"}),"\n",(0,o.jsx)(n.admonition,{type:"info",children:(0,o.jsxs)(n.p,{children:["Each test suite runs in its own ",(0,o.jsx)(n.code,{children:"TestEnvironment"})," instance. ",(0,o.jsx)(n.code,{children:"setup"})," and ",(0,o.jsx)(n.code,{children:"teardown"})," are called once per suite."]})}),"\n",(0,o.jsxs)(n.p,{children:["When setting ",(0,o.jsx)(n.code,{children:"testEnvironment"})," option in your config, it will apply to all the test files in your project. To have more fine-grained control, you can use docblock pragmas to specify environment for specific files. Docblock pragmas are comments that start with ",(0,o.jsx)(n.code,{children:"@jest-environment"})," and are followed by the environment name:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"With built-in environments:"}),"\n"]}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="my-test.spec.js"',children:"/**\n * @jest-environment jsdom\n */\n\ntest('use jsdom in this test file', () => {\n const element = document.createElement('div');\n expect(element).not.toBeNull();\n});\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="my-test.spec.ts"',children:"/**\n * @jest-environment jsdom\n */\n\ntest('use jsdom in this test file', () => {\n const element = document.createElement('div');\n expect(element).not.toBeNull();\n});\n"})})})]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"With custom environment:"}),"\n"]}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="my-test.spec.js"',children:"/**\n * @jest-environment ./my-custom-environment.js\n */\n\ntest('use jsdom in this test file', () => {\n const element = document.createElement('div');\n expect(element).not.toBeNull();\n});\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="my-test.spec.ts"',children:"/**\n * @jest-environment ./my-custom-environment.ts\n */\n\ntest('use jsdom in this test file', () => {\n const element = document.createElement('div');\n expect(element).not.toBeNull();\n});\n"})})})]}),"\n",(0,o.jsx)(n.h2,{id:"extending-built-in-environments",children:"Extending built-in Environments"}),"\n",(0,o.jsxs)(n.p,{children:["Jest allows you to extend the built-in environments, such as ",(0,o.jsx)(n.code,{children:"NodeEnvironment"})," or ",(0,o.jsx)(n.code,{children:"JSDOMEnvironment"}),", to create your own custom environment. This is useful when you want to add additional functionality or modify the behavior of the existing environments."]}),"\n",(0,o.jsxs)(n.p,{children:["Here is an example of how to extend the ",(0,o.jsx)(n.code,{children:"NodeEnvironment"}),":"]}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="custom-node-environment.js"',children:"// An example of a custom Node environment\n\nconst NodeEnvironment = require('jest-environment-node');\n\n/**\n * @implements {import('jest-environment-node').NodeEnvironment}\n */\nclass CustomNodeEnvironment extends NodeEnvironment {\n constructor(config, context) {\n super(config, context);\n console.log(config.globalConfig);\n console.log(config.projectConfig);\n this.testPath = context.testPath;\n this.docblockPragmas = context.docblockPragmas;\n }\n\n async setup() {\n await super.setup();\n await someSetupTasks(this.testPath);\n this.global.someGlobalObject = createGlobalObject();\n\n // Will trigger if docblock contains @my-custom-pragma my-pragma-value\n if (this.docblockPragmas['my-custom-pragma']
1=== 'my-pragma-value') {\n // ...\n }\n }\n\n async teardown() {\n this.global.someGlobalObject = destroyGlobalObject();\n await someTeardownTasks();\n await super.teardown();\n }\n\n getVmContext() {\n return super.getVmContext();\n }\n\n async handleTestEvent(event, state) {\n if (event.name === 'test_start') {\n // ...\n }\n }\n}\n\nmodule.exports = CustomNodeEnvironment;\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="custom-node-environment.ts"',children:"// An example of a custom Node environment\n\nimport NodeEnvironment from 'jest-environment-node';\n\nexport default class CustomNodeEnvironment extends NodeEnvironment {\n constructor(config, context) {\n super(config, context);\n console.log(config.globalConfig);\n console.log(config.projectConfig);\n this.testPath = context.testPath;\n this.docblockPragmas = context.docblockPragmas;\n }\n\n async setup() {\n await super.setup();\n await someSetupTasks(this.testPath);\n this.global.someGlobalObject = createGlobalObject();\n\n // Will trigger if docblock contains @my-custom-pragma my-pragma-value\n if (this.docblockPragmas['my-custom-pragma'] === 'my-pragma-value') {\n // ...\n }\n }\n\n async teardown() {\n this.global.someGlobalObject = destroyGlobalObject();\n await someTeardownTasks();\n await super.teardown();\n }\n\n getVmContext() {\n return super.getVmContext();\n }\n\n async handleTestEvent(event, state) {\n if (event.name === 'test_start') {\n // ...\n }\n }\n}\n"})})})]}),"\n",(0,o.jsx)(n.p,{children:"and declare in your Jest config"}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="jest.config.js"',children:"const {defineConfig} = require('jest');\n\nmodule.exports = defineConfig({\n testEnvironment: './custom-node-environment.js',\n});\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="jest.config.ts"',children:"import {defineConfig} from 'jest';\n\nexport default defineConfig({\n testEnvironment: './custom-node-environment.ts',\n});\n"})})})]}),"\n",(0,o.jsxs)(n.admonition,{type:"note",children:[(0,o.jsxs)(n.p,{children:["Any docblock pragmas in a test file (e.g. ",(0,o.jsx)(n.code,{children:"@my-custom-pragma my-value"}),") are passed to the environment constructor as ",(0,o.jsx)(n.code,{children:"context.docblockPragmas"}),"."]}),(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.code,{children:"handleTestEvent"})," is optional. When it returns a Promise, jest-circus waits for it to settle before continuing \u2014 ",(0,o.jsx)(n.strong,{children:"except"})," for these sync events: ",(0,o.jsx)(n.code,{children:"start_describe_definition"}),", ",(0,o.jsx)(n.code,{children:"finish_describe_definition"}),", ",(0,o.jsx)(n.code,{children:"add_hook"}),", ",(0,o.jsx)(n.code,{children:"add_test"}),", and ",(0,o.jsx)(n.code,{children:"error"}),"."]})]}),"\n",(0,o.jsxs)(n.admonition,{type:"tip",children:[(0,o.jsxs)(n.p,{children:["Jest also provides ",(0,o.jsx)(n.code,{children:"@jest/environment-jsdom-abstract"})," package to make it easier for you to compose your own custom test environment based on ",(0,o.jsx)(n.code,{children:"jsdom"})," or use your own ",(0,o.jsx)(n.code,{children:"jsdom"})," installed version."]}),(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="custom-jsdom-environment.js"',children:"const JSDOMEnvironment = require('@jest/environment-jsdom-abstract');\nconst jsdom = require('jsdom');\n\nclass CustomJSDOMEnvironment extends JSDOMEnvironment {\n constructor(config, context) {\n super(config, context, jsdom);\n }\n\n // Override methods to customize behavior\n}\n\nmodule.exports = CustomJSDOMEnvironment;\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="custom-jsdom-environment.ts"',children:"import JSDOMEnvironment from '@jest/environment-jsdom-abstract';\nimport jsdom from 'jsdom';
1\n\nexport default class CustomJSDOMEnvironment extends JSDOMEnvironment {\n constructor(config, context) {\n super(config, context, jsdom);\n }\n\n // Override methods to customize behavior\n}\n"})})})]}),(0,o.jsx)(n.p,{children:"and declare in your Jest config"}),(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="jest.config.js"',children:"const {defineConfig} = require('jest');\n\nmodule.exports = defineConfig({\n testEnvironment: './custom-jsdom-environment.js',\n});\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="jest.config.ts"',children:"import {defineConfig} from 'jest';\n\nexport default defineConfig({\n testEnvironment: './custom-jsdom-environment.ts',\n});\n"})})})]})]}),"\n",(0,o.jsx)(n.h2,{id:"custom-environment",children:"Custom Environment"}),"\n",(0,o.jsx)(n.p,{children:"You can create your own package to extend Jest environment. To do so, create package with a name, or specify a path to a valid JS/TS file. That package should export an object with the shape of Environment:"}),"\n",(0,o.jsx)(n.admonition,{type:"tip",children:(0,o.jsxs)(n.p,{children:["It's a best practice to name your custom environment with ",(0,o.jsx)(n.code,{children:"jest-environment-"})," prefix, so that it is clearly identifiable as a Jest environment."]})}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="environment.js"',children:"/**\n * @implements {import('@jest/environment').JestEnvironment}\n */\nclass CustomEnvironment {\n // Implement the required methods here\n\n // Example of a method\n getVmContext() {\n return null;\n }\n}\n\nmodule.exports = CustomEnvironment;\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="environment.ts"',children:"import type {JestEnvironment} from '@jest/environment';\n\nexport default class CustomEnvironment implements JestEnvironment {\n // Implement the required methods here\n\n // Example of a method\n getVmContext() {\n return null;\n }\n}\n"})})})]}),"\n",(0,o.jsx)(n.p,{children:"and declare in your Jest config"}),"\n",(0,o.jsxs)(i.A,{groupId:"code-examples",children:[(0,o.jsx)(a.A,{value:"js",label:"JavaScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-js",metastring:'tab title="jest.config.js"',children:"const {defineConfig} = require('jest');\n\nmodule.exports = defineConfig({\n testEnvironment: './environment.js',\n});\n"})})}),(0,o.jsx)(a.A,{value:"ts",label:"TypeScript",children:(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-ts",metastring:'tab title="jest.config.ts"',children:"import {defineConfig} from 'jest';\n\nexport default defineConfig({\n testEnvironment: './environment.ts',\n});\n"})})})]}),"\n",(0,o.jsx)(n.h2,{id:"see-also",children:"See Also"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/docs/configuration#testenvironment-string",children:"Configuration - testEnvironment"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"/docs/configuration#testenvironmentoptions-object",children:"Configuration - testEnvironmentOptions"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://github.com/jsdom/jsdom",children:"JSDOM Documentation"})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(m,{...e})}):m(e)}},22491(e,n,t){t.d(n,{A:()=>l});t(63696);var s=t(11750),o=t(75373);const r="tabItem_wHwb";var i=t(62540);function a({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,s.A)(r,n),hidden:t,children:e})}function l({children:e,className:n,value:t}){const{selectedValue:s,lazy:r}=(0,o.uc)(),l=t===s;return!l&&r?null:(0,i.jsx)(a,{className:n,hidden:!l,children:e})}},49479(e,n,t){t.d(n,{A:()=>p});t(63696);var s=t(11750),o=t(53237),r=t(75373),i=t(90766),a=t(86681);const l="tabList_J5MA",c="tabItem_l0OV";var d=t(62540);function u({className:e}){const{selectedValue:n,selectValue:t,tabValues:o,block:a}=(0,r.uc)(),l=[],{blockElementScrollPositionUntilNextRender:u}=(0,i.a_)(),m=e=>{const s=e.currentTarget,r=l.indexOf(s),i=o[r].value;i!==n&&(u(s),t(i))},h=e=>{let n=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":a},e),children:o.map(({value:e,label:t,attributes:o})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:h,onClick:m,...o,className:(0,s.A)("tabs__item",c,o?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function m({children:e}){return(0,d.jsx)("div",{className:"margin-top--md",children:e})}function h({className:e,children:n}){return(0,d.jsxs)("div",{className:(0,s.A)(o.G.tabs.container,"tabs-container",l),children:[(0,d.jsx)(u,{className:e}),(0,d.jsx)(m,{children:n})]})}function p(e){const n=(0,a.A)(),t=(0,r.OC)(e);return(0,d.jsx)(r.O_,{value:t,children:(0,d.jsx)(h,{className:e.className,children:(0,r.vT)(e.children)})},String(n))}},75373(e,n,t){t.d(n,{OC:()=>p,O_:()=>x,uc:()=>j,vT:()=>d});var s=t(63696),o=t(49519),r=t(14395),i=t(35043),a=t(94243),l=t(44544),c=t(62540);function d(e){return s.Children.toArray(e).filter(e=>"\n"!==e)}function u(e){const{values:n,children:t}=e;return(0,s.useMemo)(()=>{const e=n??function(e){return s.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,s.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:s}})=>({value:e,label:n,attributes:t,default:s}))}(t);return function(e){const n=(0,l.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function m({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h({queryString:e=!1,groupId:n}){const t=(0,o.W6)(),r=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(r),(0,s.useCallback)(e=>{if(!r)return;const n=new URLSearchParams(t.location.search);n.set(r,e),t.replace({...t.location,search:n.toString()})},[r,t])]}function p(e){const{defaultValue:n,queryString:t=!1,groupId:o}=e,i=u(e),[l,c]=(0,s.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!m({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i})),[d,p]=h({queryString:t,groupId:o}),[v,j]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,o]=(0,a.Dv)(n);return[t,(0,s.useCallback)(e=>{n&&o.set(e)},[n,o])]}({groupId:o}),x=(()=>{const e=d??v;return m({value:e,tabValues:i})?e:null})();(0,r.A)(()=>{x&&c(x)},[x]);return{selectedValue:l,selectValue:(0,s.useCallback)(e=>{if(!m({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);c(e),p(e),j(e)},[p,j,i]),tabValues:i,lazy:e.lazy??!1,block:e.block??!1}}const v=(0,s.createContext)(null);function j(){const e=s.useContext(v);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function x(e){return(0,c.jsx)(v.Provider,{value:e.value,children:e.children})}},43023(e,n,t){t.d(n,{R:()=>i,x:()=>a});var s=t(63696);const o={},r=s.createContext(o);function i(e){const n=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),s.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.