Testing Libraryで複数の<option>を選択する<selectbox>のテストを記載する

ドロップダウンには複数の<option>を選択できるパターンが存在する。これをテストする例は探しても中々出てこないため、記載する。

複数要素は<select>multiple属性を設定することで実現できる。

export default function StayExtensionMultipleSelectBox() {
  return (
    // multipleを追加した
    <select multiple>
      <option value="none">宿泊なし</option>
      <option value="previous">前泊のみ</option>
      <option value="next">後泊のみ</option>
      <option value="both">前泊と後泊</option>
    </select>
  );
}

テストする場合、<option>を複数選択する必要がある。実際はCtrlキーやCommandキーを押しながら複数の<option>をクリックするが、Testing Libraryではこの操作を再現するのは難しい。そこで、公式のselectOptions1を使ってテストを行う。selectOptionsの第二引数に配列で複数の<option>のラベルまたはvalueを渡す。すると、選択要素を特定してくれる。

複数の値が選択状態であることを確かめる場合、toHaveValueの引数を配列にし、選択している値を配列に全て含めれば良い。

実際のテストケースは次のようになる。

it("複数の選択肢を選択したときも対応するvalueが選択状態になる", async () => {
  // イベント検知のセットアップ
  const event = userEvent.setup();
  const content = render(<StayExtensionMultipleSelectBox />);
  // 前泊と後泊を選択
  await event.selectOptions(content.getByRole("listbox"), [
    "前泊のみ",
    "後泊のみ",
  ]);
  // 前泊と後泊が選択されている
  expect(content.getByRole("listbox")).toHaveValue(["previous", "next"]);
});

注意点として、<select>要素の特定方法がmultiple属性の有無で変わる点である。MDN2にも次のように解説されている。

combobox with no multiple attribute and no size attribute greater than 1, otherwise listbox

multiple属性がなく、かつsize属性が1以下ならcomboboxを指定する。それ以外はlistboxを指定する。sizeは選択肢をいくつ表示するか?を指定する属性である。 multipleがない場合、デフォルトで0が指定される3。よって、大半の場合はcomboboxを使う。複数の選択肢を選択させる場合はlistboxを使う。この点を見落とすと「<select>が存在するのにクエリで見つけられない!」という事態にはまってしまう。

comboboxの場合は次のようにテストを記載する。

  it("オプションラベルを選択すると対応するvalueが選択状態になる", async () => {
    // イベント検知のセットアップ
    const event = userEvent.setup();
    const content = render(<StayExtensionSelectBox />);
    // オプションを全て取得
    const options = content.getAllByRole("option");
    // 前泊を選択
    await event.selectOptions(content.getByRole("combobox"), [options[1]]);
    // 前泊が選択されている
    expect(content.getByRole("combobox")).toHaveValue("previous");
  });