jettypod 4.1.2 โ 4.1.4
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/.nvmrc +1 -0
- package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
- package/docs/DECISIONS.md +10 -12
- package/docs/NODE_VERSION.md +83 -0
- package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
- package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
- package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
- package/hooks/post-checkout +17 -0
- package/hooks/post-merge +17 -0
- package/hooks/pre-commit +30 -0
- package/jettypod.js +259 -120
- package/lib/coverage-tracker.js +218 -0
- package/lib/database.js +2 -0
- package/lib/db-export.js +192 -0
- package/lib/db-import.js +193 -0
- package/lib/external-transition-handler.js +32 -0
- package/lib/git-hook-helpers.js +174 -0
- package/lib/git-root.js +90 -0
- package/lib/infrastructure-chore-generator.js +45 -0
- package/lib/install-hooks.js +52 -0
- package/lib/jettypod-backup.js +238 -0
- package/lib/merge-lock.js +193 -0
- package/lib/migrations/012-add-worktree-path.js +38 -0
- package/lib/migrations/013-worktrees-table.js +86 -0
- package/lib/migrations/014-migrate-worktree-data.js +161 -0
- package/lib/migrations/015-merge-locks-table.js +67 -0
- package/lib/pattern-finder.js +152 -0
- package/lib/process-manager.js +140 -0
- package/lib/production-standards-reader.js +13 -2
- package/lib/production-standards-writer.js +85 -0
- package/lib/skills/feature-planning/dry-run-validator.js +135 -0
- package/lib/skills/feature-planning/validation-formatter.js +160 -0
- package/lib/smart-conflict-detection.js +168 -0
- package/lib/smart-fetch-rebase.js +614 -0
- package/lib/step-definition-parser.js +76 -0
- package/lib/unit-test-generator.js +232 -0
- package/lib/verification-command-generator.js +66 -0
- package/lib/worktree-diagnostics.js +413 -0
- package/lib/worktree-facade.js +174 -0
- package/lib/worktree-manager.js +636 -0
- package/lib/worktree-reconciler.js +429 -0
- package/package.json +30 -3
- package/skills-templates/external-transition/SKILL.md +34 -3
- package/skills-templates/feature-planning/SKILL.md +190 -24
- package/skills-templates/production-mode/SKILL.md +127 -9
- package/skills-templates/speed-mode/SKILL.md +454 -51
- package/skills-templates/stable-mode/SKILL.md +285 -76
- package/.claude/PROTECT_SKILLS.md +0 -28
- package/.claude/settings.json +0 -24
- package/.claude/settings.local.json +0 -16
- package/.claude/skills/epic-planning/SKILL.md +0 -297
- package/.claude/skills/external-transition/SKILL.md +0 -384
- package/.claude/skills/feature-planning/SKILL.md +0 -464
- package/.claude/skills/production-mode/SKILL.md +0 -369
- package/.claude/skills/speed-mode/SKILL.md +0 -481
- package/.claude/skills/stable-mode/SKILL.md +0 -713
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
- package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
- package/.devpod/current-work.json +0 -10
- package/.devpod/work.db +0 -0
- package/.github/workflows/test-safety.yml +0 -85
- package/.jettypod/config.json +0 -5
- package/.jettypod/current-work.json +0 -10
- package/.jettypod/hooks/README.md +0 -77
- package/.jettypod/hooks/protect-claude-md.js +0 -338
- package/.jettypod/test-work.db +0 -0
- package/.jettypod/work.db +0 -0
- package/CLAUDE.md +0 -49
- package/SPEED-STABLE-AUDIT.md +0 -853
- package/SYSTEM-BEHAVIOR.md +0 -2199
- package/TEST_SAFETY_AUDIT.md +0 -314
- package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
- package/cucumber-report.html +0 -45
- package/dist/devpod-linux +0 -0
- package/dist/devpod-macos +0 -0
- package/dist/devpod-win.exe +0 -0
- package/docs/features/jettypod-standards-explained.md +0 -543
- package/docs/features/standards-inventory.md +0 -257
- package/features/auto-generate-production-chores.feature +0 -13
- package/features/backlog-command.feature +0 -26
- package/features/backlog-filtering-production.feature +0 -10
- package/features/claude-md-protection/steps.js +0 -498
- package/features/decisions/index.js +0 -490
- package/features/decisions/index.test.js +0 -208
- package/features/fix-text-wrapping.feature +0 -42
- package/features/git-hooks/git-hooks.feature +0 -30
- package/features/git-hooks/index.js +0 -93
- package/features/git-hooks/index.test.js +0 -137
- package/features/git-hooks/post-commit +0 -56
- package/features/git-hooks/post-merge +0 -47
- package/features/git-hooks/pre-commit +0 -28
- package/features/git-hooks/simple-steps.js +0 -53
- package/features/git-hooks/simple-test.feature +0 -10
- package/features/git-hooks/steps.js +0 -196
- package/features/jettypod-update-command.feature +0 -46
- package/features/mode-prompts/index.js +0 -95
- package/features/mode-prompts/simple-steps.js +0 -44
- package/features/mode-prompts/simple-test.feature +0 -9
- package/features/mode-prompts/validation.test.js +0 -120
- package/features/multiple-claude-instances.feature +0 -121
- package/features/production-mode-skill.feature +0 -121
- package/features/refactor-mode/steps.js +0 -217
- package/features/refactor-mode.feature +0 -49
- package/features/simplify-external-transition.feature +0 -166
- package/features/skills-update/index.test.js +0 -216
- package/features/step_definitions/backlog-command.steps.js +0 -37
- package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
- package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
- package/features/step_definitions/production-mode-skill.steps.js +0 -862
- package/features/step_definitions/simplify-external-transition.steps.js +0 -370
- package/features/step_definitions/terminal-logo.steps.js +0 -145
- package/features/step_definitions/update-command.steps.js +0 -183
- package/features/support/hooks.js +0 -9
- package/features/terminal-logo/index.js +0 -39
- package/features/terminal-logo/terminal-logo.feature +0 -30
- package/features/update-command/index.js +0 -181
- package/features/update-command/index.test.js +0 -225
- package/features/work-commands/bug-workflow-display.feature +0 -22
- package/features/work-commands/index.js +0 -498
- package/features/work-commands/simple-steps.js +0 -69
- package/features/work-commands/stable-tests.feature +0 -57
- package/features/work-commands/steps.js +0 -1174
- package/features/work-commands/validation.test.js +0 -88
- package/features/work-commands/work-commands.feature +0 -13
- package/features/work-tracking/discovery-validation.test.js +0 -228
- package/features/work-tracking/index.js +0 -1921
- package/features/work-tracking/mode-required.feature +0 -112
- package/features/work-tracking/phase-tracking.test.js +0 -482
- package/features/work-tracking/prototype-tracking.test.js +0 -485
- package/features/work-tracking/tree-view.test.js +0 -310
- package/features/work-tracking/work-set-mode.feature +0 -71
- package/features/work-tracking/work-start-mode.feature +0 -88
- package/full-test.txt +0 -0
- package/lib/bug-workflow.test.js +0 -177
- package/lib/claudemd.test.js +0 -195
- package/lib/config.test.js +0 -511
- package/lib/constants.test.js +0 -164
- package/lib/current-work.test.js +0 -146
- package/lib/database-project-config.test.js +0 -111
- package/lib/database.test.js +0 -106
- package/lib/decisions-generator.test.js +0 -457
- package/lib/decisions-helpers.test.js +0 -310
- package/lib/git-coordinator.js +0 -167
- package/lib/git.test.js +0 -145
- package/lib/migrations/002-default-work-item-modes.test.js +0 -351
- package/lib/production-chore-generator.test.js +0 -432
- package/lib/production-context-detector.test.js +0 -277
- package/lib/production-scenario-appender.test.js +0 -235
- package/lib/production-scenario-validator.test.js +0 -246
- package/lib/production-standards-reader.test.js +0 -270
- package/lib/project-state.test.js +0 -92
- package/lib/push-queue.js +0 -417
- package/lib/queue-processor.js +0 -74
- package/lib/test-helpers.js +0 -202
- package/lib/test-helpers.test.js +0 -255
- package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
- package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
- package/prototypes/2025-01-11-production-mode-guided.js +0 -217
- package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
- package/prototypes/2025-01-11-production-standards-example.md +0 -204
- package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
- package/prototypes/test/index.html +0 -1
- package/setup-dist-repo.sh +0 -68
- package/test-production-standards-engine.js +0 -130
- package/test-results.json +0 -2195
- package/test-safety-check.sh +0 -80
- package/work-item-tracking-plan.md +0 -199
- /package/{.jettypod/devpod.db โ jettypod.db} +0 -0
|
@@ -0,0 +1,1588 @@
|
|
|
1
|
+
# Testing Strategy for Non-Engineers: JettyPod Methodology
|
|
2
|
+
|
|
3
|
+
**Date**: 2025-11-14
|
|
4
|
+
**Audience**: Non-engineers building software with AI + JettyPod
|
|
5
|
+
**Purpose**: Tests as documentation, AI guidance, and quality assurance
|
|
6
|
+
**Coverage Target**: 100% (tests replace traditional documentation)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Philosophy: Tests Are Your Documentation
|
|
11
|
+
|
|
12
|
+
### Why 100% Coverage Makes Sense Here
|
|
13
|
+
|
|
14
|
+
**Traditional software development:**
|
|
15
|
+
- Tests = quality assurance
|
|
16
|
+
- Docs = separate markdown files
|
|
17
|
+
- 80% coverage = good enough
|
|
18
|
+
|
|
19
|
+
**JettyPod methodology:**
|
|
20
|
+
- Tests = documentation + quality + AI guidance
|
|
21
|
+
- No separate docs (except work item notes)
|
|
22
|
+
- 100% coverage = necessary for AI to understand system
|
|
23
|
+
|
|
24
|
+
### What This Means
|
|
25
|
+
|
|
26
|
+
**Every piece of functionality has:**
|
|
27
|
+
1. BDD scenario (readable by humans)
|
|
28
|
+
2. Step definitions (executable by AI)
|
|
29
|
+
3. Unit tests (detailed documentation of behavior)
|
|
30
|
+
|
|
31
|
+
**AI assistants read tests to understand:**
|
|
32
|
+
- How does login work? โ Read `features/login.feature`
|
|
33
|
+
- What validation exists? โ Read `lib/validators.test.js`
|
|
34
|
+
- What errors are handled? โ Read error scenarios in BDD
|
|
35
|
+
|
|
36
|
+
**Users read tests to understand:**
|
|
37
|
+
- What does this feature do? โ Read BDD scenario
|
|
38
|
+
- How do I use this? โ Read Given/When/Then steps
|
|
39
|
+
- What can go wrong? โ Read error scenarios
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Test Types in JettyPod
|
|
44
|
+
|
|
45
|
+
### 1. BDD Scenarios (Primary Documentation)
|
|
46
|
+
|
|
47
|
+
**Purpose**: Human-readable documentation of features
|
|
48
|
+
|
|
49
|
+
**Written in**: Plain English (Gherkin syntax)
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
```gherkin
|
|
53
|
+
Feature: User Login
|
|
54
|
+
As a user
|
|
55
|
+
I want to log in with email and password
|
|
56
|
+
So that I can access my account
|
|
57
|
+
|
|
58
|
+
Scenario: Successful login
|
|
59
|
+
Given I am on the login page
|
|
60
|
+
And I have an account with email "user@example.com"
|
|
61
|
+
When I enter my email and password
|
|
62
|
+
And I click the "Login" button
|
|
63
|
+
Then I am redirected to the dashboard
|
|
64
|
+
And I see a welcome message "Welcome back!"
|
|
65
|
+
|
|
66
|
+
Scenario: Login with wrong password
|
|
67
|
+
Given I am on the login page
|
|
68
|
+
And I have an account with email "user@example.com"
|
|
69
|
+
When I enter my email and wrong password
|
|
70
|
+
And I click the "Login" button
|
|
71
|
+
Then I remain on the login page
|
|
72
|
+
And I see an error message "Invalid password"
|
|
73
|
+
And my password field is cleared
|
|
74
|
+
|
|
75
|
+
Scenario: Login with unregistered email
|
|
76
|
+
Given I am on the login page
|
|
77
|
+
When I enter email "unknown@example.com" and any password
|
|
78
|
+
And I click the "Login" button
|
|
79
|
+
Then I see an error message "No account found with that email"
|
|
80
|
+
And I see a link to "Create an account"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Who reads this:**
|
|
84
|
+
- โ
Users (understand what feature does)
|
|
85
|
+
- โ
AI assistants (understand behavior to implement)
|
|
86
|
+
- โ
Future developers (onboarding)
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### 2. Step Definitions (AI Implementation Guide)
|
|
91
|
+
|
|
92
|
+
**Purpose**: Executable code that AI can reference
|
|
93
|
+
|
|
94
|
+
**Written in**: JavaScript (with detailed comments)
|
|
95
|
+
|
|
96
|
+
**Example:**
|
|
97
|
+
```javascript
|
|
98
|
+
// features/step_definitions/login.steps.js
|
|
99
|
+
|
|
100
|
+
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Sets up a user account for testing login
|
|
104
|
+
* Creates user in test database with hashed password
|
|
105
|
+
*/
|
|
106
|
+
Given('I have an account with email {string}', async function(email) {
|
|
107
|
+
// AI reads this to understand user creation flow
|
|
108
|
+
const hashedPassword = await hashPassword('correct-password');
|
|
109
|
+
|
|
110
|
+
this.testUser = await createUser({
|
|
111
|
+
email: email,
|
|
112
|
+
password: hashedPassword,
|
|
113
|
+
name: 'Test User'
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Track for cleanup
|
|
117
|
+
this.createdUsers.push(this.testUser.id);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Simulates user entering credentials in login form
|
|
122
|
+
* Sets form values without actually rendering UI
|
|
123
|
+
*/
|
|
124
|
+
When('I enter my email and password', async function() {
|
|
125
|
+
// AI reads this to understand login form structure
|
|
126
|
+
this.loginForm = {
|
|
127
|
+
email: this.testUser.email,
|
|
128
|
+
password: 'correct-password',
|
|
129
|
+
rememberMe: false
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Executes login logic and captures result
|
|
135
|
+
* This is the core login implementation
|
|
136
|
+
*/
|
|
137
|
+
When('I click the {string} button', async function(buttonText) {
|
|
138
|
+
if (buttonText === 'Login') {
|
|
139
|
+
// AI reads this to understand login flow
|
|
140
|
+
try {
|
|
141
|
+
this.loginResult = await login(
|
|
142
|
+
this.loginForm.email,
|
|
143
|
+
this.loginForm.password
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Store session for subsequent steps
|
|
147
|
+
this.session = this.loginResult.session;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
this.loginError = error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Verifies successful login redirects to dashboard
|
|
156
|
+
* Confirms session is established
|
|
157
|
+
*/
|
|
158
|
+
Then('I am redirected to the dashboard', function() {
|
|
159
|
+
// AI reads this to understand success criteria
|
|
160
|
+
expect(this.loginResult.redirectTo).toBe('/dashboard');
|
|
161
|
+
expect(this.session).toBeDefined();
|
|
162
|
+
expect(this.session.userId).toBe(this.testUser.id);
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Who reads this:**
|
|
167
|
+
- โ
AI assistants (detailed implementation guidance)
|
|
168
|
+
- โ
Advanced users (debugging)
|
|
169
|
+
- โ ๏ธ Non-technical users (too complex)
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### 3. Unit Tests (Detailed Behavior Documentation)
|
|
174
|
+
|
|
175
|
+
**Purpose**: Document edge cases and implementation details
|
|
176
|
+
|
|
177
|
+
**Written in**: JavaScript (with descriptive test names)
|
|
178
|
+
|
|
179
|
+
**Example:**
|
|
180
|
+
```javascript
|
|
181
|
+
// lib/auth.test.js
|
|
182
|
+
|
|
183
|
+
describe('login(email, password)', () => {
|
|
184
|
+
describe('successful login', () => {
|
|
185
|
+
it('returns user data when credentials are correct', async () => {
|
|
186
|
+
const user = await createTestUser('user@example.com', 'password123');
|
|
187
|
+
|
|
188
|
+
const result = await login('user@example.com', 'password123');
|
|
189
|
+
|
|
190
|
+
expect(result.success).toBe(true);
|
|
191
|
+
expect(result.user.email).toBe('user@example.com');
|
|
192
|
+
expect(result.session).toBeDefined();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('creates a session that expires in 24 hours', async () => {
|
|
196
|
+
const user = await createTestUser('user@example.com', 'password123');
|
|
197
|
+
|
|
198
|
+
const result = await login('user@example.com', 'password123');
|
|
199
|
+
|
|
200
|
+
const expiresAt = new Date(result.session.expiresAt);
|
|
201
|
+
const expectedExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000);
|
|
202
|
+
|
|
203
|
+
expect(expiresAt.getTime()).toBeCloseTo(expectedExpiry.getTime(), -3);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('updates last_login timestamp on user record', async () => {
|
|
207
|
+
const user = await createTestUser('user@example.com', 'password123');
|
|
208
|
+
|
|
209
|
+
await login('user@example.com', 'password123');
|
|
210
|
+
|
|
211
|
+
const updatedUser = await getUserById(user.id);
|
|
212
|
+
expect(updatedUser.lastLogin).toBeDefined();
|
|
213
|
+
expect(new Date(updatedUser.lastLogin).getTime())
|
|
214
|
+
.toBeCloseTo(Date.now(), -3);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('failed login attempts', () => {
|
|
219
|
+
it('returns error when email does not exist', async () => {
|
|
220
|
+
const result = await login('unknown@example.com', 'password123');
|
|
221
|
+
|
|
222
|
+
expect(result.success).toBe(false);
|
|
223
|
+
expect(result.error).toBe('No account found with that email');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('returns error when password is incorrect', async () => {
|
|
227
|
+
await createTestUser('user@example.com', 'correct-password');
|
|
228
|
+
|
|
229
|
+
const result = await login('user@example.com', 'wrong-password');
|
|
230
|
+
|
|
231
|
+
expect(result.success).toBe(false);
|
|
232
|
+
expect(result.error).toBe('Invalid password');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('does not reveal whether email exists (security)', async () => {
|
|
236
|
+
// Test that error messages don't leak information
|
|
237
|
+
const noEmailResult = await login('unknown@example.com', 'password');
|
|
238
|
+
const wrongPassResult = await login('real@example.com', 'wrong');
|
|
239
|
+
|
|
240
|
+
// Both should have similar response structure
|
|
241
|
+
expect(noEmailResult.error).toContain('Invalid');
|
|
242
|
+
expect(wrongPassResult.error).toContain('Invalid');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('increments failed login counter on wrong password', async () => {
|
|
246
|
+
const user = await createTestUser('user@example.com', 'correct');
|
|
247
|
+
|
|
248
|
+
await login('user@example.com', 'wrong');
|
|
249
|
+
await login('user@example.com', 'wrong');
|
|
250
|
+
|
|
251
|
+
const updatedUser = await getUserById(user.id);
|
|
252
|
+
expect(updatedUser.failedLoginAttempts).toBe(2);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('locks account after 5 failed attempts', async () => {
|
|
256
|
+
const user = await createTestUser('user@example.com', 'correct');
|
|
257
|
+
|
|
258
|
+
// Fail 5 times
|
|
259
|
+
for (let i = 0; i < 5; i++) {
|
|
260
|
+
await login('user@example.com', 'wrong');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 6th attempt should be blocked
|
|
264
|
+
const result = await login('user@example.com', 'correct');
|
|
265
|
+
expect(result.success).toBe(false);
|
|
266
|
+
expect(result.error).toBe('Account locked due to too many failed attempts');
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe('edge cases', () => {
|
|
271
|
+
it('handles null email', async () => {
|
|
272
|
+
const result = await login(null, 'password');
|
|
273
|
+
|
|
274
|
+
expect(result.success).toBe(false);
|
|
275
|
+
expect(result.error).toContain('Email is required');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('handles empty password', async () => {
|
|
279
|
+
const result = await login('user@example.com', '');
|
|
280
|
+
|
|
281
|
+
expect(result.success).toBe(false);
|
|
282
|
+
expect(result.error).toContain('Password is required');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('handles extremely long email (buffer overflow test)', async () => {
|
|
286
|
+
const longEmail = 'a'.repeat(10000) + '@example.com';
|
|
287
|
+
|
|
288
|
+
const result = await login(longEmail, 'password');
|
|
289
|
+
|
|
290
|
+
expect(result.success).toBe(false);
|
|
291
|
+
expect(result.error).toContain('Email too long');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('handles SQL injection attempt in email', async () => {
|
|
295
|
+
const maliciousEmail = "'; DROP TABLE users; --";
|
|
296
|
+
|
|
297
|
+
const result = await login(maliciousEmail, 'password');
|
|
298
|
+
|
|
299
|
+
// Should not crash or execute SQL
|
|
300
|
+
expect(result.success).toBe(false);
|
|
301
|
+
expect(result.error).toBeDefined();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Who reads this:**
|
|
308
|
+
- โ
AI assistants (understand edge cases and security)
|
|
309
|
+
- โ
Advanced users (detailed behavior)
|
|
310
|
+
- โ Non-technical users (too detailed)
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## JettyPod's Role in Testing
|
|
315
|
+
|
|
316
|
+
### What JettyPod Provides Automatically
|
|
317
|
+
|
|
318
|
+
**1. Test Infrastructure Setup**
|
|
319
|
+
|
|
320
|
+
When user runs `jettypod init`:
|
|
321
|
+
```javascript
|
|
322
|
+
// JettyPod creates:
|
|
323
|
+
project/
|
|
324
|
+
โโโ features/ โ BDD scenarios directory
|
|
325
|
+
โ โโโ support/
|
|
326
|
+
โ โ โโโ hooks.js โ Cleanup patterns
|
|
327
|
+
โ โ โโโ world.js โ Shared test context
|
|
328
|
+
โ โโโ step_definitions/ โ Step implementations
|
|
329
|
+
โโโ test/ โ Unit tests directory
|
|
330
|
+
โโโ .git/hooks/ โ Smart git hooks
|
|
331
|
+
โ โโโ pre-commit
|
|
332
|
+
โ โโโ pre-push
|
|
333
|
+
โ โโโ post-merge
|
|
334
|
+
โโโ jest.config.js โ Jest configuration
|
|
335
|
+
โโโ cucumber.js โ Cucumber configuration
|
|
336
|
+
โโโ .jettypod/
|
|
337
|
+
โโโ test-config.json โ Testing strategy config
|
|
338
|
+
โโโ coverage-targets.json โ Coverage requirements
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**2. Test Templates**
|
|
342
|
+
|
|
343
|
+
JettyPod provides templates for common patterns:
|
|
344
|
+
```javascript
|
|
345
|
+
// .jettypod/templates/bdd-scenario.template
|
|
346
|
+
Feature: {{featureName}}
|
|
347
|
+
{{featureDescription}}
|
|
348
|
+
|
|
349
|
+
Scenario: {{happyPathScenario}}
|
|
350
|
+
Given {{initialState}}
|
|
351
|
+
When {{userAction}}
|
|
352
|
+
Then {{expectedOutcome}}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**3. Git Hooks (Context-Aware)**
|
|
356
|
+
|
|
357
|
+
JettyPod installs smart hooks that understand workflow phase:
|
|
358
|
+
```javascript
|
|
359
|
+
// .git/hooks/pre-commit (installed by JettyPod)
|
|
360
|
+
#!/usr/bin/env node
|
|
361
|
+
|
|
362
|
+
const { getCurrentWork } = require('.jettypod/current-work');
|
|
363
|
+
|
|
364
|
+
const currentWork = getCurrentWork();
|
|
365
|
+
const mode = currentWork?.mode; // speed, stable, production
|
|
366
|
+
|
|
367
|
+
if (mode === 'speed') {
|
|
368
|
+
// Speed mode: Fast smoke test only
|
|
369
|
+
console.log('๐ Speed mode: Running quick validation...');
|
|
370
|
+
execSync('npm run test:smoke', { stdio: 'inherit' });
|
|
371
|
+
|
|
372
|
+
} else if (mode === 'stable') {
|
|
373
|
+
// Stable mode: Run tests for current feature
|
|
374
|
+
console.log('๐งช Stable mode: Running feature tests...');
|
|
375
|
+
const featureFile = getFeatureFile(currentWork.parent_id);
|
|
376
|
+
execSync(`npx cucumber-js ${featureFile}`, { stdio: 'inherit' });
|
|
377
|
+
|
|
378
|
+
} else if (mode === 'production') {
|
|
379
|
+
// Production mode: Comprehensive validation
|
|
380
|
+
console.log('๐ก๏ธ Production mode: Running full test suite...');
|
|
381
|
+
execSync('npm test', { stdio: 'inherit' });
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Testing in Each Workflow Phase
|
|
388
|
+
|
|
389
|
+
### Feature-Planning Phase
|
|
390
|
+
|
|
391
|
+
**JettyPod's Role:**
|
|
392
|
+
|
|
393
|
+
1. **Guides user to write BDD scenarios**
|
|
394
|
+
```
|
|
395
|
+
Claude (via skill): "Let's write scenarios for your login feature.
|
|
396
|
+
|
|
397
|
+
I'll help you write them in plain English. Here's the template:
|
|
398
|
+
|
|
399
|
+
Feature: What this feature does
|
|
400
|
+
|
|
401
|
+
Scenario: What happens in the happy path
|
|
402
|
+
Given [starting situation]
|
|
403
|
+
When [user takes action]
|
|
404
|
+
Then [what they see/experience]
|
|
405
|
+
|
|
406
|
+
Let's start with the happy path. When a user successfully logs in,
|
|
407
|
+
what should happen?"
|
|
408
|
+
|
|
409
|
+
User: "They should see their dashboard with a welcome message"
|
|
410
|
+
|
|
411
|
+
Claude: "Great! Here's the scenario:
|
|
412
|
+
|
|
413
|
+
Scenario: User logs in successfully
|
|
414
|
+
Given I am registered with email 'user@example.com'
|
|
415
|
+
When I enter my email and password
|
|
416
|
+
And I click 'Login'
|
|
417
|
+
Then I am redirected to my dashboard
|
|
418
|
+
And I see 'Welcome back!'
|
|
419
|
+
|
|
420
|
+
Does this capture it? Any other details?"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
2. **Generates step definitions automatically**
|
|
424
|
+
```javascript
|
|
425
|
+
// JettyPod auto-generates:
|
|
426
|
+
Given('I am registered with email {string}', async function(email) {
|
|
427
|
+
// TODO: Implement user creation
|
|
428
|
+
this.testUser = await createUser({ email });
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
When('I enter my email and password', async function() {
|
|
432
|
+
// TODO: Implement form interaction
|
|
433
|
+
this.loginData = {
|
|
434
|
+
email: this.testUser.email,
|
|
435
|
+
password: 'test-password'
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
Then('I am redirected to my dashboard', function() {
|
|
440
|
+
// TODO: Implement redirect check
|
|
441
|
+
expect(this.response.redirect).toBe('/dashboard');
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
3. **Validates test infrastructure**
|
|
446
|
+
```bash
|
|
447
|
+
# JettyPod runs automatically:
|
|
448
|
+
npx cucumber-js --dry-run features/login.feature
|
|
449
|
+
|
|
450
|
+
# Output:
|
|
451
|
+
โ
3 scenarios (3 undefined)
|
|
452
|
+
โ
12 steps (12 undefined)
|
|
453
|
+
โ ๏ธ You need to implement these step definitions
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**User Experience:**
|
|
457
|
+
```
|
|
458
|
+
You: "I want to add a login feature"
|
|
459
|
+
|
|
460
|
+
Claude: "Let's plan it! First, how should login work?"
|
|
461
|
+
|
|
462
|
+
You: "User enters email and password, clicks login, goes to dashboard"
|
|
463
|
+
|
|
464
|
+
Claude: "Perfect! I've created the BDD scenario in features/login.feature.
|
|
465
|
+
Let me show you:
|
|
466
|
+
|
|
467
|
+
Feature: User Login
|
|
468
|
+
Scenario: User logs in successfully
|
|
469
|
+
Given I am on the login page
|
|
470
|
+
When I enter valid credentials
|
|
471
|
+
Then I am redirected to dashboard
|
|
472
|
+
|
|
473
|
+
โ
Scenario created
|
|
474
|
+
โ
Step definitions scaffolded
|
|
475
|
+
๐ Ready for speed mode implementation
|
|
476
|
+
|
|
477
|
+
Next: jettypod work start [chore-id]"
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
### Speed-Mode Phase
|
|
483
|
+
|
|
484
|
+
**JettyPod's Role:**
|
|
485
|
+
|
|
486
|
+
1. **Guides implementation with TDD**
|
|
487
|
+
```
|
|
488
|
+
Claude: "Let's implement the login feature. I'll use TDD:
|
|
489
|
+
|
|
490
|
+
Step 1: Make the first scenario step pass
|
|
491
|
+
Given I am on the login page
|
|
492
|
+
|
|
493
|
+
I need to implement:
|
|
494
|
+
- Login page route
|
|
495
|
+
- Basic form rendering
|
|
496
|
+
|
|
497
|
+
Writing the step definition first..."
|
|
498
|
+
|
|
499
|
+
[Claude writes step definition]
|
|
500
|
+
|
|
501
|
+
Claude: "Now implementing the route and form...
|
|
502
|
+
|
|
503
|
+
[Claude writes code]
|
|
504
|
+
|
|
505
|
+
Running test: npx cucumber-js features/login.feature
|
|
506
|
+
|
|
507
|
+
โ
Step 1 passing
|
|
508
|
+
๐ Moving to Step 2: 'When I enter valid credentials'..."
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
2. **Runs tests continuously**
|
|
512
|
+
```javascript
|
|
513
|
+
// Inside speed-mode skill loop:
|
|
514
|
+
while (!scenarioPasses) {
|
|
515
|
+
// 1. Implement next piece
|
|
516
|
+
await implementNextStep();
|
|
517
|
+
|
|
518
|
+
// 2. Run BDD test
|
|
519
|
+
const result = execSync(`npx cucumber-js ${featureFile}`);
|
|
520
|
+
|
|
521
|
+
// 3. Show progress to user
|
|
522
|
+
console.log(`
|
|
523
|
+
Progress:
|
|
524
|
+
โ
Given I am on the login page
|
|
525
|
+
โ
When I enter valid credentials
|
|
526
|
+
๐ Then I am redirected to dashboard (working on this)
|
|
527
|
+
`);
|
|
528
|
+
|
|
529
|
+
// 4. Iterate until passing
|
|
530
|
+
if (allStepsPass(result)) {
|
|
531
|
+
scenarioPasses = true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
3. **Generates unit tests for new functions**
|
|
537
|
+
```javascript
|
|
538
|
+
// Claude (via skill): "I created a validateEmail() function.
|
|
539
|
+
// Let me write unit tests to document its behavior..."
|
|
540
|
+
|
|
541
|
+
describe('validateEmail', () => {
|
|
542
|
+
it('accepts valid email addresses', () => {
|
|
543
|
+
expect(validateEmail('user@example.com')).toBe(true);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('rejects emails without @', () => {
|
|
547
|
+
expect(validateEmail('userexample.com')).toBe(false);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// etc...
|
|
551
|
+
});
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**User Experience:**
|
|
555
|
+
```
|
|
556
|
+
You: "Start implementing login"
|
|
557
|
+
|
|
558
|
+
Claude: "I'll implement the login feature to pass the scenario we wrote.
|
|
559
|
+
|
|
560
|
+
๐ Iteration 1:
|
|
561
|
+
โ๏ธ Creating login form component...
|
|
562
|
+
๐งช Testing... 1 of 3 steps passing
|
|
563
|
+
|
|
564
|
+
๐ Iteration 2:
|
|
565
|
+
โ๏ธ Adding authentication logic...
|
|
566
|
+
๐งช Testing... 2 of 3 steps passing
|
|
567
|
+
|
|
568
|
+
๐ Iteration 3:
|
|
569
|
+
โ๏ธ Adding dashboard redirect...
|
|
570
|
+
๐งช Testing... โ
All steps passing!
|
|
571
|
+
|
|
572
|
+
โ
Login feature works (happy path)
|
|
573
|
+
|
|
574
|
+
Next: Should I create stable mode chores to add error handling?"
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
### Stable-Mode Phase
|
|
580
|
+
|
|
581
|
+
**JettyPod's Role:**
|
|
582
|
+
|
|
583
|
+
1. **Guides user to identify error scenarios**
|
|
584
|
+
```
|
|
585
|
+
Claude: "The happy path works! Now let's think about what can go wrong.
|
|
586
|
+
|
|
587
|
+
What should happen if:
|
|
588
|
+
1. User enters wrong password?
|
|
589
|
+
2. User's email doesn't exist?
|
|
590
|
+
3. User is already logged in?
|
|
591
|
+
4. Login fails due to server error?
|
|
592
|
+
|
|
593
|
+
For each one, we'll:
|
|
594
|
+
- Add a BDD scenario (human-readable)
|
|
595
|
+
- Implement error handling (code)
|
|
596
|
+
- Add unit tests (edge cases)
|
|
597
|
+
|
|
598
|
+
Let's start with #1: Wrong password..."
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
2. **Adds error scenarios to feature file**
|
|
602
|
+
```gherkin
|
|
603
|
+
# JettyPod appends to features/login.feature:
|
|
604
|
+
|
|
605
|
+
Scenario: Login with wrong password
|
|
606
|
+
Given I am registered with email 'user@example.com'
|
|
607
|
+
When I enter my email and wrong password
|
|
608
|
+
And I click 'Login'
|
|
609
|
+
Then I remain on the login page
|
|
610
|
+
And I see error "Invalid password"
|
|
611
|
+
And my password field is cleared
|
|
612
|
+
|
|
613
|
+
Scenario: Login with unregistered email
|
|
614
|
+
Given no user exists with email 'unknown@example.com'
|
|
615
|
+
When I enter email 'unknown@example.com' and any password
|
|
616
|
+
And I click 'Login'
|
|
617
|
+
Then I see error "No account found"
|
|
618
|
+
And I see a link to "Create account"
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
3. **Implements error handling with tests first**
|
|
622
|
+
```javascript
|
|
623
|
+
// TDD cycle for error handling:
|
|
624
|
+
|
|
625
|
+
// 1. RED: Add test for error case
|
|
626
|
+
it('returns error when password is wrong', async () => {
|
|
627
|
+
await createUser('user@example.com', 'correct-password');
|
|
628
|
+
|
|
629
|
+
const result = await login('user@example.com', 'wrong-password');
|
|
630
|
+
|
|
631
|
+
expect(result.success).toBe(false);
|
|
632
|
+
expect(result.error).toBe('Invalid password');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// 2. GREEN: Implement error handling
|
|
636
|
+
async function login(email, password) {
|
|
637
|
+
const user = await findUser(email);
|
|
638
|
+
|
|
639
|
+
if (!user) {
|
|
640
|
+
return { success: false, error: 'No account found' };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const validPassword = await verifyPassword(password, user.passwordHash);
|
|
644
|
+
|
|
645
|
+
if (!validPassword) {
|
|
646
|
+
return { success: false, error: 'Invalid password' };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return { success: true, session: createSession(user) };
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// 3. REFACTOR: Extract validation
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
4. **Tracks coverage and gaps**
|
|
656
|
+
```javascript
|
|
657
|
+
// JettyPod runs coverage after each iteration:
|
|
658
|
+
npx jest --coverage
|
|
659
|
+
|
|
660
|
+
// Shows user:
|
|
661
|
+
File | % Stmts | % Branch | % Funcs | % Lines |
|
|
662
|
+
lib/auth.js | 95.2 | 89.5 | 100 | 94.8 |
|
|
663
|
+
|
|
664
|
+
Uncovered lines:
|
|
665
|
+
auth.js:45-47 (rare edge case: database connection lost)
|
|
666
|
+
|
|
667
|
+
Claude: "We have 95% coverage. The uncovered lines handle database
|
|
668
|
+
connection loss. Should we test that edge case or is it handled elsewhere?"
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
**User Experience:**
|
|
672
|
+
```
|
|
673
|
+
You: "Add error handling to login"
|
|
674
|
+
|
|
675
|
+
Claude: "Let's think about what can go wrong:
|
|
676
|
+
1. Wrong password
|
|
677
|
+
2. Email doesn't exist
|
|
678
|
+
3. Account locked
|
|
679
|
+
4. Server error
|
|
680
|
+
|
|
681
|
+
I'll add scenarios for each and implement error handling.
|
|
682
|
+
|
|
683
|
+
๐ Adding 'Wrong password' scenario...
|
|
684
|
+
โ๏ธ Writing BDD scenario
|
|
685
|
+
โ๏ธ Writing unit tests (TDD)
|
|
686
|
+
โ๏ธ Implementing error handling
|
|
687
|
+
๐งช Testing... โ
Scenario passes
|
|
688
|
+
|
|
689
|
+
๐ Adding 'Email doesn't exist' scenario...
|
|
690
|
+
[repeat]
|
|
691
|
+
|
|
692
|
+
โ
All error scenarios pass
|
|
693
|
+
๐ Coverage: 98% (2 lines in rare edge case not covered)
|
|
694
|
+
|
|
695
|
+
Login feature is now robust with error handling!"
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
### Production-Mode Phase
|
|
701
|
+
|
|
702
|
+
**JettyPod's Role:**
|
|
703
|
+
|
|
704
|
+
1. **Adds security/performance/scale scenarios**
|
|
705
|
+
```gherkin
|
|
706
|
+
# JettyPod appends production scenarios:
|
|
707
|
+
|
|
708
|
+
@production @security
|
|
709
|
+
Scenario: Login is protected against brute force attacks
|
|
710
|
+
Given I have an account with email 'user@example.com'
|
|
711
|
+
When I attempt to login with wrong password 10 times
|
|
712
|
+
Then my account is temporarily locked
|
|
713
|
+
And I see "Too many failed attempts. Try again in 15 minutes"
|
|
714
|
+
|
|
715
|
+
@production @performance
|
|
716
|
+
Scenario: Login completes within 200ms
|
|
717
|
+
Given I have an account
|
|
718
|
+
When I login with valid credentials
|
|
719
|
+
Then the response time is under 200ms
|
|
720
|
+
And the session is created
|
|
721
|
+
|
|
722
|
+
@production @security
|
|
723
|
+
Scenario: Login protects against SQL injection
|
|
724
|
+
When I attempt to login with email "'; DROP TABLE users; --"
|
|
725
|
+
Then no SQL is executed
|
|
726
|
+
And I see "Invalid email format"
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
2. **Implements production hardening**
|
|
730
|
+
```javascript
|
|
731
|
+
// Claude adds:
|
|
732
|
+
// - Rate limiting
|
|
733
|
+
// - Brute force protection
|
|
734
|
+
// - SQL injection prevention
|
|
735
|
+
// - XSS prevention
|
|
736
|
+
// - Performance monitoring
|
|
737
|
+
|
|
738
|
+
async function login(email, password) {
|
|
739
|
+
// Production: Rate limiting
|
|
740
|
+
await checkRateLimit(email);
|
|
741
|
+
|
|
742
|
+
// Production: Input sanitization
|
|
743
|
+
const sanitizedEmail = sanitizeInput(email);
|
|
744
|
+
|
|
745
|
+
// Production: SQL injection prevention (parameterized queries)
|
|
746
|
+
const user = await db.query(
|
|
747
|
+
'SELECT * FROM users WHERE email = $1',
|
|
748
|
+
[sanitizedEmail]
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
// Production: Timing attack prevention
|
|
752
|
+
await constantTimeCompare(password, user.passwordHash);
|
|
753
|
+
|
|
754
|
+
// Production: Brute force protection
|
|
755
|
+
await checkFailedAttempts(user);
|
|
756
|
+
|
|
757
|
+
// Rest of logic...
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
3. **Validates all scenarios still pass**
|
|
762
|
+
```bash
|
|
763
|
+
# JettyPod runs full suite:
|
|
764
|
+
npx cucumber-js --tags "not @wip"
|
|
765
|
+
|
|
766
|
+
# Output:
|
|
767
|
+
โ
15 scenarios (15 passed)
|
|
768
|
+
โ
87 steps (87 passed)
|
|
769
|
+
๐ Coverage: 100%
|
|
770
|
+
|
|
771
|
+
Production scenarios:
|
|
772
|
+
โ
Brute force protection
|
|
773
|
+
โ
SQL injection prevention
|
|
774
|
+
โ
Performance under 200ms
|
|
775
|
+
โ
XSS prevention
|
|
776
|
+
โ
Rate limiting
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
**User Experience:**
|
|
780
|
+
```
|
|
781
|
+
You: "Make login production-ready"
|
|
782
|
+
|
|
783
|
+
Claude: "I'll harden login for production with:
|
|
784
|
+
1. Security (brute force, injection protection)
|
|
785
|
+
2. Performance (response time under 200ms)
|
|
786
|
+
3. Monitoring (error tracking, metrics)
|
|
787
|
+
|
|
788
|
+
๐ Adding security scenarios...
|
|
789
|
+
โ๏ธ Rate limiting
|
|
790
|
+
โ๏ธ Brute force protection
|
|
791
|
+
๐งช Testing... โ
Security tests pass
|
|
792
|
+
|
|
793
|
+
๐ Adding performance scenarios...
|
|
794
|
+
โ๏ธ Response time monitoring
|
|
795
|
+
โ๏ธ Database query optimization
|
|
796
|
+
๐งช Testing... โ
Performance tests pass (avg 145ms)
|
|
797
|
+
|
|
798
|
+
โ
Login is production-ready!
|
|
799
|
+
๐ Coverage: 100%
|
|
800
|
+
๐ก๏ธ All security tests pass
|
|
801
|
+
โก Performance: 145ms avg (target: 200ms)
|
|
802
|
+
|
|
803
|
+
Login feature is complete and ready for real users."
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## The Complete Testing Experience
|
|
809
|
+
|
|
810
|
+
### From User's Perspective
|
|
811
|
+
|
|
812
|
+
**Day 1: Feature Planning**
|
|
813
|
+
```
|
|
814
|
+
You: "I want users to be able to log in"
|
|
815
|
+
|
|
816
|
+
JettyPod: "Let's write scenarios describing how login should work."
|
|
817
|
+
|
|
818
|
+
[Collaborative scenario writing in plain English]
|
|
819
|
+
|
|
820
|
+
โ
Scenarios written (readable documentation)
|
|
821
|
+
โ
Test infrastructure ready
|
|
822
|
+
๐ฏ Ready to implement
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
**Day 2-3: Speed Mode**
|
|
826
|
+
```
|
|
827
|
+
You: "Build the login feature"
|
|
828
|
+
|
|
829
|
+
JettyPod: "I'll implement it step by step, running tests as I go."
|
|
830
|
+
|
|
831
|
+
[TDD implementation with continuous feedback]
|
|
832
|
+
|
|
833
|
+
โ
Happy path works
|
|
834
|
+
๐ Tests document how login works
|
|
835
|
+
๐ Ready for error handling
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Day 4-5: Stable Mode**
|
|
839
|
+
```
|
|
840
|
+
You: "Add error handling"
|
|
841
|
+
|
|
842
|
+
JettyPod: "Let's think about what can go wrong and handle each case."
|
|
843
|
+
|
|
844
|
+
[Error scenarios + TDD implementation]
|
|
845
|
+
|
|
846
|
+
โ
All error cases handled
|
|
847
|
+
๐ Tests document all edge cases
|
|
848
|
+
๐ก๏ธ Ready for production hardening
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
**Day 6: Production Mode**
|
|
852
|
+
```
|
|
853
|
+
You: "Make it production-ready"
|
|
854
|
+
|
|
855
|
+
JettyPod: "I'll add security, performance, and monitoring."
|
|
856
|
+
|
|
857
|
+
[Production scenarios + hardening]
|
|
858
|
+
|
|
859
|
+
โ
Production-ready
|
|
860
|
+
๐ Tests document security measures
|
|
861
|
+
๐ Ready to deploy
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
## Coverage: Why 100% Makes Sense
|
|
867
|
+
|
|
868
|
+
### Traditional Software: 80% Coverage Goal
|
|
869
|
+
|
|
870
|
+
**Why 80% is common:**
|
|
871
|
+
- Diminishing returns on last 20%
|
|
872
|
+
- Some code is hard to test (UI, third-party integration)
|
|
873
|
+
- Docs explain the system
|
|
874
|
+
- Developers understand the codebase
|
|
875
|
+
|
|
876
|
+
**What 80% means:**
|
|
877
|
+
- Core logic: 100% covered
|
|
878
|
+
- Edge cases: Mostly covered
|
|
879
|
+
- Trivial code: Not covered
|
|
880
|
+
- Acceptable because docs supplement tests
|
|
881
|
+
|
|
882
|
+
### JettyPod Methodology: 100% Coverage Goal
|
|
883
|
+
|
|
884
|
+
**Why 100% makes sense here:**
|
|
885
|
+
- Tests ARE the documentation (no separate docs)
|
|
886
|
+
- AI needs complete understanding of system
|
|
887
|
+
- Non-engineers read tests to understand features
|
|
888
|
+
- Tests explain every edge case and error condition
|
|
889
|
+
|
|
890
|
+
**What 100% means:**
|
|
891
|
+
- Every function has unit tests documenting behavior
|
|
892
|
+
- Every user workflow has BDD scenario
|
|
893
|
+
- Every error condition has test explaining how it's handled
|
|
894
|
+
- Every edge case is documented via test
|
|
895
|
+
|
|
896
|
+
**The uncovered 0%:**
|
|
897
|
+
```javascript
|
|
898
|
+
// Even trivial code gets documented via tests:
|
|
899
|
+
|
|
900
|
+
// lib/formatters.js
|
|
901
|
+
function capitalize(str) {
|
|
902
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// lib/formatters.test.js
|
|
906
|
+
describe('capitalize', () => {
|
|
907
|
+
it('capitalizes first letter of a word', () => {
|
|
908
|
+
expect(capitalize('hello')).toBe('Hello');
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
it('leaves already capitalized words unchanged', () => {
|
|
912
|
+
expect(capitalize('Hello')).toBe('Hello');
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
it('handles empty string', () => {
|
|
916
|
+
expect(capitalize('')).toBe('');
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
it('handles single character', () => {
|
|
920
|
+
expect(capitalize('a')).toBe('A');
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**Why test trivial code?**
|
|
926
|
+
- AI reads these tests to understand string formatting
|
|
927
|
+
- Non-engineers see examples of edge cases
|
|
928
|
+
- Future changes won't break assumptions
|
|
929
|
+
- Tests are the documentation
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
## How AI Uses Tests
|
|
934
|
+
|
|
935
|
+
### AI Reading Tests as Documentation
|
|
936
|
+
|
|
937
|
+
**Scenario: User asks AI to add password reset**
|
|
938
|
+
|
|
939
|
+
```
|
|
940
|
+
User: "Add a password reset feature"
|
|
941
|
+
|
|
942
|
+
AI (Claude): "Let me understand how authentication works first..."
|
|
943
|
+
|
|
944
|
+
[AI reads features/login.feature]
|
|
945
|
+
|
|
946
|
+
AI: "I see login uses email and password. Let me check password storage..."
|
|
947
|
+
|
|
948
|
+
[AI reads lib/auth.test.js]
|
|
949
|
+
|
|
950
|
+
AI: "I understand:
|
|
951
|
+
- Passwords are hashed with bcrypt
|
|
952
|
+
- Failed attempts are tracked
|
|
953
|
+
- Account lockout after 5 attempts
|
|
954
|
+
- Sessions expire after 24 hours
|
|
955
|
+
|
|
956
|
+
For password reset, I should:
|
|
957
|
+
1. Generate reset token (similar to session token)
|
|
958
|
+
2. Send email with reset link
|
|
959
|
+
3. Verify token on reset page
|
|
960
|
+
4. Allow new password (hash with bcrypt like login)
|
|
961
|
+
5. Clear failed attempts on successful reset
|
|
962
|
+
|
|
963
|
+
Let me write the scenarios first..."
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
**Without tests as documentation:**
|
|
967
|
+
```
|
|
968
|
+
AI: "I'll add password reset functionality."
|
|
969
|
+
[Makes assumptions, might conflict with existing auth logic]
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**With tests as documentation:**
|
|
973
|
+
```
|
|
974
|
+
AI: "Based on the auth tests, I'll integrate password reset properly:
|
|
975
|
+
- Use same hashing as login (bcrypt with cost 10)
|
|
976
|
+
- Clear failed attempts on reset (prevents lockout)
|
|
977
|
+
- Generate token with 1-hour expiry (like sessions but shorter)
|
|
978
|
+
|
|
979
|
+
Let me write scenarios that explain the reset flow..."
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
---
|
|
983
|
+
|
|
984
|
+
## JettyPod's Testing Automation
|
|
985
|
+
|
|
986
|
+
### What JettyPod Does Automatically
|
|
987
|
+
|
|
988
|
+
**1. Test Infrastructure Setup**
|
|
989
|
+
```javascript
|
|
990
|
+
// When user runs: jettypod init
|
|
991
|
+
|
|
992
|
+
async function initializeProject() {
|
|
993
|
+
// Create test directories
|
|
994
|
+
fs.mkdirSync('features', { recursive: true });
|
|
995
|
+
fs.mkdirSync('features/step_definitions', { recursive: true });
|
|
996
|
+
fs.mkdirSync('features/support', { recursive: true });
|
|
997
|
+
fs.mkdirSync('test', { recursive: true });
|
|
998
|
+
|
|
999
|
+
// Install test dependencies
|
|
1000
|
+
execSync('npm install --save-dev jest @cucumber/cucumber chai');
|
|
1001
|
+
|
|
1002
|
+
// Create configuration files
|
|
1003
|
+
createJestConfig();
|
|
1004
|
+
createCucumberConfig();
|
|
1005
|
+
|
|
1006
|
+
// Create support files with safety patterns
|
|
1007
|
+
createHooksFile(); // Before/After cleanup
|
|
1008
|
+
createWorldFile(); // Shared test context
|
|
1009
|
+
|
|
1010
|
+
// Install git hooks
|
|
1011
|
+
installSmartGitHooks();
|
|
1012
|
+
|
|
1013
|
+
console.log('โ
Test infrastructure ready');
|
|
1014
|
+
console.log('๐ Tests will serve as your documentation');
|
|
1015
|
+
console.log('๐ค AI will read tests to understand your system');
|
|
1016
|
+
}
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
**2. Scenario Generation**
|
|
1020
|
+
```javascript
|
|
1021
|
+
// In feature-planning skill:
|
|
1022
|
+
|
|
1023
|
+
async function generateScenarios(featureDescription) {
|
|
1024
|
+
// Use AI to generate scenarios from user's description
|
|
1025
|
+
const scenarios = await aiGenerateScenarios({
|
|
1026
|
+
feature: featureDescription,
|
|
1027
|
+
context: readExistingScenarios(), // Learn from existing patterns
|
|
1028
|
+
style: 'non-technical' // Plain English
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
// Write to feature file
|
|
1032
|
+
const featureFile = `features/${slugify(featureDescription)}.feature`;
|
|
1033
|
+
fs.writeFileSync(featureFile, scenarios);
|
|
1034
|
+
|
|
1035
|
+
// Generate step definitions
|
|
1036
|
+
const steps = extractSteps(scenarios);
|
|
1037
|
+
const stepDefinitions = generateStepDefinitions(steps);
|
|
1038
|
+
|
|
1039
|
+
const stepsFile = `features/step_definitions/${slugify(featureDescription)}.steps.js`;
|
|
1040
|
+
fs.writeFileSync(stepsFile, stepDefinitions);
|
|
1041
|
+
|
|
1042
|
+
// Validate infrastructure
|
|
1043
|
+
execSync(`npx cucumber-js --dry-run ${featureFile}`);
|
|
1044
|
+
|
|
1045
|
+
console.log('โ
Scenarios created and validated');
|
|
1046
|
+
}
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
**3. Test Execution During Implementation**
|
|
1050
|
+
```javascript
|
|
1051
|
+
// In speed-mode skill:
|
|
1052
|
+
|
|
1053
|
+
async function implementFeature(featureId) {
|
|
1054
|
+
const feature = getFeature(featureId);
|
|
1055
|
+
const scenarios = readScenarios(feature.scenarioFile);
|
|
1056
|
+
|
|
1057
|
+
for (const scenario of scenarios) {
|
|
1058
|
+
console.log(`\n๐ Implementing: ${scenario.title}`);
|
|
1059
|
+
|
|
1060
|
+
// TDD loop
|
|
1061
|
+
let passing = false;
|
|
1062
|
+
let iteration = 0;
|
|
1063
|
+
|
|
1064
|
+
while (!passing && iteration < 10) {
|
|
1065
|
+
iteration++;
|
|
1066
|
+
|
|
1067
|
+
// 1. Implement next piece
|
|
1068
|
+
await aiImplementNextStep(scenario, iteration);
|
|
1069
|
+
|
|
1070
|
+
// 2. Run tests
|
|
1071
|
+
console.log('๐งช Running tests...');
|
|
1072
|
+
const result = execSync(
|
|
1073
|
+
`npx cucumber-js ${feature.scenarioFile}`,
|
|
1074
|
+
{ encoding: 'utf-8' }
|
|
1075
|
+
);
|
|
1076
|
+
|
|
1077
|
+
// 3. Check progress
|
|
1078
|
+
const progress = parseTestProgress(result);
|
|
1079
|
+
console.log(`
|
|
1080
|
+
Progress (${progress.passed}/${progress.total} steps):
|
|
1081
|
+
${formatProgressSteps(progress)}
|
|
1082
|
+
`);
|
|
1083
|
+
|
|
1084
|
+
// 4. Check if scenario passes
|
|
1085
|
+
if (progress.passed === progress.total) {
|
|
1086
|
+
console.log('โ
Scenario passes!');
|
|
1087
|
+
passing = true;
|
|
1088
|
+
} else {
|
|
1089
|
+
console.log('๐ Iterating...');
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// Generate unit tests for any new functions
|
|
1095
|
+
await generateUnitTestsForNewCode();
|
|
1096
|
+
|
|
1097
|
+
console.log('\nโ
Feature implementation complete');
|
|
1098
|
+
console.log('๐ All scenarios pass (tests document behavior)');
|
|
1099
|
+
console.log('๐ Running coverage...');
|
|
1100
|
+
|
|
1101
|
+
const coverage = execSync('npx jest --coverage', { encoding: 'utf-8' });
|
|
1102
|
+
console.log(coverage);
|
|
1103
|
+
}
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
**4. Coverage Tracking**
|
|
1107
|
+
```javascript
|
|
1108
|
+
// After each phase:
|
|
1109
|
+
|
|
1110
|
+
function checkCoverage(phase) {
|
|
1111
|
+
const coverage = getCoverageReport();
|
|
1112
|
+
const target = getCoverageTarget(phase);
|
|
1113
|
+
|
|
1114
|
+
console.log(`\n๐ Coverage Report (${phase}):`);
|
|
1115
|
+
console.log(`Current: ${coverage.lines}%`);
|
|
1116
|
+
console.log(`Target: ${target.lines}%`);
|
|
1117
|
+
|
|
1118
|
+
if (coverage.lines < target.lines) {
|
|
1119
|
+
console.log(`\nโ ๏ธ Coverage below target`);
|
|
1120
|
+
console.log(`Missing coverage in:`);
|
|
1121
|
+
|
|
1122
|
+
// Show which files need more tests
|
|
1123
|
+
const gaps = findCoverageGaps(coverage);
|
|
1124
|
+
gaps.forEach(gap => {
|
|
1125
|
+
console.log(` - ${gap.file}: ${gap.uncoveredLines.length} uncovered lines`);
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
console.log(`\nShould I write tests to cover these gaps?`);
|
|
1129
|
+
} else {
|
|
1130
|
+
console.log(`\nโ
Coverage target met!`);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
**5. Documentation Generation from Tests**
|
|
1136
|
+
```javascript
|
|
1137
|
+
// JettyPod generates human-readable docs from tests:
|
|
1138
|
+
|
|
1139
|
+
function generateDocumentation() {
|
|
1140
|
+
console.log('๐ Generating documentation from tests...');
|
|
1141
|
+
|
|
1142
|
+
// Parse BDD scenarios
|
|
1143
|
+
const features = parseAllFeatures();
|
|
1144
|
+
|
|
1145
|
+
// Generate markdown documentation
|
|
1146
|
+
const docs = [];
|
|
1147
|
+
|
|
1148
|
+
features.forEach(feature => {
|
|
1149
|
+
docs.push(`# ${feature.title}\n`);
|
|
1150
|
+
docs.push(`${feature.description}\n\n`);
|
|
1151
|
+
|
|
1152
|
+
feature.scenarios.forEach(scenario => {
|
|
1153
|
+
docs.push(`## ${scenario.title}\n\n`);
|
|
1154
|
+
docs.push('**What happens:**\n');
|
|
1155
|
+
|
|
1156
|
+
scenario.steps.forEach(step => {
|
|
1157
|
+
docs.push(`- ${step.type} ${step.text}\n`);
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
docs.push('\n');
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
fs.writeFileSync('FEATURES.md', docs.join(''));
|
|
1165
|
+
|
|
1166
|
+
console.log('โ
Documentation generated from tests');
|
|
1167
|
+
console.log('๐ See FEATURES.md for human-readable feature list');
|
|
1168
|
+
}
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
|
|
1173
|
+
## Test Execution Optimization
|
|
1174
|
+
|
|
1175
|
+
### The Performance Problem (Solved)
|
|
1176
|
+
|
|
1177
|
+
**Initial implementation challenge:**
|
|
1178
|
+
- Test execution was slow during TDD iterations
|
|
1179
|
+
- Running entire feature files when only testing single scenarios
|
|
1180
|
+
- 5-10 minute iteration cycles
|
|
1181
|
+
- Poor developer experience
|
|
1182
|
+
|
|
1183
|
+
**Root cause:**
|
|
1184
|
+
```javascript
|
|
1185
|
+
// Original approach: Run entire feature file every iteration
|
|
1186
|
+
const result = execSync(`npm run test:bdd -- ${featureFile}`);
|
|
1187
|
+
// Problem: Tests ALL scenarios when we only care about one
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
**Solution: Scenario-specific execution:**
|
|
1191
|
+
```javascript
|
|
1192
|
+
// Optimized: Run only the target scenario during iteration
|
|
1193
|
+
const happyPathLine = getFirstScenarioLine(featureFile);
|
|
1194
|
+
const result = runBddScenarioWithTimeout(featureFile, happyPathLine);
|
|
1195
|
+
// Result: 20-30x faster (10-30 seconds vs 5-10 minutes)
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
### How It Works: Context-Aware Testing
|
|
1199
|
+
|
|
1200
|
+
**During iteration (speed/stable/production modes):**
|
|
1201
|
+
- Run ONLY the target scenario being implemented
|
|
1202
|
+
- Fast feedback loop (10-30 seconds)
|
|
1203
|
+
- Focus on the specific functionality being built
|
|
1204
|
+
|
|
1205
|
+
**After GREEN is achieved:**
|
|
1206
|
+
- Run full feature file for regression detection
|
|
1207
|
+
- Ensures new code doesn't break existing scenarios
|
|
1208
|
+
- Comprehensive validation before marking work complete
|
|
1209
|
+
|
|
1210
|
+
**On git commit (pre-commit hook):**
|
|
1211
|
+
- Context-aware test execution based on workflow phase
|
|
1212
|
+
- Speed mode: Happy path only (10-30 seconds)
|
|
1213
|
+
- Stable mode: All current feature scenarios (30-60 seconds)
|
|
1214
|
+
- Production mode: Full test suite (1-2 minutes)
|
|
1215
|
+
|
|
1216
|
+
### Test Execution Functions
|
|
1217
|
+
|
|
1218
|
+
**1. runBddScenarioWithTimeout()**
|
|
1219
|
+
```javascript
|
|
1220
|
+
// Run a specific scenario by line number
|
|
1221
|
+
const result = await runBddScenarioWithTimeout(
|
|
1222
|
+
'features/login.feature',
|
|
1223
|
+
13, // Line number of "Scenario:" keyword
|
|
1224
|
+
60000 // 60-second timeout
|
|
1225
|
+
);
|
|
1226
|
+
|
|
1227
|
+
// Or by scenario name
|
|
1228
|
+
const result = await runBddScenarioWithTimeout(
|
|
1229
|
+
'features/login.feature',
|
|
1230
|
+
'User logs in successfully', // Scenario name
|
|
1231
|
+
60000
|
|
1232
|
+
);
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
**2. runBddTestWithTimeout()**
|
|
1236
|
+
```javascript
|
|
1237
|
+
// Run entire feature file (for full verification)
|
|
1238
|
+
const result = await runBddTestWithTimeout(
|
|
1239
|
+
'features/login.feature',
|
|
1240
|
+
120000 // 120-second timeout
|
|
1241
|
+
);
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
**3. Timeout Protection**
|
|
1245
|
+
```javascript
|
|
1246
|
+
// All test execution protected by timeout with graceful termination
|
|
1247
|
+
if (result.timedOut) {
|
|
1248
|
+
console.log('โ ๏ธ Tests timed out - may have infinite loop');
|
|
1249
|
+
// Continue implementation, don't block
|
|
1250
|
+
}
|
|
1251
|
+
```
|
|
1252
|
+
|
|
1253
|
+
### Performance Comparison
|
|
1254
|
+
|
|
1255
|
+
**Before optimization:**
|
|
1256
|
+
```
|
|
1257
|
+
Speed mode iteration:
|
|
1258
|
+
๐ Iteration 1: Running tests... 5-10 minutes
|
|
1259
|
+
๐ Iteration 2: Running tests... 5-10 minutes
|
|
1260
|
+
๐ Iteration 3: Running tests... 5-10 minutes
|
|
1261
|
+
Total: 15-30 minutes for 3 iterations
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
**After optimization:**
|
|
1265
|
+
```
|
|
1266
|
+
Speed mode iteration:
|
|
1267
|
+
๐ Iteration 1: Running target scenario... 10-30 seconds
|
|
1268
|
+
๐ Iteration 2: Running target scenario... 10-30 seconds
|
|
1269
|
+
๐ Iteration 3: Running target scenario... 10-30 seconds
|
|
1270
|
+
โ
GREEN: Running full verification... 1-2 minutes
|
|
1271
|
+
Total: 2-3 minutes for 3 iterations + verification
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
**20-30x speedup in iteration cycles**
|
|
1275
|
+
|
|
1276
|
+
### Pre-commit Hook Intelligence
|
|
1277
|
+
|
|
1278
|
+
**Context detection:**
|
|
1279
|
+
```javascript
|
|
1280
|
+
// Reads CLAUDE.md and database to determine current work
|
|
1281
|
+
const context = detectWorkContext();
|
|
1282
|
+
// Returns: {mode: 'speed'|'stable'|'production', feature_id, scenario_file}
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
**Adaptive test execution:**
|
|
1286
|
+
```javascript
|
|
1287
|
+
switch (context.mode) {
|
|
1288
|
+
case 'speed':
|
|
1289
|
+
// Fast smoke test: Happy path scenario only
|
|
1290
|
+
const happyPathLine = getFirstScenarioLine(context.scenario_file);
|
|
1291
|
+
result = runBddScenarioWithTimeout(context.scenario_file, happyPathLine, 30000);
|
|
1292
|
+
break;
|
|
1293
|
+
|
|
1294
|
+
case 'stable':
|
|
1295
|
+
// Feature-specific: All scenarios in current feature
|
|
1296
|
+
result = runBddTestWithTimeout(context.scenario_file, 60000);
|
|
1297
|
+
break;
|
|
1298
|
+
|
|
1299
|
+
case 'production':
|
|
1300
|
+
// Comprehensive: Entire test suite
|
|
1301
|
+
result = runBddTestWithTimeout(context.scenario_file, 120000);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
**Non-blocking failures:**
|
|
1307
|
+
- Infrastructure failures allow commit to proceed
|
|
1308
|
+
- Test failures block commit (as expected)
|
|
1309
|
+
- Prevents git hook issues from blocking development
|
|
1310
|
+
|
|
1311
|
+
### Automatic Unit Test Generation
|
|
1312
|
+
|
|
1313
|
+
**Problem solved:**
|
|
1314
|
+
- "I'll add tests later" syndrome
|
|
1315
|
+
- Missing unit test coverage
|
|
1316
|
+
- Inconsistent test patterns
|
|
1317
|
+
|
|
1318
|
+
**Three-phase approach:**
|
|
1319
|
+
|
|
1320
|
+
**Phase 1: Feature Planning (Scaffolding)**
|
|
1321
|
+
```javascript
|
|
1322
|
+
// After BDD validation passes, scaffold empty test files
|
|
1323
|
+
const { scaffoldUnitTestFile } = require('../../lib/unit-test-generator');
|
|
1324
|
+
|
|
1325
|
+
for (const implementationFile of inferredFiles) {
|
|
1326
|
+
if (implementationFile.endsWith('.js') && !implementationFile.includes('test')) {
|
|
1327
|
+
const testFile = scaffoldUnitTestFile(implementationFile);
|
|
1328
|
+
console.log(` โ
Created ${testFile}`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
**Phase 2: Speed Mode (Generation)**
|
|
1334
|
+
```javascript
|
|
1335
|
+
// After GREEN achieved, generate tests for new functions
|
|
1336
|
+
const { generateTestsAfterGreen } = require('../../lib/unit-test-generator');
|
|
1337
|
+
|
|
1338
|
+
for (const modifiedFile of getModifiedFiles()) {
|
|
1339
|
+
const result = generateTestsAfterGreen(modifiedFile);
|
|
1340
|
+
|
|
1341
|
+
if (result.functionsAdded > 0) {
|
|
1342
|
+
console.log(` โ
Generated tests for ${result.functionsAdded} functions`);
|
|
1343
|
+
console.log(` ${result.testFile}`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
**Phase 3: Stable Mode (Edge Cases)**
|
|
1349
|
+
```javascript
|
|
1350
|
+
// Human review and enhancement of generated tests
|
|
1351
|
+
// Add edge case tests, error condition tests, security tests
|
|
1352
|
+
// Fill in TODO placeholders with actual assertions
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
### Coverage Tracking
|
|
1356
|
+
|
|
1357
|
+
**After stable mode completion:**
|
|
1358
|
+
```javascript
|
|
1359
|
+
const { trackFeatureCoverage } = require('../../lib/coverage-tracker');
|
|
1360
|
+
|
|
1361
|
+
const coverageResult = trackFeatureCoverage(implementationFiles, {
|
|
1362
|
+
coverageThreshold: 80,
|
|
1363
|
+
verbose: false
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
console.log(coverageResult.report);
|
|
1367
|
+
// Output:
|
|
1368
|
+
// ๐ Unit Test Coverage Report
|
|
1369
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1370
|
+
// Statements: โ
85.23%
|
|
1371
|
+
// Branches: โ
82.15%
|
|
1372
|
+
// Functions: โ
88.50%
|
|
1373
|
+
// Lines: โ
84.90%
|
|
1374
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1375
|
+
// โ
All coverage metrics meet 80% threshold
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
**What it does:**
|
|
1379
|
+
- Identifies test files for modified implementation files
|
|
1380
|
+
- Runs Jest with coverage for those specific tests
|
|
1381
|
+
- Reports coverage metrics with threshold comparison
|
|
1382
|
+
- Highlights gaps needing additional tests
|
|
1383
|
+
|
|
1384
|
+
### The Complete Optimized Experience
|
|
1385
|
+
|
|
1386
|
+
**Feature planning:**
|
|
1387
|
+
```
|
|
1388
|
+
You: "Add login feature"
|
|
1389
|
+
Claude: "Writing scenarios..."
|
|
1390
|
+
โ
Scenarios validated (dry-run: 10 seconds)
|
|
1391
|
+
โ
Test files scaffolded
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
**Speed mode (optimized):**
|
|
1395
|
+
```
|
|
1396
|
+
๐ Iteration 1: Implementing first step...
|
|
1397
|
+
๐งช Running happy path scenario... 15 seconds
|
|
1398
|
+
๐ 1 of 3 steps passing
|
|
1399
|
+
|
|
1400
|
+
๐ Iteration 2: Implementing auth logic...
|
|
1401
|
+
๐งช Running happy path scenario... 12 seconds
|
|
1402
|
+
๐ 2 of 3 steps passing
|
|
1403
|
+
|
|
1404
|
+
๐ Iteration 3: Adding redirect...
|
|
1405
|
+
๐งช Running happy path scenario... 10 seconds
|
|
1406
|
+
โ
Happy path passes!
|
|
1407
|
+
|
|
1408
|
+
๐ Running full verification... 45 seconds
|
|
1409
|
+
โ
All scenarios passing
|
|
1410
|
+
|
|
1411
|
+
๐ Generating unit tests... 5 seconds
|
|
1412
|
+
โ
Generated tests for 4 functions
|
|
1413
|
+
|
|
1414
|
+
Total time: ~1.5 minutes (vs 15-30 minutes before)
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
**Stable mode (optimized):**
|
|
1418
|
+
```
|
|
1419
|
+
๐ Iteration 1: Adding error handling...
|
|
1420
|
+
๐งช Running error scenario... 18 seconds
|
|
1421
|
+
๐ Target scenario passing
|
|
1422
|
+
|
|
1423
|
+
๐ Running full verification... 45 seconds
|
|
1424
|
+
โ
All scenarios still passing (no regressions)
|
|
1425
|
+
|
|
1426
|
+
๐ Running coverage... 10 seconds
|
|
1427
|
+
โ
85% coverage
|
|
1428
|
+
|
|
1429
|
+
Total time per chore: ~1.2 minutes
|
|
1430
|
+
```
|
|
1431
|
+
|
|
1432
|
+
**Git commit (optimized):**
|
|
1433
|
+
```
|
|
1434
|
+
git commit -m "feat: Add login validation"
|
|
1435
|
+
|
|
1436
|
+
๐งช Pre-commit: Speed mode context detected
|
|
1437
|
+
Running happy path scenario... 12 seconds
|
|
1438
|
+
โ
Tests pass
|
|
1439
|
+
|
|
1440
|
+
โ
Commit allowed
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
---
|
|
1444
|
+
|
|
1445
|
+
## Configuration: Test Strategy Settings
|
|
1446
|
+
|
|
1447
|
+
### .jettypod/test-config.json
|
|
1448
|
+
|
|
1449
|
+
```json
|
|
1450
|
+
{
|
|
1451
|
+
"coverage": {
|
|
1452
|
+
"target": 100,
|
|
1453
|
+
"enforce": true,
|
|
1454
|
+
"excludePatterns": [
|
|
1455
|
+
"**/node_modules/**",
|
|
1456
|
+
"**/test/**",
|
|
1457
|
+
"**/*.test.js",
|
|
1458
|
+
"**/*.spec.js"
|
|
1459
|
+
]
|
|
1460
|
+
},
|
|
1461
|
+
|
|
1462
|
+
"testing": {
|
|
1463
|
+
"framework": {
|
|
1464
|
+
"bdd": "cucumber",
|
|
1465
|
+
"unit": "jest"
|
|
1466
|
+
},
|
|
1467
|
+
|
|
1468
|
+
"runOn": {
|
|
1469
|
+
"featurePlanning": "dry-run",
|
|
1470
|
+
"speedMode": "continuous",
|
|
1471
|
+
"stableMode": "continuous",
|
|
1472
|
+
"productionMode": "comprehensive"
|
|
1473
|
+
},
|
|
1474
|
+
|
|
1475
|
+
"gitHooks": {
|
|
1476
|
+
"preCommit": {
|
|
1477
|
+
"enabled": true,
|
|
1478
|
+
"runTests": true,
|
|
1479
|
+
"contextAware": true
|
|
1480
|
+
},
|
|
1481
|
+
"prePush": {
|
|
1482
|
+
"enabled": true,
|
|
1483
|
+
"runTests": true,
|
|
1484
|
+
"requirePassing": true
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
},
|
|
1488
|
+
|
|
1489
|
+
"documentation": {
|
|
1490
|
+
"testsAreDocumentation": true,
|
|
1491
|
+
"generateMarkdownDocs": true,
|
|
1492
|
+
"autoUpdateOnTest": true
|
|
1493
|
+
},
|
|
1494
|
+
|
|
1495
|
+
"ai": {
|
|
1496
|
+
"readTestsForContext": true,
|
|
1497
|
+
"generateTestsFromScenarios": true,
|
|
1498
|
+
"suggestMissingTests": true
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
---
|
|
1504
|
+
|
|
1505
|
+
## Summary: Testing for Non-Engineers
|
|
1506
|
+
|
|
1507
|
+
### The JettyPod Testing Philosophy
|
|
1508
|
+
|
|
1509
|
+
**1. Tests = Documentation**
|
|
1510
|
+
- No separate docs needed
|
|
1511
|
+
- Tests explain how everything works
|
|
1512
|
+
- AI reads tests to understand system
|
|
1513
|
+
- Users read BDD scenarios to understand features
|
|
1514
|
+
|
|
1515
|
+
**2. 100% Coverage Goal**
|
|
1516
|
+
- Every function documented via unit tests
|
|
1517
|
+
- Every workflow documented via BDD scenarios
|
|
1518
|
+
- Every error case explained via tests
|
|
1519
|
+
- No undocumented behavior
|
|
1520
|
+
|
|
1521
|
+
**3. JettyPod Facilitates Everything**
|
|
1522
|
+
- Auto-generates test infrastructure
|
|
1523
|
+
- Guides scenario writing (plain English)
|
|
1524
|
+
- Runs tests continuously during implementation
|
|
1525
|
+
- Tracks coverage and identifies gaps
|
|
1526
|
+
- Generates documentation from tests
|
|
1527
|
+
|
|
1528
|
+
**4. User Experience**
|
|
1529
|
+
- Write scenarios in plain English
|
|
1530
|
+
- AI implements with TDD
|
|
1531
|
+
- Tests run automatically
|
|
1532
|
+
- Coverage tracked automatically
|
|
1533
|
+
- Documentation generated automatically
|
|
1534
|
+
|
|
1535
|
+
### What Users See
|
|
1536
|
+
|
|
1537
|
+
**Feature planning:**
|
|
1538
|
+
```
|
|
1539
|
+
You: "Add login"
|
|
1540
|
+
JettyPod: "Let's write scenarios..."
|
|
1541
|
+
[Collaborative scenario writing]
|
|
1542
|
+
โ
Scenarios created (your documentation)
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
**Speed mode:**
|
|
1546
|
+
```
|
|
1547
|
+
You: "Implement login"
|
|
1548
|
+
JettyPod: "Implementing with tests..."
|
|
1549
|
+
[TDD implementation with progress]
|
|
1550
|
+
โ
Happy path works (tests show how)
|
|
1551
|
+
```
|
|
1552
|
+
|
|
1553
|
+
**Stable mode:**
|
|
1554
|
+
```
|
|
1555
|
+
You: "Add error handling"
|
|
1556
|
+
JettyPod: "Adding error scenarios..."
|
|
1557
|
+
[Error cases + tests]
|
|
1558
|
+
โ
All cases handled (tests document errors)
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1561
|
+
**Production mode:**
|
|
1562
|
+
```
|
|
1563
|
+
You: "Make it production-ready"
|
|
1564
|
+
JettyPod: "Adding security/performance..."
|
|
1565
|
+
[Production hardening + tests]
|
|
1566
|
+
โ
Production-ready (tests prove it)
|
|
1567
|
+
```
|
|
1568
|
+
|
|
1569
|
+
### The Result
|
|
1570
|
+
|
|
1571
|
+
**For users:**
|
|
1572
|
+
- Features explained in plain English (BDD scenarios)
|
|
1573
|
+
- No need to understand code
|
|
1574
|
+
- Clear documentation of what works and what doesn't
|
|
1575
|
+
|
|
1576
|
+
**For AI assistants:**
|
|
1577
|
+
- Complete understanding of system (unit tests)
|
|
1578
|
+
- All edge cases documented (test cases)
|
|
1579
|
+
- Implementation patterns clear (step definitions)
|
|
1580
|
+
|
|
1581
|
+
**For future developers:**
|
|
1582
|
+
- Onboard by reading tests
|
|
1583
|
+
- Understand behavior from scenarios
|
|
1584
|
+
- See examples of every pattern
|
|
1585
|
+
|
|
1586
|
+
**100% coverage = Complete documentation of the system**
|
|
1587
|
+
|
|
1588
|
+
This is the JettyPod way: Tests aren't overhead, they're the deliverable.
|