phantom-pr 0.4.19 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +216 -80
  2. package/dist/core/analysis/astExtractor.d.ts +15 -0
  3. package/dist/core/analysis/astExtractor.js +354 -0
  4. package/dist/core/analysis/astExtractor.js.map +1 -0
  5. package/dist/core/analysis/index.d.ts +3 -0
  6. package/dist/core/analysis/index.js +3 -0
  7. package/dist/core/analysis/index.js.map +1 -0
  8. package/dist/core/analysis/types.d.ts +50 -0
  9. package/dist/core/analysis/types.js +16 -0
  10. package/dist/core/analysis/types.js.map +1 -0
  11. package/dist/core/context/contextSelector.d.ts +29 -0
  12. package/dist/core/context/contextSelector.js +293 -0
  13. package/dist/core/context/contextSelector.js.map +1 -0
  14. package/dist/core/context/index.d.ts +4 -0
  15. package/dist/core/context/index.js +3 -0
  16. package/dist/core/context/index.js.map +1 -0
  17. package/dist/core/generator/enhancedContext.d.ts +34 -0
  18. package/dist/core/generator/enhancedContext.js +144 -0
  19. package/dist/core/generator/enhancedContext.js.map +1 -0
  20. package/dist/core/generator/postValidation.d.ts +24 -0
  21. package/dist/core/generator/postValidation.js +57 -0
  22. package/dist/core/generator/postValidation.js.map +1 -0
  23. package/dist/core/retry/errorParser.d.ts +12 -0
  24. package/dist/core/retry/errorParser.js +264 -0
  25. package/dist/core/retry/errorParser.js.map +1 -0
  26. package/dist/core/retry/feedbackGenerator.d.ts +12 -0
  27. package/dist/core/retry/feedbackGenerator.js +164 -0
  28. package/dist/core/retry/feedbackGenerator.js.map +1 -0
  29. package/dist/core/retry/index.d.ts +3 -0
  30. package/dist/core/retry/index.js +3 -0
  31. package/dist/core/retry/index.js.map +1 -0
  32. package/dist/core/retry/types.d.ts +40 -0
  33. package/dist/core/retry/types.js +5 -0
  34. package/dist/core/retry/types.js.map +1 -0
  35. package/dist/core/testRunner/retryEnhancer.d.ts +32 -0
  36. package/dist/core/testRunner/retryEnhancer.js +58 -0
  37. package/dist/core/testRunner/retryEnhancer.js.map +1 -0
  38. package/dist/core/validation/coverageVerifier.d.ts +16 -0
  39. package/dist/core/validation/coverageVerifier.js +226 -0
  40. package/dist/core/validation/coverageVerifier.js.map +1 -0
  41. package/dist/core/validation/index.d.ts +2 -0
  42. package/dist/core/validation/index.js +2 -0
  43. package/dist/core/validation/index.js.map +1 -0
  44. package/dist/core/validation/types.d.ts +24 -0
  45. package/dist/core/validation/types.js +6 -0
  46. package/dist/core/validation/types.js.map +1 -0
  47. package/package.json +1 -1
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Post-generation validator that verifies generated tests cover all identified behaviors.
3
+ * Returns structured validation result with specific rejection codes.
4
+ *
5
+ * Static analysis only - does NOT execute the test.
6
+ */
7
+ /**
8
+ * Extract test block names from test file content.
9
+ * Matches it('...'), test('...'), describe('...') patterns.
10
+ */
11
+ function extractTestNames(testContent) {
12
+ const names = [];
13
+ // Match it('name', ...), test('name', ...), describe('name', ...)
14
+ const testBlockRe = /(?:it|test|describe)\s*\(\s*['"`]([^'"`]+)['"`]/g;
15
+ for (const match of testContent.matchAll(testBlockRe)) {
16
+ const name = match[1];
17
+ if (name)
18
+ names.push(name.toLowerCase());
19
+ }
20
+ return names;
21
+ }
22
+ /**
23
+ * Extract jest.mock() and vi.mock() calls from test content.
24
+ */
25
+ function extractMockedModules(testContent) {
26
+ const mocked = new Set();
27
+ // jest.mock('path') or vi.mock('path')
28
+ const mockRe = /(?:jest|vi)\.mock\s*\(\s*['"`]([^'"`]+)['"`]/g;
29
+ for (const match of testContent.matchAll(mockRe)) {
30
+ const path = match[1];
31
+ if (path)
32
+ mocked.add(path);
33
+ }
34
+ return mocked;
35
+ }
36
+ /**
37
+ * Check if test content appears to test branch conditions.
38
+ * Looks for patterns that suggest conditional testing.
39
+ */
40
+ function countBranchTests(testContent, branches) {
41
+ const testNames = extractTestNames(testContent);
42
+ const contentLower = testContent.toLowerCase();
43
+ let testedCount = 0;
44
+ for (const branch of branches) {
45
+ const conditionLower = branch.condition.toLowerCase();
46
+ // Check if any test name mentions the condition
47
+ const mentionedInTestName = testNames.some(name => name.includes(conditionLower) ||
48
+ conditionLower.includes(name.split(' ')[0] ?? ''));
49
+ // Check for common branch testing patterns
50
+ const hasConditionTest = contentLower.includes(`when ${conditionLower}`) ||
51
+ contentLower.includes(`if ${conditionLower}`) ||
52
+ contentLower.includes(`${conditionLower} is true`) ||
53
+ contentLower.includes(`${conditionLower} is false`) ||
54
+ contentLower.includes(`should render when ${conditionLower}`) ||
55
+ contentLower.includes(`should not render when`);
56
+ // Check for assertion patterns that might test the branch
57
+ const hasAssertions = contentLower.includes('tobetruthy') ||
58
+ contentLower.includes('tobefalsy') ||
59
+ contentLower.includes('tobeinthedocument') ||
60
+ contentLower.includes('not.tobeinthedocument');
61
+ if (mentionedInTestName || hasConditionTest || hasAssertions) {
62
+ testedCount++;
63
+ }
64
+ }
65
+ // Heuristic: if there are multiple test blocks covering different scenarios,
66
+ // assume branches are being adequately tested (lenient to avoid false rejections)
67
+ if (branches.length > 0 && testNames.length >= 2) {
68
+ // If we have descriptive test names, count them as covering branches
69
+ testedCount = Math.max(testedCount, Math.min(branches.length, testNames.length));
70
+ }
71
+ return testedCount;
72
+ }
73
+ /**
74
+ * Check if test content appears to test React effects.
75
+ */
76
+ function countEffectTests(testContent, effects) {
77
+ const contentLower = testContent.toLowerCase();
78
+ const testNames = extractTestNames(testContent);
79
+ // Check for effect-related patterns
80
+ const hasEffectTest = contentLower.includes('useeffect') ||
81
+ contentLower.includes('effect') ||
82
+ contentLower.includes('on mount') ||
83
+ contentLower.includes('onmount') ||
84
+ contentLower.includes('lifecycle') ||
85
+ contentLower.includes('side effect');
86
+ // Check for async patterns often used with effects
87
+ const hasAsyncPattern = contentLower.includes('await') ||
88
+ contentLower.includes('waitfor') ||
89
+ contentLower.includes('act(');
90
+ // Check for cleanup patterns (testing cleanup implies testing the effect)
91
+ const hasCleanupPattern = contentLower.includes('unmount') ||
92
+ contentLower.includes('cleanup') ||
93
+ contentLower.includes('clearinterval') ||
94
+ contentLower.includes('cleartimeout');
95
+ // Check for render patterns (basic effect tests render the component)
96
+ const hasRenderPattern = contentLower.includes('render(');
97
+ // If any effect-related pattern is found, assume effects are being tested
98
+ if (hasEffectTest || hasAsyncPattern || hasCleanupPattern) {
99
+ return effects.length;
100
+ }
101
+ // If there are multiple test cases and render is used, be lenient
102
+ if (testNames.length >= 2 && hasRenderPattern) {
103
+ return effects.length;
104
+ }
105
+ return 0;
106
+ }
107
+ /**
108
+ * Check if test content appears to test cleanup behavior.
109
+ */
110
+ function hasCleanupTest(testContent) {
111
+ const contentLower = testContent.toLowerCase();
112
+ return (contentLower.includes('unmount') ||
113
+ contentLower.includes('cleanup') ||
114
+ contentLower.includes('clearinterval') ||
115
+ contentLower.includes('cleartimeout') ||
116
+ contentLower.includes('removelistener') ||
117
+ contentLower.includes('unsubscribe') ||
118
+ contentLower.includes('dispose'));
119
+ }
120
+ /**
121
+ * Extract child component names from import path.
122
+ */
123
+ function normalizeImportToName(importPath) {
124
+ // Get the last segment and strip extension
125
+ const segments = importPath.split('/');
126
+ const last = segments[segments.length - 1] ?? importPath;
127
+ return last.replace(/\.(tsx?|jsx?)$/, '');
128
+ }
129
+ /**
130
+ * Check which child components are mocked in the test.
131
+ */
132
+ function findMockedChildren(testContent, children) {
133
+ const mockedModules = extractMockedModules(testContent);
134
+ const mocked = [];
135
+ for (const child of children) {
136
+ // Check if the child's import path is mocked
137
+ const isMocked = mockedModules.has(child.importPath) ||
138
+ [...mockedModules].some(m => m.includes(child.name) ||
139
+ normalizeImportToName(m).toLowerCase() === child.name.toLowerCase());
140
+ if (isMocked) {
141
+ mocked.push(child.name);
142
+ }
143
+ }
144
+ // Also check for inline mocks: jest.fn() assignments that mention component names
145
+ for (const child of children) {
146
+ if (!mocked.includes(child.name)) {
147
+ const inlineMockRe = new RegExp(`${child.name}[\\s\\S]*?=.*?jest\\.fn|vi\\.fn`, 'i');
148
+ if (inlineMockRe.test(testContent)) {
149
+ mocked.push(child.name);
150
+ }
151
+ }
152
+ }
153
+ return [...new Set(mocked)].sort();
154
+ }
155
+ /**
156
+ * Verify that generated test adequately covers the analyzed component.
157
+ *
158
+ * @param analysis - Component analysis from AST extraction
159
+ * @param generatedTestContent - The generated test file content
160
+ * @returns ValidationResult with coverage details and any rejections
161
+ */
162
+ export function verifyCoverage(analysis, generatedTestContent) {
163
+ const rejections = [];
164
+ const warnings = [];
165
+ // Count branch coverage
166
+ const branchesIdentified = analysis.branches.length;
167
+ const branchesTested = countBranchTests(generatedTestContent, analysis.branches);
168
+ // Count effect coverage
169
+ const effectsIdentified = analysis.effects.length;
170
+ const effectsTested = countEffectTests(generatedTestContent, analysis.effects);
171
+ // Check cleanup requirement
172
+ const cleanupRequired = analysis.effects.some(e => e.hasCleanup);
173
+ const cleanupTested = cleanupRequired ? hasCleanupTest(generatedTestContent) : true;
174
+ // Check child mocking
175
+ const childrenIdentified = analysis.children.map(c => c.name).sort();
176
+ const childrenMocked = findMockedChildren(generatedTestContent, analysis.children);
177
+ // Build coverage report
178
+ const coverage = {
179
+ branchesIdentified,
180
+ branchesTested,
181
+ effectsIdentified,
182
+ effectsTested,
183
+ cleanupRequired,
184
+ cleanupTested,
185
+ childrenIdentified,
186
+ childrenMocked,
187
+ };
188
+ // Apply rejection rules
189
+ // Rule: All branches should be tested
190
+ if (branchesIdentified > 0 && branchesTested < branchesIdentified) {
191
+ // Only reject if significantly under-tested
192
+ const coverage = branchesTested / branchesIdentified;
193
+ if (coverage < 0.5) {
194
+ rejections.push('llm_reject_incomplete_branch_coverage');
195
+ }
196
+ else {
197
+ warnings.push(`Branch coverage: ${branchesTested}/${branchesIdentified} (${Math.round(coverage * 100)}%)`);
198
+ }
199
+ }
200
+ // Rule: Cleanup must be tested if required
201
+ if (cleanupRequired && !cleanupTested) {
202
+ rejections.push('llm_reject_missing_cleanup_test');
203
+ }
204
+ // Rule: All child components should be mocked
205
+ const unmockedChildren = childrenIdentified.filter(c => !childrenMocked.includes(c));
206
+ if (unmockedChildren.length > 0) {
207
+ // Only reject if more than half are unmocked
208
+ if (unmockedChildren.length > childrenIdentified.length / 2) {
209
+ rejections.push('llm_reject_unmocked_child');
210
+ }
211
+ else {
212
+ warnings.push(`Unmocked children: ${unmockedChildren.join(', ')}`);
213
+ }
214
+ }
215
+ // Rule: Effects should be tested
216
+ if (effectsIdentified > 0 && effectsTested === 0) {
217
+ rejections.push('llm_reject_missing_effect_test');
218
+ }
219
+ return {
220
+ valid: rejections.length === 0,
221
+ rejections,
222
+ warnings,
223
+ coverage,
224
+ };
225
+ }
226
+ //# sourceMappingURL=coverageVerifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverageVerifier.js","sourceRoot":"","sources":["../../../src/core/validation/coverageVerifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export type { RejectionCode, CoverageReport, ValidationResult, } from './types.js';
2
+ export { verifyCoverage } from './coverageVerifier.js';
@@ -0,0 +1,2 @@
1
+ export { verifyCoverage } from './coverageVerifier.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/validation/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Types for post-generation validation.
3
+ * Verifies generated tests cover all identified behaviors.
4
+ */
5
+ /** Rejection codes matching existing guardrail pattern (llm_reject_*). */
6
+ export type RejectionCode = 'llm_reject_incomplete_branch_coverage' | 'llm_reject_missing_cleanup_test' | 'llm_reject_unmocked_child' | 'llm_reject_missing_effect_test';
7
+ /** Coverage report showing what was analyzed vs tested. */
8
+ export type CoverageReport = {
9
+ branchesIdentified: number;
10
+ branchesTested: number;
11
+ effectsIdentified: number;
12
+ effectsTested: number;
13
+ cleanupRequired: boolean;
14
+ cleanupTested: boolean;
15
+ childrenIdentified: string[];
16
+ childrenMocked: string[];
17
+ };
18
+ /** Result of coverage verification. */
19
+ export type ValidationResult = {
20
+ valid: boolean;
21
+ rejections: RejectionCode[];
22
+ warnings: string[];
23
+ coverage: CoverageReport;
24
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Types for post-generation validation.
3
+ * Verifies generated tests cover all identified behaviors.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/validation/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phantom-pr",
3
- "version": "0.4.19",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "description": "CI-friendly CLI that opens PRs with passing Jest unit tests.",
6
6
  "keywords": [