End-to-end testing with Playwright for Google Antigravity projects including page objects, fixtures, and assertions.
# Playwright E2E Test Patterns for Google Antigravity
Master end-to-end testing with Playwright in your Google Antigravity IDE projects. This comprehensive guide covers page objects, fixtures, assertions, and CI integration optimized for Gemini 3 agentic development.
## Playwright Configuration
Configure Playwright:
```typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['html'], ['json', { outputFile: 'test-results.json' }]],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
```
## Page Objects
Create reusable page objects:
```typescript
// e2e/pages/HomePage.ts
import { Page, Locator, expect } from '@playwright/test';
export class HomePage {
readonly page: Page;
readonly searchInput: Locator;
readonly promptCards: Locator;
readonly loadMoreButton: Locator;
constructor(page: Page) {
this.page = page;
this.searchInput = page.getByRole('searchbox');
this.promptCards = page.getByTestId('prompt-card');
this.loadMoreButton = page.getByRole('button', { name: /load more/i });
}
async goto() {
await this.page.goto('/');
await this.page.waitForLoadState('networkidle');
}
async search(query: string) {
await this.searchInput.fill(query);
await this.page.waitForLoadState('networkidle');
}
async getPromptCount() {
return this.promptCards.count();
}
async clickPrompt(index: number) {
await this.promptCards.nth(index).click();
}
async loadMore() {
await this.loadMoreButton.click();
await this.page.waitForLoadState('networkidle');
}
async expectPromptsVisible(minCount: number) {
await expect(this.promptCards).toHaveCount({ minimum: minCount });
}
}
```
## Test Fixtures
Create reusable fixtures:
```typescript
// e2e/fixtures.ts
import { test as base } from '@playwright/test';
import { HomePage } from './pages/HomePage';
import { PromptPage } from './pages/PromptPage';
type Fixtures = {
homePage: HomePage;
promptPage: PromptPage;
};
export const test = base.extend<Fixtures>({
homePage: async ({ page }, use) => {
const homePage = new HomePage(page);
await use(homePage);
},
promptPage: async ({ page }, use) => {
const promptPage = new PromptPage(page);
await use(promptPage);
},
});
export { expect } from '@playwright/test';
```
## Test Examples
Write comprehensive tests:
```typescript
// e2e/home.spec.ts
import { test, expect } from './fixtures';
test.describe('Home Page', () => {
test('displays prompts on load', async ({ homePage }) => {
await homePage.goto();
await homePage.expectPromptsVisible(12);
});
test('filters prompts by search', async ({ homePage }) => {
await homePage.goto();
await homePage.search('react');
const count = await homePage.getPromptCount();
expect(count).toBeGreaterThan(0);
});
test('loads more prompts', async ({ homePage }) => {
await homePage.goto();
const initialCount = await homePage.getPromptCount();
await homePage.loadMore();
const newCount = await homePage.getPromptCount();
expect(newCount).toBeGreaterThan(initialCount);
});
test('navigates to prompt detail', async ({ homePage, page }) => {
await homePage.goto();
await homePage.clickPrompt(0);
await expect(page).toHaveURL(//prompts/.+/);
});
});
```
## Authentication
Handle authentication:
```typescript
// e2e/auth.setup.ts
import { test as setup } from '@playwright/test';
const authFile = 'e2e/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: /sign in/i }).click();
await page.waitForURL('/dashboard');
await page.context().storageState({ path: authFile });
});
// Use in tests
test.use({ storageState: 'e2e/.auth/user.json' });
test('authenticated user can create prompt', async ({ page }) => {
await page.goto('/create');
// Test as authenticated user
});
```
## Visual Testing
Add visual regression:
```typescript
// e2e/visual.spec.ts
import { test, expect } from '@playwright/test';
test('homepage visual regression', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', {
maxDiffPixelRatio: 0.02,
});
});
test('prompt card visual regression', async ({ page }) => {
await page.goto('/prompts/react-typescript');
const card = page.getByTestId('prompt-card');
await expect(card).toHaveScreenshot('prompt-card.png');
});
```
## Best Practices
1. **Use Page Object Model** for maintainability
2. **Create fixtures** for reusable setup
3. **Use test.describe** for grouping
4. **Handle auth** with storage state
5. **Run tests in parallel** for speed
6. **Capture traces** on failure
7. **Add visual regression** testsThis 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.