| name | testing |
| description | Write unit tests with Vitest and E2E tests with Playwright. Use when creating tests, writing test cases, setting up test fixtures, or when the user mentions test, testing, vitest, playwright, or coverage. |
| allowed-tools | ["Read","Write","Edit","Glob","Grep","Bash"] |
Writing Tests
Create tests following Pixel Studio's testing patterns with Vitest and Playwright.
Test Commands
npm run test
npm run test:run
npm run test:coverage
npm run test:e2e
Unit Tests with Vitest
Basic Test Structure
import { describe, it, expect, beforeEach, vi } from "vitest";
import { myFunction } from "./myUtil";
describe("myFunction", () => {
beforeEach(() => {
});
it("should return expected value", () => {
const result = myFunction("input");
expect(result).toBe("expected");
});
it("should handle edge case", () => {
expect(() => myFunction("")).toThrow("Invalid input");
});
});
Testing Utilities
import { describe, it, expect } from "vitest";
import { cn } from "./cn";
describe("cn utility", () => {
it("merges class names", () => {
expect(cn("foo", "bar")).toBe("foo bar");
});
it("handles conditional classes", () => {
expect(cn("base", false && "hidden", true && "visible")).toBe(
"base visible",
);
});
it("handles undefined/null", () => {
expect(cn("base", undefined, null)).toBe("base");
});
});
Testing with Mocks
import { describe, it, expect, vi, beforeEach } from "vitest";
vi.mock("~/services/prisma.server", () => ({
prisma: {
user: {
findUnique: vi.fn(),
create: vi.fn(),
},
},
}));
import { prisma } from "~/services/prisma.server";
import { getUser } from "./getUser.server";
describe("getUser", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns user when found", async () => {
const mockUser = { id: "1", name: "Test" };
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser);
const result = await getUser("1");
expect(result).toEqual(mockUser);
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: "1" },
});
});
it("returns null when not found", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
const result = await getUser("invalid");
expect(result).toBeNull();
});
});
Testing Async Functions
import { describe, it, expect } from "vitest";
describe("async operations", () => {
it("resolves with data", async () => {
const result = await fetchData();
expect(result).toHaveProperty("data");
});
it("rejects with error", async () => {
await expect(fetchInvalid()).rejects.toThrow("Not found");
});
});
Snapshot Testing
import { describe, it, expect } from "vitest";
describe("formatOutput", () => {
it("matches snapshot", () => {
const output = formatComplexData(input);
expect(output).toMatchSnapshot();
});
});
E2E Tests with Playwright
Basic E2E Test
import { test, expect } from "@playwright/test";
test.describe("Public pages", () => {
test("home page loads", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveTitle(/Pixel Studio/);
await expect(page.locator("h1")).toBeVisible();
});
test("explore page shows images", async ({ page }) => {
await page.goto("/explore");
await expect(page.locator("[data-testid='image-grid']")).toBeVisible();
});
});
Testing Authentication
test.describe("Protected pages", () => {
test("redirects to login when not authenticated", async ({ page }) => {
await page.goto("/generate");
await expect(page).toHaveURL(/\/auth\/login/);
});
});
test.describe("Authenticated user", () => {
test.use({ storageState: "tests/.auth/user.json" });
test("can access generate page", async ({ page }) => {
await page.goto("/generate");
await expect(page.locator("form")).toBeVisible();
});
});
Testing Forms
test("creates new collection", async ({ page }) => {
await page.goto("/collections/new");
await page.fill("[name='title']", "My Collection");
await page.fill("[name='description']", "Test description");
await page.click("button[type='submit']");
await expect(page).toHaveURL(/\/collections\//);
await expect(page.locator("h1")).toContainText("My Collection");
});
Testing API Endpoints
test("API returns data", async ({ request }) => {
const response = await request.get("/api/images");
expect(response.ok()).toBeTruthy();
const data = await response.json();
expect(data).toHaveProperty("images");
expect(Array.isArray(data.images)).toBe(true);
});
test("API requires authentication", async ({ request }) => {
const response = await request.post("/api/collections", {
data: { title: "Test" },
});
expect(response.status()).toBe(401);
});
Page Object Pattern
import { Page, Locator } from "@playwright/test";
export class CollectionPage {
readonly page: Page;
readonly titleInput: Locator;
readonly submitButton: Locator;
constructor(page: Page) {
this.page = page;
this.titleInput = page.locator("[name='title']");
this.submitButton = page.locator("button[type='submit']");
}
async goto() {
await this.page.goto("/collections/new");
}
async create(title: string) {
await this.titleInput.fill(title);
await this.submitButton.click();
}
}
test("creates collection", async ({ page }) => {
const collectionPage = new CollectionPage(page);
await collectionPage.goto();
await collectionPage.create("My Collection");
});
Test File Organization
tests/
├── pages.spec.ts # E2E page tests
├── api.spec.ts # E2E API tests
├── .auth/ # Auth state storage
└── pages/ # Page objects
app/
├── utils/
│ ├── myUtil.ts
│ └── myUtil.test.ts # Co-located unit test
└── server/
├── getUser.server.ts
└── getUser.test.ts # Server function test
Common Assertions
expect(value).toBe(expected);
expect(value).toEqual(expected);
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeDefined();
expect(array).toContain(item);
expect(array).toHaveLength(3);
expect(object).toHaveProperty("key");
expect(fn).toHaveBeenCalled();
expect(fn).toHaveBeenCalledWith(arg);
expect(async fn).rejects.toThrow();
await expect(page).toHaveURL(/pattern/);
await expect(page).toHaveTitle(/title/);
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();
await expect(locator).toBeEnabled();
await expect(locator).toBeDisabled();
await expect(locator).toContainText("text");
await expect(locator).toHaveAttribute("name", "value");
await expect(locator).toHaveCount(5);
Test Data Fixtures
export const testUser = {
id: "test-user-1",
email: "test@example.com",
name: "Test User",
credits: 100,
};
export const testImage = {
id: "test-image-1",
prompt: "A beautiful sunset",
url: "https://example.com/image.png",
model: "dall-e-3",
};
Checklist