Access-Control-Allow-Origin
とかでエラーになるあれです。
React+Node.js+axios+jsdomでRequest投げた場合に発生しました。
ぱっと見の原因は、jsdomの作成時にURLが設定されていない場合、originに文字列で "null"
が入ってしまい、jsdomのオリジンチェックで、文字列じゃない null
で (null !== "null")
という感じで比較してエラー判定しているからだと思いました。
そして、文字列の "null"
が入るのはバグじゃないか?と調べてみたのですが、npmモジュールのwhatwg-urlのこの辺で、文字列nullが正しい的な記述がある。
古川さんのissueでも正しいように見えると答えているので、whatwgの仕様が気になったのですが、ちょうどNode.jsが実装したみたいな記事があったため試してみた。
(原典に当たれとか言われそうですが、今回やりたいのはCORS問題の解決なのでそこは捨てました。)
確認したいのは、originが存在しない場合に文字列nullが返ってくるのかどうか?です。ローカルのファイルアクセス用URLを使えばoriginが設定されていない状況になるので、それで試しました。
node -e 'const URL = require("url").URL; console.log(new URL("file://aaa.log"))'
URL {
href: 'file://aaa.log/',
origin: 'null',
protocol: 'file:',
username: '',
password: '',
host: 'aaa.log',
hostname: 'aaa.log',
port: '',
pathname: '/',
search: '',
searchParams: URLSearchParams {},
hash: '' }
結果、文字列"null"
が帰ってくるという。(意外)
そもそもaxiosでエラーを投げるのは、CORS違反だからで、それ自体は正しい挙動だと思い直しました。
そして、originが設定されていないという状況がイレギュラーなんじゃないか?(Chromeの別タブ状態でXHRリクエスト投げた場合でも、originはgoogleのやつが入っていたりする。)
ということで、jsdomの生成時にURLを設定して試してみました。
// setup用のjs
require('babel-register')()
var JSDOM = require('jsdom').JSDOM
var dom = new JSDOM(`...`, {
url: `localhost:3000` // ←ここ!!
})
global.window = dom.window
global.document = dom.window.document
var exposedProperties = ['window', 'navigator', 'document']
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property)
global[property] = document.defaultView[property]
}
})
global.navigator = {
userAgent: 'node.js'
}
結果おk。
これで localhost:8080で立ち上がっているWebサーバにリクエストを投げてもエラーは発生しなかったです。ちなみにlocalhost指定だと駄目で、localhost:3000とかポートまで指定するとうまくいくという謎現象もありました。。。
また、global.window.document.origin
の値が使われていたので、そいつを上書きすれば解決か?とか思ったのですが、set出来ない書き方をしていたため、変更できずでした。(個人的に分かりづらいので、この書き方は嫌いです。)
// set出来なくする書き方。。
// react-tdd/node_modules/jsdom/lib/jsdom/living/generated/Document.js#L798
Object.defineProperty(Document.prototype, "origin", {
get() {
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
return this[impl]["origin"];
},
enumerable: true,
configurable: true
});
npm iしたあとに作成されるgeneratedフォルダ配下なので、npm iしたあとの中身で見ないと見れないかもです(jsdom@11.4.0)
githubのリンクを貼れないからそれもやだなぁとか、結構調べるのに骨が折れたので共有でした。