Skip to content

Commit 12b0832

Browse files
authored
fix: no-conditional-in-test does not trigger for conditionals in test metadata (fixes #363) (#372)
* fix: no-conditional-in-test rule triggers for test metadata conditionals * Agents win * Sync * Sync
1 parent 543ce16 commit 12b0832

File tree

4 files changed

+350
-15
lines changed

4 files changed

+350
-15
lines changed
Lines changed: 315 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,321 @@
1-
---
2-
alwaysApply: true
3-
---
1+
# ESLint Plugin Playwright - Agent Guidelines
42

5-
## Pull Requests
3+
## Project Overview
64

7-
- When creating pull requests, add the `cursor` label to the pull request.
5+
This is a comprehensive ESLint plugin for Playwright testing that provides 50+
6+
rules to enforce best practices and catch common issues in Playwright test code.
7+
The plugin is built with TypeScript, uses modern ESLint APIs, and follows strict
8+
testing practices.
89

9-
## Commits
10+
## Architecture & Code Structure
11+
12+
### Core Architecture
13+
14+
- **Entry Point**: `src/index.ts` - Exports all rules and configurations
15+
- **Rule Structure**: Each rule follows the pattern `src/rules/{rule-name}.ts`
16+
with corresponding tests
17+
- **Utilities**: Shared utilities in `src/utils/` for AST manipulation, parsing,
18+
and common patterns
19+
- **Configurations**: Supports both flat config (ESLint 9+) and legacy config
20+
formats
21+
22+
### Key Patterns & Conventions
23+
24+
#### Rule Implementation Pattern
25+
26+
```typescript
27+
import { createRule } from '../utils/createRule.js'
28+
import { parseFnCall } from '../utils/parseFnCall.js'
29+
// ... other imports
30+
31+
export default createRule({
32+
create(context) {
33+
// Rule logic here
34+
return {
35+
CallExpression(node) {
36+
const call = parseFnCall(context, node)
37+
if (call?.type !== 'expect') return
38+
// Rule-specific logic
39+
}
40+
}
41+
},
42+
meta: {
43+
docs: {
44+
category: 'Best Practices',
45+
description: 'Rule description',
46+
recommended: true,
47+
url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/{rule-name}.md',
48+
},
49+
messages: {
50+
messageId: 'Message text with {{ interpolation }}',
51+
},
52+
schema: [/* JSON schema for options */],
53+
type: 'problem' | 'suggestion',
54+
fixable?: 'code' | 'whitespace',
55+
},
56+
})
57+
```
58+
59+
#### AST Utilities & Patterns
60+
61+
- **`parseFnCall`**: Core utility for parsing Playwright function calls (test,
62+
expect, describe, etc.)
63+
- **`createRule`**: Wrapper that handles custom message interpolation and
64+
context modification
65+
- **AST Helpers**: `getStringValue`, `isIdentifier`, `findParent`, `dereference`
66+
for node manipulation
67+
- **Type Safety**: Extensive TypeScript types for AST nodes with parent
68+
extensions
69+
70+
#### Testing Pattern
71+
72+
```typescript
73+
import rule from '../../src/rules/{rule-name}.js'
74+
import { javascript, runRuleTester } from '../utils/rule-tester.js'
75+
76+
runRuleTester('{rule-name}', rule, {
77+
invalid: [
78+
{
79+
code: 'test("should fail", () => {});',
80+
errors: [{ messageId: 'errorMessage', type: 'Identifier' }],
81+
},
82+
],
83+
valid: ['test("should pass", () => expect(true).toBe(true))'],
84+
})
85+
```
86+
87+
## Development Workflow
88+
89+
### Test-Driven Development (TDD)
90+
91+
1. **Always write tests first** - Every rule must have comprehensive test
92+
coverage
93+
2. **Test both valid and invalid cases** - Include edge cases and complex
94+
scenarios
95+
3. **Use the `runRuleTester` utility** - Provides consistent testing framework
96+
4. **Test with different configurations** - Options, settings, global aliases
97+
5. **Test with TypeScript** - Use `runTSRuleTester` for TypeScript-specific
98+
rules
99+
100+
### Rule Development Process
101+
102+
1. **Analyze the problem** - Understand what Playwright pattern needs
103+
enforcement
104+
2. **Write failing tests** - Define what should and shouldn't trigger the rule
105+
3. **Implement the rule** - Use existing utilities and patterns
106+
4. **Add documentation** - Create markdown file in `docs/rules/`
107+
5. **Update README** - Add rule to the rules table
108+
6. **Register the rule** - Add to `src/index.ts` exports and configurations
109+
110+
### Code Quality Standards
111+
112+
- **TypeScript strict mode** - All code must be properly typed
113+
- **ESLint compliance** - Code must pass the project's own linting rules
114+
- **Prettier formatting** - Consistent code formatting
115+
- **Comprehensive testing** - Aim for 100% test coverage on new rules
116+
117+
## Key Utilities & APIs
118+
119+
### AST Manipulation (`src/utils/ast.ts`)
120+
121+
- `getStringValue(node)` - Extract string value from various node types
122+
- `isIdentifier(node, name?)` - Check if node is identifier with optional name
123+
- `findParent(node, type)` - Find parent node of specific type
124+
- `dereference(context, node)` - Resolve variable references
125+
- `isPageMethod(node, name)` - Check if call is page method
126+
127+
### Function Call Parsing (`src/utils/parseFnCall.ts`)
128+
129+
- `parseFnCall(context, node)` - Parse Playwright function calls
130+
- `isTypeOfFnCall(context, node, types)` - Check function call types
131+
- Supports: `test`, `expect`, `describe`, `hook`, `step`, `config`
132+
133+
### Rule Creation (`src/utils/createRule.ts`)
134+
135+
- Handles custom message interpolation via `{{ variable }}` syntax
136+
- Supports global settings for message customization
137+
- Provides consistent rule context wrapper
138+
139+
### Fixing Utilities (`src/utils/fixer.ts`)
140+
141+
- `replaceAccessorFixer` - Replace property accessors safely
142+
- `removePropertyFixer` - Remove object properties with cleanup
143+
144+
## Configuration & Settings
145+
146+
### Plugin Settings
147+
148+
```json
149+
{
150+
"settings": {
151+
"playwright": {
152+
"globalAliases": {
153+
"test": ["it", "spec"],
154+
"expect": ["assert"]
155+
},
156+
"messages": {
157+
"customMessageId": "Custom error message"
158+
}
159+
}
160+
}
161+
}
162+
```
163+
164+
### Rule Options
165+
166+
- Most rules support configuration options via JSON schema
167+
- Options are validated at runtime
168+
- Default values should be sensible and well-documented
169+
170+
## Testing Strategies
171+
172+
### Test Structure
173+
174+
- **Invalid cases**: Code that should trigger the rule
175+
- **Valid cases**: Code that should not trigger the rule
176+
- **Edge cases**: Complex scenarios, nested structures, different syntax
177+
- **Configuration tests**: Different options and settings
178+
179+
### Test Utilities
180+
181+
- `javascript` template literal for multi-line code
182+
- `typescript` template literal for TypeScript code
183+
- `test()` wrapper for common test patterns
184+
- Global alias testing with settings
185+
186+
### Test Coverage Requirements
187+
188+
- All rule logic paths must be tested
189+
- All message variations must be tested
190+
- All configuration options must be tested
191+
- Edge cases and error conditions must be tested
192+
193+
## Documentation Standards
194+
195+
### Rule Documentation (`docs/rules/`)
196+
197+
- Clear description of what the rule does
198+
- Examples of correct and incorrect usage
199+
- Configuration options with examples
200+
- Rationale for the rule's existence
201+
202+
### README Integration
203+
204+
- Rules table with status indicators (✅ recommended, 🔧 fixable, 💡
205+
suggestions)
206+
- Clear categorization and descriptions
207+
- Links to detailed documentation
208+
209+
## Issue Resolution Process
210+
211+
### GitHub Issue Workflow
212+
213+
1. **Get issue details**:
214+
`gh issue view <issue-number> --json 'title,body,comments'`
215+
2. **Analyze the problem**: Understand the specific Playwright pattern or issue
216+
3. **Create a plan**: Step-by-step approach to solving the issue
217+
4. **Write tests first**: Implement failing tests that demonstrate the problem
218+
5. **Implement the solution**: Write the rule or fix the bug
219+
6. **Verify with tests**: Run `pnpm test` to ensure all tests pass
220+
7. **Create branch and PR**: Use semantic commit messages and add `ai` label
221+
222+
### Common Issue Types
223+
224+
- **New rule requests**: Implement new Playwright best practices
225+
- **Bug fixes**: Existing rules not working correctly
226+
- **Enhancement requests**: Additional options or functionality
227+
- **Documentation updates**: Clarifying rule behavior or usage
228+
229+
## Build & Release Process
230+
231+
### Development Commands
232+
233+
- `pnpm install` - Install dependencies
234+
- `pnpm test` - Run all tests
235+
- `pnpm test:watch` - Run tests in watch mode
236+
- `pnpm build` - Build the plugin
237+
- `pnpm lint` - Run ESLint
238+
- `pnpm fmt` - Format code with Prettier
239+
240+
### Release Process
241+
242+
- Uses semantic-release for automated versioning
243+
- Triggered via GitHub Actions workflow
244+
- Requires all tests to pass before release
245+
- Automatically publishes to npm
246+
247+
## Best Practices for Agents
248+
249+
### When Implementing New Rules
250+
251+
1. **Study existing rules** - Understand patterns from similar rules
252+
2. **Use existing utilities** - Don't reinvent AST parsing logic
253+
3. **Follow naming conventions** - Rule names should be descriptive and
254+
consistent
255+
4. **Consider edge cases** - Think about complex Playwright patterns
256+
5. **Test thoroughly** - Include both obvious and subtle test cases
257+
258+
### When Fixing Bugs
259+
260+
1. **Reproduce the issue** - Create a minimal test case
261+
2. **Understand the root cause** - Is it AST parsing, logic, or configuration?
262+
3. **Fix the core issue** - Don't just patch symptoms
263+
4. **Add regression tests** - Ensure the bug doesn't return
264+
265+
### When Adding Features
266+
267+
1. **Backward compatibility** - Don't break existing configurations
268+
2. **Performance considerations** - Rules run on every file
269+
3. **User experience** - Clear error messages and helpful suggestions
270+
4. **Documentation** - Update docs and examples
271+
272+
### Code Review Checklist
273+
274+
- [ ] Tests cover all code paths
275+
- [ ] Error messages are clear and helpful
276+
- [ ] Configuration options are well-documented
277+
- [ ] Performance impact is reasonable
278+
- [ ] Backward compatibility is maintained
279+
- [ ] Documentation is updated
280+
281+
## Common Patterns & Anti-patterns
282+
283+
### Good Patterns
284+
285+
- Use `parseFnCall` for function identification
286+
- Use `createRule` wrapper for consistency
287+
- Provide helpful error messages with context
288+
- Include auto-fix capabilities when possible
289+
- Test with both JavaScript and TypeScript
290+
291+
### Anti-patterns to Avoid
292+
293+
- Reinventing AST parsing logic
294+
- Hard-coding string literals instead of using utilities
295+
- Not testing edge cases and complex scenarios
296+
- Ignoring performance implications
297+
- Breaking backward compatibility without good reason
298+
299+
## Semantic Commit Messages
300+
301+
This project uses semantic-release, so commit messages must follow the format:
302+
303+
- `feat: add new rule or feature`
304+
- `fix: fix bug in existing rule`
305+
- `docs: update documentation`
306+
- `refactor: refactor existing code`
307+
- `test: add or update tests`
308+
- `chore: maintenance tasks`
309+
- `style: formatting changes`
310+
311+
## Pull Request Guidelines
312+
313+
- Add the `ai` label to all PRs
314+
- Ensure all tests pass before submitting
315+
- Include comprehensive test coverage
316+
- Update documentation as needed
317+
- Follow the existing code style and patterns
318+
- Provide clear description of changes and rationale
10319

11-
- This project uses semantic-release to automatically publish new versions of
12-
the package.
13-
- When creating commits, use the following format:
14-
- `feat: add new feature`
15-
- `fix: fix bug`
16-
- `chore: update dependencies`
17-
- `docs: update documentation`
18-
- `refactor: refactor code`
19320
- `test: add tests`
20321
- `style: update styles`

CLAUDE.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# ESLint Plugin Playwright Development Guidelines
2+
3+
For project-specific development guidelines and instructions, please refer to:
4+
`.cursor/rules/eslint-plugin-playwright.mdc`.
5+
6+
This file contains essential information about:
7+
8+
- Test-driven development practices
9+
- Issue resolution workflow
10+
- Pull request requirements
11+
- Commit conventions using semantic-release
12+
13+
Always reference the .mdc file for the most up-to-date project-specific
14+
instructions.

src/rules/no-conditional-in-test.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,5 +311,9 @@ runRuleTester('no-conditional-in-test', rule, {
311311
},
312312
},
313313
},
314+
// Issue 363: conditionals in test metadata should not trigger the rule
315+
`test('My Test', { tag: productType === 'XYZ' ? '@regression' : '@smoke' }, () => {
316+
expect(1).toBe(1);
317+
})`,
314318
],
315319
})

src/rules/no-conditional-in-test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,23 @@ export default createRule({
1010
if (!call) return
1111

1212
if (isTypeOfFnCall(context, call, ['test', 'step'])) {
13-
context.report({ messageId: 'conditionalInTest', node })
13+
// Check if the conditional is inside the test body (the function passed as the last argument)
14+
const testFunction = call.arguments[call.arguments.length - 1]
15+
16+
// Use findParent to check if the conditional is inside the test function body
17+
const functionBody = findParent(node, 'BlockStatement')
18+
if (!functionBody) return
19+
20+
// Check if this BlockStatement belongs to our test function
21+
let currentParent = functionBody.parent
22+
while (currentParent && currentParent !== testFunction) {
23+
currentParent = (currentParent as Rule.NodeParentExtension).parent
24+
}
25+
26+
// Only report if the conditional is inside the test function body
27+
if (currentParent === testFunction) {
28+
context.report({ messageId: 'conditionalInTest', node })
29+
}
1430
}
1531
}
1632

0 commit comments

Comments
 (0)