© TOOLS BOX — Next.js / React / TypeScript コードサンプル集

サンプルガイド
←サンプル一覧
nextjstesting

Playwright でソート変更時の URL 同期と表示順をE2Eテストする

一覧ページのソート選択(更新順・作成順・タイトル順)を Playwright でE2Eテストし、URL の sort クエリパラメータと表示順の整合性を検証する例。

難易度: 中級·更新: 2026-04-18·
playwright

対応バージョン

nextjs 15react 19playwright 1

前提環境

Playwright の基本的なテスト記述と Next.js App Router のページ構造を理解していること

概要

一覧ページのソートセレクトを操作したときに URL の sort クエリパラメータが更新されること、およびカード表示順が変化することを Playwright でE2Eテストする。select 要素の操作・waitForURL での遷移確認・先頭カードのテキスト検証を組み合わせて実装する。

インストール

npm install @playwright/test
npx playwright install

実装

ページオブジェクト

// e2e/pages/SamplesPage.ts
import type { Page, Locator } from "@playwright/test";

export class SamplesPage {
  readonly page: Page;
  readonly sortSelect: Locator;
  readonly sampleCards: Locator;

  constructor(page: Page) {
    this.page = page;
    this.sortSelect = page.getByRole("combobox", { name: "並び順" });
    this.sampleCards = page.getByTestId("sample-card");
  }

  async goto(params?: Record<string, string>) {
    const query = params
      ? "?" + new URLSearchParams(params).toString()
      : "";
    await this.page.goto(`/samples${query}`);
  }

  async selectSort(value: string) {
    await this.sortSelect.selectOption(value);
  }

  async firstCardTitle(): Promise<string> {
    return (await this.sampleCards.first().getByRole("heading").textContent()) ?? "";
  }
}

ソート URL 同期テスト

// e2e/sort-flow.spec.ts
import { test, expect } from "@playwright/test";
import { SamplesPage } from "./pages/SamplesPage";

test.describe("ソート変更", () => {
  test("sort=title を選択すると URL に sort=title が付く", async ({ page }) => {
    const samplesPage = new SamplesPage(page);
    await samplesPage.goto();

    await samplesPage.selectSort("title");

    await page.waitForURL((url) => url.searchParams.get("sort") === "title");
    expect(page.url()).toContain("sort=title");
  });

  test("sort=createdAt を選択すると URL に sort=createdAt が付く", async ({ page }) => {
    const samplesPage = new SamplesPage(page);
    await samplesPage.goto();

    await samplesPage.selectSort("createdAt");

    await page.waitForURL((url) => url.searchParams.get("sort") === "createdAt");
    expect(page.url()).toContain("sort=createdAt");
  });

  test("ソートを変更するとカードの並び順が変わる", async ({ page }) => {
    const samplesPage = new SamplesPage(page);
    await samplesPage.goto();

    // デフォルト(updatedAt)の先頭タイトルを記録
    const defaultFirst = await samplesPage.firstCardTitle();

    // タイトル順に変更
    await samplesPage.selectSort("title");
    await page.waitForURL((url) => url.searchParams.get("sort") === "title");

    const titleSortFirst = await samplesPage.firstCardTitle();

    // ソートが異なれば先頭も変わる(データが2件以上ある前提)
    expect(titleSortFirst).not.toBe(defaultFirst);
  });
});

他のフィルタと組み合わせたソートテスト

// e2e/sort-with-filter.spec.ts
import { test, expect } from "@playwright/test";
import { SamplesPage } from "./pages/SamplesPage";

test("カテゴリ絞り込み中にソートを変更しても絞り込みが維持される", async ({ page }) => {
  const samplesPage = new SamplesPage(page);

  // カテゴリ絞り込み済みの状態から開始
  await samplesPage.goto({ category: "routing" });

  await samplesPage.selectSort("title");

  await page.waitForURL(
    (url) =>
      url.searchParams.get("sort") === "title" &&
      url.searchParams.get("category") === "routing"
  );

  const url = new URL(page.url());
  expect(url.searchParams.get("sort")).toBe("title");
  expect(url.searchParams.get("category")).toBe("routing");
});

playwright.config.ts での設定

// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./e2e",
  use: {
    baseURL: "http://localhost:3000",
  },
  webServer: {
    command: "npm run dev",
    url: "http://localhost:3000",
    reuseExistingServer: !process.env.CI,
  },
});

ポイント

  • waitForURL にコールバックを渡すことで、URL の searchParams を直接検査できる。文字列マッチより意図が明確で、クエリパラメータの順序に依存しない
  • ソート変更後に selectSort を呼ぶと Next.js の router.push が走り、URL が変化するまでの非同期待機が必要。waitForURL がその完了を保証する
  • ページオブジェクトパターン(SamplesPage クラス)を使うことで、セレクタをテストコードから分離する。UI 構造が変わったときの修正が 1 箇所で済む
  • 表示順変化の検証は「先頭カードのタイトルが変わること」で代替する。全件順序の厳密な検証は DB 依存度が高く壊れやすいため、変化の有無を確認するアプローチが堅牢
  • reuseExistingServer: !process.env.CI で、ローカル開発時は既存の dev サーバーを再利用できる。CI では毎回起動して一貫性を保つ
  • getByRole("combobox", { name: "並び順" }) で aria-label か <label> テキストを使ってセレクトを特定する。data-testid より意味のあるセレクタで可読性が高い

注意点

playwright-pagination-flow-e2e はページネーション操作の E2E。playwright-search-flow-e2e は検索フローの E2E。これはソート選択変更時の URL sort クエリ同期と表示順変化を検証する E2E に特化。

関連サンプル

同じテーマや技術スタックを使った実装例

  • Playwright でページネーション付き一覧のページ遷移をE2Eテストする

    一覧ページのページネーション操作(次へ・前へ・ページ番号クリック)を Playwright でE2Eテストし、URL クエリと表示件数の整合性を検証する例。

  • Playwright でログインから保護ページ到達までの認証フローを E2E テストする

    Playwright でログインフォームへの入力・送信・Cookie 設定・保護ページへのリダイレクト確認・ログアウトまでの認証フローを E2E テストする例。

  • Playwright でファイルアップロードの E2E テストを書く

    Playwright の setInputFiles を使って file input へのファイル選択をシミュレートし、アップロードの成功・エラー・複数ファイルケースを E2E テストする例。

  • Playwright で検索フォーム入力から結果表示・0 件状態までを E2E テストする

    Playwright で検索フォームへの入力・URL パラメータ更新・一覧フィルタ結果確認・0 件 EmptyState 表示・フィルタリセットまでの検索フロー全体を E2E テストする例。

  • Playwright でログインフローの E2E テストを書く

    Playwright を使ってログインフォームの E2E テストを実装する例。Page Object Model パターンと認証状態の再利用を示す。

関連仕様

このサンプルを理解するのに役立つ仕様や概念

  • FrameworkNext.jsReact ベースのフルスタックフレームワーク。SSR・SSG・App Router・API Routes を提供する。
←サンプル一覧に戻る