かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript, React   なスペースのあるHTMLのテスト

HTMLで時々見かけるやつ。

 

  Non-Breaking Space 文中で折り返しさせたくない語句を繋ぐ時に使う特殊文字

JavaScript で   が含まれるHTMLを扱う時のトラップ

コンポーネント時代の昨今だとJSXとかJavaScript内にHTMLを書いたりすることもあるので、そこで   が使われてるのを見かけたりすることもあります。
例えばこんなん。

// IconButton.js
function IconButtton(props) {
  return (
    <button className="iconBtn" onClick={props.onClick}>
      <i className="icon"></i>
      &nbsp;{props.btnLabel}
    </button>
  );
}

アイコンとラベルの間にスペース入れたいけど、HTMLだと前にある半角スペース消えちゃうから&nbsp;でスペース作ってるとか。(.icon に margin 設定して欲しい
こんなふうに &nbsp; が含まれているコンポーネントをテストしようとした時に問題が発生することがあります。

e.g. jest + enzyme で IconButtton コンポーネントをテスト

ラベルの文字をテストしようと思うとこんな感じの書き方になります

// __test__/IconButton.test.js
describe("IconButton", () => {
  it("Icon Button display label by props", () => {
    const testMocFunc = jest.fn();
    const labelText = "BUTTON";
    const iconButton = shallow(<IconButton
      onClick={testMocFunc}
      btnLabel={labelText}
    />);

    expect(iconButton.text()).toEqual(" BUTTON");
  });
});

.toEqual(" BUTTON") このスペースの部分が &nbsp; のスペースならテストが通るのですが、通常のスペースだとテストは次のような感じで落ちます。 👇

 FAIL  src/__tests__/IconButton.test.js
  ● IconButton › Icon Button display label by props

    expect(received).toEqual(expected) // deep equality

    Expected: " BUTTON"
    Received: " BUTTON"

JSの innerTexttextContent で取得されるテキストノードは &nbsp; なスペースも通常のスペースのように表示されてしまうので、違いが表示わかるように表示されるエディタなどでないと判別が付きづらいという罠があります。

解決方法

1. \u00A0 を使う

テキストノードでの &nbsp;\u00A0 なので、&nbsp; なスペースの部分を \u00A0 と記述すればマッチします

expect(iconButton.text()).toEqual("\u00A0BUTTON");

完全に決め撃ちで事前に &nbsp; が使われている事を知ってないと難しそううです。

2. &nbsp; なスペースを通常のスペースに置き換えてしまう

JavaScript のテキストノードの場合 &nbsp; なスペースも \s でマッチするので通常のスペースに置き換えてしまえばスッキリします。

expect(iconButton.text().replace(/\s/g, ' ')).toEqual(" BUTTON");

この方法なら \u00A0 で書く方法と異なり汎用性がありそうです。
ただ元のコンテンツ内容を変換してテストしているので若干微妙な気もしています。。。

3. &nbsp; なスペースを trim してしまう

JavaScript のテキストノードの場合 &nbsp; なスペースは trim() で除去できるので、文頭/文末にある場合は除去してしまうのも良さそうです。

expect(iconButton.text().trim()).toEqual("BUTTON");

この方法は文頭/文末に &nbsp; なスペースが有る場合に限られますし、replace する場合と同じく元のコンテンツを変換してるので微妙かもという問題は残ります。

4. expect.stringContaining() を使う

expect.stringContaining(string) matches the received value if it is a string that contains the exact expected string.
ref. expect.stringContaining(string) - Expect · Jest

expect(iconButton.text()).toEqual( expect.stringContaining("BUTTON") );

テキストを含んでいれば OK とする場合はこの方法が良さそうです。

ポエム

&nbsp; は改行させたくない箇所に使う特殊文字なので半角スペースとは本来用途が異なるので、必然性があって書いている箇所以外はでは極力避けたほうが良いかなと思っています。


[参考]