diff --git a/FixJSDOMEnvironment.ts b/FixJSDOMEnvironment.ts new file mode 100644 index 000000000..61c3c5e68 --- /dev/null +++ b/FixJSDOMEnvironment.ts @@ -0,0 +1,15 @@ +import JSDOMEnvironment from "jest-environment-jsdom"; + +// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string +export default class FixJSDOMEnvironment extends JSDOMEnvironment { + constructor(...args: ConstructorParameters) { + super(...args); + + // FIXME https://github.com/nodejs/node/issues/35889 + // Add missing importActual() function to mirror requireActual(), + // which lets us work around the ESM bug. + // Wrap the construction of the function in eval, so that transpilers + // don't touch the import() call. + this.global.importActual = eval("url => import(url)"); + } +} diff --git a/jest.config.js b/jest.config.js index c40995a9a..92f04d6c5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,10 +1,11 @@ module.exports = { + roots: ["/src/", "/test/"], moduleFileExtensions: ["ts", "tsx", "js", "jsx"], transform: { "^.+\\.(js|jsx|ts|tsx)$": "babel-jest", }, testPathIgnorePatterns: [".cypress", "node_modules", "dist"], - testEnvironment: "jsdom", + testEnvironment: "./FixJSDOMEnvironment.ts", moduleNameMapper: { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts index 83e5e6a84..652209f49 100644 --- a/src/NetscriptJSEvaluator.ts +++ b/src/NetscriptJSEvaluator.ts @@ -18,6 +18,22 @@ function makeScriptBlob(code: string): Blob { return new Blob([code], { type: "text/javascript" }); } +// Webpack likes to turn the import into a require, which sort of +// but not really behaves like import. So we use a "magic comment" +// to disable that and leave it as a dynamic import. +// +// However, we need to be able to replace this implementation in tests. Ideally +// it would be fine, but Jest causes segfaults when using dynamic import: see +// https://github.com/nodejs/node/issues/35889 and +// https://github.com/facebook/jest/issues/11438 +// import() is not a function, so it can't be replaced. We need this separate +// config object to provide a hook point. +export const config = { + doImport(url: string): Promise { + return import(/*webpackIgnore:true*/ url); + }, +}; + export async function compile(script: Script, scripts: Script[]): Promise { //!shouldCompile ensures that script.module is non-null, hence the "as". if (!shouldCompile(script, scripts)) return script.module as Promise; @@ -36,12 +52,7 @@ export async function compile(script: Script, scripts: Script[]): Promise