TrixとLexxyのクロスエディタ互換性テストハーネスを追加

basecamp/lexxy

Lexxyのテストスイートに、TrixエディタとLexxyエディタ間でAction Textコンテンツが双方向に保持されることを検証するシステムテストが追加されました。これにより、同一コンテンツを両エディタで編集するシナリオを網羅的にカバーできるようになります。

背景

LexxyはBasecampが開発するリッチテキストエディタであり、Railsの標準エディタである Trix と同じAction Text基盤を共有しています。両エディタが同一のAction Textフィールドを編集する場面では、添付ファイルやギャラリーを含むコンテンツが相互変換を経ても失われないことが重要です。

これまでLexxy単体の動作検証はテストが存在していましたが、「Lexxyで作成したコンテンツをTrixで編集する」「Trixで作成したコンテンツをLexxyで読み込む」という双方向のクロスエディタシナリオをカバーするテストハーネスは存在しませんでした。本PRはそのギャップを埋め、エッジケースを積み上げていくための初期基盤として位置づけられています。

技術的な変更

ダミーアプリへのTrixリソース追加

テスト基盤の中核となるのは、ダミーアプリへの TrixPostsController と関連ビューの追加です。既存の PostsController(Lexxyエディタを使用)と対になる形で、同じ Post モデルのレコードをTrixエディタで編集できるエンドポイントを提供します。

config/routes.rbresources :trix_posts が追加され、posts/edit.html.erb には「Edit in Trix」リンクが、trix_posts/edit.html.erb には「Edit in Lexxy」リンクが追加されています。この双方向のナビゲーションにより、同一レコードを両エディタで往来する操作がシステムテスト上で自然に表現できます。

to_trix_html によるコンテンツの橋渡し

Trixフォームのビューにおける重要な実装ポイントは、hidden inputへの値の渡し方です。

<%= hidden_field_tag "post[body]", post.body.to_trix_html, id: "trix_post_body_input" %>
<trix-editor input="trix_post_body_input"
  data-direct-upload-url="<%= main_app.rails_direct_uploads_url %>"
  data-blob-url-template="<%= main_app.rails_service_blob_url(":signed_id", ":filename") %>"></trix-editor>

post.body.to_trix_html を使用している点が肝心です。生の body_before_type_cast ではなく to_trix_html を使うことで、<action-text-attachment> タグに content 属性がレンダリングされます。Trixは添付ファイルを表示・保持するためにこの content 属性を必要とするため、raw値を渡すと添付ファイルが失われます。

TrixHelper によるテストユーティリティ

システムテストからTrixエディタを操作するための TrixHelper モジュールが追加され、test_helper.rb のミックスイン対象に組み込まれました。

module TrixHelper
  def fill_trix_editor(with:)
    find("trix-editor")
    page.execute_script <<~JS, with
      const editor = document.querySelector("trix-editor").editor
      const position = editor.getDocument().toString().trimEnd().length
      editor.setSelectedRange([position, position])
      editor.insertString(arguments[0])
    JS
  end

  def upload_trix_file(path)
    find("trix-editor")
    attach_file(path) do
      find("button[data-trix-action='attachFiles']").click
    end
  end
end

fill_trix_editor はJavaScriptを介してTrix内部のエディタオブジェクトを直接操作し、ドキュメント末尾にカーソルを移動してから文字列を挿入します。upload_trix_file はTrixのファイル添付ボタン(data-trix-action='attachFiles')をCapybaraの attach_file と組み合わせて操作します。

テストケースの構成

2つのテストファイルが追加され、合計6つのシナリオをカバーしています。

from_lexxy_to_trix_test.rb(Lexxy → Trix方向):
- リッチテキスト: Lexxyで作成 → Trixで追記 → 結合テキストを検証
- 添付ファイル: Lexxyで画像アップロード → Trixで保存 → action-text-attachment を検証
- ギャラリー: Lexxyで複数画像アップロード → Trixで保存 → ギャラリー保持を検証

from_trix_to_lexxy_test.rb(Trix → Lexxy方向):
- リッチテキスト: Trixで入力 → LexxyのHTML構造を検証 → 追記して保存
- 添付ファイル: Trixでアップロード → Lexxyで action-text-attachment を検証
- ギャラリー: Trixで2画像アップロード → Lexxyで両添付ファイルを検証

設計判断

既存の Post モデルを TrixPostsController で再利用する設計 が採用されました。Trix専用のモデルを作らず、同じレコードを別コントローラが扱うことで、「同一のAction Textフィールドを両エディタが編集する」という実際のユースケースを忠実に再現しています。

TrixのロードはCDN(unpkg.com/trix@2.1.12)からのSRI(Subresource Integrity)付き読み込みで実現されています。これはテスト用ダミーアプリに閉じた構成であり、Lexxy本体への依存追加を避けつつ、ファイルアップロードを含む実際のTrix動作を検証できる実用的な選択です。

また、フォームに data: { turbo: false } が設定されています。Trixとの組み合わせでTurboドライブが干渉することを避けるための措置であり、テストの信頼性を高めています。

まとめ

本PRは、LexxyとTrixが同一のAction Textコンテンツを介してシームレスに連携できることを継続的に保証するためのテスト基盤を確立しました。to_trix_html を使った正確なコンテンツシリアライズと、JavaScriptを介したTrix内部API操作という2つの技術的工夫が、エディタ間の互換性を実際の操作フローで検証することを可能にしています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
90231461

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

記事全体の構成が「リード文(総論)→各論(背景、技術詳細、設計判断)→まとめ(結論)」という理想的な構造になっています。各セクションの役割が明確で、非常に理解しやすいです。

カスタムMarkdown構文 ✓ PASS

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

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

対象読者への適合性 ✓ PASS

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

Railsのテスト、Action Text、Trixといった専門的なトピックを前提としており、対象読者であるエンジニアに適した技術レベルで書かれています。

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

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

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

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロック(`_form.html.erb`, `trix_helper.rb`)は、提供されたDiffの内容と完全に一致しており、正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Action Text」「システムテスト」「Capybara」「SRI」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

「`to_trix_html`の役割」や「`TrixHelper`の実装内容」に関する説明は、PR DescriptionやDiffのコードから裏付けが取れており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(テストハーネスの追加、双方向テストの網羅、コントローラの追加など)は、PRのTitle, Description, Diffによって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#811)やTrixのバージョン番号(2.1.12)などの数値・固有名詞はすべて正確に記載されています。

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

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

記事のタイトル「TrixとLexxyのクロスエディタ互換性テストハーネスを追加」は、PRのタイトル「Add Trix compatibility tests」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

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

記事はPR情報とDiffの内容に忠実であり、サポート状況やリリース予定といったPR外の知識を持ち込んでいません。Trixのバージョン番号はDiffに含まれる情報であり、問題ありません。

時間表現の正確性 ✓ PASS

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

「追加されました」「カバーできるようになります」といった時間表現は、PRによって新しい機能が追加されたという事実を正確に反映しています。