Jsdom: 开玩笑:SecurityError:localStorage 不适用于不透明来源

创建于 2018-07-27  ·  80评论  ·  资料来源: jsdom/jsdom

当我用我的测试用例运行jest时。 当我升级包时,它显示以下错误。 在我的测试用例中,没有使用 localStorage。 我该如何解决这个问题?

 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

最有用的评论

@gokulkrishh我将它设置为http://localhost/但任何有效的 URL 似乎都有效。

所有80条评论

以下讨论摘要:

  • 解决方法是为您的 jsdom 设置一个 URL,这可能需要通过您的测试环境配置来完成。 “ about:blank ”的默认 URL 在尝试访问 localStorage 时会导致错误。
  • 根本原因通常是库循环遍历所有 jsdom 属性并将它们添加到全局; 即使您从未在测试中使用 localStorage,您所依赖的某些库或测试框架也会以这种方式“使用”它。 请注意,这种循环和复制技术明确不受支持,因此执行它的代码可能会在次要版本中中断也就不足为奇了。

关于这是否是“重大更改”的问题的以下评论的转录,此后已被 GitHub 的“查看更多评论”功能隐藏:

谢谢,但截至目前,我认为这不是最好的前进道路。 对于 jsdom 来说,实现新的 Web 平台功能并不是一个重大变化; 那是次要版本。

依赖项,甚至是广泛使用的依赖项,可能会编写不幸的代码来检测新功能并在出现异常时抛出异常。 我希望我们都能同意,即使存在这样的依赖,jsdom 也不应该永远放弃在次要版本中添加新功能。

这里的情况并没有那么剧烈,但似乎是相似的。 我还没有看到任何人追踪到有问题的 Jest 代码,所以很难确切地说,但看起来他们正在执行一个被 jsdom 开发的正常过程明确破坏的操作,并且在网络中不起作用实现 localStorage 的浏览器。

我意识到这对你们中的那些人来说是一个艰难的情况,这不是你自己的过错,而是因为你的直接依赖是如何(ab)使用你的间接依赖的不幸互动。 但我希望这可以在正确的级别解决,修复 Jest 的错误代码,而不是由于某个依赖的错误而从所有 jsdom 用户中删除有用的功能。

对 OP 的原始回复,用于上下文:

您似乎没有在测试中设置 URL,但是您,或者 Jest,正在访问window.localStorage 。 Jest 维护者可能知道更多关于最佳修复的信息,但我听说有一种方法可以在 Jest 测试中设置 URL?

这是最近出现的一个问题,似乎与11.12.0 。 将jestenzyme一起使用时,我遇到了这些相同的错误。

是的,我从11.11.0升级到11.12.0后开始遇到这个问题。 在 jest 配置中设置testURL解决了这个问题。

@ben-mckernan 嗨,您提供的解决此问题的 URL 是什么?

@gokulkrishh我将它设置为http://localhost/但任何有效的 URL 似乎都有效。

我想这可能是 Enzyme 特有的,因为 Enzyme 做了 jsdom 文档明确警告不要做的事情? 不幸的是,在次要版本中爆发并不奇怪:(

这对我来说也坏了:(

@ben-mckernan 谢谢👍

(“+1”评论将被标记为垃圾邮件;向问题线程中的每个人发送电子邮件无济于事。)

道歉。 只是想帮忙。

可以确认将testURL到 jestConfig,因为@ben-mckernan 建议确实修复了它。

如果这有助于证实您的直觉,我们也在使用 Enzyme。

对于电子应用程序测试,我只是将其设置为“文件:/”,这也有效。

@miamollie我按照@ben-mckernan 的建议添加了testURL (使用 Jest + Enzyme,不确定它的酶是否相关。错误来自使用 jsdom 的jest-environment-jsdom )。 因此,我的其他一些测试文件失败了。 仅供参考。 看看它是否适合你。 您的测试用例可能与我的不同(TestURL 可能适合您)。

@domenic我只是在开玩笑。 所以我不确定这是酶的问题。

@golkrishh是的,同样,它停止了 localStorage 安全错误,但使其他一些测试失败。

@ben-mckernan 解决方案修复了它。 谢谢!

@ben-mckernan 我在角度设置中使用 jest(使用 jest-preset-angular),同样的错误,同样的解决方案。 所以这不是酶的问题。

似乎 Jest 需要更改testURL的默认值才能缓解这种情况(目前是about:blank )。

@DcsMarcRemolt我只是在调试这个问题。 Jest在它的 package.json 中使用了一个名为jest-environment-jsdom的依赖模块 --> "jsdom": "^11.5.1" caret(^)因为这个 npm 已经将 jsdom 安装为11.12.0 (这是今天发布的新版本)。 所以它对大多数用户来说都失败了。 问题已经在玩笑中创建并链接到这里。 当心它。

我将重新打开此问题以提高其知名度。 有关解决方案,请参阅 Domenic的第一条评论

将以下内容添加到 package.json 中的 jest 配置中:
"testEnvironment": "node"
为我解决了这个问题。
感谢@blu3printchris帮助解决了这个问题。

自此更新以来,我无法再覆盖和模拟 localStorage 上的 JSDOM 实现。

由于这对很多人来说是一个无意的破坏性更改,因此最好的损坏控制选项可能是:

  • 还原此更改。
  • 发布版本11.12.1与恢复的更改以恢复预期的行为。
  • 拉取或弃用版本11.12.0并发出警告。
  • 如果需要按原样使用新行为,请释放12.0.0以指示存在重大更改。

谢谢,但截至目前,我认为这不是最好的前进道路。 对于 jsdom 来说,实现新的 Web 平台功能并不是一个重大变化; 那是次要版本。

依赖项,甚至是广泛使用的依赖项,可能会编写不幸的代码来检测新功能并在出现异常时抛出异常。 我希望我们都能同意,即使存在这样的依赖,jsdom 也不应该永远放弃在次要版本中添加新功能。

这里的情况并没有那么剧烈,但似乎是相似的。 我还没有看到任何人追踪到有问题的 Jest 代码,所以很难确切地说,但看起来他们正在执行一个被 jsdom 开发的正常过程明确破坏的操作,并且在网络中不起作用实现 localStorage 的浏览器。

我意识到这对你们中的那些人来说是一个艰难的情况,这不是你自己的过错,而是因为你的直接依赖是如何(ab)使用你的间接依赖的不幸互动。 但我希望这可以在正确的级别解决,修复 Jest 的错误代码,而不是由于某个依赖的错误而从所有 jsdom 用户中删除有用的功能。

谢谢澄清。 我同意,如果问题是由于如上所述以不受支持的方式使用 jsdom,那么从技术上讲这不是 semver 违规,jest 应该发布补丁。

我的解决方法是,在循环所有jsdom属性并将它们添加到全局时使用黑名单


编码

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;


不要将 jsdom 全局变量填充到 Node 全局变量中

好吧,我暂时通过。 我需要在 jsdom 和真实浏览器中运行测试。 这是我能想到的最简单的方法,多年来一直有效。 我在建议的替代方案中没有看到相同的潜力。
使用jsdom-global也可以。

好吧,我暂时通过。 我需要在 jsdom 和真实浏览器中运行测试。

如果您的测试可以在“真正的浏览器”中运行,它们也可以在 jsdom 中以相同的方式运行 - 只需提供相同的 HTML。 与在浏览器中运行测试的方式相比,通过分配给全局变量,您会引入额外的复杂性和差异。

仅供参考,在将jsdom升级到11.12.0后,我遇到了与 Mocha 相同的问题,而不是 Jest。

嗨,我只是想了解为什么首先引入这些更改。

我的用例只是断言一个函数以确保我正确编写它,它很简单:

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

然而,测试结果似乎是错误消息,我刚刚用 Redux 库和类似的东西在 React 上做了一些大的应用程序,而真正的事情是我只是简单地测试了一个简单的函数

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

顺便说一句, testURLtestEnvironment "hack" 对我不起作用。 这是我的 package.json:

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

所以我的问题是为什么要引入破坏性更改的所有麻烦,而有时我们只想要一个“有效”的测试运行器

@khmy2010你的代码和问题更多地涉及 Jest,而不是 jsdom。 我建议你在他们的存储库中创建一个问题。

操作员和这里几乎每个人的错误消息都相同(除了我不是
使用酶)。 如果不相关,我应该开玩笑地打开一个问题。 为......感到抱歉
那。

2018 年 7 月 30 日 21:32,“Zirro”通知@github.com 写道:

@khmy2010 https://github.com/khmy2010你的代码和问题涉及
开玩笑比他们做 jsdom 多。 我建议你在他们的
存储库https://github.com/facebook/jest代替。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/jsdom/jsdom/issues/2304#issuecomment-408864079或静音
线程
https://github.com/notifications/unsubscribe-auth/AVrB3uOLy7l4JKbStKWPtGi0oHfAaQbYks5uLwrpgaJpZM4Vi8gP
.

如果您正在创建jsdom实例,则可以将自定义 url 作为第二个参数传递:

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

如果您使用的是 Enzyme + Mocha @srodrigo,这可能很有用

这破坏了现有的基础设施。 这应该是主要版本 12.0.0 而不是次要版本 11.12.0。 次要版本不应破坏现有代码。

Domenic在此评论中解释执行我们多年来一直不鼓励的

除了上面的修复之外,我还必须将其添加到测试配置的根目录中: --env=jsdom

将以下内容添加到我的 jest.config.js

testURL: 'http://localhost',

解决了这个问题。 谢谢!

(这里是 Jest 维护者。)从 Jest 的角度来看,从默认的about:blank更改为例如localhost是否有意义?

不确定我是否足够聪明来表达我对此的看法,但从开发人员的角度来看,我会说 localhost 更有意义, about:blank是一个永远不会成为现实的案例。 我们正在测试几乎从来没有about:blank url 的应用

Jest 团队为testURL添加了更智能的默认值: https :

@SimenB我会说这是个好主意。

得到与 OP 相同的错误,但在我的情况下,它导致了很多问题。 我们锁定了我们使用的包的版本,我们只能在发布周期开始时更改它们。 我们目前正接近发布周期的尾声,所以我们的开发人员环境拥有大约一个月前所有包及其依赖项的版本,但是当我们让构建服务器创建构建时,它抓取了所有包的当前版本包。 因此,虽然所有测试都在本地通过,但它们在构建服务器上都失败了。

我们使用 jest 配置文件中的“setupTestFrameworkScriptFile”选项进行一些设置,包括(除其他外)localStorage 和 sessionStorage(因为我们在我们的应用程序中同时使用这两者)的 polyfill。 它基本上是window.localStorage = window.localStorage || { ... } ,对于 sessionStorage 也是如此,其中...是一堆模拟函数。 现在这些都不起作用,即使我将其更改为始终覆盖默认值( window.localStorage = { ... } )。

此外,我们有单元测试专门测试诸如sessionStorage.getItem被调用之类的东西,但是在将“testURL”设置为“ http://localhost ”之后,按照上面的建议解决 localStorage 错误,这些都失败了。 即使我们有window.sessionStorage.getItem = jest.fn(); ,执行后续的expect(window.sesssionStorage.getItem).toHaveBeenCalled()也会失败,说明它不是模拟函数。

虽然我同意添加一个特性是一个小的版本更改,而不是一个破坏性的更改,但当它是浏览器的标准部分,并且新实现似乎不能被覆盖或嘲笑时,_is_一个破坏性更改.

我找到的唯一解决方案是将 jsdom 添加到我的 package.json 并指定 11.11.0 的版本。 这并不理想,以后我们再次升级软件包时会导致额外的工作,但至少现在它可以让我们畅通无阻。

如果您有代码,例如模拟代码,它在浏览器中有效但在 jsdom 中无效,请按照问题模板提交新问题,我们可以进行调查。 据我所知,jsdom 中的 localStorage 与浏览器中的一样可模拟。

我建议不要在您的构建服务器上运行不同的版本,因为您的开发人员正在运行。

“我建议不要在您的构建服务器上运行不同的版本,因为您的开发人员正在运行。”

虽然这在理论上听起来不错,但除非每个包开发人员在指定依赖项版本时停止使用“^”,或者我们在 node_modules 文件夹中提交了数千个文件夹,否则这永远不会发生。 从一台开发人员机器到另一台开发人员机器,某些依赖项可能会在一两个级别上略有不同。 我所能做的就是指定作为我们应用程序直接依赖项的包的确切版本。

完全同意@mrobrian关于“^”的看法。 有史以来最疯狂的事情。 npm 应该删除这种描述依赖关系的方式。

带有packge-lock.jsonyarn.lock @domenic解决方法被设计破坏了。 也许它对版本有帮助,但随后会在代码审查和合并方面产生另一个问题,并且通常您需要通过删除来重新创建此文件。 如果依赖项很少更新也没关系,但是我们经常更新。

因此我们的.npmrc

save-exact = true
package-lock = false

它帮助我将这些新行添加到 package.json:

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

如果您使用的是 jsdom,请确保包含 url

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

开玩笑的开发人员是否愿意评论为什么 CLI/节点项目现在需要"testEnvironment": "node" (以避免localStorage错误),而以前不需要它? 这是一个错误吗?

如果这是设计使然,它确实需要更好的错误消息! 我在使用 Jest 的两个项目中都遇到了这个错误——两个简单的非浏览器项目,几乎没有依赖项。 他们当然不使用 jsdom/localStorage。

这不是向 Jest 开发人员提问的正确地方 - jsdom 是一个单独的项目。 也就是说,上面的评论已经包含了您问题的答案。

包.json

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

这个对我有用。

@p8ul是对的,不要忘记指定“ http://localhost ”(自 Jest 23.5.0 以来的默认设置 URL,参见 #6792):

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

整个对我有用。
甚至不需要补充:

"testEnvironment": "node"

jest 23.5.0 现在包含对此的修复,因此不再需要解决方法:

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

类似于@mica16 https://github.com/jsdom/jsdom/issues/2304#issuecomment -412663502

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

是我们需要进行的唯一更改以避免此错误。

我们正在使用摩卡咖啡/酶。 我们的测试套件中不包含任何玩笑。

在命令行上设置--env node也有效。

@gokulkrishh我将它设置为http://localhost/但任何有效的 URL 似乎都有效。

“location.href”会很好。 :)

@domenic我建议在顶部更新您的评论,说 Jest 维护者在 23.5.0 版中修复了这个错误: https :

我承诺将 jest-config 的testURLhttp://localhost

在配置文件中添加密钥并重试 "jest": { "testURL": " http://localhost%26quot%3B/ },
使用 ip-port 而不是 localhost
这可能会解决问题

我在运行npm audit时发现一些安全问题后遇到了这个问题。 使用npm audit fix修复它们后,我遇到了这个问题。

由于这已经在 J​​est 中修复了一段时间,让我们尝试关闭它,看看我们是否最终有很多重复,或者事情是否已经足够稳定。

“开玩笑”:{
“详细”:真实,
"testURL": " http://localhost/ "
}
在 package.json 文件中添加此代码片段。
它对我有用。

如果那里不需要 jsdom,您也可以将其添加到受影响的测试中

``` javascript 1.6
/**

  • @jest-environment 节点
    */

it('我的测试', () => {
期望(2 + 2).toBe(4);
});
``

我测试了两个选项:

1) 将此添加到测试文件的顶部:

/**
 * @jest-environment node
 */

2) 将此节添加到 package.json:

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

两种选择都有效。

我通过在 package.json 文件中添加它来工作:

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

@gokulkrishh我将它设置为http://localhost/但任何有效的 URL 似乎都有效。

我是新手。 你能告诉我我在 Jest.config.js 中设置 testURL 的确切位置吗?

@haiphujest.config.js任何地方,如下所示

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

// Your other config
}

我将以下内容添加到我的 package.json 中,现在工作正常:)

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

不知道为什么,但我的错误是由不同的打字稿版本引起的。

我有一个带有纱线工作区 + lerna 的单一回购设置。 所有包的 package.json 中都有typescript@^3.3.3 。 我添加了一个新包并安装了最新的typescript@^3.5.3 。 当我在现有包中运行测试时,出现此错误。

如果我将所有软件包移动到同一版本 - typescript@^3.3.3typescript@^3.5.3 ,错误就会消失。 我不必摆弄testURL

@tylerreece22 @gokulkrishh为我工作! 😄

对于那些想知道 jest 的testURL配置指令的作用的人,请参阅https://jestjs.io/docs/en/configuration#testurl -string

对于那些直接在 jsdom 上设置选项的人(例如使用 Mocha 时)。 把它放在你的 setup.js 中:

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

奇怪的是.. 我没有使用 jsdom.. 我正在使用 jest 来测试一些仅限节点的包,但是当将 jest 版本升级到^11范围内的最新版本时,这个错误开始阻塞 CI.. 我相信其他人看到类似的问题.. 到目前为止推荐的更改似乎都没有解决它

如果您不使用jsdom那么您需要将 jest 配置属性testEnvironmentnode 。 (无需触摸testURL
https://jestjs.io/docs/en/configuration#testenvironment -string

对于任何寻找实际修复的人来说,它是new JSDOM('', { url: 'https://localhost' })

尝试在 package.json 文件中使用它

“开玩笑”:{
“详细”:真实,
"testURL": " http://localhost/ "
}

为什么设置 url 对我不起作用...我正在使用 react-native .. 还有什么我遗漏的吗?

为什么设置 url 对我不起作用...我正在使用 react-native .. 还有什么我遗漏的吗?

我们有同样的问题。 在我们的应用程序中,我们有这个代码

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

原来我们必须将 URL 添加到 JSDOM 构造函数

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

这解决了问题。

为什么设置 url 对我不起作用...我正在使用 react-native .. 还有什么我遗漏的吗?

我们有同样的问题。 在我们的应用程序中,我们有这个代码

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

原来我们必须将 URL 添加到 JSDOM 构造函数

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

这解决了问题。

这对我有用,非常感谢! 将 url 放在 jest 配置中似乎不适用于 react-native。 将 url 放在 de jsdom 构造函数中就可以了。

更新笑话从 22 到 26 固定问题。

只需使用最新版本的 jest。 目前我在 2020 年使用 26.5.0,我的问题已解决

尝试在 package.json 文件中使用它

“开玩笑”:{
“详细”:真实,
"testURL": " http://localhost/ "
}

testURL是默认 URL,这意味着它不能解决问题,至少对于 Jest 26.x 。 我不得不做@zuccha所做的事情来解决它。

此页面是否有帮助?
0 / 5 - 0 等级