
review-pr
Review a pull request for Playwright test automation using project standards. Use when user asks to
Pull Request Review
Process
1. Gather PR Information
# Get PR metadata
gh pr view <number> --json title,body,state,additions,deletions,changedFiles,baseRefName,headRefName
# Get full diff
gh pr diff <number>
2. Read modified files for context
Use Read tool to view complete files mentioned in the diff.
3. Review Checklist (Production Focus)
Critical Issues (Block Merge)
- Build/compilation errors or type issues
- Test failures or broken fixtures
- Logic errors in test scenarios
- Data integrity issues (duplicate users, race conditions)
Test Design & Business Logic
- Test covers actual user scenarios - not just happy paths
- Business logic is correct - verifies the right behavior
- Test scope is appropriate - not too broad or too narrow
- Edge cases considered - what could go wrong?
- Assertions validate meaningful outcomes - not just presence of elements
Test Stability & Reliability
- User-Facing Locators - prefer
getByRole/getByTextover CSS/XPath - Selectors are resilient - won't break on UI changes (use role, text, semantic HTML where possible)
- No race conditions - proper waits for async operations
- No hard-coded waits -
page.waitForTimeout()is a red flag - Parallel execution safe - no shared state or dependencies
- Data isolation - each test has unique data, no conflicts
- Flaky patterns avoided - timing-dependent assertions, external dependencies
- Idempotent for retries - test can rerun without manual cleanup
- Proper cleanup on failure - no partial state left behind
Test Independence & Isolation
- Tests can run in any order - no implicit dependencies between tests
- No shared mutable state - each test creates its own data
- Statelessness - no global variable modification
- Fixture Scoping - use fixtures for setup/teardown (auth, seeding)
- Proper setup/teardown - beforeEach/afterEach used correctly
- No test pollution - one test's failure doesn't cascade
Test Data Strategy
- Data creation strategy - prefer Factories over hardcoded JSON
- Unique data per test/worker - no conflicts in parallel
- Cleanup strategy defined - how is test data removed?
- No hardcoded magic values - data is meaningful and traceable
Code Quality & Architecture
File & Folder Organization:
Consistent structure makes the codebase navigable and predictable.
File naming conventions:
- kebab-case for all files -
checkout.page.ts, notCheckoutPage.tsorcheckout_page.ts - Suffix indicates purpose:
.page.ts- page objects (cart.page.ts,payment.page.ts).component.ts- reusable UI components (header.component.ts,order-table.component.ts).types.ts- type definitions (cart.types.ts,product.types.ts).spec.ts- test files (complete-order.spec.ts).fixture.ts- test fixtures.factory.ts- data factories (person.factory.ts)
- No suffix for utilities -
table.ts,convert-data.ts(pure functions)
Folder structure:
- Pages grouped by feature/domain:
src/ui/pages/ ├── cart/ # cart.page.ts, checkout.page.ts, payment.page.ts ├── product/ # product.page.ts, products.page.ts ├── auth/ # login.page.ts, signup.page.ts └── components/ # Shared components (header, modals, tables) - Tests mirror source structure:
tests/ui/ ├── e2e/ # End-to-end flows (complete-order.spec.ts) ├── cart/ # Cart-specific tests └── product/ # Product-specific tests - Shared code in dedicated folders:
src/ui/types/- shared TypeScript typessrc/ui/test-data/constants/- test data constantssrc/utils/- utility functions
Red flags to catch:
- File without proper suffix (e.g.,
Checkout.tsinstead ofcheckout.page.ts) - Page object in wrong folder (e.g., cart page in
product/folder) - Component that should be in
components/but is nested in a page folder - Test file not following
.spec.tsconvention - Mixed concerns in one folder (pages + utils + types together)
- Missing explicit return types on exported functions (non-exported local/private arrow functions may omit return types when the type is obvious from context)
Naming Conventions:
-
Variables/methods follow conventions - camelCase, descriptive, no abbreviations
-
File names are consistent - kebab-case, clear purpose
-
Declarative Naming - follow
should [outcome] when [scenario]pattern -
Constants are UPPER_CASE - configuration values, magic numbers extracted
-
Page Object method names match their return type and intent:
Prefix Return type Purpose Example waitFor*Promise<this>Wait for a condition, enable chaining waitForLoad(): Promise<this>expect*Promise<void>Thin Playwright expectwrapper (single assertion)expectUrl(url: string): Promise<void>assert*Promise<void>Custom business-logic assertion (may combine multiple checks) assertCartTotal(expected: number): Promise<void>is*/has*Promise<boolean>Query state, must return boolean isSubmitEnabled(): Promise<boolean>get*Promise<string | number | …>Extract a value from the page getHeaderText(): Promise<string>verb (action) Promise<void>Describe user intent, not mechanism goBack(): Promise<void>,fillEmail(v: string): Promise<void>Red flags to catch:
is*method that returnsPromise<this>orPromise<void>instead ofPromise<boolean>(e.g.,isLoaded(): Promise<this>— should bewaitForLoad())click*method name — describes mechanism, not intent (e.g.,clickBack()should begoBack())assert*for a thin Playwright wrapper — useexpect*prefix instead (e.g.,assertUrl()→expectUrl())- Ambiguous names like
check*— unclear whether it asserts or returns a boolean
Over-Engineering:
- No premature abstraction - don't create utilities for single use
- Avoid Hasty Abstractions (AHA) - avoid complex helpers handling many cases
- No unnecessary complexity - KISS principle, straightforward solutions
- No over-generic methods - specific is better than flexible
- No unused code - dead code removed, imports cleaned up
Method Optimization:
- Methods do one thing - single responsibility
- No duplicate logic - DRY principle applied where it makes sense
- Explicit return types on exported functions - all exported/public functions must have explicit return types (e.g.,
Promise<void>,string,number). Non-exported local arrow functions may omit return types when obvious from context. - Strict Type Safety - NO 'any' type, use generics/unknown
- Async/await used correctly - no unnecessary awaits, proper error handling
Code Style:
- No redundant assignments - don't assign parameter to new variable of same type (e.g.,
const userData = userwhenuseris already typed) - No unnecessary intermediate variables - use parameters directly when no transformation needed
- Consistent access modifiers - follow base class patterns (e.g.,
protectedforuniqueElementif BasePage usesprotected) - No shadowing - avoid variable names that shadow outer scope variables
- No duplicated assertion blocks - extract repeated assertions into private helper methods (e.g., same assertions for delivery/invoice address →
assertAddressBlock(locator, data)) - Use template literals - prefer
`${a} ${b}`overa + ' ' + bfor string concatenation
Types & Constants Organization:
This is a tricky area with multiple valid approaches. Flag for discussion when patterns are inconsistent.
Where types should live:
- Shared types in
src/ui/types/- types used across multiple pages (e.g.,CartItemused in cart.page.ts AND checkout.page.ts → extract totypes/cart.types.ts) - No duplicate type definitions - same type defined in multiple files is a red flag; search for duplicates
- Page-specific types can stay in page files - if a type is ONLY used in one page, it can stay there
- Derived types stay with constants - when type uses
typeof CONSTANT(e.g.,type CreditCard = typeof CREDIT_CARDS[keyof typeof CREDIT_CARDS]), keep them together in constants file - this is OK - Utility Types used -
Pick,Omit,Partialfor leaner types - Interface Segregation - functions take only needed data (e.g.
idnotUserobj) - No Enums - prefer
as conststring unions
Constants organization:
- Module Boundaries - no circular refs or reaching into private folders
- Constants in
test-data/constants/- test data like credit cards, URLs, timeouts - Constants file = one domain -
credit-card.tsfor cards,urls.tsfor URLs, don't mix domains - UPPER_CASE for constants -
CREDIT_CARDSnotcreditCards -
as constfor literal types - enables type inference from values
Recommended structure:
src/ui/
├── types/ # Shared types
│ ├── cart.types.ts # CartItem, OrderItem
│ ├── user.types.ts # Person, Address
│ └── index.ts # Re-exports
├── test-data/
│ └── constants/ # Test data with derived types
│ ├── credit-card.ts # CREDIT_CARDS + CreditCard type (OK together)
│ └── timeouts.ts # TIMEOUTS
└── pages/
└── cart/
└── cart.page.ts # Imports from types/, no local CartItem
Red flags to catch:
- Same interface/type defined in 2+ files → extract to shared types
- Type in page file that's imported by another page → move to types/
- Constants scattered across page files → consolidate to constants/
- Mixing unrelated constants in one file → split by domain
Config Organization:
- Environment configs are clean - no hardcoded URLs/credentials in code
- Config structure makes sense - easy to find and modify settings
- Secrets are not committed - .env files in .gitignore
- Config is typed - TypeScript interfaces for config objects
Utils & Helpers:
- Utils are truly reusable - used in multiple places, not premature
- Utils have clear purpose - not a dumping ground
- Utils are well-documented - JSDoc or clear naming
- Utils are testable - pure functions when possible
Reporting & Debugging:
-
@step()usage is meaningful - steps tell a story in reports - Error messages are actionable - include context, expected vs actual
- Console logs are removed - no debug statements left behind
- Screenshots/videos will be useful - failures capture enough context
Page Object Quality
- Single Responsibility (SRP) - elements & interactions only, NO assertions
- Encapsulation - private/protected locators, public action methods
- Action-Oriented Methods -
submitFormnotclickSubmit - Dependency Inversion - injected via Fixtures (no
new Page(page)) - Component Composition - use shared components (e.g. Header)
- Abstractions make sense - methods represent user actions
- Not over-engineered - simple, clear, maintainable
- Proper error handling - failures are debuggable with clear messages
CI/CD & Pipeline Considerations
- Timeouts are reasonable - won't fail in slow CI environments
- Resource efficient - not creating unnecessary browser contexts
- Environment agnostic - works in CI without local dependencies
- Retry-friendly - tests work with Playwright's retry mechanism
- Parallelization ready - consider worker count and resource limits
Test Maintainability (6-month test)
- Easy to update - when requirements change, is this easy to modify?
- Tests behavior, not implementation - UI changes shouldn't break logic tests
- New team member readable - can someone understand this without context?
- Change impact is limited - page object change doesn't break 50 tests
Assertion Strategy
- Asserting the right things - meaningful business outcomes
- Not over-asserting - too many assertions = brittle tests
- Not under-asserting - weak tests that pass when they shouldn't
- Soft vs hard assertions used correctly - collect multiple failures vs fail fast
Security in Tests
- No credentials in code - use environment variables
- Sensitive data not in screenshots - mask or avoid capturing
- API keys/tokens managed properly - not committed, rotatable
- Test users are isolated - not using production accounts
Regression Risk Assessment
- Shared fixture changes reviewed - could break other tests
- Page object modifications - check downstream impact
- Config changes - verify no unintended side effects
- New dependencies introduced - npm packages reviewed
Git Hygiene
- Commit messages are clear - explain WHY, not just WHAT
- PR title is descriptive - explains the feature/fix clearly
- Commits are logical - each commit is a coherent unit of work
- No "WIP", "test", "fix" commit messages - be professional
4. Output Format
Review Style - Senior AQA Mindset:
- Direct, concise, production-focused
- CRITICAL: Re-read ALL changed files before posting - don't review stale code
- Only show sections with issues - skip sections that are fine
- At the end, list areas that are good in one line: "✅ Good: [area1], [area2], [area3]"
- For commits with issues, provide specific rewrite suggestions
- Think long-term: "Will this be maintainable in 6 months?"
- Challenge decisions: "Why this approach? Is it necessary?"
- Consider CI/CD: "Will this work reliably in pipeline?"
- Assess risk: "What could this break? What's not covered?"
- DO NOT include "Test Coverage Analysis" section - this will be handled by a separate agent
Patterns to IGNORE (not issues):
- Duplicate fixture definitions across different fixture files (e.g.,
pagesin bothfixtures/index.tsandapi-fixtures/api-user.fixture.ts) — these may coexist intentionally for A/B performance comparison between fixture strategies. Do not flag as duplication or suggestmergeTestsunless the duplicates have diverged in behavior.
Patterns to flag for extra review (not issues, just note them):
- Public locators used for assertion flexibility
- Generic assertion helpers (e.g.,
assertElementVisible(locator)) - Trade-offs between encapsulation and pragmatism
- Type defined in page file that might be shared later - ask: "Will other pages need this?"
- Constants with derived types - verify
as constis used for type inference - Helper methods that could be private but are public - intentional or oversight?
- New folder created - does it fit the existing structure? Is it the right location?
- File without standard suffix - intentional (utility) or missing convention?
## PR Review: [Title] (#[Number])
**Changes**: +X / -Y across Z files
---
[ONLY INCLUDE SECTIONS WITH ISSUES - SKIP SECTIONS THAT ARE FINE]
### 🔴 Blockers
Critical issues that must be fixed.
### ⚠️ Issues
| File:Line | Issue | Suggestion |
|-----------|-------|------------|
| `file.ts:42` | Description | How to fix |
### 👀 For Your Review
Design decisions that may be intentional - verify they fit your context:
- [pattern] at `file.ts:line` - [brief description]
### 💡 Discussion Points
Areas with multiple valid approaches - worth a team decision:
- [topic] - Option A: [approach]. Option B: [approach]. Recommendation: [suggestion]
*Common discussion topics: type location (page vs shared), helper extraction timing, assertion granularity*
### 📝 Git Hygiene
**PR title:** `Current` → `Suggested improvement`
**Commits to improve:**
| Current | Suggested |
|---------|-----------|
| `bad commit msg` | `feat: clear description of change` |
---
✅ **Good:** [list areas with no issues, e.g., "test isolation, factory patterns, step decorator usage"]
**Verdict:** Approved / Changes requested
🤖 Generated with [Claude Code](https://claude.com/claude-code)
5. Automatically Post Review
After generating the review, ALWAYS post it as a comment on the PR:
gh pr review <number> --comment --body "$(cat <<'EOF'
[review content]
EOF
)"
Important: Do not ask the user if they want to post - automatically post the review after completing the analysis.
Reference
See docs/design-docs/core-beliefs.md for detailed testing standards.