スキル一覧に戻る
webdevcody

agentic-jumpstart-testing

by webdevcody

15🍴 10📅 2026年1月22日
GitHubで見るManusで実行

SKILL.md


name: agentic-jumpstart-testing description: Testing patterns with Playwright for E2E tests and Vitest for unit tests. Use when writing tests, testing components, E2E testing, integration tests, mocking, or when the user mentions testing, Playwright, Vitest, test, or coverage.

Testing Patterns

Playwright E2E Testing

Test Structure

// tests/courses.spec.ts
import { test, expect } from "@playwright/test";

test.describe("Courses", () => {
  test.beforeEach(async ({ page }) => {
    // Setup before each test
    await page.goto("/courses");
  });

  test("should display course list", async ({ page }) => {
    await expect(page.getByRole("heading", { name: "Courses" })).toBeVisible();
    await expect(page.getByTestId("course-card")).toHaveCount.greaterThan(0);
  });

  test("should navigate to course details", async ({ page }) => {
    await page.getByTestId("course-card").first().click();
    await expect(page).toHaveURL(/\/courses\/\d+/);
    await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
  });
});

Authentication in Tests

// tests/auth.setup.ts
import { test as setup, expect } from "@playwright/test";

const authFile = "playwright/.auth/user.json";

setup("authenticate", async ({ page }) => {
  // Login flow
  await page.goto("/login");
  await page.getByLabel("Email").fill("test@example.com");
  await page.getByLabel("Password").fill("password");
  await page.getByRole("button", { name: "Sign In" }).click();

  // Wait for login to complete
  await expect(page).toHaveURL("/dashboard");

  // Save authentication state
  await page.context().storageState({ path: authFile });
});

// In playwright.config.ts
export default defineConfig({
  projects: [
    { name: "setup", testMatch: /.*\.setup\.ts/ },
    {
      name: "authenticated",
      testMatch: /.*\.spec\.ts/,
      dependencies: ["setup"],
      use: { storageState: authFile },
    },
  ],
});

Testing Protected Routes

test.describe("Admin Dashboard", () => {
  test.use({ storageState: "playwright/.auth/admin.json" });

  test("should access admin dashboard", async ({ page }) => {
    await page.goto("/admin");
    await expect(page.getByRole("heading", { name: "Admin Dashboard" })).toBeVisible();
  });
});

Page Object Pattern

// tests/pages/CoursePage.ts
import { Page, Locator } from "@playwright/test";

export class CoursePage {
  readonly page: Page;
  readonly title: Locator;
  readonly enrollButton: Locator;
  readonly moduleList: Locator;

  constructor(page: Page) {
    this.page = page;
    this.title = page.getByRole("heading", { level: 1 });
    this.enrollButton = page.getByRole("button", { name: "Enroll" });
    this.moduleList = page.getByTestId("module-list");
  }

  async goto(courseId: string) {
    await this.page.goto(`/courses/${courseId}`);
  }

  async enroll() {
    await this.enrollButton.click();
    await this.page.waitForURL(/\/checkout/);
  }

  async getModuleCount() {
    return this.moduleList.getByTestId("module-item").count();
  }
}

// Usage in test
test("should enroll in course", async ({ page }) => {
  const coursePage = new CoursePage(page);
  await coursePage.goto("1");
  await coursePage.enroll();
  await expect(page).toHaveURL(/\/checkout/);
});

Testing Forms

test("should submit contact form", async ({ page }) => {
  await page.goto("/contact");

  // Fill form
  await page.getByLabel("Name").fill("John Doe");
  await page.getByLabel("Email").fill("john@example.com");
  await page.getByLabel("Message").fill("Test message");

  // Submit
  await page.getByRole("button", { name: "Submit" }).click();

  // Verify success
  await expect(page.getByText("Message sent successfully")).toBeVisible();
});

Testing Async Operations

test("should load data", async ({ page }) => {
  await page.goto("/dashboard");

  // Wait for network request
  const responsePromise = page.waitForResponse("**/api/stats");
  await page.getByRole("button", { name: "Refresh" }).click();
  await responsePromise;

  // Verify data is displayed
  await expect(page.getByTestId("stats-card")).toBeVisible();
});

Visual Testing

test("should match visual snapshot", async ({ page }) => {
  await page.goto("/landing");
  await expect(page).toHaveScreenshot("landing-page.png");
});

Vitest Unit Testing

Testing Utility Functions

// src/utils/format.test.ts
import { describe, it, expect } from "vitest";
import { formatCurrency, formatDate, truncate } from "./format";

describe("formatCurrency", () => {
  it("should format USD currency", () => {
    expect(formatCurrency(1234.56)).toBe("$1,234.56");
  });

  it("should handle zero", () => {
    expect(formatCurrency(0)).toBe("$0.00");
  });
});

describe("truncate", () => {
  it("should truncate long strings", () => {
    expect(truncate("Hello World", 5)).toBe("Hello...");
  });

  it("should not truncate short strings", () => {
    expect(truncate("Hi", 10)).toBe("Hi");
  });
});

Testing Hooks

// src/hooks/useDebounce.test.ts
import { describe, it, expect, vi } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { useDebounce } from "./useDebounce";

describe("useDebounce", () => {
  it("should debounce value updates", async () => {
    vi.useFakeTimers();

    const { result, rerender } = renderHook(
      ({ value }) => useDebounce(value, 500),
      { initialProps: { value: "initial" } }
    );

    expect(result.current).toBe("initial");

    rerender({ value: "updated" });
    expect(result.current).toBe("initial");

    act(() => {
      vi.advanceTimersByTime(500);
    });

    expect(result.current).toBe("updated");

    vi.useRealTimers();
  });
});

Mocking

import { describe, it, expect, vi, beforeEach } from "vitest";
import { processPayment } from "./payment";

// Mock external module
vi.mock("stripe", () => ({
  default: vi.fn().mockImplementation(() => ({
    paymentIntents: {
      create: vi.fn().mockResolvedValue({ id: "pi_123", status: "succeeded" }),
    },
  })),
}));

describe("processPayment", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("should process payment successfully", async () => {
    const result = await processPayment({ amount: 1000, currency: "usd" });
    expect(result.status).toBe("succeeded");
  });
});

Testing React Components

// src/components/Button.test.tsx
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { Button } from "./Button";

describe("Button", () => {
  it("should render children", () => {
    render(<Button>Click me</Button>);
    expect(screen.getByRole("button")).toHaveTextContent("Click me");
  });

  it("should call onClick handler", () => {
    const handleClick = vi.fn();
    render(<Button onClick={handleClick}>Click</Button>);

    fireEvent.click(screen.getByRole("button"));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it("should be disabled when loading", () => {
    render(<Button disabled>Loading</Button>);
    expect(screen.getByRole("button")).toBeDisabled();
  });
});

Test Commands

# Run Playwright E2E tests
npm run test

# Run Playwright in headed mode
npm run test:headed

# Run Playwright with UI
npm run test:ui

# Run specific browser
npm run test:chrome
npm run test:firefox
npm run test:mobile

# Run Vitest unit tests
npm run test:unit

# Run Vitest in watch mode
npm run test:unit:watch

# Run Vitest with coverage
npm run test:unit:coverage

Test Data Setup

// tests/setup/database.ts
import { database } from "~/db";
import { users, courses } from "~/db/schema";

export async function seedTestData() {
  // Clear existing data
  await database.delete(courses);
  await database.delete(users);

  // Insert test user
  const [testUser] = await database
    .insert(users)
    .values({
      email: "test@example.com",
      name: "Test User",
    })
    .returning();

  // Insert test courses
  await database.insert(courses).values([
    { title: "Test Course 1", authorId: testUser.id },
    { title: "Test Course 2", authorId: testUser.id },
  ]);

  return { testUser };
}

Testing Checklist

  • E2E tests cover critical user flows
  • Authentication tests verify protected routes
  • Forms are tested with valid and invalid input
  • Page objects used for complex pages
  • Unit tests for utility functions
  • Mocks used for external services
  • Tests run in CI/CD pipeline
  • Test data is isolated between tests
  • Visual snapshots for critical pages
  • Coverage reports generated

スコア

総合スコア

65/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

+5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です