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 とサーバーの起動・終了を管理します。 自分のアプリケーションで使う場合は、URL、セレクター、期待値をアプリケーションの出力に合わせて変更してください。

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 で拒否されます。 このテストは読み取り処理を対象とし、永続化や認可は検証していません。