Jsdom: jest : SecurityError: localStorage is not available for opaque origins

Created on 27 Jul 2018  ·  80Comments  ·  Source: jsdom/jsdom

When I run a jest with my test cases. It shows the following error when I upgrade the package. In my test cases, there is no localStorage is used. How can I fix this problem?

 SecurityError: localStorage is not available for opaque origins

      at Window.get localStorage [as localStorage] (node_modules/jsdom/lib/jsdom/browser/Window.js:257:15)
          at Array.forEach (<anonymous>)


working as intended

Most helpful comment

@gokulkrishh I set it to http://localhost/ but any valid URL seems to work.

All 80 comments

Summary of below discussion:

  • The fix is to set a URL for your jsdom, which might need to be done through your testing environment configuration. The default URL, of "about:blank", will cause errors when trying to access localStorage.
  • The root cause is often libraries looping over all jsdom properties and adding them to the global; even if you never use localStorage in your tests, some library or test framework you are depending on is "using" it in this manner. Note that this looping-and-copying technique is explicitly unsupported, so it's not surprising that code that does it could break in a minor release.

Transcription of below comment on the question of whether this is a "breaking change", which has since been hidden by GitHub's "see more comments" feature:

Thanks, but as of this time I don't believe that's the best path forward. It isn't a breaking change for jsdom to implement new web platform features; that's a minor version.

It's possible for dependents, even widely-used dependents, to write unfortunate code that detects new features and throws an exception if they appear. I hope we can all agree that even if such dependents exist, jsdom should not forever give up on adding new features in minor releases.

The situation here is not quite that drastic, but seems to be similar. I haven't seen anyone track down the offending Jest code yet, so it's hard to say exactly, but it appears they're performing an operation which is explicitly broken by the normal course of jsdom development, and which would not work in a web browser that implements localStorage.

I realize this is a hard situation for those of you impacted, by no fault of your own, by an unfortunate interaction regarding how your direct dependency is (ab)using your indirect dependency. But I'm hopeful this can be addressed at the right level, of fixing Jest's buggy code, instead of removing a useful feature from all jsdom users due to one dependent's bugs.

Original reply to the OP, for context:

It seems likely you are not setting a URL in your tests, but you, or perhaps Jest, are accessing window.localStorage. The Jest maintainers may know more about the best fix, but I hear there is a way to set URLs in your Jest tests?

This is a very recent issue that seems to have popped up with 11.12.0. I get these same errors when using jest with enzyme.

Yes, I started having the issue after upgrading from 11.11.0 to 11.12.0. Setting testURL in the jest config fixes the issue.

@ben-mckernan Hi, What's the URL you gave to fix this ??

@gokulkrishh I set it to http://localhost/ but any valid URL seems to work.

I guess this is probably Enzyme-specific, because Enzyme does the thing that the jsdom docs explicitly warn not to do? Not too surprising that broke on a minor release, unfortunately :(

This broke for me too :(

@ben-mckernan Thanks 👍

("+1" comments will be marked as spam; emailing everyone in the issue thread is not helpful.)

Apologies. Just trying to help.

Can confirm adding testURL to jestConfig as @ben-mckernan suggested did fix it.

And we are using Enzyme too, if that helps confirm your intuition.

For electron app test I just set it to "file:/" which also works.

@miamollie I added testURL as per @ben-mckernan suggestion (Using Jest + Enzyme, not sure its enzyme related. Error coming from jest-environment-jsdom which uses jsdom). Due to that my some other test files are failing. Just FYI. See if its works for you. Your test cases might be diff from mine (TestURL might work for you).

@domenic I am only using jest. So I'm not sure that it is an enzyme issue.

@gokulkrishh Yeah, same, it stopped the localStorage security error but made some other tests fail.

@ben-mckernan solution fixed it. Thanks!

@ben-mckernan I'm using jest in an angular setup (with jest-preset-angular), same bug, same solution. So it is not an enzyme problem.

Seems like Jest needs to change the default value of testURL for this to be mitigated (currently it is about:blank).

@DcsMarcRemolt I was just debugging the issue. Jest is using a dependency module called jest-environment-jsdom in its package.json --> "jsdom": "^11.5.1" caret(^) because of this npm have installed jsdom as 11.12.0 (which is new version published today). So it broke for most of the users. Issue is already created in jest and linked here. Watch out for it.

I'll reopen this issue to give it more visibility. See the first comment from Domenic for a solution.

Adding the following to the jest config in package.json:
"testEnvironment": "node"
solves the problem for me.
Credit to @blu3printchris for helping to solve this.

I can no longer overwrite and mock the JSDOM implementation on localStorage since this update.

Since this was an unintentional breaking change for a lot of people, perhaps the best damage control option would be:

  • Revert this change.
  • Release version 11.12.1 with the reverted change to restore expected behaviour.
  • Pull or deprecate version 11.12.0 with a warning.
  • If the new behaviour is desired as-is, release 12.0.0 to indicate there is a breaking change.

Thanks, but as of this time I don't believe that's the best path forward. It isn't a breaking change for jsdom to implement new web platform features; that's a minor version.

It's possible for dependents, even widely-used dependents, to write unfortunate code that detects new features and throws an exception if they appear. I hope we can all agree that even if such dependents exist, jsdom should not forever give up on adding new features in minor releases.

The situation here is not quite that drastic, but seems to be similar. I haven't seen anyone track down the offending Jest code yet, so it's hard to say exactly, but it appears they're performing an operation which is explicitly broken by the normal course of jsdom development, and which would not work in a web browser that implements localStorage.

I realize this is a hard situation for those of you impacted, by no fault of your own, by an unfortunate interaction regarding how your direct dependency is (ab)using your indirect dependency. But I'm hopeful this can be addressed at the right level, of fixing Jest's buggy code, instead of removing a useful feature from all jsdom users due to one dependent's bugs.

Thanks for clarifying. I agree that if the issue is from using jsdom in an unsupported way as described above, then it's not a technically a semver violation and jest should release a patch.

My workaround, use a blacklist when looping over all jsdom properties and adding them to the global.


The code

const { JSDOM } = require('jsdom');
const Node = require('jsdom/lib/jsdom/living/node-document-position');

// We can use jsdom-global at some point if maintaining these lists is a burden.
const whitelist = ['HTMLElement', 'Performance'];
const blacklist = ['sessionStorage', 'localStorage'];

function createDOM() {
  const dom = new JSDOM('', { pretendToBeVisual: true });
  global.window = dom.window;
  global.Node = Node;
  global.document = dom.window.document;
  // Not yet supported: https://github.com/jsdom/jsdom/issues/317
  global.document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: 'BODY',
      ownerDocument: document,
    },
  });
  global.navigator = {
    userAgent: 'node.js',
  };

  Object.keys(dom.window)
    .filter(key => !blacklist.includes(key))
    .concat(whitelist)
    .forEach(key => {
      if (typeof global[key] === 'undefined') {
        global[key] = dom.window[key];
      }
    });
}

module.exports = createDOM;


Don't stuff jsdom globals onto the Node global

Well, I pass for now. I need the tests to run in jsdom and in real browsers. It's the simplest approach I can think of, it has been working for years. I don't see the same potential in the suggested alternatives.
Using jsdom-global could work too.

Well, I pass for now. I need the tests to run in jsdom and in real browsers.

If your tests can run inside "real browsers", they can run the same way in jsdom too - just provide the same HTML. By assigning to the global instead you're introducing additional complexity and differences compared to how you would run the test in a browser.

FYI I'm running into the same problem with Mocha, not Jest, after upgrading jsdom to 11.12.0.

Hi, I just want to understand why the changes are introduced at the first place.

My use cases are simply to assert a function to ensure I wrote it correctly, it is as simple as:

const fib = require('./index');

test('Fib function is defined', () => {
  expect(typeof fib).toEqual('function');
});

test('calculates correct fib value for 1', () => {
  expect(fib(1)).toEqual(1);
});

screenshot 2018-07-30 21 10 39

and yet the test result appears to be error messages that I just did some big application on React with Redux library and things like that, while the real thing is I just simply test out a function as simple as

//index.js, yes, only one line, no react no redux no enzyme 
function add(a, b) {}

By the way, testURL and testEnvironment "hack" does not work for me. This is my package.json:

    "jest": {
        "testURL": "http://localhost/",
        "testEnvironment": "node"
    },

so my question is why all the hassle to introduce breaking changes while sometimes we just want a test runner that just "works"

@khmy2010 Your code and questions involve Jest more than they do jsdom. I'd suggest you create an issue in their repository instead.

Same error message with the op and almost everyone here (except I am not
using enzyme). If not related I should open an issue at jest. Sorry about
that.

On 30 Jul 2018 21:32, "Zirro" notifications@github.com wrote:

@khmy2010 https://github.com/khmy2010 Your code and questions involve
Jest more than they do jsdom. I'd suggest you create an issue in their
repository https://github.com/facebook/jest instead.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/jsdom/jsdom/issues/2304#issuecomment-408864079, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AVrB3uOLy7l4JKbStKWPtGi0oHfAaQbYks5uLwrpgaJpZM4Vi8gP
.

If you're creating your jsdom instance, you can pass a custom url as a second parameter:

const url = 'http://localhost';
const jsdom = new JSDOM('<!doctype html><html><body></body></html>, { url });

This might be useful if you're using Enzyme + Mocha @srodrigo

This broke existing infrastructure. This should be major release 12.0.0 and not minor release 11.12.0. Minor releases should not break existing code.

Domenic explains in this comment (if it doesn't show when clicking the link, scroll up and load the hidden comments) why this was not seen as a breaking change. The code from dependent packages that does things we've discouraged for years will hopefully be fixed by their maintainers soon.

Additionally to the fix above, I had to add this to the root of test config: --env=jsdom

Adding the following to my jest.config.js

testURL: 'http://localhost',

solved the problem. Thanks!

(Jest maintainer here.) Do you think it makes sense from Jest's perspective to change from the default about:blank to e.g. localhost?

Not sure if I am smart enough to voice my opinion about this, but from the developer point of view I would say localhost makes more sense, about:blank is a case that is never a reality. We are testing our apps that virtually never have about:blank url

The Jest team has added a smarter default value for testURL: https://github.com/facebook/jest/pull/6792

@SimenB I would say that's a good idea.

Getting the same error as the OP, but in my situation it has been causing a LOT of problems. We lock down the versions of packages we use, and we can only change them at the beginning of a release cycle. We are currently toward the end of the release cycle, so our developer environments have the versions of all the packages and their dependencies as of about a month ago, but when we had the build server create a build, it grabbed the current version of all packages. So while all tests pass locally, they all fail on the build server.

We use the "setupTestFrameworkScriptFile" option in the jest config file to do some setup, including polyfills for (among other things) localStorage and sessionStorage (since we use both in our app). It's basically window.localStorage = window.localStorage || { ... }, and the same for sessionStorage, where ... is a bunch of mock functions. Now none of that is working, even if I change it to always override the default (window.localStorage = { ... }).

Additionally, we have unit tests that specifically test for things like sessionStorage.getItem being called, but after setting "testURL" to "http://localhost", as recommended above to resolve the localStorage errors, those all fail. Even though we have window.sessionStorage.getItem = jest.fn();, doing a subsequent expect(window.sesssionStorage.getItem).toHaveBeenCalled() fails saying that it's not a mock function.

While I agree that the addition of a feature is a minor version change, and not a breaking change, when it's something that is a standard part of browsers, and the new implementation seemingly can't be overridden or mocked, that _is_ a breaking change.

The only solution I have found to my problem is to add jsdom to my package.json and specify a version of 11.11.0. This is not ideal, and will cause additional work later when we upgrade packages again, but at lease for now it gets us unblocked.

If you have code, e.g. mocking code, which works in browsers but not in jsdom, file a new issue following the issue template and we can investigate. To my knowledge localStorage in jsdom is exactly as mockable as it is in browsers.

I do suggest not running different versions on your build server as your developers are running.

"I do suggest not running different versions on your build server as your developers are running."

While that sounds good in theory, unless every package developer stops using "^" when specifying dependency versions, or we commit the thousands of folders in our node_modules folder, that's never going to happen. From one developer machine to the next there is likely going to be slight differences in some of the dependencies a level or two down. All I can do is specify the exact versions of the packages that are direct dependencies of our application.

Fully agree with @mrobrian about "^". Craziest thing ever. npm should remove this way to describe dependencies.

@domenic workaround with packge-lock.json and yarn.lock is broken by design. Maybe it helps with versions, but then create another problems with code-review and merging and often you need to recreate this file by deletion. It's OK if dependencies updated seldom, but we update very often.

Therefore our .npmrc:

save-exact = true
package-lock = false

it helped me adding this new lines to the package.json:

"jest": {
    "verbose": true,
    "testURL": "http://localhost/"
  },

If you are using jsdom, make sure you include url

const dom = new JSDOM(``, {
url: "https://example.org/",
});

Would the jest devs like to comment on why "testEnvironment": "node" is now required for CLI/node projects (to avoid the localStorage error) when previously it was not required? Is it a bug?

If this is somehow by design, it really needs a better error message! I'm getting this error in both of my projects that use Jest - two simple non-browser projects with few dependencies. They certainly don't use jsdom/localStorage.

This isn't the right place to ask questions for Jest developers - jsdom is a separate project. That said, the comments above already contain the answers to your questions.

package.json

   ...
  "jest": {
    "testEnvironment": "node",
    "roots": [
      "test/javascript"
    ]
  },

It works for me.

@p8ul was right, don't forget to specify "http://localhost" (default set URL since Jest 23.5.0, see #6792) :

const dom = new JSDOM(``, {
url: "http://localhost",
});

The whole works for me.
Not even need to add:

"testEnvironment": "node"

jest 23.5.0 now includes a fix for this, so the workarounds should no longer be needed:

https://github.com/facebook/jest/issues/6766#issuecomment-412516712

Similar to @mica16 https://github.com/jsdom/jsdom/issues/2304#issuecomment-412663502

const dom = new JSDOM(``, {
  url: "http://localhost",
});

Was the only change we needed to make to avoid this error.

We are using mocha/enzyme. No jest included in our test suite.

Setting --env node on the command line works too.

@gokulkrishh I set it to http://localhost/ but any valid URL seems to work.

"location.href" would be nice then. :)

@domenic I recommend updating your comment at the top to say that the Jest maintainers fixed this bug in version 23.5.0: https://github.com/facebook/jest/issues/6766#issuecomment-412516712

I commit setting the testURL for jest-config to http://localhost workes.

add key in config file and try agains "jest": { "testURL": "http://localhost%26quot%3B/ },
use ip-port instead of localhost
This might resolve the issue

I encountered the issue after spotting some security issues when running npm audit. After fixing them using npm audit fix I encountered this issue.

Since this has been fixed in Jest for some time, let's try closing it and seeing if we end up with lots of duplicates, or if things have settled down enough.

"jest": {
"verbose": true,
"testURL": "http://localhost/"
}
Add this code snippet in package.json file.
It worked for me.

you can also add this to the affected test if jsdom is not needed there

``` javascript 1.6
/**

  • @jest-environment node
    */

it('my test', () => {
expect(2 + 2).toBe(4);
});
```

I tested both options:

1) Add this to the top of a test file:

/**
 * @jest-environment node
 */

2) Add this stanza to the package.json:

"jest": {
    "testURL": "http://localhost/"
  }

Both options worked.

I had it work by adding in the package.json file:

"jest": {
    "verbose": true,
    "testURL": "http://localhost/"
  }

@gokulkrishh I set it to http://localhost/ but any valid URL seems to work.

I am a newbie. Can you tell me exactly where in the Jest.config.js that I set the testURL?

@haiphu Anywhere in jest.config.js like below

{
"testURL": "http://localhost/"

// Your other config
}

I added the below to my package.json and it works fine now :)

  "jest": {
    "testURL": "http://localhost/"
  },

Not sure why, but my error was caused by having different typescript versions.

I have a mono-repo setup with yarn workspaces + lerna. All packages had typescript@^3.3.3 in their package.json. I added a new package and installed the latest typescript@^3.5.3. When I ran tests in the existing packages, I got this error.

If I move all packages to the same version - either typescript@^3.3.3 or typescript@^3.5.3, the error goes away. I didn't have to fiddle with the testURL.

@tylerreece22 @gokulkrishh Worked for me! 😄

For those wondering what jest's testURL configuration directive does, see https://jestjs.io/docs/en/configuration#testurl-string

For those who sets the options on jsdom directly (when using Mocha for example). Put this in your setup.js:

let jsdom = require('jsdom-global')(
    undefined,
    {
        url: "http://localhost"
    }
);

Strangely.. I have no jsdom being used.. I'm using jest for testing some node only packages yet this error started blocking CI when upgrading jest versions to latest within ^11 range.. I'm sure other folks are seeing similar issues.. none of the so far recommended changes seem to fix it

if you're not using jsdom then you'll want to set the jest config property testEnvironment to node. (no need to touch testURL)
https://jestjs.io/docs/en/configuration#testenvironment-string

For anyone looking for the actual fix, it's new JSDOM('', { url: 'https://localhost' })

Try using this in your package.json file

"jest": {
"verbose": true,
"testURL": "http://localhost/"
}

Why setting url doesn't work for me... i am using react-native.. is there anything else that i am missing?

Why setting url doesn't work for me... i am using react-native.. is there anything else that i am missing?

We had the same issue. In our application we had this code

const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>');

Turns out we had to add the URL to the JSDOM constructor

const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>', {
  url: 'http://localhost/',
});

That fixed the issue.

Why setting url doesn't work for me... i am using react-native.. is there anything else that i am missing?

We had the same issue. In our application we had this code

const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>');

Turns out we had to add the URL to the JSDOM constructor

const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>', {
  url: 'http://localhost/',
});

That fixed the issue.

This worked for me thank you so much! putting the url in the jest config doesnt seem to work with react-native. putting the url in de jsdom constructor did the trick.

Update jest from 22 to 26 fixed problem.

simply use the latest version of jest. currently I'm using 26.5.0 in year 2020 and my problem is solved

Try using this in your package.json file

"jest": {
"verbose": true,
"testURL": "http://localhost/"
}

That testURL is the default URL, which means it doesn't solve the issue, at least not with Jest 26.x. I had to do what @zuccha did to get around it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jhegedus42 picture jhegedus42  ·  4Comments

kilianc picture kilianc  ·  4Comments

domenic picture domenic  ·  3Comments

kentmw picture kentmw  ·  3Comments

cg433n picture cg433n  ·  3Comments