Lexicalの更新完了を待つ非同期テスト機構の導入
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_key と send_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更新の両方を確実に捉え、テストの信頼性と効率性を同時に向上させています。