正直これで良いのかわからないけど、とりえあえず手元の clientWidth の値を使う処理の試験は走るようになった。

メソッド系

しれっとstub的な関数で上書きする。

const { JSDOM } = require('jsdom');

const dom = new JSDOM();
const el = dom.window.document.createElement('div');

console.log(el.getBoundingClientRect()); // => { width: 0, top: 0, ... }

// こうする
el.getBoundingClientRect = () => ({
  width: 200,
});
console.log(el.getBoundingClientRect()); // => { width: 200 }

試験を通すだけなので不要な値まで全部書く必要はない。

本当なら sinon.stub() ↓の方が丁寧な感じがするけど、まあ使い捨てるインスタンスだし、良いでしょ。

sinon.stub(el, 'getBoundingClientRect").returns({ width: 200 });

プロパティ系

clientWidth や clientHeight は単純に代入してもだめなので、 Object.defineProerty() で上書きする。

const { JSDOM } = require("jsdom");

const dom = new JSDOM();
const el = dom.window.document.createElement("div");

console.log(el.clientWidth); // => 0

// こうする
Object.defineProperty(el, 'clientWidth', { value: 100 });
console.log(el.clientWidth); // => 100

JSDomはレイアウト計算をサポートしない

とREADMEにあるので、バグじゃないです。

Beyond just features that we haven’t gotten to yet, there are two major features that are currently outside the scope of jsdom. These are:

  • Navigation: the ability to change the global object, and all other objects, when clicking a link or assigning location.href or similar.
  • Layout: the ability to calculate where elements will be visually laid out as a result of CSS, which impacts methods like getBoundingClientRects() or properties like offsetTop.

まだやっていないだけの機能もありますが、今のところjsdomのスコープ外である主要な機能が2つあります。

  • ナビゲーション: リンクをクリックしたり location.href を設定する等した際に、 グローバルオブジェクト他を変更する機能
  • レイアウト: CSSの結果として要素が視覚的に配置された結果を計算する機能。 getBoundingClientRects() のようなメソッドや、 offsetTop といったプロパティへ影響するもの

その他

ちなみに本物のDOMでも視覚的に配置されるまで、例えば document.body.appendChild(el) とかするまでは値が 0 だったりします。

参考