import { JSDOM } from 'jsdom' ; global . Error . stackTraceLimit = 100 ; const mountedContainers = new Set < HTMLDivElement > ( ) ; export function setup ( ) { if ( global . window ) { throw new Error ( 'JSDOM globals already exist, did you forget to run teardown()?' ) ; } const dom = new JSDOM ( '<!DOCTYPE html>' , { pretendToBeVisual : true , runScripts : 'dangerously' , url : 'http://localhost/' , } ) ; global . window = dom . window . document . defaultView ! ; global . document = global . window . document ; } export function teardown ( ) { if ( ! global . window ) { throw new Error ( 'No JSDOM globals exist, did you forget to run setup()?' ) ; } global . window . close ( ) ; global . window = global . document = undefined ; } export interface RenderResult { /** A wrapper DIV which contains your mounted component. */ container : HTMLDivElement ; param el debug ( el ? ) : void ; unmount ( ) : void ; } export function render ( component ) { const container = document . createElement ( 'div' ) ; container . appendChild ( component ) ; document . body . appendChild ( container ) ; mountedContainers . add ( container ) ; return { container , debug ( el = container ) { console . log ( 'DEBUG:\n' , el . innerHTML ) ; } , unmount ( ) { container . removeChild ( component ) ; } , } ; } export function cleanup ( ) { if ( ! mountedContainers || mountedContainers . size === 0 ) { throw new Error ( 'No mounted components exist, did you forget to call render()?' ) ; } mountedContainers . forEach ( ( container ) => { if ( container . parentNode === document . body ) { container . remove ( ) ; } mountedContainers . delete ( container ) ; } ) ; } type DeepPartial < T > = T extends Function ? T : T extends object ? { [ P in keyof T ] ? : DeepPartial < T [ P ] > ; } : T ; type ChromeAPI = DeepPartial < typeof window . chrome > ; type MockFn < T > = T & { calledTimes ( ) : number ; } ; const noop = ( ) => { } ; export function mockFn < T extends Function > ( imlp = noop ) { let callCount = 0 ; const fn = new Proxy ( imlp , { apply ( target , thisArg , args ) { callCount += 1 ; return Reflect . apply ( target , thisArg , args ) ; } , } ) ; fn . calledTimes = ( ) => callCount ; return fn ; } export function mocksSetup ( ) { const mockChrome = { bookmarks : { getTree : noop , search : noop , } , history : { search : noop , } , runtime : { openOptionsPage : noop , } , sessions : { getRecentlyClosed : noop , } , storage : { local : { get : ( _keys , callback ) => { callback ( { } ) ; } , set : noop , } , } , tabs : { create : noop , getCurrent : noop , onMoved : { addListener : noop , } , onRemoved : { addListener : noop , } , onUpdated : { addListener : noop , } , query : noop , remove : noop , update : noop , } , topSites : { get : noop , } , windows : { getCurrent : noop , update : noop , } , } ; global . chrome = mockChrome ; global . DocumentFragment = window . DocumentFragment ; global . localStorage = window . localStorage ; global . fetch = ( ) => Promise . resolve ( { json : ( ) => Promise . resolve ( { } ) , } ) ; } export function mocksTeardown ( ) { global . chrome = global . DocumentFragment = global . localStorage = global . fetch = undefined ; }