ブロッククォートへのペースト時のリグレッションテストを追加

basecamp/lexxy

ブロッククォートにテキストをペーストすると内容が引用外に脱出するバグ(#4979)に対し、再発防止のためのPlaywrightリグレッションテストが追加されました。バグ修正とテストが別々のPRで管理されてきた経緯から、テストが失われていたことへの対処です。

背景

このバグは、修正・削除・偶発的な再修正という複雑な経緯をたどっています。#839 でブロッククォートへのペースト時にテキストが外部に脱出する問題が修正されましたが、その後のツールバーリワーク (#891) のリファクタリング中に、修正コードとテストの両方が誤って削除されました。

#891 のリファクタリングでは、toggleBlockquote の構造が変更され、<blockquote> 内のコンテンツを常に <p> 要素でラップするようになりました。この変更により、Lexicalの selection.insertNodes() がペーストされたノードをブロッククォートの外に分割するのではなく、内部の段落に挿入するようになり、バグが副作用として偶発的に修正されていました。

しかしテストが存在しないまま修正が維持されていたため、将来のリファクタリングで再び同じバグが混入するリスクがありました。本PRはそのリスクを明示的なテストで封じるものです。

技術的な変更

test/browser/tests/paste/paste.test.js に、ブロッククォートへのペーストシナリオをカバーする3つのPlaywrightテストが新たに追加されました。

追加されたテストスイートは Paste — Blockquote として整理されており、以下の3つのシナリオを網羅します:

  • 改行を含むプレーンテキストを空のブロッククォートにペーストした場合、すべての行が引用内に留まること
  • HTMLの複数段落構造(<p> タグ)を含むリッチテキストをブロッククォートにペーストした場合、すべての段落が引用内に留まること
  • 既存のコンテンツがあるブロッククォートにプレーンテキストをペーストした場合の動作

最初のテストの実装を例に挙げると、ユーザーの操作を忠実に再現する構造になっています。

test("pasting plain text with newlines into an empty blockquote keeps all lines inside the quote", async ({
  page,
  editor,
}) => {
  await page.goto("/")
  await editor.waitForConnected()

  await editor.click()
  await page.getByRole("button", { name: "Quote" }).click()
  await editor.flush()

  await editor.paste("this \nis\nwhat\n\nI am pasting")
  await editor.flush()

  await assertEditorContent(editor, async (content) => {
    await expect(content.locator("blockquote")).toHaveCount(1)
    const outerParagraphs = content.locator(":scope > p")
    await expect(outerParagraphs).toHaveCount(0)
  })
})

アサーションの設計が明確で、「blockquote が1つ存在すること」と「トップレベルに <p> が存在しないこと」という2軸でコンテンツの脱出を検出します。これにより、ペーストされたテキストが引用外に分割される現象を確実に検知できます。

設計判断

バグ修正コードそのものではなく、テストのみを追加するという方針が採られています。

現在のバグは #891 のリファクタリングによって偶発的に修正済みであるため、コード変更は不要な状態です。本PRは「動いているコードに手を加えない」原則を守りつつ、将来の変更に対して安全網を張ることに集中しています。

また、\n(単一改行)と \n\n(段落区切り)の両方を含む入力文字列 "this \nis\nwhat\n\nI am pasting" をテストケースに使用し、#839 が特定した複数のエッジケースをカバーしています。これはオリジナルの修正PRが対処した「単一改行・複数行・段落区切り」の3パターンを踏まえた設計です。

まとめ

バグ修正・削除・再修正という経緯を経て、本PRはリグレッション防止の最後のピースであるテストを補完しました。コードの変更を伴わない純粋なテスト追加ですが、toggleBlockquote の構造変更という暗黙の前提を明示的な期待値として記録することで、将来のリファクタリングに対する耐性を高める意義があります。

記事メタデータ

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

この記事は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リンク記法の正確性

ファイル名付きのシンタックスハイライトやPR/Issue番号のリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

リグレッションテストやリファクタリングといった専門用語を適切に使用し、対象読者であるエンジニアに適した技術レベルで書かれています。

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

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

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

Diff内容との照合 ✓ PASS

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

Diffから引用されたコードブロックは、ファイル名を含め正確に反映されています。可読性のためのコメント省略は許容範囲内です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

Playwright、リグレッションテスト、アサーションなどの技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

バグが偶発的に修正された技術的な理由について、PR Descriptionの内容を基に正確かつ分かりやすく解説できています。

事実の突合 ✓ PASS

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

記事内のすべての主張がPRのDescriptionやDiffの内容によって裏付けられており、ハルシネーション(捏造)は見られません。

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

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

PR番号(#904, #839, #891)やIssue番号(#4979)などの固有名詞が正確に記載されています。

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

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

記事のタイトルはPR「Add regression tests for pasting into blockquotes」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、バージョンサポート状況など、PR外の不確かな外部知識は含まれていません。

時間表現の正確性 ✓ PASS

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

バグの修正、削除、再修正という一連の出来事の時系列が、PR情報と一致した正確な時間表現で記述されています。