Playwrightブラウザテストをセマンティックなフォルダ構成へ再編
19本のPlaywrightテストファイルを6つのセマンティックなサブディレクトリに整理し、共通ヘルパー関数を helpers/toolbar.js に集約することで、テストコードの保守性と拡張性を高めた変更です。
背景
test/browser/tests/ 直下に19本のテストファイルが平置きされており、テストの責務と配置場所の対応が不明確な状態でした。ファイル数が増えるにつれ、新しいテストをどこに追加すべきか判断しにくくなるという問題が生じていました。また、toolbar.test.js や paste.test.js のような大きなファイルの中に、責務の異なるテストが混在していました。
加えて、HELLO_EVERYONE 定数や placeCaretAtEndOfInlineCode、applyHighlightOption といったヘルパー関数が各テストファイルにローカルで定義されており、同じロジックが重複していました。
技術的な変更
テストファイルが6つのサブディレクトリに再編され、それぞれの責務が明確に定義されました。
| フォルダ | 対象テスト |
|---|---|
attachments/ |
アップロード、削除、キャプション、ギャラリー |
editor/ |
フォーカス、isEmpty/isBlank、toString、setValue、CSSステータスクラス |
formatting/ |
ツールバー、インラインマーク、ハイライト、ブロックフォーマット、リスト、HR、エスケープ、カラー |
modes/ |
プレーンテキストモード、シングルラインモード |
paste/ |
Markdown変換、URL/リンクペースト、ファイル/添付ペースト、スタイル正規化 |
tables/ |
テーブル構造(作成・行列の追加削除)、ヘッダー |
単体で完結するテスト(code_highlighting、events)はルートレベルに残されています。
サブディレクトリへの移動に伴い、すべてのテストファイルの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.js と color_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.js も paste/markdown.test.js、paste/links.test.js、paste/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_highlighting、events)をルートに残すという判断も、無理にカテゴリに押し込めない実用的な判断として AGENTS.md に記載されています。
まとめ
この変更は、テストコードそのもののロジックには手を加えず、構造とドキュメントを整備することでテストスイートの長期的な保守性を高めた好例です。大きなファイルを責務ごとに分割し、共通コードをヘルパーに集約し、配置ルールを AGENTS.md に記録するという3つのアプローチが組み合わさり、新規テスト追加時のガイドラインが明確になっています。