---
name: e2e-test-generation
description: Create E2E tests for Chrome extension using Playwright
license: MIT
compatibility: opencode
metadata:
audience: developers
framework: playwright
target: extension
---
## What I do
- Create Playwright E2E tests for the Chrome extension
- Use `data-testid` and `getByTestId()` as the primary test selector pattern
- Add semantic selectors (role-based, data-testid) instead of class selectors
- Add `data-testid` attributes to components when needed for robust test selectors
- Define test constants for reusable test data in `apps/extension/tests/constants.ts`
- Follow project conventions: test.describe for 2+ tests, exact text matches, no evaluate() for clicks
- Use project fixtures: `import { test, expect } from '../fixtures/bookmark-fixture'`
- Run the generated test file after making changes
## When to use me
Use this when you need to create new E2E tests or update existing tests for the Chrome extension.
I will ask clarifying questions if:
- The feature to test is not clearly defined
- You need to test across multiple projects (extension vs web app)
- Test data requirements are unclear
## Selector Priority (use in this order)
1. **data-testid with getByTestId()** (highest priority - standard for this project)
```typescript
bookmarksPage.getByTestId('folder-item-Main');
bookmarksPage.getByTestId('bookmark-item-React Docs');
bookmarksPage.getByTestId('person-item-John Doe');
// For dynamic patterns, use locator with ^=
bookmarksPage.locator('[data-testid^="bookmark-item-"]');
```
2. **Accessible Role Selectors** (when data-testid not available)
```typescript
bookmarksPage.getByRole('button', { name: 'Add' });
bookmarksPage.getByRole('dialog', { name: 'Add folder' });
```
3. **Placeholder/Label Selectors**
```typescript
dialog.getByPlaceholder('Enter folder name');
bookmarksPage.getByLabel('History');
```
4. **Title/Alt Selectors**
```typescript
bookmarksPage.getByTitle('Edit Bookmark');
bookmarksPage.getByAltText('User avatar');
```
5. **Text Content** (use sparingly)
```typescript
bookmarksPage.getByText('Tagged Persons');
```
## Selector Anti-Patterns (NEVER use)
- Class selectors: `[class*="Folder-module__container"]`, `.some-generated-class`
- Custom data attributes (other than `data-testid`): `data-folder-name`, `data-context-id`, etc.
- Regex for simple text: `{ name: /add/i }` → use `{ name: 'Add' }`
- Positional selectors when specific selection is possible: `.first()`, `.nth()`
- Generic CSS selectors without semantic meaning
- Generic element selectors: `img`, `div`, `span` (use `data-testid` instead)
- `.evaluate()` for clicks (use direct `.click()` instead)
## When Positional Selectors Are OK
- Generic UI elements without unique `data-testid` attributes
- Tests that need "any" element rather than a specific one
- Temporary UI elements (tooltips, context menus) where adding `data-testid` is impractical
## Coding Style Guidelines
- **Exact text matches**: Use `{ name: 'Add' }` not `{ name: /add/i }`
- **Specific selectors**: Target parent div to avoid duplicate elements
- **Test constants**: Define constants in `apps/extension/tests/constants.ts` for reusable data
- **Group tests**: Only use `test.describe` for 2+ tests
- **Clear comments**: Explain "why", not "what"
## Common Patterns
**Opening a dialog:**
```typescript
const addButton = bookmarksPage.getByRole('button', { name: 'Add' });
await addButton.click();
const dialog = bookmarksPage.getByRole('dialog', { name: 'Add folder' });
await expect(dialog).toBeVisible();
```
**Filling a form:**
```typescript
await dialog.getByPlaceholder('Enter folder name').fill('Test Folder');
await dialog.getByRole('button', { name: 'Save' }).click();
await expect(dialog).toBeHidden();
```
**Context menu:**
```typescript
await element.click({ button: 'right' });
const editOption = bookmarksPage.getByTestId('context-menu-item-edit');
await expect(editOption).toBeVisible();
await editOption.click();
```
**Multi-select with keyboard:**
```typescript
await firstItem.click();
await secondItem.click({ modifiers: ['Meta'] }); // Cmd/Ctrl+click
```
**Waiting for navigation:**
```typescript
const [newPage] = await Promise.all([
context.waitForEvent('page', { timeout: 15_000 }),
element.dblclick(),
]);
expect(newPage).toBeTruthy();
```
## After Test Creation
Run the generated test file to verify it works:
```bash
playwright test apps/extension/tests/specs/<your-test-file>.spec.ts
```
## Checklist Before Committing
- [ ] Tests use `getByTestId()` with `data-testid` as primary selector
- [ ] Tests use semantic selectors (roles) when `data-testid` not available
- [ ] No regex for simple text matches
- [ ] No class-based selectors
- [ ] No custom data attributes (only `data-testid`)
- [ ] Avoid `.evaluate()` for clicks (use direct `.click()`)
- [ ] Test constants defined for reusable data
- [ ] `data-testid` attributes added to components if needed
- [ ] Only use `test.describe` for 2+ tests
- [ ] Clear, descriptive test names
- [ ] All tests pass locally
- [ ] Tests are deterministic (no flaky behavior)