Lexicalの更新完了を待つ非同期テスト機構の導入

basecamp/lexxy

Lexxyのテストコードから sleep コマンドをすべて削除し、Lexicalエディタの更新完了を確実に待機する仕組みが導入されました。これにより、テストの信頼性が向上し、実行時間の最適化も期待できます。

背景

これまでのテストコードでは、キー入力やタブ操作の後に sleep 0.1 を挿入してLexicalエディタの更新を待っていました。この固定時間待機は、環境によっては不十分で失敗する可能性があり、逆に十分すぎる場合はテスト実行時間を無駄に延ばす原因となっていました。wait_until ヘルパーでも sleep 0.05 を繰り返すポーリング方式が使われており、エディタの状態が実際に更新されたかを確認する手段がありませんでした。

技術的な変更

flush_lexical_updates メソッドが EditorHandler クラスに追加され、Lexicalの更新完了を明示的に待機できるようになりました。

def flush_lexical_updates
  page.evaluate_async_script <<~JS
    const [ done ] = arguments
    const editor = document.querySelector('lexxy-editor').editor
    editor.update(() => null, {
      onUpdate: () => requestAnimationFrame(done)
    });
  JS
end

このメソッドは、Seleniumの evaluate_async_script を使用して、Lexicalの onUpdate コールバックが発火するまで待機します。editor.update() に空の更新関数を渡し、その onUpdate コールバック内で requestAnimationFrame を経由してSeleniumの done コールバックを呼び出す設計です。これにより、Lexicalのレンダリングサイクルが完了したタイミングでテストが次のステップに進みます。

変更前:

def send_key(key, ctrl: false)
  # キー送信処理
  sleep 0.1
end

変更後:

def send_key(key, ctrl: false)
  # キー送信処理
  # sleep削除
end

send_keysend_tab メソッドから sleep 0.1 が削除され、wait_until ヘルパーでは sleep 0.05 の代わりに flush_lexical_updates が使われるようになりました。

def wait_until(timeout: Capybara.default_max_wait_time)
  Timeout.timeout(timeout) do
    find_editor.flush_lexical_updates until yield
  end
end

これにより、条件が満たされるまで毎回Lexicalの更新サイクルを待機し、確実に最新の状態でアサーションを実行できます。

設計判断

Lexicalの onUpdate コールバックと requestAnimationFrame を組み合わせる方式 が採用されました。

onUpdate コールバックはLexicalのエディタ状態が更新された直後に発火しますが、DOMへの反映はブラウザのレンダリングサイクル後に完了します。requestAnimationFrame を挟むことで、ブラウザの次の描画フレームまで待機し、DOM更新が確実に完了した状態でテストを継続できます。この2段階の待機により、エディタの内部状態とDOM表示の両方が同期したタイミングを捉えられます。

evaluate_async_script の使用により、Seleniumのイベントループとブラウザの非同期処理を統合し、固定時間待機を排除しました。テストは必要最小限の時間だけ待機し、更新が完了次第すぐに次のステップに進みます。

まとめ

本PRは、固定時間待機に依存していたテストコードを、Lexicalの更新完了を明示的に確認する非同期機構に置き換えた変更です。onUpdate コールバックと requestAnimationFrame を組み合わせることで、エディタの状態変化とDOM更新の両方を確実に捉え、テストの信頼性と効率性を同時に向上させています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→セクション群(各論)→まとめ(結論)の3部構成が明確に適用されており、必須要素(背景、技術的変更)と任意要素(設計判断)もすべて含まれています。構成は模範的です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)とGitHubのPRリンク記法([#番号](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Selenium、Lexicalのコールバック、requestAnimationFrameなど専門的な内容を扱っており、対象読者であるエンジニアに適した技術レベルと表現です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。非常に読みやすい構造です。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロックは、提供されたDiffの内容と正確に一致しています。変更前のコードを例示することで、読者の理解を助ける工夫も適切です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「evaluate_async_script」「onUpdate callback」「requestAnimationFrame」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「onUpdate」と「requestAnimationFrame」を組み合わせた理由の説明など、コードの意図を正確に捉えた技術的に正しい解説が行われています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのDescriptionやDiff内のコードで裏付けられています。根拠のない推測や創作(ハルシネーション)は見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#746)やコード内の数値(sleep 0.1など)が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

PRのユーモラスなタイトル「Wake testing up ☕☕☕」を、変更内容を的確に表現する「Lexicalの更新完了を待つ非同期テスト機構の導入」という技術的に分かりやすいタイトルに変換できています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事には、PR情報に基づかないバージョン情報やリリース予定などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「これまでのテストコードでは」といった時間表現は、PRの文脈と一致しており、正確です。