jettypod 3.0.1
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/PROTECT_SKILLS.md +28 -0
- package/.claude/settings.json +24 -0
- package/.claude/settings.local.json +16 -0
- package/.claude/skills/epic-discover/SKILL.md +262 -0
- package/.claude/skills/feature-discover/SKILL.md +393 -0
- package/.claude/skills/speed-mode/SKILL.md +364 -0
- package/.claude/skills/stable-mode/SKILL.md +591 -0
- package/.github/workflows/test-safety.yml +85 -0
- package/README.md +25 -0
- package/SPEED-STABLE-AUDIT.md +853 -0
- package/SYSTEM-BEHAVIOR.md +1241 -0
- package/TEST_SAFETY_AUDIT.md +314 -0
- package/TEST_SAFETY_IMPLEMENTATION.md +97 -0
- package/cucumber.js +8 -0
- package/docs/COMMAND_REFERENCE.md +903 -0
- package/docs/DECISIONS.md +68 -0
- package/docs/README.md +48 -0
- package/docs/STANDARDS-SYSTEM-DOCUMENTATION.md +374 -0
- package/docs/TEST-REWRITE-PLAN.md +261 -0
- package/docs/ai-test-writing-requirements.md +219 -0
- package/docs/claude-code-skills.md +607 -0
- package/docs/core-jettypod-methodology/comprehensive-jettypod-methodology.md +582 -0
- package/docs/core-jettypod-methodology/deprecated/jettypod-comprehensive-standards.md +1222 -0
- package/docs/core-jettypod-methodology/deprecated/jettypod-operating-guide.md +3399 -0
- package/docs/core-jettypod-methodology/deprecated/jettypod-technical-checklist.md +1325 -0
- package/docs/core-jettypod-methodology/deprecated/jettypod-vibe-coding-framework.md +1544 -0
- package/docs/core-jettypod-methodology/deprecated/prompt-engineering-guide.md +320 -0
- package/docs/core-jettypod-methodology/deprecated/vibe-coding-cheatsheet (1).md +516 -0
- package/docs/core-jettypod-methodology/deprecated/vibe-coding-framework.md +1544 -0
- package/docs/features/jettypod-standards-explained.md +543 -0
- package/docs/features/standards-inventory.md +257 -0
- package/docs/gap-analysis-current-vs-comprehensive-methodology.md +939 -0
- package/docs/jettypod-system-overview.md +409 -0
- package/features/auto-generate-production-chores.feature +14 -0
- package/features/claude-md-protection/steps.js +487 -0
- package/features/decisions/index.js +490 -0
- package/features/decisions/index.test.js +208 -0
- package/features/git-hooks/git-hooks.feature +30 -0
- package/features/git-hooks/index.js +93 -0
- package/features/git-hooks/index.test.js +137 -0
- package/features/git-hooks/post-commit +56 -0
- package/features/git-hooks/post-merge +47 -0
- package/features/git-hooks/pre-commit +28 -0
- package/features/git-hooks/simple-steps.js +53 -0
- package/features/git-hooks/simple-test.feature +10 -0
- package/features/git-hooks/steps.js +196 -0
- package/features/jettypod-update-command.feature +46 -0
- package/features/mode-prompts/index.js +95 -0
- package/features/mode-prompts/simple-steps.js +44 -0
- package/features/mode-prompts/simple-test.feature +9 -0
- package/features/mode-prompts/validation.test.js +120 -0
- package/features/refactor-mode/steps.js +217 -0
- package/features/refactor-mode.feature +49 -0
- package/features/skills-update/index.test.js +216 -0
- package/features/step_definitions/auto-generate-production-chores.steps.js +162 -0
- package/features/step_definitions/terminal-logo.steps.js +145 -0
- package/features/step_definitions/update-command.steps.js +183 -0
- package/features/terminal-logo/index.js +39 -0
- package/features/terminal-logo/terminal-logo.feature +30 -0
- package/features/update-command/index.js +181 -0
- package/features/update-command/index.test.js +225 -0
- package/features/work-commands/bug-workflow-display.feature +22 -0
- package/features/work-commands/index.js +311 -0
- package/features/work-commands/simple-steps.js +69 -0
- package/features/work-commands/stable-tests.feature +57 -0
- package/features/work-commands/steps.js +1120 -0
- package/features/work-commands/validation.test.js +88 -0
- package/features/work-commands/work-commands.feature +13 -0
- package/features/work-tracking/discovery-validation.test.js +228 -0
- package/features/work-tracking/index.js +1511 -0
- package/features/work-tracking/mode-required.feature +112 -0
- package/features/work-tracking/phase-tracking.test.js +482 -0
- package/features/work-tracking/prototype-tracking.test.js +485 -0
- package/features/work-tracking/tree-view.test.js +310 -0
- package/features/work-tracking/work-set-mode.feature +71 -0
- package/features/work-tracking/work-start-mode.feature +88 -0
- package/full-test.txt +0 -0
- package/install.sh +89 -0
- package/jettypod.js +1640 -0
- package/lib/bug-workflow.js +94 -0
- package/lib/bug-workflow.test.js +177 -0
- package/lib/claudemd.js +130 -0
- package/lib/claudemd.test.js +195 -0
- package/lib/comprehensive-standards-full.json +1778 -0
- package/lib/config.js +181 -0
- package/lib/config.test.js +511 -0
- package/lib/constants.js +107 -0
- package/lib/constants.test.js +164 -0
- package/lib/current-work.js +130 -0
- package/lib/current-work.test.js +146 -0
- package/lib/database-project-config.test.js +107 -0
- package/lib/database.js +256 -0
- package/lib/database.test.js +106 -0
- package/lib/decisions-generator.js +102 -0
- package/lib/decisions-generator.test.js +457 -0
- package/lib/decisions-helpers.js +119 -0
- package/lib/decisions-helpers.test.js +310 -0
- package/lib/discovery-checkpoint.js +83 -0
- package/lib/docs-generator.js +280 -0
- package/lib/external-checklist.js +177 -0
- package/lib/git.js +142 -0
- package/lib/git.test.js +145 -0
- package/lib/logo.js +3 -0
- package/lib/migrations/001-epic-to-parent.js +24 -0
- package/lib/migrations/002-default-work-item-modes.js +37 -0
- package/lib/migrations/002-default-work-item-modes.test.js +351 -0
- package/lib/migrations/003-epic-discovery-fields.js +52 -0
- package/lib/migrations/004-discovery-decisions-table.js +32 -0
- package/lib/migrations/005-migrate-decision-data.js +62 -0
- package/lib/migrations/006-feature-phase-field.js +61 -0
- package/lib/migrations/007-prototype-tracking.js +38 -0
- package/lib/migrations/008-scenario-file-field.js +24 -0
- package/lib/migrations/index.js +74 -0
- package/lib/production-helpers.js +69 -0
- package/lib/project-state.test.js +92 -0
- package/lib/test-helpers.js +184 -0
- package/lib/test-helpers.test.js +255 -0
- package/package.json +36 -0
- package/prototypes/test/index.html +1 -0
- package/setup-dist-repo.sh +68 -0
- package/test-safety-check.sh +80 -0
- package/work-item-tracking-plan.md +199 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# JettyPod Test Rewrite Plan
|
|
2
|
+
|
|
3
|
+
## Current State
|
|
4
|
+
- **32 total scenarios**: 6 passing, 8 failing, 18 undefined
|
|
5
|
+
- Tests written for legacy dual-standards system
|
|
6
|
+
- Many step definitions missing or outdated
|
|
7
|
+
- No unit tests (only BDD with Cucumber)
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
Rewrite tests to match the new comprehensive standards engine that uses violation-based progressive disclosure.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Phase 1: Remove Legacy Test Scenarios
|
|
15
|
+
**Priority: High | Estimated: 30 minutes**
|
|
16
|
+
|
|
17
|
+
### Tasks:
|
|
18
|
+
- [ ] Remove/update scenarios that test for `.jettypod/standards.json` creation
|
|
19
|
+
- [ ] Remove tests checking for "Legacy Standards" section in CLAUDE.md
|
|
20
|
+
- [ ] Remove tests for old standards filtering (by stage/priority/bundle)
|
|
21
|
+
- [ ] Remove tests expecting mode-specific formatting (speed/discovery/production)
|
|
22
|
+
|
|
23
|
+
### Files to Update:
|
|
24
|
+
- `.gherkin/features/01-initialization.feature`
|
|
25
|
+
- `.gherkin/features/05-standards-filtering.feature`
|
|
26
|
+
- `.gherkin/features/06-claude-generation.feature`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Phase 2: Add New Standards Engine Tests
|
|
31
|
+
**Priority: Critical | Estimated: 1 hour**
|
|
32
|
+
|
|
33
|
+
### New Test Scenarios Needed:
|
|
34
|
+
|
|
35
|
+
#### 2.1 Violation Tracking Tests
|
|
36
|
+
```gherkin
|
|
37
|
+
Feature: Violation-Based Standards
|
|
38
|
+
Scenario: Standards appear after threshold
|
|
39
|
+
Given JettyPod is initialized
|
|
40
|
+
When I record 2 violations for "file.naming.constants"
|
|
41
|
+
Then CLAUDE.md should not contain "SCREAMING_SNAKE"
|
|
42
|
+
When I record 1 more violation for "file.naming.constants"
|
|
43
|
+
Then CLAUDE.md should contain "SCREAMING_SNAKE for constants (3 violations)"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### 2.2 Critical Standards Tests
|
|
47
|
+
```gherkin
|
|
48
|
+
Feature: Critical Standards Always Show
|
|
49
|
+
Scenario: Critical standards appear immediately
|
|
50
|
+
Given JettyPod is initialized in "starting" stage
|
|
51
|
+
And bundles are ["core"]
|
|
52
|
+
When I run "jettypod generate"
|
|
53
|
+
Then CLAUDE.md should contain all critical standards for core bundle
|
|
54
|
+
And CLAUDE.md should not contain non-critical standards without violations
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### 2.3 Bundle Detection Tests
|
|
58
|
+
```gherkin
|
|
59
|
+
Feature: Automatic Bundle Detection
|
|
60
|
+
Scenario: React standards auto-include when React detected
|
|
61
|
+
Given package.json contains React dependency
|
|
62
|
+
When I run "jettypod generate"
|
|
63
|
+
Then CLAUDE.md should contain React critical standards
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### 2.4 Mode Filtering Tests
|
|
67
|
+
```gherkin
|
|
68
|
+
Feature: Mode-Based Filtering
|
|
69
|
+
Scenario: Speed mode filters by priority
|
|
70
|
+
Given JettyPod is in speed mode
|
|
71
|
+
And "test.medium.standard" has 5 violations (medium priority)
|
|
72
|
+
When I run "jettypod generate"
|
|
73
|
+
Then CLAUDE.md should not contain "test.medium.standard"
|
|
74
|
+
|
|
75
|
+
Scenario: Discovery mode shows all violated standards
|
|
76
|
+
Given JettyPod is in discovery mode
|
|
77
|
+
And "test.medium.standard" has 3 violations
|
|
78
|
+
When I run "jettypod generate"
|
|
79
|
+
Then CLAUDE.md should contain "test.medium.standard"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Phase 3: Fix Existing Step Definitions
|
|
85
|
+
**Priority: High | Estimated: 45 minutes**
|
|
86
|
+
|
|
87
|
+
### Step Definitions to Add/Fix:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// Violation simulation
|
|
91
|
+
Given('{string} has {int} violations', function(standardId, count) {
|
|
92
|
+
// Clear existing violations for this standard
|
|
93
|
+
const violationsPath = '.jettypod/violations.json';
|
|
94
|
+
let violations = {};
|
|
95
|
+
if (fs.existsSync(violationsPath)) {
|
|
96
|
+
violations = JSON.parse(fs.readFileSync(violationsPath));
|
|
97
|
+
}
|
|
98
|
+
violations[standardId] = {
|
|
99
|
+
count: count,
|
|
100
|
+
lastSeen: new Date().toISOString()
|
|
101
|
+
};
|
|
102
|
+
fs.writeFileSync(violationsPath, JSON.stringify(violations, null, 2));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Check for specific standards sections
|
|
106
|
+
Then('CLAUDE.md should contain all critical standards for {word} bundle', function(bundle) {
|
|
107
|
+
const claude = readClaude();
|
|
108
|
+
const library = JSON.parse(fs.readFileSync('lib/comprehensive-standards-full.json'));
|
|
109
|
+
const criticalStandards = Object.values(library.standards)
|
|
110
|
+
.filter(s => s.bundle === bundle && s.priority === 'critical');
|
|
111
|
+
|
|
112
|
+
criticalStandards.forEach(standard => {
|
|
113
|
+
expect(claude).to.include(standard.shortRule);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Verify violation counts
|
|
118
|
+
Then('CLAUDE.md should contain {string} with {int} violations', function(text, count) {
|
|
119
|
+
const claude = readClaude();
|
|
120
|
+
expect(claude).to.include(`${text} (${count} violations)`);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Check standards NOT present
|
|
124
|
+
Then('CLAUDE.md should not contain non-critical standards without violations', function() {
|
|
125
|
+
const claude = readClaude();
|
|
126
|
+
const standardsSection = claude.match(/<standards>([\s\S]*?)<\/standards>/);
|
|
127
|
+
if (standardsSection) {
|
|
128
|
+
// Should only have CRITICAL RULES section, no WATCH section if no violations
|
|
129
|
+
expect(standardsSection[1]).to.not.include('WATCH:');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Phase 4: Create Unit Tests
|
|
137
|
+
**Priority: Medium | Estimated: 1 hour**
|
|
138
|
+
|
|
139
|
+
### New Unit Test Files:
|
|
140
|
+
- `tests/standards-engine.test.js`
|
|
141
|
+
- `tests/violation-tracking.test.js`
|
|
142
|
+
|
|
143
|
+
### Test Coverage Needed:
|
|
144
|
+
```javascript
|
|
145
|
+
// tests/standards-engine.test.js
|
|
146
|
+
describe('StandardsEngine', () => {
|
|
147
|
+
describe('prescribe()', () => {
|
|
148
|
+
it('should include critical standards for active bundles')
|
|
149
|
+
it('should include React criticals when React detected')
|
|
150
|
+
it('should add standards that exceed violation threshold')
|
|
151
|
+
it('should filter by mode (speed vs discovery)')
|
|
152
|
+
it('should respect stage filtering')
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('recordViolation()', () => {
|
|
156
|
+
it('should create violation record if none exists')
|
|
157
|
+
it('should increment violation count')
|
|
158
|
+
it('should update lastSeen timestamp')
|
|
159
|
+
it('should not modify comprehensive standards file')
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('formatForClaude()', () => {
|
|
163
|
+
it('should separate critical and violated standards')
|
|
164
|
+
it('should show violation counts in WATCH section')
|
|
165
|
+
it('should not include non-violated standards in WATCH')
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Phase 5: Clean Up Test Structure
|
|
173
|
+
**Priority: Low | Estimated: 30 minutes**
|
|
174
|
+
|
|
175
|
+
### Improvements:
|
|
176
|
+
- [ ] Consolidate duplicate step definitions
|
|
177
|
+
- [ ] Add helper functions for common operations
|
|
178
|
+
- [ ] Improve test data setup/teardown
|
|
179
|
+
- [ ] Add test utilities file for shared functions
|
|
180
|
+
|
|
181
|
+
### Helper Functions Needed:
|
|
182
|
+
```javascript
|
|
183
|
+
// test-helpers.js
|
|
184
|
+
function resetJettyPod() {
|
|
185
|
+
// Clean .jettypod directory
|
|
186
|
+
// Reset violations
|
|
187
|
+
// Set default config
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function setViolations(violations) {
|
|
191
|
+
// Set specific violation state for testing
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function assertStandardPresent(standardId) {
|
|
195
|
+
// Check if standard appears in CLAUDE.md
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function assertStandardAbsent(standardId) {
|
|
199
|
+
// Verify standard does NOT appear
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Phase 6: Documentation
|
|
206
|
+
**Priority: Low | Estimated: 15 minutes**
|
|
207
|
+
|
|
208
|
+
### Documentation Updates:
|
|
209
|
+
- [ ] Update README with test running instructions
|
|
210
|
+
- [ ] Document test structure and patterns
|
|
211
|
+
- [ ] Add troubleshooting guide for common test failures
|
|
212
|
+
- [ ] Document how to add new test scenarios
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Implementation Order
|
|
217
|
+
|
|
218
|
+
1. **Immediate (Fix Breaking Tests)**
|
|
219
|
+
- Add missing step definitions
|
|
220
|
+
- Fix regex patterns for Cucumber expressions
|
|
221
|
+
- Update paths and file references
|
|
222
|
+
|
|
223
|
+
2. **Next (Remove Legacy)**
|
|
224
|
+
- Clean out old standards.json checks
|
|
225
|
+
- Remove legacy formatting tests
|
|
226
|
+
- Update CLAUDE.md content assertions
|
|
227
|
+
|
|
228
|
+
3. **Then (Add New Coverage)**
|
|
229
|
+
- Implement violation threshold tests
|
|
230
|
+
- Add bundle detection tests
|
|
231
|
+
- Create mode filtering tests
|
|
232
|
+
|
|
233
|
+
4. **Finally (Nice to Have)**
|
|
234
|
+
- Add unit tests for standards engine
|
|
235
|
+
- Improve test organization
|
|
236
|
+
- Add documentation
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Success Criteria
|
|
241
|
+
|
|
242
|
+
- [ ] All 32 scenarios have proper step definitions (no undefined)
|
|
243
|
+
- [ ] Tests accurately reflect new standards engine behavior
|
|
244
|
+
- [ ] Violation tracking is properly tested
|
|
245
|
+
- [ ] Critical standards always-show behavior is verified
|
|
246
|
+
- [ ] Mode filtering (speed vs others) is tested
|
|
247
|
+
- [ ] Stage cumulative filtering is tested
|
|
248
|
+
- [ ] Bundle detection (React) is tested
|
|
249
|
+
- [ ] Test run completes in under 10 seconds
|
|
250
|
+
- [ ] No false positives or flaky tests
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Notes
|
|
255
|
+
|
|
256
|
+
- Focus on integration tests over unit tests initially
|
|
257
|
+
- Prioritize testing the user-facing behavior
|
|
258
|
+
- Keep tests simple and readable
|
|
259
|
+
- Avoid testing implementation details
|
|
260
|
+
- Each test should have a clear purpose
|
|
261
|
+
- Use descriptive test names that explain the behavior
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# AI Test Writing Requirements
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document provides guidelines for AI assistants when writing tests for this codebase. These requirements balance safety with practicality.
|
|
5
|
+
|
|
6
|
+
## Core Principles
|
|
7
|
+
|
|
8
|
+
### 1. Track Everything You Create
|
|
9
|
+
- Maintain lists of all files and directories created during tests
|
|
10
|
+
- Ensure cleanup even if tests fail
|
|
11
|
+
- Use try/catch blocks around cleanup operations
|
|
12
|
+
|
|
13
|
+
### 2. Snapshot and Restore Critical Files
|
|
14
|
+
- Take snapshots of critical files (package.json, config files) before tests
|
|
15
|
+
- Restore them in After hooks unconditionally
|
|
16
|
+
- Verify restoration was successful
|
|
17
|
+
|
|
18
|
+
### 3. Clean Up Test Artifacts
|
|
19
|
+
- Remove all test-created files (temp_*, test_*, etc.)
|
|
20
|
+
- Clean up test directories you explicitly created
|
|
21
|
+
- Never delete production directories (lib/, src/, node_modules/)
|
|
22
|
+
|
|
23
|
+
## Required Test Structure
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
const { Before, After } = require('@cucumber/cucumber');
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
|
|
30
|
+
// Initialize tracking
|
|
31
|
+
function initializeTracking(context) {
|
|
32
|
+
context.createdFiles = [];
|
|
33
|
+
context.createdDirs = [];
|
|
34
|
+
context.modifiedFiles = new Map();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Before(function() {
|
|
38
|
+
// Initialize tracking
|
|
39
|
+
initializeTracking(this);
|
|
40
|
+
|
|
41
|
+
// Snapshot critical files
|
|
42
|
+
this.originalSnapshot = {
|
|
43
|
+
packageJson: fs.existsSync('package.json')
|
|
44
|
+
? fs.readFileSync('package.json', 'utf-8')
|
|
45
|
+
: null,
|
|
46
|
+
config: fs.existsSync('.jettypod/config.json')
|
|
47
|
+
? fs.readFileSync('.jettypod/config.json', 'utf-8')
|
|
48
|
+
: null
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
After(function() {
|
|
53
|
+
// Restore critical files FIRST
|
|
54
|
+
if (this.originalSnapshot?.packageJson) {
|
|
55
|
+
fs.writeFileSync('package.json', this.originalSnapshot.packageJson);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Clean up created files
|
|
59
|
+
if (this.createdFiles) {
|
|
60
|
+
this.createdFiles.forEach(file => {
|
|
61
|
+
if (fs.existsSync(file)) {
|
|
62
|
+
try { fs.unlinkSync(file); } catch (e) {}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Clean up created directories (in reverse order)
|
|
68
|
+
if (this.createdDirs) {
|
|
69
|
+
[...this.createdDirs].reverse().forEach(dir => {
|
|
70
|
+
if (fs.existsSync(dir)) {
|
|
71
|
+
try { fs.rmSync(dir, { recursive: true }); } catch (e) {}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Safe Patterns
|
|
79
|
+
|
|
80
|
+
### Creating Test Files
|
|
81
|
+
```javascript
|
|
82
|
+
// GOOD - Track what you create
|
|
83
|
+
Given('I create a test file', function() {
|
|
84
|
+
const filename = 'test_file.js';
|
|
85
|
+
fs.writeFileSync(filename, '// test content');
|
|
86
|
+
this.createdFiles.push(filename);
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Modifying Existing Files
|
|
91
|
+
```javascript
|
|
92
|
+
// GOOD - Snapshot before modifying
|
|
93
|
+
Given('package.json contains React', function() {
|
|
94
|
+
if (!this.packageBackup) {
|
|
95
|
+
this.packageBackup = fs.readFileSync('package.json', 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
|
98
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
99
|
+
pkg.dependencies.react = '^18.0.0';
|
|
100
|
+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Creating Directories
|
|
105
|
+
```javascript
|
|
106
|
+
// GOOD - Track all levels
|
|
107
|
+
Given('I create nested directories', function() {
|
|
108
|
+
const dir = 'test/nested/dir';
|
|
109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
+
|
|
111
|
+
// Track all levels for cleanup
|
|
112
|
+
const parts = dir.split('/');
|
|
113
|
+
let currentPath = '';
|
|
114
|
+
parts.forEach(part => {
|
|
115
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
116
|
+
if (!this.createdDirs.includes(currentPath)) {
|
|
117
|
+
this.createdDirs.push(currentPath);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Dangerous Patterns to Avoid
|
|
124
|
+
|
|
125
|
+
### L NEVER Do These:
|
|
126
|
+
|
|
127
|
+
1. **Direct package.json modification without backup**
|
|
128
|
+
```javascript
|
|
129
|
+
// L WRONG - No backup
|
|
130
|
+
fs.writeFileSync('package.json', modifiedContent);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
2. **Cleanup with dangerous patterns**
|
|
134
|
+
```javascript
|
|
135
|
+
// L WRONG - Could delete production directories
|
|
136
|
+
const dirsToDelete = ['lib', 'src', 'node_modules'];
|
|
137
|
+
dirsToDelete.forEach(dir => fs.rmSync(dir, { recursive: true }));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
3. **Untracked file creation**
|
|
141
|
+
```javascript
|
|
142
|
+
// L WRONG - Not tracked for cleanup
|
|
143
|
+
fs.writeFileSync('test.js', 'content');
|
|
144
|
+
// No this.createdFiles.push('test.js');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
4. **Assuming cleanup will run**
|
|
148
|
+
```javascript
|
|
149
|
+
// L WRONG - Cleanup might not run if test crashes
|
|
150
|
+
After(function() {
|
|
151
|
+
// Critical cleanup here might never execute
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Simplified Approach (Recommended)
|
|
156
|
+
|
|
157
|
+
For most tests, use a simplified approach that:
|
|
158
|
+
1. Runs in the current directory (not isolated)
|
|
159
|
+
2. Tracks all created files/directories
|
|
160
|
+
3. Takes snapshots of critical files
|
|
161
|
+
4. Restores everything in After hooks
|
|
162
|
+
|
|
163
|
+
This approach is simpler than full isolation but still safe because:
|
|
164
|
+
- All changes are tracked and reversed
|
|
165
|
+
- Critical files are protected by snapshots
|
|
166
|
+
- Cleanup is guaranteed even on failure
|
|
167
|
+
|
|
168
|
+
## Testing Checklist
|
|
169
|
+
|
|
170
|
+
When writing tests, ensure:
|
|
171
|
+
- [ ] All created files are tracked in `this.createdFiles`
|
|
172
|
+
- [ ] All created directories are tracked in `this.createdDirs`
|
|
173
|
+
- [ ] package.json is snapshotted before any modification
|
|
174
|
+
- [ ] Config files are snapshotted before changes
|
|
175
|
+
- [ ] After hook restores all snapshots
|
|
176
|
+
- [ ] After hook removes all tracked items
|
|
177
|
+
- [ ] No production directories in cleanup lists
|
|
178
|
+
- [ ] Cleanup uses try/catch to continue on errors
|
|
179
|
+
- [ ] Test artifacts use clear naming (test_*, temp_*)
|
|
180
|
+
|
|
181
|
+
## Advanced: Full Isolation (Optional)
|
|
182
|
+
|
|
183
|
+
For complete safety, tests can run in isolated temp directories:
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const os = require('os');
|
|
187
|
+
const fs = require('fs');
|
|
188
|
+
const path = require('path');
|
|
189
|
+
|
|
190
|
+
Before(function() {
|
|
191
|
+
// Create isolated test directory
|
|
192
|
+
this.testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-'));
|
|
193
|
+
|
|
194
|
+
// Copy only necessary files
|
|
195
|
+
[.jettypod.js', 'package.json', 'lib'].forEach(item => {
|
|
196
|
+
// Copy to test directory
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
process.chdir(this.testDir);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
After(function() {
|
|
203
|
+
process.chdir(this.originalCwd);
|
|
204
|
+
fs.rmSync(this.testDir, { recursive: true, force: true });
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
However, this approach has complications with module loading and is often overkill for most test scenarios.
|
|
209
|
+
|
|
210
|
+
## Summary
|
|
211
|
+
|
|
212
|
+
The key to safe test writing is:
|
|
213
|
+
1. **Track everything** you create or modify
|
|
214
|
+
2. **Snapshot** critical files before changes
|
|
215
|
+
3. **Restore** everything in After hooks
|
|
216
|
+
4. **Never** assume cleanup will run
|
|
217
|
+
5. **Never** delete production directories
|
|
218
|
+
|
|
219
|
+
Following these guidelines ensures tests can't corrupt the production codebase while keeping the test implementation simple and maintainable.
|