Playwrightブラウザテストをセマンティックなフォルダ構成へ再編

basecamp/lexxy

19本のPlaywrightテストファイルを6つのセマンティックなサブディレクトリに整理し、共通ヘルパー関数を helpers/toolbar.js に集約することで、テストコードの保守性と拡張性を高めた変更です。

背景

test/browser/tests/ 直下に19本のテストファイルが平置きされており、テストの責務と配置場所の対応が不明確な状態でした。ファイル数が増えるにつれ、新しいテストをどこに追加すべきか判断しにくくなるという問題が生じていました。また、toolbar.test.jspaste.test.js のような大きなファイルの中に、責務の異なるテストが混在していました。

加えて、HELLO_EVERYONE 定数や placeCaretAtEndOfInlineCodeapplyHighlightOption といったヘルパー関数が各テストファイルにローカルで定義されており、同じロジックが重複していました。

技術的な変更

テストファイルが6つのサブディレクトリに再編され、それぞれの責務が明確に定義されました。

フォルダ 対象テスト
attachments/ アップロード、削除、キャプション、ギャラリー
editor/ フォーカス、isEmpty/isBlank、toString、setValue、CSSステータスクラス
formatting/ ツールバー、インラインマーク、ハイライト、ブロックフォーマット、リスト、HR、エスケープ、カラー
modes/ プレーンテキストモード、シングルラインモード
paste/ Markdown変換、URL/リンクペースト、ファイル/添付ペースト、スタイル正規化
tables/ テーブル構造(作成・行列の追加削除)、ヘッダー

単体で完結するテスト(code_highlightingevents)はルートレベルに残されています。

サブディレクトリへの移動に伴い、すべてのテストファイルのimportパスが ../ から ../../ に更新されています。たとえば attachments/attachments.test.js では以下のように変更されました。

変更前:

import { test } from "../test_helper.js"
import { assertEditorHtml } from "../helpers/assertions.js"
import { mockActiveStorageUploads } from "../helpers/active_storage_mock.js"

変更後:

import { test } from "../../test_helper.js"
import { assertEditorHtml } from "../../helpers/assertions.js"
import { mockActiveStorageUploads } from "../../helpers/active_storage_mock.js"

共通ヘルパーの集約として、新たに test/browser/helpers/toolbar.js が追加されました。toolbar.test.jscolor_highlighter.test.js に重複して存在していた HELLO_EVERYONE 定数、placeCaretAtEndOfInlineCode 関数、applyHighlightOption 関数がこのファイルに集約され、各テストファイルからエクスポートとしてインポートする形に変わっています。

export const HELLO_EVERYONE = "<p>Hello everyone</p>"

export async function applyHighlightOption(page, attribute, buttonIndex) {
  await page.locator("[name='highlight']").click()
  const buttons = page.locator(
    `lexxy-highlight-dropdown .lexxy-highlight-colors .lexxy-highlight-button[data-style='${attribute}']`,
  )
  await buttons.nth(buttonIndex - 1).click()
}

export async function placeCaretAtEndOfInlineCode(editor) {
  await editor.content.evaluate((content) => {
    const code = content.querySelector("code")
    const walker = document.createTreeWalker(code, NodeFilter.SHOW_TEXT)
    // ...
  })
}

また、360行あった toolbar.test.js は責務ごとに分割され、formatting/toolbar.test.js(ツールバーUI)、formatting/inline_formatting.test.js(インライン書式)、formatting/block_formatting.test.js(ブロック書式)、formatting/highlights.test.js(ハイライト)の4ファイルに整理されました。同様に195行あった paste.test.jspaste/markdown.test.jspaste/links.test.jspaste/paste.test.js(ファイル・添付)、paste/paste_with_styles.test.js に分割されています。

テーブルテストでは、tables/structure.test.js からヘッダー操作のテストが切り出され、tables/headers.test.js として独立しました。あわせて test.describe の名前も "Tables" から "Tables — Structure" へリネームされ、テストレポート上での識別が容易になっています。

設計判断

フォルダ構成のルールが AGENTS.md に明文化され、新しいテストの配置先を迷わず決定できるようにしたことが本PRの重要な点です。テーブル形式でフォルダと対象テストの対応が示され、「どのフォルダにも当てはまらない場合は新しいフォルダが必要かどうか検討する」という指針まで記述されています。

コードの重複排除については、ヘルパー関数を helpers/toolbar.js に集約することで、テストファイルのローカル定義を廃止しています。color_highlighter.test.js では末尾に定義されていた applyHighlightOption 関数が削除され、helpers/toolbar.js からのインポートに切り替わっています。

単体ファイルのテスト(code_highlightingevents)をルートに残すという判断も、無理にカテゴリに押し込めない実用的な判断として AGENTS.md に記載されています。

まとめ

この変更は、テストコードそのもののロジックには手を加えず、構造とドキュメントを整備することでテストスイートの長期的な保守性を高めた好例です。大きなファイルを責務ごとに分割し、共通コードをヘルパーに集約し、配置ルールを AGENTS.md に記録するという3つのアプローチが組み合わさり、新規テスト追加時のガイドラインが明確になっています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
042d6301

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術詳細、設計判断、まとめが「総論→各論→結論」の構成で明確に配置されており、ガイドラインに準拠しています。

カスタムMarkdown構文 ✓ PASS

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

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

対象読者への適合性 ✓ PASS

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

Playwrightやテスト構成に関する知識を持つエンジニアを対象としており、専門用語の使い方が適切で、冗長な説明がありません。

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

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

各セクションが「総論→各論」で構成され、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、高い可読性を確保しています。

Diff内容との照合 ⚠ WARNING

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

`placeCaretAtEndOfInlineCode`関数の実装が`// ...`で省略されていますが、ヘルパーへの集約という主旨の理解を妨げるものではありません。その他のコード引用やファイルパスは正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

Playwright, ヘルパー関数, importパス, test.describeなどの技術用語が文脈に応じて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

テストファイルの分割、ヘルパー関数の集約、importパスの更新といった技術的な説明が、提供されたDiffの内容と完全に一致しており、正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(ファイル数、行数、ファイル分割の事実など)は、PRのDescriptionやDiffの内容によって裏付けられています。ハルシネーションは見られません。

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

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

PR番号(#858)、分割されたファイルの行数(360行、195行)、整理されたファイル数(19本)など、記事に含まれる数値や固有名詞はすべて正確です。

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

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

記事のタイトル「Playwrightブラウザテストをセマンティックなフォルダ構成へ再編」は、PRの主題であるテスト構成の再編成を的確に表現しています。

外部知識の正確性 ✓ PASS

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

記事内容はPR情報に限定されており、バージョンサポート状況などの外部知識の持ち込みはありません。

時間表現の正確性 ✓ PASS

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

変更内容を報告する形式で記述されており、時間表現に関する誤りはありません。