9

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:

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?

2 Answers 2

9

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

Sign up to request clarification or add additional context in comments.

3 Comments

The 2nd Approach (using "env") does not work. However the first one does.
In the first approach, replace setTimeout(..., 5000); with dom.window.addEventListerner('load', ...);, to avoid introducing a pause.
@BlackMantha, thank you, just not .addEventListerner, but .addEventListener.
3

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);
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.