Build comprehensive end-to-end tests with Playwright for modern web applications
# Advanced Playwright E2E Testing
Master end-to-end testing with Playwright using Google Antigravity IDE. This guide covers advanced patterns, visual testing, and CI/CD integration.
## Why Playwright?
Playwright provides reliable cross-browser testing with modern web support. Google Antigravity IDE's Gemini 3 engine suggests optimal test patterns.
## Configuration
```typescript
// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests/e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
reporter: [
["html", { open: "never" }],
["json", { outputFile: "test-results/results.json" }],
["junit", { outputFile: "test-results/junit.xml" }],
],
use: {
baseURL: process.env.BASE_URL || "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},
{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
{
name: "mobile-chrome",
use: { ...devices["Pixel 5"] },
},
{
name: "mobile-safari",
use: { ...devices["iPhone 12"] },
},
],
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
```
## Page Object Model
```typescript
// tests/e2e/pages/LoginPage.ts
import { Page, Locator, expect } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
readonly forgotPasswordLink: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.getByLabel("Email");
this.passwordInput = page.getByLabel("Password");
this.submitButton = page.getByRole("button", { name: "Sign In" });
this.errorMessage = page.getByRole("alert");
this.forgotPasswordLink = page.getByRole("link", { name: "Forgot password?" });
}
async goto() {
await this.page.goto("/login");
await expect(this.page).toHaveTitle(/Sign In/);
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(message: string) {
await expect(this.errorMessage).toBeVisible();
await expect(this.errorMessage).toContainText(message);
}
async expectLoggedIn() {
await expect(this.page).toHaveURL(/\/dashboard/);
}
}
```
## Test Fixtures
```typescript
// tests/e2e/fixtures.ts
import { test as base, expect } from "@playwright/test";
import { LoginPage } from "./pages/LoginPage";
import { DashboardPage } from "./pages/DashboardPage";
type Fixtures = {
loginPage: LoginPage;
dashboardPage: DashboardPage;
authenticatedPage: Page;
};
export const test = base.extend<Fixtures>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await use(loginPage);
},
dashboardPage: async ({ page }, use) => {
const dashboardPage = new DashboardPage(page);
await use(dashboardPage);
},
authenticatedPage: async ({ page, context }, use) => {
// Set up authentication state
await context.addCookies([
{
name: "auth_token",
value: process.env.TEST_AUTH_TOKEN!,
domain: "localhost",
path: "/",
},
]);
await page.goto("/dashboard");
await expect(page.getByText("Welcome")).toBeVisible();
await use(page);
},
});
export { expect };
```
## Comprehensive Test Suite
```typescript
// tests/e2e/auth.spec.ts
import { test, expect } from "./fixtures";
test.describe("Authentication", () => {
test.describe("Login", () => {
test("should login with valid credentials", async ({ loginPage }) => {
await loginPage.goto();
await loginPage.login("test@example.com", "password123");
await loginPage.expectLoggedIn();
});
test("should show error with invalid credentials", async ({ loginPage }) => {
await loginPage.goto();
await loginPage.login("test@example.com", "wrongpassword");
await loginPage.expectError("Invalid credentials");
});
test("should validate required fields", async ({ loginPage }) => {
await loginPage.goto();
await loginPage.submitButton.click();
await expect(loginPage.emailInput).toHaveAttribute("aria-invalid", "true");
});
});
test.describe("Protected Routes", () => {
test("should redirect to login when not authenticated", async ({ page }) => {
await page.goto("/dashboard");
await expect(page).toHaveURL(/\/login/);
});
test("should access dashboard when authenticated", async ({ authenticatedPage }) => {
await expect(authenticatedPage.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
});
});
```
## Visual Regression Testing
```typescript
// tests/e2e/visual.spec.ts
import { test, expect } from "@playwright/test";
test.describe("Visual Regression", () => {
test("homepage should match snapshot", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveScreenshot("homepage.png", {
fullPage: true,
animations: "disabled",
});
});
test("dashboard components should match snapshots", async ({ page }) => {
await page.goto("/dashboard");
// Wait for dynamic content
await page.waitForLoadState("networkidle");
// Screenshot specific components
const sidebar = page.getByRole("navigation");
await expect(sidebar).toHaveScreenshot("sidebar.png");
const chart = page.locator("[data-testid=analytics-chart]");
await expect(chart).toHaveScreenshot("analytics-chart.png");
});
});
```
## API Mocking
```typescript
// tests/e2e/mocked.spec.ts
import { test, expect } from "@playwright/test";
test("should display mocked data", async ({ page }) => {
await page.route("**/api/users", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
users: [
{ id: "1", name: "John Doe", email: "john@example.com" },
{ id: "2", name: "Jane Doe", email: "jane@example.com" },
],
}),
});
});
await page.goto("/users");
await expect(page.getByText("John Doe")).toBeVisible();
await expect(page.getByText("Jane Doe")).toBeVisible();
});
```
## Best Practices
- Use Page Object Model for maintainability
- Create custom fixtures for reuse
- Implement visual regression testing
- Mock external APIs for reliability
- Configure proper retry strategies
- Run tests in CI/CD pipelines
Google Antigravity IDE provides Playwright test templates and automatically suggests test patterns for comprehensive coverage.This Playwright prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their playwright implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Playwright projects, consider mentioning your framework version, coding style, and any specific libraries you're using.