AGENTS.md
e2e/AGENTS.mdAGENTS.md
Quality
100/100
Scores the file, not the repository.Length
758 words
21 headings · 4 code blocksRepository
55k
— · pushed 0 days agoLast changed
3 days ago
First indexed 3 days ago.1# AGENTS.md23E2E testing guidance for AI assistants (Claude, Codex, etc.) working with Ghost tests.45**IMPORTANT**: When creating or modifying E2E tests, always refer to `./.claude/E2E_TEST_WRITING_GUIDE.md` for comprehensive testing guidelines and patterns.67## Critical Rules81. **Always follow ADRs** in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)92. **Always use pnpm**, never npm103. **Always run after changes**: `pnpm lint` and `pnpm test:types`114. **Never use CSS/XPath selectors** - only semantic locators or data-testid125. **Prefer less comments and giving things clear names**1314## Running E2E Tests1516**`pnpm dev` must be running before you run E2E tests.** The E2E test runner auto-detects17whether the admin dev server is reachable at `http://127.0.0.1:5174`. If it is, tests run18in **dev mode** (fast, no pre-built Docker image required). If not, tests fall back to19**build mode** which requires a `ghost-e2e:local` Docker image that is only built in CI.2021**If you see the error `Build image not found: ghost-e2e:local`, it means `pnpm dev` is22not running.** Start it first, wait for the admin dev server to be ready, then re-run tests.2324```bash25# Terminal 1 (or background): Start dev environment from the repo root26pnpm dev2728# Wait for the admin dev server to be reachable (http://127.0.0.1:5174)2930# Terminal 2: Run e2e tests from the e2e/ directory31pnpm test # Run all tests32pnpm test tests/path/to/test.ts # Run specific test33pnpm lint # Required after writing tests34pnpm test:types # Check TypeScript errors35pnpm build # Required after factory changes36pnpm test --debug # See browser during execution, for debugging37PRESERVE_ENV=true pnpm test # Debug failed tests (keeps containers)38```39## Test Structure4041### Naming Conventions42- **Test suites**: `Ghost Admin - Feature` or `Ghost Public - Feature`43- **Test names**: `what is tested - expected outcome` (lowercase)44- **One test = one scenario** (never mix multiple scenarios)4546### AAA Pattern47```typescript48test('action performed - expected result', async ({page}) => {49 const analyticsPage = new AnalyticsGrowthPage(page);50 const postFactory = createPostFactory(page.request);51 const post = await postFactory.create({status: 'published'});5253 await analyticsPage.goto();54 await analyticsPage.topContent.postsButton.click();5556 await expect(analyticsPage.topContent.contentCard).toContainText('No conversions');57});58```5960## Page Objects6162### Structure63```typescript64export class AnalyticsPage extends AdminPage {65 // Public readonly locators only66 public readonly saveButton = this.page.getByRole('button', {name: 'Save'});67 public readonly emailInput = this.page.getByLabel('Email');6869 // Semantic action methods70 async saveSettings() {71 await this.saveButton.click();72 }73}74```7576### Rules77- Page Objects are located in `helpers/pages/`78- Expose locators as `public readonly` when used with assertions79- Methods use semantic names (`login()` not `clickLoginButton()`)80- Use `waitFor()` for guards, never `expect()` in page objects81- Keep all assertions in test files8283## Locators (Strict Priority)84851. **Semantic** (always prefer):86 - `getByRole('button', {name: 'Save'})`87 - `getByLabel('Email')`88 - `getByText('Success')`89902. **Test IDs** (when semantic unavailable):91 - `getByTestId('analytics-card')`92 - Suggest adding `data-testid` to Ghost codebase when needed93943. **Never use**: CSS selectors, XPath, nth-child, class names9596### Playwright MCP Usage97- Use `mcp__playwright__browser_snapshot` to find elements98- Use `mcp__playwright__browser_click` with semantic descriptions99- If no good locator exists, suggest `data-testid` addition to Ghost100101## Test Data102103### Factory Pattern (Required)104```typescript105import {PostFactory, UserFactory} from '../data-factory';106107const postFactory = createPostFactory(page.request);108const post = await postFactory.create({userId: user.id});109```110111## Best Practices112113### DO ✅114- Use `usePerTestIsolation()` from `@/helpers/playwright/isolation` if a file needs per-test isolation115- Treat `config` and `labs` as environment-identity inputs: changing them should be an intentional part of test setup116- Use `resetEnvironment()` only in `beforeEach` hooks when you need a forced recycle inside per-file mode117- Keep `stripeEnabled` tests in per-test mode; the fixture forces this automatically118- Use factories for all test data119- Use Playwright's auto-waiting120- Run tests multiple times to ensure stability121- Use `test.only()` for debugging single tests122123### DON'T ❌124- Use `test.describe.parallel(...)` or `test.describe.serial(...)` in e2e tests125- Use nested `test.describe.configure({mode: ...})` (mode toggles are root-level only)126- Call `resetEnvironment()` after resolving `baseURL`, `page`, `pageWithAuthenticatedUser`, or `ghostAccountOwner`127- Hard-coded waits (`waitForTimeout`)128- networkidle in waits (`networkidle`)129- Test dependencies (Test B needs Test A)130- Direct database manipulation131- Multiple scenarios in one test132- Assertions in page objects133- Manual login (auto-authenticated via fixture)134135## Project Structure136- `tests/admin/` - Admin area tests137- `tests/public/` - Public site tests138- `helpers/pages/` - Page objects139- `helpers/environment/` - Container management140- `data-factory/` - Test data factories141142## Validation Checklist143After writing tests, verify:1441. Test passes: `pnpm test path/to/test.ts`1452. Linting passes: `pnpm lint`1463. Types check: `pnpm test:types`1474. Follows AAA pattern with clear sections1485. Uses page objects appropriately1496. Uses semantic locators or data-testid only1507. No hard-coded waits or CSS selectors151
Also in TryGhost/Ghost
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| TryGhost/GhostAGENTS.md · 55k | AGENTS.md | setupbuildtestlint-format+10 | 89/100 | 3 days ago | |
| TryGhost/Ghostapps/shade/AGENTS.md · 55k | AGENTS.md | buildtestlint-formatstyle+6 | 96/100 | 3 days ago | |
| TryGhost/Ghostkoenig/koenig-lexical/CLAUDE.md · 55k | CLAUDE.md | setuptestarchagent-behaviour | 78/100 | 3 days ago |
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| aaif-goose/gooseAGENTS.md · 52k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 199k | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| SkeneTechnologies/skene-cookbookAGENTS.md · 51 | AGENTS.md | setupbuildtestlint-format+7 | 100/100 | 2 days ago | |
| wpscanteam/wpscanAGENTS.md · 9.7k | AGENTS.md | setupbuildteststyle+6 | 100/100 | 2 days ago | |
| code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 67k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 2 days ago | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 3 days ago | |
| elastic/elasticsearchx-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/AGENTS.md · 78k | AGENTS.md | buildtestlint-formatstyle+2 | 100/100 | 3 days ago |
