Best practices
アプリケーションのテスト
Route、Page、Layout、Server Function の動作を検証する、実行可能な Playwright テストの例。
実行中のアプリケーションに Playwright でアクセスし、Route、Page、Layout、Server Function の動作をテストします。 Effront は、Page / Layout の描画や Server Function のリクエストを Vitest で検証する公開テストハーネスを提供していません。
実行できるサンプルを使う
以下のテストは Basic サンプルアプリケーションを対象にしています。 保守されている Playwright のテスト構成でサンプルをビルドし、Cloudflare の認証なしでローカルの workerd ホストを起動できます。 この構成はリポジトリー内の参照例であり、Effront に付属するテストヘルパーではありません。
セットアップと実行の手順に従ってサンプルを実行してください。
以下のコードブロックを指定したファイル名でテストパッケージの alchemy.e2e.ts と同じディレクトリーに保存し、ブラウザーテストを再実行します。
既存の Playwright 設定が、参照サンプルの baseURL とサーバーの起動・終了を管理します。
Route の応答をテストする
サンプルは EFFRONT.Routes.make().page(...) で / と /about を登録しています。
HTTP リクエストで、登録したルートの応答と未登録ルートのステータスを確認します。
routes.e2e.ts として保存してください。
import { expect, test } from "@playwright/test";
test("registered routes return HTML and unknown routes return 404", async ({ request }) => {
for (const path of ["/", "/about"]) {
const response = await request.get(path);
expect(response.status()).toBe(200);
expect(response.headers()["content-type"]).toContain("text/html");
}
const missing = await request.get("/not-a-route");
expect(missing.status()).toBe(404);
});ビルドしたアプリケーションに対して、設定済みのミドルウェアを含むルートの応答を検証します。 登録内容はサンプルのルート定義を参照してください。
Page の出力とハイドレーションをテストする
HomePage はアプリケーションの Host サービスから取得した挨拶と、クライアント側のカウンターを描画します。
page.e2e.ts として保存してください。
import { expect, test } from "@playwright/test";
test("HomePage renders service data and hydrates its counter", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("heading", { level: 1 })).toHaveText("Hello, world!");
await expect(page.getByTestId("kv-greeting")).toHaveText("Hello from Alchemy KV");
await page.waitForLoadState("networkidle");
await page.getByRole("button", { name: "Count: 0", exact: true }).click();
await expect(page.getByRole("button", { name: "Count: 1", exact: true })).toBeVisible();
});挨拶の表示で、サーバー側で描画したデータを確認します。
カウンターの増加で、ハイドレーション後のクライアントコンポーネントを確認します。
この固定サンプルの既存のテストは、クライアント側の操作前に networkidle を待ちます。
画面遷移で Layout が維持されることをテストする
RootLayout は両方の Page を共通の Shellで囲みます。
画面遷移で Page が切り替わっても、元のナビゲーション要素が DOM に接続されたままであることを検証します。
layout.e2e.ts として保存してください。
import { expect, test } from "@playwright/test";
test("RootLayout retains its navigation when the Page changes", async ({ page }) => {
await page.goto("/");
await page.waitForLoadState("networkidle");
const navigation = await page.getByRole("navigation").elementHandle();
if (!navigation) throw new Error("Expected the shared navigation");
await page.getByRole("link", { name: "About", exact: true }).click();
await expect(page).toHaveURL(/\/about$/);
await expect(page.getByRole("main").getByRole("heading", { level: 1 })).toHaveText("About");
await expect(page.getByTestId("label")).toHaveText("Effront + Alchemy");
await expect(page.getByRole("navigation").getByRole("link", { name: "Home" })).toBeVisible();
expect(await navigation.evaluate((element) => element.isConnected)).toBe(true);
});元の DOM 要素を確認することで、同じ見た目のナビゲーションを作り直した場合も検出できます。 この維持のテストは、クライアントナビゲーションに使う Navigation API に対応した Chromium で実行してください。 ドキュメント全体のナビゲーションでは、Layout の DOM も置き換わります。
クライアント経由で Server Function をテストする
サンプルの GreetingActionは、Client Component から greet("Ada") を送信します。
greet Server Functionはリクエストの Host サービスを読み、挨拶を返します。
server-function.e2e.ts として保存してください。
import { expect, test } from "@playwright/test";
test("the Server Function returns a greeting to its client", async ({ page }) => {
await page.goto("/");
await page.waitForLoadState("networkidle");
const result = page.getByTestId("action-greeting");
await expect(result).toHaveText("");
await page.getByRole("button", { name: "Read KV through a Server Function" }).click();
await expect(result).toHaveText("Hello from Alchemy KV, Ada!");
});ボタン操作で、ブラウザーからのリクエスト、サーバー側の実行、戻り値の表示を検証します。
サーバー側の Vitest テストから Effront の Server Function を直接呼ぶと、TypeError で拒否されます。
このテストは読み取り処理を対象とし、永続化や認可は検証していません。