How to load local JavaScript file with JSDOM?

I am unable to use JSDOM (version 13.0.0) to load scripts from the local filesystem with a relative path.

I have taken a look at the following questions but they do not answer my question:

  • jsdom can't load local html and javascript (I have already followed the runScripts and resources suggestion there).

File foo.js:

var jsdom = require('jsdom')

var html = `<!DOCTYPE html>
<html>
  <head>
    <title>Test</title>
    <script src="bar.js"></script>
  </head>
  <body>
    <div>Test</div>
  </body>
</html>`

global.window = new jsdom.JSDOM(html, { runScripts: "dangerously", resources: "usable" }).window
console.log('foo')

File bar.js:

console.log('bar')

Here is the error I get:

$ node foo.js
foo
Error: Could not load script: "bar.js"
    at onErrorWrapped (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:41:19)
    at Object.check (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:72:23)
    at request.then.catch.err (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/resources/resource-queue.js:124:14)
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:746:11)
    at startup (internal/bootstrap/node.js:240:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3) Error: Tried to fetch invalid URL bar.js
    at ResourceLoader.fetch (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js:84:29)
    at PerDocumentResourceLoader.fetch (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/resources/per-document-resource-loader.js:16:42)
    at HTMLScriptElementImpl._fetchExternalScript (/Users/lone/so/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:92:30)
    at HTMLScriptElementImpl._eval (/Users/lone/so/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:161:12)
    at HTMLScriptElementImpl._poppedOffStackOfOpenElements (/Users/lone/so/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:126:10)
    at OpenElementStack.pop (/Users/lone/so/node_modules/jsdom/lib/jsdom/browser/htmltodom.js:17:12)
    at Object.endTagInText [as END_TAG_TOKEN] (/Users/lone/so/node_modules/parse5/lib/parser/index.js:2153:20)
    at Parser._processToken (/Users/lone/so/node_modules/parse5/lib/parser/index.js:657:55)
    at Parser._processInputToken (/Users/lone/so/node_modules/parse5/lib/parser/index.js:684:18)
    at Parser._runParsingLoop (/Users/lone/so/node_modules/parse5/lib/parser/index.js:440:18)

How can I load a local JavaScript file while using JSDOM?


JSDOM doesn't know where to look for that file locally while executing. So running your example you can follow any of this two approaches.

1st Approach

You have to wait for the script file to load and execute.

Create a three files index.html,index.js and test.js into the same folder.

index.html

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title></title>
  </head>
  <body>
    abc
    <script src='index.js'></script>
  </body>
</html>

index.js

document.body.textContent = 123;

test.js

'use strict';

const { JSDOM } = require('jsdom');

const options = {
  resources: 'usable',
  runScripts: 'dangerously',
};

JSDOM.fromFile('index.html', options).then((dom) => {
  console.log(dom.window.document.body.textContent.trim());

  setTimeout(() => {
    console.log(dom.window.document.body.textContent.trim());
  }, 5000);
});

 // console output
 // abc
 // 123

2nd Approach Set the external scripts base root folder in JSDOM env.

js/index.js

console.log('load from jsdom');
var loadFromJSDOM = 'load from jsdom';

test.js

'use strict';
const { JSDOM } = require('jsdom');
JSDOM.env({
        html: "<html><body></body></html>",
        documentRoot: __dirname + '/js',
        scripts: [
            'index.js'
        ]
    }, function (err, window) {
        console.log(window.loadFromJSDOM);
    }
);

Read more from these references

https://github.com/jsdom/jsdom/issues/1867

jsdom.env: local jquery script doesn't work


Great answer from front_end_dev. It helped me a lot and I will share how my code works with this solution to be more clear. Maybe will help others.

import "@testing-library/jest-dom";

import { logDOM } from "@testing-library/dom";
import { JSDOM } from "jsdom";

import fs from "fs";
import path from "path";

const html = fs.readFileSync(path.resolve(__dirname, "../index.html"), "utf8");

let dom;
let container;

jest.dontMock("fs");

function waitForDom() {
  return new Promise((resolve) => {
    dom = new JSDOM(html, {
      runScripts: "dangerously",
      resources: "usable",
      url: `file://${path.resolve(__dirname, "..")}/index.html`,
    });
    dom.window.document.addEventListener("DOMContentLoaded", () => {
      resolve();
    });
  });
}

beforeAll(() => waitForDom());

beforeEach(() => {
  container = dom.window.document.body;
});

afterEach(() => container = null)

it("should ", () => {
  logDOM(container);
});