import { Script } from "vm" ; import { Config , Global } from "@jest/types" ; import { installCommonGlobals } from "jest-util" ; import { ModuleMocker } from "jest-mock" ; import { JestFakeTimers as FakeTimers } from "@jest/fake-timers" ; import { EnvironmentContext , JestEnvironment } from "@jest/environment" ; import { JSDOM , VirtualConsole } from "jsdom" ; type Win = Window & Global . Global & { Error : { stackTraceLimit : number ; } ; } ; class JSDOMEnvironment implements JestEnvironment { dom : JSDOM | null ; fakeTimers : FakeTimers < number > | null ; global : Win ; errorEventListener : ( ( event : Event & { error : Error ; } ) => void ) | null ; moduleMocker : ModuleMocker | null ; constructor ( config , options = { } ) { this . dom = new JSDOM ( "<!DOCTYPE html>" , { pretendToBeVisual : true , runScripts : "dangerously" , url : config . testURL , virtualConsole : new VirtualConsole ( ) . sendTo ( options . console || console ) , ... config . testEnvironmentOptions , } ) ; const global = ( this . global = this . dom . window . document . defaultView as Win ) ; if ( ! global ) { throw new Error ( "JSDOM did not return a Window object" ) ; } this . global . Error . stackTraceLimit = 100 ; installCommonGlobals ( global as any , config . globals ) ; this . errorEventListener = event => { if ( userErrorListenerCount === 0 && event . error ) { process . emit ( "uncaughtException" , event . error ) ; } } ; global . addEventListener ( "error" , this . errorEventListener ) ; const originalAddListener = global . addEventListener ; const originalRemoveListener = global . removeEventListener ; let userErrorListenerCount = 0 ; global . addEventListener = function ( ... args ) { if ( args [ 0 ] === "error" ) { userErrorListenerCount ++ ; } return originalAddListener . apply ( this , args ) ; } ; global . removeEventListener = function ( ... args ) { if ( args [ 0 ] === "error" ) { userErrorListenerCount -- ; } return originalRemoveListener . apply ( this , args ) ; } ; this . moduleMocker = new ModuleMocker ( global as any ) ; const timerConfig = { idToRef : ( id ) => id , refToId : ( ref ) => ref , } ; this . fakeTimers = new FakeTimers ( { config , global : global as any , moduleMocker : this . moduleMocker , timerConfig , } ) ; } setup ( ) { return Promise . resolve ( ) ; } teardown ( ) { if ( this . fakeTimers ) { this . fakeTimers . dispose ( ) ; } if ( this . global ) { if ( this . errorEventListener ) { this . global . removeEventListener ( "error" , this . errorEventListener ) ; } Object . defineProperty ( this . global , "document" , { value : null } ) ; this . global . close ( ) ; } this . errorEventListener = null ; this . global = null ; this . dom = null ; this . fakeTimers = null ; return Promise . resolve ( ) ; } runScript ( script ) { if ( this . dom ) { return this . dom . runVMScript ( script ) as any ; } return null ; } } export = JSDOMEnvironment ;