vibe-and-thrive 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/add-tests.md +240 -0
- package/.claude/commands/e2e-scaffold.md +212 -0
- package/.claude/commands/explain.md +110 -0
- package/.claude/commands/fix-types.md +238 -0
- package/.claude/commands/refactor.md +184 -0
- package/.claude/commands/review.md +136 -0
- package/.claude/commands/security-check.md +223 -0
- package/.claude/commands/styleguide.md +446 -0
- package/.claude/commands/tdd-feature.md +227 -0
- package/.claude/commands/vibe-check.md +112 -0
- package/.pre-commit-hooks.yaml +77 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/bin/vibe-check.js +19 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +206 -0
- package/dist/cli.js.map +1 -0
- package/dist/eslint-plugin/index.d.ts +66 -0
- package/dist/eslint-plugin/index.d.ts.map +1 -0
- package/dist/eslint-plugin/index.js +67 -0
- package/dist/eslint-plugin/index.js.map +1 -0
- package/dist/eslint-plugin/rules/max-function-length.d.ts +8 -0
- package/dist/eslint-plugin/rules/max-function-length.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/max-function-length.js +69 -0
- package/dist/eslint-plugin/rules/max-function-length.js.map +1 -0
- package/dist/eslint-plugin/rules/no-any-type.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-any-type.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-any-type.js +29 -0
- package/dist/eslint-plugin/rules/no-any-type.js.map +1 -0
- package/dist/eslint-plugin/rules/no-debug-statements.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-debug-statements.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-debug-statements.js +59 -0
- package/dist/eslint-plugin/rules/no-debug-statements.js.map +1 -0
- package/dist/eslint-plugin/rules/no-deep-nesting.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-deep-nesting.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-deep-nesting.js +56 -0
- package/dist/eslint-plugin/rules/no-deep-nesting.js.map +1 -0
- package/dist/eslint-plugin/rules/no-empty-catch.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-empty-catch.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-empty-catch.js +31 -0
- package/dist/eslint-plugin/rules/no-empty-catch.js.map +1 -0
- package/dist/eslint-plugin/rules/no-magic-numbers.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-magic-numbers.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-magic-numbers.js +58 -0
- package/dist/eslint-plugin/rules/no-magic-numbers.js.map +1 -0
- package/dist/eslint-plugin/rules/no-snake-case-props.d.ts +8 -0
- package/dist/eslint-plugin/rules/no-snake-case-props.d.ts.map +1 -0
- package/dist/eslint-plugin/rules/no-snake-case-props.js +48 -0
- package/dist/eslint-plugin/rules/no-snake-case-props.js.map +1 -0
- package/dist/hooks/check-any-types.d.ts +6 -0
- package/dist/hooks/check-any-types.d.ts.map +1 -0
- package/dist/hooks/check-any-types.js +73 -0
- package/dist/hooks/check-any-types.js.map +1 -0
- package/dist/hooks/check-commented-code.d.ts +6 -0
- package/dist/hooks/check-commented-code.d.ts.map +1 -0
- package/dist/hooks/check-commented-code.js +81 -0
- package/dist/hooks/check-commented-code.js.map +1 -0
- package/dist/hooks/check-console-error.d.ts +6 -0
- package/dist/hooks/check-console-error.d.ts.map +1 -0
- package/dist/hooks/check-console-error.js +41 -0
- package/dist/hooks/check-console-error.js.map +1 -0
- package/dist/hooks/check-debug-statements.d.ts +6 -0
- package/dist/hooks/check-debug-statements.d.ts.map +1 -0
- package/dist/hooks/check-debug-statements.js +120 -0
- package/dist/hooks/check-debug-statements.js.map +1 -0
- package/dist/hooks/check-deep-nesting.d.ts +6 -0
- package/dist/hooks/check-deep-nesting.d.ts.map +1 -0
- package/dist/hooks/check-deep-nesting.js +116 -0
- package/dist/hooks/check-deep-nesting.js.map +1 -0
- package/dist/hooks/check-docker-platform.d.ts +6 -0
- package/dist/hooks/check-docker-platform.d.ts.map +1 -0
- package/dist/hooks/check-docker-platform.js +42 -0
- package/dist/hooks/check-docker-platform.js.map +1 -0
- package/dist/hooks/check-dry-violations.d.ts +6 -0
- package/dist/hooks/check-dry-violations.d.ts.map +1 -0
- package/dist/hooks/check-dry-violations.js +124 -0
- package/dist/hooks/check-dry-violations.js.map +1 -0
- package/dist/hooks/check-empty-catch.d.ts +6 -0
- package/dist/hooks/check-empty-catch.d.ts.map +1 -0
- package/dist/hooks/check-empty-catch.js +111 -0
- package/dist/hooks/check-empty-catch.js.map +1 -0
- package/dist/hooks/check-function-length.d.ts +6 -0
- package/dist/hooks/check-function-length.d.ts.map +1 -0
- package/dist/hooks/check-function-length.js +152 -0
- package/dist/hooks/check-function-length.js.map +1 -0
- package/dist/hooks/check-hardcoded-urls.d.ts +6 -0
- package/dist/hooks/check-hardcoded-urls.d.ts.map +1 -0
- package/dist/hooks/check-hardcoded-urls.js +124 -0
- package/dist/hooks/check-hardcoded-urls.js.map +1 -0
- package/dist/hooks/check-magic-numbers.d.ts +6 -0
- package/dist/hooks/check-magic-numbers.d.ts.map +1 -0
- package/dist/hooks/check-magic-numbers.js +116 -0
- package/dist/hooks/check-magic-numbers.js.map +1 -0
- package/dist/hooks/check-secrets.d.ts +6 -0
- package/dist/hooks/check-secrets.d.ts.map +1 -0
- package/dist/hooks/check-secrets.js +138 -0
- package/dist/hooks/check-secrets.js.map +1 -0
- package/dist/hooks/check-snake-case-ts.d.ts +6 -0
- package/dist/hooks/check-snake-case-ts.d.ts.map +1 -0
- package/dist/hooks/check-snake-case-ts.js +78 -0
- package/dist/hooks/check-snake-case-ts.js.map +1 -0
- package/dist/hooks/check-todo-fixme.d.ts +6 -0
- package/dist/hooks/check-todo-fixme.d.ts.map +1 -0
- package/dist/hooks/check-todo-fixme.js +41 -0
- package/dist/hooks/check-todo-fixme.js.map +1 -0
- package/dist/hooks/check-unsafe-html.d.ts +6 -0
- package/dist/hooks/check-unsafe-html.d.ts.map +1 -0
- package/dist/hooks/check-unsafe-html.js +101 -0
- package/dist/hooks/check-unsafe-html.js.map +1 -0
- package/dist/hooks/index.d.ts +29 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +54 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/lint-staged/config.d.ts +20 -0
- package/dist/lint-staged/config.d.ts.map +1 -0
- package/dist/lint-staged/config.js +27 -0
- package/dist/lint-staged/config.js.map +1 -0
- package/dist/utils/file-reader.d.ts +24 -0
- package/dist/utils/file-reader.d.ts.map +1 -0
- package/dist/utils/file-reader.js +140 -0
- package/dist/utils/file-reader.js.map +1 -0
- package/dist/utils/patterns.d.ts +27 -0
- package/dist/utils/patterns.d.ts.map +1 -0
- package/dist/utils/patterns.js +84 -0
- package/dist/utils/patterns.js.map +1 -0
- package/dist/utils/reporters.d.ts +21 -0
- package/dist/utils/reporters.d.ts.map +1 -0
- package/dist/utils/reporters.js +115 -0
- package/dist/utils/reporters.js.map +1 -0
- package/dist/utils/types.d.ts +71 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +5 -0
- package/dist/utils/types.js.map +1 -0
- package/integrations/cursorrules.template +147 -0
- package/integrations/eslint.config.js +34 -0
- package/integrations/lint-staged.config.js +34 -0
- package/integrations/ruff.toml +125 -0
- package/integrations/vibe-check.yml +116 -0
- package/integrations/vscode-settings.json +127 -0
- package/package.json +81 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Add Tests
|
|
2
|
+
|
|
3
|
+
Add tests to existing code to improve coverage and confidence.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
When asked to add tests:
|
|
8
|
+
|
|
9
|
+
### Step 1: Analyze the Code
|
|
10
|
+
|
|
11
|
+
1. Read the code to understand what it does
|
|
12
|
+
2. Identify the public interface (what should be tested)
|
|
13
|
+
3. Find edge cases and error conditions
|
|
14
|
+
4. Check existing test patterns in the project
|
|
15
|
+
|
|
16
|
+
### Step 2: Determine Test Type
|
|
17
|
+
|
|
18
|
+
| Code Type | Test Type | Framework |
|
|
19
|
+
|-----------|-----------|-----------|
|
|
20
|
+
| Utility functions | Unit tests | Jest, pytest |
|
|
21
|
+
| React components | Component tests | React Testing Library |
|
|
22
|
+
| API endpoints | Integration tests | supertest, pytest |
|
|
23
|
+
| User flows | E2E tests | Playwright, Cypress |
|
|
24
|
+
| Hooks | Hook tests | @testing-library/react-hooks |
|
|
25
|
+
|
|
26
|
+
### Step 3: Identify Test Cases
|
|
27
|
+
|
|
28
|
+
For each function/component, consider:
|
|
29
|
+
|
|
30
|
+
**Happy Path**
|
|
31
|
+
- Normal inputs produce expected outputs
|
|
32
|
+
- Main use case works correctly
|
|
33
|
+
|
|
34
|
+
**Edge Cases**
|
|
35
|
+
- Empty inputs
|
|
36
|
+
- Null/undefined values
|
|
37
|
+
- Boundary values (0, -1, max)
|
|
38
|
+
- Single item vs multiple items
|
|
39
|
+
|
|
40
|
+
**Error Cases**
|
|
41
|
+
- Invalid inputs
|
|
42
|
+
- Network failures
|
|
43
|
+
- Permission errors
|
|
44
|
+
- Timeout scenarios
|
|
45
|
+
|
|
46
|
+
**State Transitions**
|
|
47
|
+
- Before/after state changes
|
|
48
|
+
- Loading → success → idle
|
|
49
|
+
- Loading → error → retry
|
|
50
|
+
|
|
51
|
+
### Step 4: Write Tests
|
|
52
|
+
|
|
53
|
+
Use this structure for each test:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
test('descriptive name of what is being tested', () => {
|
|
57
|
+
// Arrange: Set up test data and conditions
|
|
58
|
+
const input = createTestInput();
|
|
59
|
+
|
|
60
|
+
// Act: Perform the action being tested
|
|
61
|
+
const result = functionUnderTest(input);
|
|
62
|
+
|
|
63
|
+
// Assert: Verify the expected outcome
|
|
64
|
+
expect(result).toEqual(expectedOutput);
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 5: Document Test Purpose
|
|
69
|
+
|
|
70
|
+
Use the SCENARIO/EXPECTED/FAILURE pattern:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
test('user can submit form with valid data', () => {
|
|
74
|
+
/**
|
|
75
|
+
* SCENARIO: User fills out all required fields correctly
|
|
76
|
+
* EXPECTED: Form submits successfully, success message shown
|
|
77
|
+
* FAILURE: Form doesn't submit, or error message shown
|
|
78
|
+
*/
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Output Format
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
## Tests for: [filename]
|
|
86
|
+
|
|
87
|
+
### Test File Location
|
|
88
|
+
`[path/to/test/file.test.ts]`
|
|
89
|
+
|
|
90
|
+
### Test Cases
|
|
91
|
+
|
|
92
|
+
1. **Happy Path**: [description]
|
|
93
|
+
2. **Edge Case**: [description]
|
|
94
|
+
3. **Error Case**: [description]
|
|
95
|
+
|
|
96
|
+
### Generated Tests
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
[complete test file]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Coverage Summary
|
|
103
|
+
- Functions tested: X/Y
|
|
104
|
+
- Edge cases covered: [list]
|
|
105
|
+
- Not covered (intentionally): [list with reasons]
|
|
106
|
+
|
|
107
|
+
### Running the Tests
|
|
108
|
+
```bash
|
|
109
|
+
[command to run tests]
|
|
110
|
+
```
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Test Patterns
|
|
114
|
+
|
|
115
|
+
### Unit Test (Function)
|
|
116
|
+
```typescript
|
|
117
|
+
describe('calculateTotal', () => {
|
|
118
|
+
it('returns sum of item prices', () => {
|
|
119
|
+
const items = [{ price: 10 }, { price: 20 }];
|
|
120
|
+
expect(calculateTotal(items)).toBe(30);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('returns 0 for empty array', () => {
|
|
124
|
+
expect(calculateTotal([])).toBe(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('handles single item', () => {
|
|
128
|
+
expect(calculateTotal([{ price: 15 }])).toBe(15);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Component Test (React)
|
|
134
|
+
```typescript
|
|
135
|
+
describe('LoginForm', () => {
|
|
136
|
+
it('submits with valid credentials', async () => {
|
|
137
|
+
const onSubmit = vi.fn();
|
|
138
|
+
render(<LoginForm onSubmit={onSubmit} />);
|
|
139
|
+
|
|
140
|
+
await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
|
|
141
|
+
await userEvent.type(screen.getByLabelText(/password/i), 'password123');
|
|
142
|
+
await userEvent.click(screen.getByRole('button', { name: /login/i }));
|
|
143
|
+
|
|
144
|
+
expect(onSubmit).toHaveBeenCalledWith({
|
|
145
|
+
email: 'test@example.com',
|
|
146
|
+
password: 'password123',
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('shows error for invalid email', async () => {
|
|
151
|
+
render(<LoginForm onSubmit={vi.fn()} />);
|
|
152
|
+
|
|
153
|
+
await userEvent.type(screen.getByLabelText(/email/i), 'invalid');
|
|
154
|
+
await userEvent.click(screen.getByRole('button', { name: /login/i }));
|
|
155
|
+
|
|
156
|
+
expect(screen.getByText(/valid email/i)).toBeInTheDocument();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### API Test (Integration)
|
|
162
|
+
```python
|
|
163
|
+
def test_create_user_success(client, db):
|
|
164
|
+
"""
|
|
165
|
+
SCENARIO: POST /api/users with valid data
|
|
166
|
+
EXPECTED: 201 Created, user in database
|
|
167
|
+
"""
|
|
168
|
+
response = client.post('/api/users', json={
|
|
169
|
+
'email': 'test@example.com',
|
|
170
|
+
'name': 'Test User',
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
assert response.status_code == 201
|
|
174
|
+
assert User.objects.filter(email='test@example.com').exists()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_create_user_duplicate_email(client, db, existing_user):
|
|
178
|
+
"""
|
|
179
|
+
SCENARIO: POST /api/users with existing email
|
|
180
|
+
EXPECTED: 400 Bad Request, error message
|
|
181
|
+
"""
|
|
182
|
+
response = client.post('/api/users', json={
|
|
183
|
+
'email': existing_user.email,
|
|
184
|
+
'name': 'Another User',
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
assert response.status_code == 400
|
|
188
|
+
assert 'already exists' in response.json()['error']
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### E2E Test (Playwright)
|
|
192
|
+
```typescript
|
|
193
|
+
test('user can complete checkout', async ({ page }) => {
|
|
194
|
+
/**
|
|
195
|
+
* SCENARIO: User adds item to cart and completes checkout
|
|
196
|
+
* EXPECTED: Order confirmation page shown
|
|
197
|
+
* FAILURE: Stuck on checkout, error message, or wrong page
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
// Add item to cart
|
|
201
|
+
await page.goto('/products/1');
|
|
202
|
+
await page.click('button:has-text("Add to Cart")');
|
|
203
|
+
|
|
204
|
+
// Go to checkout
|
|
205
|
+
await page.click('a:has-text("Checkout")');
|
|
206
|
+
|
|
207
|
+
// Fill shipping info
|
|
208
|
+
await page.fill('[name="address"]', '123 Main St');
|
|
209
|
+
await page.fill('[name="city"]', 'Anytown');
|
|
210
|
+
|
|
211
|
+
// Complete order
|
|
212
|
+
await page.click('button:has-text("Place Order")');
|
|
213
|
+
|
|
214
|
+
// Verify success
|
|
215
|
+
await expect(page.getByText('Order Confirmed')).toBeVisible();
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Test Modes
|
|
220
|
+
|
|
221
|
+
### Quick Tests
|
|
222
|
+
Add basic happy path tests:
|
|
223
|
+
> "Add basic tests for this function"
|
|
224
|
+
|
|
225
|
+
### Comprehensive Tests
|
|
226
|
+
Full coverage with edge cases:
|
|
227
|
+
> "Add comprehensive tests for UserService"
|
|
228
|
+
|
|
229
|
+
### Specific Scenario
|
|
230
|
+
Test a specific case:
|
|
231
|
+
> "Add a test for when the API returns a 404"
|
|
232
|
+
|
|
233
|
+
## Tips
|
|
234
|
+
|
|
235
|
+
- **Test behavior, not implementation**
|
|
236
|
+
- **One assertion per test** (when possible)
|
|
237
|
+
- **Use descriptive test names**
|
|
238
|
+
- **Don't test framework code** (React, Django, etc.)
|
|
239
|
+
- **Mock external dependencies**
|
|
240
|
+
- **Keep tests fast**
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# E2E Test Scaffold
|
|
2
|
+
|
|
3
|
+
Generate a Playwright E2E test file structure for a new feature.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Before generating the scaffold, **discover the project's testing setup**:
|
|
8
|
+
|
|
9
|
+
### Step 1: Discovery Phase
|
|
10
|
+
|
|
11
|
+
Check for existing patterns in this order:
|
|
12
|
+
|
|
13
|
+
1. **Playwright config**: Look for `playwright.config.ts` or `playwright.config.js`
|
|
14
|
+
- Note the `testDir` setting
|
|
15
|
+
- Check for `baseURL` configuration
|
|
16
|
+
- Look for any custom fixtures or global setup
|
|
17
|
+
|
|
18
|
+
2. **Test directory structure**: Search for existing E2E tests
|
|
19
|
+
- Common locations: `e2e/`, `tests/`, `tests/e2e/`, `__tests__/e2e/`
|
|
20
|
+
- Note the file naming pattern (`.spec.ts`, `.test.ts`, etc.)
|
|
21
|
+
|
|
22
|
+
3. **Existing helpers**: Look for helper files
|
|
23
|
+
- `e2e/helpers.ts`, `tests/helpers.ts`, `tests/utils.ts`
|
|
24
|
+
- Note API call patterns, auth setup, common utilities
|
|
25
|
+
|
|
26
|
+
4. **Auth patterns**: Search for how tests handle authentication
|
|
27
|
+
- Session-based with cookies
|
|
28
|
+
- JWT tokens
|
|
29
|
+
- OAuth flows
|
|
30
|
+
- No auth needed
|
|
31
|
+
|
|
32
|
+
5. **API patterns**: Check the tech stack
|
|
33
|
+
- REST endpoints
|
|
34
|
+
- GraphQL queries
|
|
35
|
+
- tRPC calls
|
|
36
|
+
|
|
37
|
+
### Step 2: Ask User
|
|
38
|
+
|
|
39
|
+
If patterns are unclear, ask:
|
|
40
|
+
- "What feature are you building tests for?"
|
|
41
|
+
- "Does this feature require authentication?"
|
|
42
|
+
- "What API endpoints will the feature use?"
|
|
43
|
+
|
|
44
|
+
### Step 3: Generate Scaffold
|
|
45
|
+
|
|
46
|
+
Create a test file with this structure:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { test, expect, Page } from '@playwright/test';
|
|
50
|
+
|
|
51
|
+
// Configuration - adapt to project's pattern
|
|
52
|
+
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
53
|
+
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8000';
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// HELPER FUNCTIONS
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* [Add helper functions for API calls, auth setup, etc.]
|
|
61
|
+
*
|
|
62
|
+
* Example API helper pattern:
|
|
63
|
+
* async function createResourceViaAPI(page: Page): Promise<{ id: number }> {
|
|
64
|
+
* const result = await page.evaluate(async (apiBase) => {
|
|
65
|
+
* const response = await fetch(`${apiBase}/api/v1/resources/`, {
|
|
66
|
+
* method: 'POST',
|
|
67
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
* credentials: 'include',
|
|
69
|
+
* });
|
|
70
|
+
* return response.json();
|
|
71
|
+
* }, API_BASE_URL);
|
|
72
|
+
* return result;
|
|
73
|
+
* }
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// TEST SUITE: [Feature Name]
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
test.describe('[Feature Name]', () => {
|
|
81
|
+
|
|
82
|
+
test.beforeEach(async ({ page }) => {
|
|
83
|
+
// Setup: navigate to the feature, log in if needed
|
|
84
|
+
await page.goto(BASE_URL);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test.afterEach(async ({ page }, testInfo) => {
|
|
88
|
+
// Capture screenshot on failure for debugging
|
|
89
|
+
if (testInfo.status !== testInfo.expectedStatus) {
|
|
90
|
+
const screenshotPath = `e2e-${testInfo.title.replace(/\s+/g, '-')}-failure.png`;
|
|
91
|
+
await page.screenshot({ path: screenshotPath });
|
|
92
|
+
console.log(`Screenshot saved: ${screenshotPath}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// --------------------------------------------------------------------------
|
|
97
|
+
// Happy Path Tests
|
|
98
|
+
// --------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
test('user can [primary action]', async ({ page }) => {
|
|
101
|
+
/**
|
|
102
|
+
* SCENARIO: As a [user type], when I [action],
|
|
103
|
+
* I should [expected outcome]
|
|
104
|
+
* EXPECTED: [Specific success criteria]
|
|
105
|
+
* FAILURE: [What would indicate failure]
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
// Arrange: Set up test data
|
|
109
|
+
|
|
110
|
+
// Act: Perform user actions
|
|
111
|
+
|
|
112
|
+
// Assert: Verify expected outcome
|
|
113
|
+
// Use the critical assertion pattern:
|
|
114
|
+
// const isSuccess = await page.locator('[data-testid="success"]').isVisible();
|
|
115
|
+
// if (!isSuccess) {
|
|
116
|
+
// console.error('FAIL: Success indicator not visible');
|
|
117
|
+
// await page.screenshot({ path: 'e2e-test-name-debug.png' });
|
|
118
|
+
// }
|
|
119
|
+
// expect(isSuccess).toBeTruthy();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// --------------------------------------------------------------------------
|
|
123
|
+
// Edge Cases
|
|
124
|
+
// --------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
test('handles [edge case scenario]', async ({ page }) => {
|
|
127
|
+
/**
|
|
128
|
+
* SCENARIO: As a [user type], when [edge case condition],
|
|
129
|
+
* I should [expected behavior]
|
|
130
|
+
* EXPECTED: [Specific handling criteria]
|
|
131
|
+
* FAILURE: [What would indicate improper handling]
|
|
132
|
+
*/
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// --------------------------------------------------------------------------
|
|
136
|
+
// Error Handling
|
|
137
|
+
// --------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
test('shows error when [error condition]', async ({ page }) => {
|
|
140
|
+
/**
|
|
141
|
+
* SCENARIO: As a [user type], when [error condition occurs],
|
|
142
|
+
* I should see an appropriate error message
|
|
143
|
+
* EXPECTED: Error message displayed, user can recover
|
|
144
|
+
* FAILURE: Silent failure, crash, or unclear error
|
|
145
|
+
*/
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Step 4: Adapt to Project
|
|
152
|
+
|
|
153
|
+
Based on discovery, customize the scaffold:
|
|
154
|
+
|
|
155
|
+
**If project uses session auth with CSRF:**
|
|
156
|
+
```typescript
|
|
157
|
+
async function apiCallWithAuth(page: Page, endpoint: string, method: string, body?: object) {
|
|
158
|
+
return await page.evaluate(async ({ apiBase, endpoint, method, body }) => {
|
|
159
|
+
const csrfToken = document.cookie.split('; ')
|
|
160
|
+
.find(row => row.startsWith('csrftoken='))?.split('=')[1];
|
|
161
|
+
const response = await fetch(`${apiBase}${endpoint}`, {
|
|
162
|
+
method,
|
|
163
|
+
headers: {
|
|
164
|
+
'Content-Type': 'application/json',
|
|
165
|
+
'X-CSRFToken': csrfToken || ''
|
|
166
|
+
},
|
|
167
|
+
credentials: 'include',
|
|
168
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
169
|
+
});
|
|
170
|
+
return response.json();
|
|
171
|
+
}, { apiBase: API_BASE_URL, endpoint, method, body });
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**If project uses JWT:**
|
|
176
|
+
```typescript
|
|
177
|
+
async function apiCallWithJWT(page: Page, endpoint: string, method: string, body?: object) {
|
|
178
|
+
return await page.evaluate(async ({ apiBase, endpoint, method, body }) => {
|
|
179
|
+
const token = localStorage.getItem('authToken');
|
|
180
|
+
const response = await fetch(`${apiBase}${endpoint}`, {
|
|
181
|
+
method,
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
'Authorization': `Bearer ${token}`
|
|
185
|
+
},
|
|
186
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
187
|
+
});
|
|
188
|
+
return response.json();
|
|
189
|
+
}, { apiBase: API_BASE_URL, endpoint, method, body });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**If project has existing helpers, import them:**
|
|
194
|
+
```typescript
|
|
195
|
+
import { loginAsTestUser, createTestData, cleanupTestData } from './helpers';
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Step 5: Output
|
|
199
|
+
|
|
200
|
+
Provide:
|
|
201
|
+
1. The scaffold file at the appropriate location
|
|
202
|
+
2. Summary of discovered patterns used
|
|
203
|
+
3. List of placeholder TODOs for the user to complete
|
|
204
|
+
4. Suggestion to run `npx playwright test --ui` to verify setup
|
|
205
|
+
|
|
206
|
+
## Notes
|
|
207
|
+
|
|
208
|
+
- Always use the SCENARIO/EXPECTED/FAILURE documentation pattern
|
|
209
|
+
- Include screenshot capture on failure
|
|
210
|
+
- Use flexible locators: `page.getByRole()` with `.or()` for fallbacks
|
|
211
|
+
- Keep tests independent - each test should set up its own data
|
|
212
|
+
- Use `test.describe()` to group related tests
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Explain Code
|
|
2
|
+
|
|
3
|
+
Explain what code does, line by line, so you can understand and learn from it.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
When the user asks you to explain code:
|
|
8
|
+
|
|
9
|
+
### Step 1: Identify the Code
|
|
10
|
+
|
|
11
|
+
If the user provides a file path, read that file. If they paste code directly, use that.
|
|
12
|
+
|
|
13
|
+
If no code is specified, ask:
|
|
14
|
+
> "What code would you like me to explain? Please provide a file path or paste the code."
|
|
15
|
+
|
|
16
|
+
### Step 2: Provide Overview
|
|
17
|
+
|
|
18
|
+
Start with a high-level summary:
|
|
19
|
+
- What is the purpose of this code?
|
|
20
|
+
- What problem does it solve?
|
|
21
|
+
- How does it fit into the larger application?
|
|
22
|
+
|
|
23
|
+
### Step 3: Walk Through Line by Line
|
|
24
|
+
|
|
25
|
+
For each significant section, explain:
|
|
26
|
+
- **What** it does (literally)
|
|
27
|
+
- **Why** it's needed (purpose)
|
|
28
|
+
- **How** it works (mechanism)
|
|
29
|
+
|
|
30
|
+
Use this format:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Lines X-Y: [Brief description]
|
|
34
|
+
- This code [what it does]
|
|
35
|
+
- It's needed because [why]
|
|
36
|
+
- It works by [how]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Step 4: Highlight Key Concepts
|
|
40
|
+
|
|
41
|
+
Point out important patterns and concepts:
|
|
42
|
+
- Design patterns used
|
|
43
|
+
- Language features that might be unfamiliar
|
|
44
|
+
- Best practices demonstrated
|
|
45
|
+
- Potential gotchas or edge cases
|
|
46
|
+
|
|
47
|
+
### Step 5: Summarize
|
|
48
|
+
|
|
49
|
+
End with:
|
|
50
|
+
- Key takeaways
|
|
51
|
+
- What the user should understand now
|
|
52
|
+
- Suggestions for further learning if applicable
|
|
53
|
+
|
|
54
|
+
## Example Output
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
This is a React custom hook that manages authentication state. It handles
|
|
60
|
+
login, logout, and automatic token refresh.
|
|
61
|
+
|
|
62
|
+
## Line-by-Line Explanation
|
|
63
|
+
|
|
64
|
+
### Lines 1-5: Imports
|
|
65
|
+
```typescript
|
|
66
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
67
|
+
import { authApi } from '@/services/api';
|
|
68
|
+
import { User } from '@/types';
|
|
69
|
+
```
|
|
70
|
+
- Imports React hooks for state management and side effects
|
|
71
|
+
- Imports the auth API service for making authentication requests
|
|
72
|
+
- Imports the User type for type safety
|
|
73
|
+
|
|
74
|
+
### Lines 7-15: Hook Definition and State
|
|
75
|
+
```typescript
|
|
76
|
+
export function useAuth() {
|
|
77
|
+
const [user, setUser] = useState<User | null>(null);
|
|
78
|
+
const [loading, setLoading] = useState(true);
|
|
79
|
+
```
|
|
80
|
+
- Creates a custom hook that can be used in any component
|
|
81
|
+
- `user` state holds the current user (or null if logged out)
|
|
82
|
+
- `loading` state tracks whether we're still checking auth status
|
|
83
|
+
- Starting with `loading: true` prevents flash of unauthenticated content
|
|
84
|
+
|
|
85
|
+
### Lines 17-30: Auto-refresh Effect
|
|
86
|
+
[... continue explanation ...]
|
|
87
|
+
|
|
88
|
+
## Key Concepts
|
|
89
|
+
|
|
90
|
+
1. **Custom Hooks**: Reusable logic extracted from components
|
|
91
|
+
2. **Optimistic Updates**: UI updates before server confirms
|
|
92
|
+
3. **Token Refresh**: Automatic re-authentication before expiry
|
|
93
|
+
|
|
94
|
+
## Takeaways
|
|
95
|
+
|
|
96
|
+
- This hook centralizes all auth logic in one place
|
|
97
|
+
- Components just need to call `useAuth()` to access auth state
|
|
98
|
+
- The refresh logic runs automatically in the background
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Tips for Users
|
|
102
|
+
|
|
103
|
+
Ask me to explain:
|
|
104
|
+
- "Explain this function" (paste code)
|
|
105
|
+
- "Explain src/hooks/useAuth.ts"
|
|
106
|
+
- "Explain lines 50-100 of api.ts"
|
|
107
|
+
- "What does this regex do?"
|
|
108
|
+
- "Why is this pattern used here?"
|
|
109
|
+
|
|
110
|
+
I'll adjust the depth based on what you ask. For simple code, I'll be brief. For complex code, I'll be thorough.
|