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

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


About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 280
  • Comments: 90 (15 by maintainers)

Commits related to this issue

Most upvoted comments

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

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.

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.

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?

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

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

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.

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.

I still had this issue with “jest”: “^29.0.3” and even with localhost url setup in the config. Any idea? thanks

i got this error and it was because i had jest 28 and jest-environment-jsdom 26. upgrading the latter to v28 and it was fixed

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.

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

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

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

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

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

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

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

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

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

I tested both options:

  1. Add this to the top of a test file:
/**
 * @jest-environment node
 */
  1. Add this stanza to the package.json:
"jest": {
    "testURL": "http://localhost/"
  }

Both options worked.

Try using this in your package.json file

“jest”: { “verbose”: true, “testURL”: “http://localhost/” }

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.

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 😦

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

Hi all. I’m actually starting to suffer this problem after upgrading to latest Jest 28.0.0-alpha.7. Rolling back to current 27.5.1 works fine.

I’ve noticed that on Jest 28.x , parameters to the testEnvironment have to be passed on a testEnvironmentOptions object.

Particularly, the old testURL: 'http://localhost' have to be passed as testEnvironmentOptions: { url: 'http://localhost' } But no matter using the old testURL or the new testEnvironmentOptions.url approach, I’m always getting the famous error:

SecurityError: localStorage is not available for opaque origins

Interestingly enough, it also seems Jest 28.x is setting jsdom’s url to http://localhost by default, so the configuration could be ommited entirely as well.

I’m using testEnvironment: 'jsdom', because I’m testing some React (non-native) application.

Any ideas ? Am I doing something wrong ? Does version 28 have issues configuring the test URL for jsdom environments ? (switched to version 28, because I want to give a try at the new resolver supporting the package.json exports)

jsdom library used: tried both 18.1.1 and 19.0.0

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

@haiphu Anywhere in jest.config.js like below

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

// Your other config
}

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”

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.

If you are using jsdom, make sure you include url

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

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

/**
 * @jest-environment node
 */

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

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.

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

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

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

@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"

package.json

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

It works for me.

@ben-mckernan solution fixed it. Thanks!

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

Quoting from the summary up near the top, https://github.com/jsdom/jsdom/issues/2304#issuecomment-408320484:

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.

jest-runtime is accessing properties in a loop, including localStorage, at https://github.com/facebook/jest/blob/3390ec4ef6a1b93afa816655f5c1f0605066b15a/packages/jest-runtime/src/index.ts#L1165. I’ve just opened https://github.com/facebook/jest/issues/12813 for that.

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

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

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

  1. yarn add --dev jsdom-global 2.jest.setup.js: require('jsdom-global')()

Not using Jest - fix for me using browser-env with Mocha was to pass options as documented:

const browserEnv = require('browser-env');

browserEnv({ url: 'http://localhost' });

Hopefully this saves people in the same boat as me (seemingly the road less travelled) some time!

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.

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

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

@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

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

“location.href” would be nice then. 😃

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

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.

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.

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

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

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.

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

@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).