/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/triple-slash-reference */
/* eslint-disable @typescript-eslint/ban-types */
///
import { IFuture } from 'fp-future'
import { readFileSync } from 'fs'
// A naive attempt at getting the global `this`. Don’t use `this`!
const getGlobalThis = function () {
// @ts-ignore
if (typeof globalThis !== 'undefined') return globalThis
// @ts-ignore
if (typeof self !== 'undefined') return self
// @ts-ignore
if (typeof window !== 'undefined') return window
// Note: this might still return the wrong result!
// @ts-ignore
if (typeof this !== 'undefined') return this
throw new Error('Unable to locate global `this`')
}
const globalObject: {
dcl: Pick
define: Function & { modules: any }
onerror: CallableFunction
} = (getGlobalThis as any)()
export function resolveFutureWith(f: IFuture, fn: () => Promise) {
fn().then(f.resolve).catch(f.catch)
}
export type ModulesMock = Record Promise>
export function mockEnvironment(modules: ModulesMock) {
const starters: CallableFunction[] = []
const loadedModuleDescriptors = new Map()
const loadedModulesByHandle = new Map()
const errors: string[] = []
it('mocks the environment', () => {
const amdModuleRequire = require.resolve('../../packages/@dcl/amd')
const content = readFileSync(amdModuleRequire).toString()
;(globalObject as any).define = null
delete (globalObject as any).dcl
delete (globalObject as any).onerror
expect(globalObject.dcl).toBeUndefined()
expect(globalObject.define).toEqual(null)
expect(globalObject.onerror).toBeUndefined()
function getModuleHandle(moduleName: string) {
return moduleName + '_handle'
}
globalObject.onerror = (err: Error) => {
errors.push(err.message)
}
globalObject.dcl = {
async loadModule(moduleName: string) {
if (loadedModuleDescriptors.has(moduleName)) {
return loadedModuleDescriptors.get(moduleName)!
}
if (moduleName in modules) {
const rpcHandle = getModuleHandle(moduleName)
const moduleInstance = await modules[moduleName]()
const ret: ModuleDescriptor = {
rpcHandle,
methods: Object.keys(moduleInstance)
.filter((key) => typeof moduleInstance[key] === 'function')
.map((key) => {
return { name: key }
})
}
loadedModuleDescriptors.set(moduleName, ret)
loadedModulesByHandle.set(rpcHandle, moduleInstance)
return ret
}
throw new Error('Unknown module ' + moduleName)
},
async callRpc(moduleHandle: string, methodName: string, args: any[]) {
if (!loadedModulesByHandle.has(moduleHandle)) {
throw new Error('Unknown module handle ' + moduleHandle)
}
const moduleInstance = loadedModulesByHandle.get(moduleHandle)!
if (!moduleInstance[methodName]) {
throw new Error(`Unknown method '${methodName}'`)
}
return moduleInstance[methodName].apply(null, args)
},
onStart(cb: CallableFunction) {
starters.push(cb)
}
}
// RUN THE AMD LOADER
eval(content + '//# sourceURL=' + amdModuleRequire)
expect(globalObject.dcl).not.toBeUndefined()
expect(globalObject.define).not.toBeUndefined()
expect(globalObject.onerror).not.toBeUndefined()
})
function start() {
for (const $ of starters) {
$()
}
}
function define(factory: any): void
function define(dependencies: string[], factory: any): void
function define(id: string, factory: any): void
function define(id: string, dependencies: string[], factory: any): void
function define(this: any, ...args: any[]): void {
return globalObject.define.apply(this, args)
}
function getModules(): Record<
string,
{
name: string
dependencies: Array
}
> {
return globalObject.define.modules
}
return {
starters,
start,
loadedModuleDescriptors,
loadedModulesByHandle,
define,
errors,
getModules
}
}