コンテンツへスキップ

一括選択チェックボックスが「チェックしたつもり」で反映されない罠 — check() 後の検証フォールバック

WordPress のプラグイン更新画面やテーマ更新画面には「すべて選択」チェックボックスがある。Playwright で check() を呼べばクリックは成功する。だが、その後に個別のチェックボックスを見ると、1件もチェックされていないことがある。

補足: Playwright の check() はチェックボックスにチェックを入れる操作。クリック自体は成功してもページ側の JavaScript ハンドラが反応しなければ、見た目の状態と実際のフォーム送信内容がずれる。

何が起きるか

「すべて選択」チェックボックスは、多くの場合 JavaScript で「クリックされたら、配下の個別チェックボックスを全部チェックする」という処理が組まれている。Playwright の check(force=True) は DOM 上のチェック状態を強制的に変更できるが、これはそのチェックボックス自身の状態を変えるだけで、配下の個別チェックボックスに連動させる JavaScript ハンドラが発火するとは限らない。

# 一見動いているように見えるが、個別チェックボックスは未チェックのまま
select_all.first.check(force=True)
page.click('input[type="submit"][name="upgrade"]')

更新ボタンを押しても、フォームが実際に送信するのは「チェックされていない」状態のデータなので、何も更新されない。エラーは出ないため、一見正常に完了したように見えてしまう。

対処 — チェック後に必ず検証する

「すべて選択」をチェックした直後に、配下の個別チェックボックスが実際にチェック状態になっているかを確認する。なっていなければ、個別チェックボックスを1つずつ手動でチェックするフォールバックに切り替える。

sel_all_sel = 'input[type="checkbox"][id^="plugins-select-all"]'
select_all = plugin_form.locator(sel_all_sel)

if select_all.count() > 0:
    select_all.first.check(force=True)
    page.wait_for_timeout(500)

    # 検証ステップ — 実際にチェックされたかを確認
    chk_sel_check = 'input[type="checkbox"][name="checked[]"]:checked'
    any_checked = plugin_form.locator(chk_sel_check).count() > 0
    if not any_checked:
        # 一括選択が反映されなかったため個別選択に切り替える
        select_all = None

if not select_all or select_all.count() == 0:
    checkboxes = plugin_form.locator('input[type="checkbox"][name="checked[]"]')
    count = checkboxes.count()
    for i in range(count):
        checkboxes.nth(i).check(force=True)

ここで重要なのは、「すべて選択」が効いたという前提ではなく「実際に何件チェックされたか」という事実をコードの中で確認している点だ。さらに更新ボタンを押す前にも、チェック済み件数と全体の件数をログに出して、0件のまま処理が進んでいないかを最終確認する。

checked_count = plugin_form.locator(chk_sel).count()
total_checkboxes = plugin_form.locator(all_chk_sel).count()
if checked_count == 0:
    # 更新対象なしとして処理を中断する
    return result

プラグインとテーマの両方に同じパターンを適用する

プラグイン更新フォームとテーマ更新フォームは別の画面だが、「一括選択チェックボックス」「個別チェックボックス」という構造は同じだ。片方だけ検証フォールバックを入れて、もう片方を素の check() のままにしておくと、テーマ更新の方だけ同じ事故を踏む。1箇所で見つけたパターンは、同じ構造を持つ別画面にも横展開して適用する。

まとめ

観点 内容
症状 「すべて選択」をチェックしたつもりでも個別チェックボックスが未チェックのまま
原因 check(force=True) がチェックボックス自身の状態は変えても、連動 JavaScript ハンドラの発火は保証しない
対処 チェック直後に配下の個別チェックボックスが実際にチェックされたか検証し、未反映なら個別選択にフォールバック
横展開 同じ構造(プラグイン更新・テーマ更新)の画面すべてに同じ検証パターンを適用する

update-core.php のバージョン番号抽出が誤検出する罠と同じく、これも「操作や読み取りが成功したように見える」ことと「実際に意図した状態になっている」ことの間にギャップがあるという話だ。クリックが成功した・例外が出なかったというだけでは、本当にやりたかったことが実現したとは限らない。失敗した時に何を信用するかという観点は、Playwright の生エラーをそのまま見せない設計にもつながる。