@theihtisham/ai-testgen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +383 -0
  3. package/dist/analyzers/analyzer.d.ts +10 -0
  4. package/dist/analyzers/analyzer.d.ts.map +1 -0
  5. package/dist/analyzers/analyzer.js +131 -0
  6. package/dist/analyzers/analyzer.js.map +1 -0
  7. package/dist/analyzers/go-analyzer.d.ts +3 -0
  8. package/dist/analyzers/go-analyzer.d.ts.map +1 -0
  9. package/dist/analyzers/go-analyzer.js +244 -0
  10. package/dist/analyzers/go-analyzer.js.map +1 -0
  11. package/dist/analyzers/index.d.ts +5 -0
  12. package/dist/analyzers/index.d.ts.map +1 -0
  13. package/dist/analyzers/index.js +15 -0
  14. package/dist/analyzers/index.js.map +1 -0
  15. package/dist/analyzers/js-ts-analyzer.d.ts +3 -0
  16. package/dist/analyzers/js-ts-analyzer.d.ts.map +1 -0
  17. package/dist/analyzers/js-ts-analyzer.js +299 -0
  18. package/dist/analyzers/js-ts-analyzer.js.map +1 -0
  19. package/dist/analyzers/python-analyzer.d.ts +3 -0
  20. package/dist/analyzers/python-analyzer.d.ts.map +1 -0
  21. package/dist/analyzers/python-analyzer.js +306 -0
  22. package/dist/analyzers/python-analyzer.js.map +1 -0
  23. package/dist/cli.d.ts +3 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +381 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/config/defaults.d.ts +6 -0
  28. package/dist/config/defaults.d.ts.map +1 -0
  29. package/dist/config/defaults.js +80 -0
  30. package/dist/config/defaults.js.map +1 -0
  31. package/dist/config/index.d.ts +3 -0
  32. package/dist/config/index.d.ts.map +1 -0
  33. package/dist/config/index.js +14 -0
  34. package/dist/config/index.js.map +1 -0
  35. package/dist/config/loader.d.ts +6 -0
  36. package/dist/config/loader.d.ts.map +1 -0
  37. package/dist/config/loader.js +126 -0
  38. package/dist/config/loader.js.map +1 -0
  39. package/dist/coverage.d.ts +4 -0
  40. package/dist/coverage.d.ts.map +1 -0
  41. package/dist/coverage.js +108 -0
  42. package/dist/coverage.js.map +1 -0
  43. package/dist/generators/ai-generator.d.ts +4 -0
  44. package/dist/generators/ai-generator.d.ts.map +1 -0
  45. package/dist/generators/ai-generator.js +175 -0
  46. package/dist/generators/ai-generator.js.map +1 -0
  47. package/dist/generators/generator.d.ts +4 -0
  48. package/dist/generators/generator.d.ts.map +1 -0
  49. package/dist/generators/generator.js +121 -0
  50. package/dist/generators/generator.js.map +1 -0
  51. package/dist/generators/go-generator.d.ts +3 -0
  52. package/dist/generators/go-generator.d.ts.map +1 -0
  53. package/dist/generators/go-generator.js +175 -0
  54. package/dist/generators/go-generator.js.map +1 -0
  55. package/dist/generators/index.d.ts +6 -0
  56. package/dist/generators/index.d.ts.map +1 -0
  57. package/dist/generators/index.js +16 -0
  58. package/dist/generators/index.js.map +1 -0
  59. package/dist/generators/js-ts-generator.d.ts +3 -0
  60. package/dist/generators/js-ts-generator.d.ts.map +1 -0
  61. package/dist/generators/js-ts-generator.js +331 -0
  62. package/dist/generators/js-ts-generator.js.map +1 -0
  63. package/dist/generators/python-generator.d.ts +3 -0
  64. package/dist/generators/python-generator.d.ts.map +1 -0
  65. package/dist/generators/python-generator.js +180 -0
  66. package/dist/generators/python-generator.js.map +1 -0
  67. package/dist/incremental.d.ts +16 -0
  68. package/dist/incremental.d.ts.map +1 -0
  69. package/dist/incremental.js +146 -0
  70. package/dist/incremental.js.map +1 -0
  71. package/dist/index.d.ts +9 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +44 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/mutation/index.d.ts +2 -0
  76. package/dist/mutation/index.d.ts.map +1 -0
  77. package/dist/mutation/index.js +9 -0
  78. package/dist/mutation/index.js.map +1 -0
  79. package/dist/mutation/mutator.d.ts +6 -0
  80. package/dist/mutation/mutator.d.ts.map +1 -0
  81. package/dist/mutation/mutator.js +237 -0
  82. package/dist/mutation/mutator.js.map +1 -0
  83. package/dist/types.d.ts +199 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +4 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/utils/file.d.ts +10 -0
  88. package/dist/utils/file.d.ts.map +1 -0
  89. package/dist/utils/file.js +108 -0
  90. package/dist/utils/file.js.map +1 -0
  91. package/dist/utils/index.d.ts +4 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +24 -0
  94. package/dist/utils/index.js.map +1 -0
  95. package/dist/utils/language.d.ts +8 -0
  96. package/dist/utils/language.d.ts.map +1 -0
  97. package/dist/utils/language.js +137 -0
  98. package/dist/utils/language.js.map +1 -0
  99. package/dist/utils/logger.d.ts +13 -0
  100. package/dist/utils/logger.d.ts.map +1 -0
  101. package/dist/utils/logger.js +57 -0
  102. package/dist/utils/logger.js.map +1 -0
  103. package/dist/watcher/index.d.ts +2 -0
  104. package/dist/watcher/index.d.ts.map +1 -0
  105. package/dist/watcher/index.js +6 -0
  106. package/dist/watcher/index.js.map +1 -0
  107. package/dist/watcher/watcher.d.ts +19 -0
  108. package/dist/watcher/watcher.d.ts.map +1 -0
  109. package/dist/watcher/watcher.js +122 -0
  110. package/dist/watcher/watcher.js.map +1 -0
  111. package/package.json +63 -0
  112. package/src/analyzers/analyzer.ts +180 -0
  113. package/src/analyzers/go-analyzer.ts +235 -0
  114. package/src/analyzers/index.ts +4 -0
  115. package/src/analyzers/js-ts-analyzer.ts +324 -0
  116. package/src/analyzers/python-analyzer.ts +306 -0
  117. package/src/cli.ts +416 -0
  118. package/src/config/defaults.ts +81 -0
  119. package/src/config/index.ts +2 -0
  120. package/src/config/loader.ts +114 -0
  121. package/src/coverage.ts +128 -0
  122. package/src/generators/ai-generator.ts +170 -0
  123. package/src/generators/generator.ts +117 -0
  124. package/src/generators/go-generator.ts +183 -0
  125. package/src/generators/index.ts +5 -0
  126. package/src/generators/js-ts-generator.ts +379 -0
  127. package/src/generators/python-generator.ts +201 -0
  128. package/src/incremental.ts +131 -0
  129. package/src/index.ts +8 -0
  130. package/src/mutation/index.ts +1 -0
  131. package/src/mutation/mutator.ts +314 -0
  132. package/src/types.ts +240 -0
  133. package/src/utils/file.ts +73 -0
  134. package/src/utils/index.ts +3 -0
  135. package/src/utils/language.ts +114 -0
  136. package/src/utils/logger.ts +61 -0
  137. package/src/watcher/index.ts +1 -0
  138. package/src/watcher/watcher.ts +103 -0
  139. package/tests/analyzer.test.ts +429 -0
  140. package/tests/config.test.ts +171 -0
  141. package/tests/coverage.test.ts +197 -0
  142. package/tests/file-utils.test.ts +121 -0
  143. package/tests/generators.test.ts +383 -0
  144. package/tests/incremental.test.ts +108 -0
  145. package/tests/language.test.ts +90 -0
  146. package/tests/mutation.test.ts +286 -0
  147. package/tests/watcher.test.ts +35 -0
  148. package/tsconfig.json +26 -0
  149. package/vitest.config.ts +25 -0
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectLanguage = detectLanguage;
37
+ exports.detectFramework = detectFramework;
38
+ exports.getTestFileExtension = getTestFileExtension;
39
+ exports.buildTestFilePath = buildTestFilePath;
40
+ exports.isTestFile = isTestFile;
41
+ exports.shouldAnalyze = shouldAnalyze;
42
+ const path = __importStar(require("path"));
43
+ const fs = __importStar(require("fs"));
44
+ const defaults_js_1 = require("../config/defaults.js");
45
+ function detectLanguage(filePath) {
46
+ const ext = path.extname(filePath).toLowerCase();
47
+ const language = defaults_js_1.LANGUAGE_EXTENSIONS[ext];
48
+ if (!language) {
49
+ throw new Error(`Unsupported file extension: ${ext}. Supported: ${Object.keys(defaults_js_1.LANGUAGE_EXTENSIONS).join(', ')}`);
50
+ }
51
+ return language;
52
+ }
53
+ function detectFramework(language, projectDir) {
54
+ const frameworks = defaults_js_1.LANGUAGE_FRAMEWORKS[language];
55
+ if (frameworks.length === 0) {
56
+ throw new Error(`No test framework support for language: ${language}`);
57
+ }
58
+ // Check for framework-specific config files
59
+ if (language === 'typescript' || language === 'javascript') {
60
+ const vitestConfig = ['vitest.config.ts', 'vitest.config.js', 'vite.config.ts'].find((f) => fs.existsSync(path.join(projectDir, f)));
61
+ if (vitestConfig)
62
+ return 'vitest';
63
+ const jestConfig = ['jest.config.ts', 'jest.config.js', 'jest.config.mjs'].find((f) => fs.existsSync(path.join(projectDir, f)));
64
+ if (jestConfig)
65
+ return 'jest';
66
+ // Check package.json for dependencies
67
+ const pkgPath = path.join(projectDir, 'package.json');
68
+ if (fs.existsSync(pkgPath)) {
69
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
70
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
71
+ if (allDeps['vitest'])
72
+ return 'vitest';
73
+ if (allDeps['jest'])
74
+ return 'jest';
75
+ }
76
+ return 'vitest'; // default for JS/TS
77
+ }
78
+ if (language === 'python')
79
+ return 'pytest';
80
+ if (language === 'go')
81
+ return 'go-test';
82
+ return frameworks[0];
83
+ }
84
+ function getTestFileExtension(language) {
85
+ const map = {
86
+ typescript: 'ts',
87
+ javascript: 'js',
88
+ python: 'py',
89
+ go: 'go',
90
+ rust: 'rs',
91
+ };
92
+ return map[language];
93
+ }
94
+ function buildTestFilePath(sourceFilePath, outputDir, language, framework) {
95
+ const dir = path.dirname(sourceFilePath);
96
+ const ext = path.extname(sourceFilePath);
97
+ const baseName = path.basename(sourceFilePath, ext);
98
+ const testExt = getTestFileExtension(language);
99
+ let testFileName;
100
+ if (framework === 'pytest') {
101
+ testFileName = `test_${baseName}.py`;
102
+ }
103
+ else if (framework === 'go-test') {
104
+ testFileName = `${baseName}_test.go`;
105
+ }
106
+ else {
107
+ testFileName = `${baseName}.test.${testExt}`;
108
+ }
109
+ if (outputDir === '__tests__' || outputDir.startsWith('./') || outputDir.startsWith('../')) {
110
+ const testDir = path.resolve(path.dirname(sourceFilePath), outputDir);
111
+ return path.join(testDir, testFileName);
112
+ }
113
+ return path.join(dir, testFileName);
114
+ }
115
+ function isTestFile(filePath) {
116
+ const base = path.basename(filePath);
117
+ return (base.includes('.test.') ||
118
+ base.includes('.spec.') ||
119
+ base.startsWith('test_') ||
120
+ base.endsWith('_test.go'));
121
+ }
122
+ function shouldAnalyze(filePath, excludePatterns) {
123
+ if (isTestFile(filePath))
124
+ return false;
125
+ const normalizedPath = filePath.replace(/\\/g, '/');
126
+ for (const pattern of excludePatterns) {
127
+ const regex = pattern
128
+ .replace(/\*\*/g, '.*')
129
+ .replace(/\*/g, '[^/]*')
130
+ .replace(/\?/g, '[^/]');
131
+ if (new RegExp(regex).test(normalizedPath)) {
132
+ return false;
133
+ }
134
+ }
135
+ return true;
136
+ }
137
+ //# sourceMappingURL=language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/utils/language.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success';
2
+ export declare function setLogLevel(level: LogLevel): void;
3
+ export declare const logger: {
4
+ debug(message: string, ...args: unknown[]): void;
5
+ info(message: string, ...args: unknown[]): void;
6
+ warn(message: string, ...args: unknown[]): void;
7
+ error(message: string, ...args: unknown[]): void;
8
+ success(message: string, ...args: unknown[]): void;
9
+ plain(message: string, ...args: unknown[]): void;
10
+ heading(message: string): void;
11
+ subheading(message: string): void;
12
+ };
13
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAMvE,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AAQD,eAAO,MAAM,MAAM;mBACF,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;kBAMlC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;kBAMjC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;mBAMhC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;qBAM/B,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;mBAMnC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;qBAI/B,MAAM,GAAG,IAAI;wBAIV,MAAM,GAAG,IAAI;CAGlC,CAAC"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ exports.setLogLevel = setLogLevel;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ let currentLogLevel = 'info';
10
+ const LOG_ORDER = ['debug', 'info', 'warn', 'error', 'success'];
11
+ function setLogLevel(level) {
12
+ currentLogLevel = level;
13
+ }
14
+ function shouldLog(level) {
15
+ if (level === 'success')
16
+ return true;
17
+ if (level === 'debug' && currentLogLevel !== 'debug')
18
+ return false;
19
+ return LOG_ORDER.indexOf(level) >= LOG_ORDER.indexOf(currentLogLevel);
20
+ }
21
+ exports.logger = {
22
+ debug(message, ...args) {
23
+ if (shouldLog('debug')) {
24
+ console.log(chalk_1.default.gray(`[debug] ${message}`), ...args);
25
+ }
26
+ },
27
+ info(message, ...args) {
28
+ if (shouldLog('info')) {
29
+ console.log(chalk_1.default.cyan(`[info] ${message}`), ...args);
30
+ }
31
+ },
32
+ warn(message, ...args) {
33
+ if (shouldLog('warn')) {
34
+ console.warn(chalk_1.default.yellow(`[warn] ${message}`), ...args);
35
+ }
36
+ },
37
+ error(message, ...args) {
38
+ if (shouldLog('error')) {
39
+ console.error(chalk_1.default.red(`[error] ${message}`), ...args);
40
+ }
41
+ },
42
+ success(message, ...args) {
43
+ if (shouldLog('success')) {
44
+ console.log(chalk_1.default.green(` ${message}`), ...args);
45
+ }
46
+ },
47
+ plain(message, ...args) {
48
+ console.log(message, ...args);
49
+ },
50
+ heading(message) {
51
+ console.log('\n' + chalk_1.default.bold.blue(` ${message}`));
52
+ },
53
+ subheading(message) {
54
+ console.log(chalk_1.default.bold.white(` ${message}`));
55
+ },
56
+ };
57
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";;;;;;AAQA,kCAEC;AAVD,kDAA0B;AAI1B,IAAI,eAAe,GAAa,MAAM,CAAC;AAEvC,MAAM,SAAS,GAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAE5E,SAAgB,WAAW,CAAC,KAAe;IACzC,eAAe,GAAG,KAAK,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,KAAK,KAAK,OAAO,IAAI,eAAe,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACnE,OAAO,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC;AAEY,QAAA,MAAM,GAAG;IACpB,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,MAAM,CAAC,UAAU,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,WAAW,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAe,EAAE,GAAG,IAAe;QACzC,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,UAAU,CAAC,OAAe;QACxB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { FileWatcher } from './watcher.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileWatcher = void 0;
4
+ var watcher_js_1 = require("./watcher.js");
5
+ Object.defineProperty(exports, "FileWatcher", { enumerable: true, get: function () { return watcher_js_1.FileWatcher; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":";;;AAAA,2CAA2C;AAAlC,yGAAA,WAAW,OAAA"}
@@ -0,0 +1,19 @@
1
+ import { FileChangeEvent, TestGenConfig } from '../types.js';
2
+ type FileWatcherCallback = (events: FileChangeEvent[]) => Promise<void>;
3
+ export declare class FileWatcher {
4
+ private watchers;
5
+ private pendingEvents;
6
+ private debounceTimer;
7
+ private debounceMs;
8
+ private ignorePatterns;
9
+ private callback;
10
+ private running;
11
+ constructor(config: TestGenConfig, callback: FileWatcherCallback);
12
+ start(directories: string[]): void;
13
+ stop(): void;
14
+ isRunning(): boolean;
15
+ private shouldIgnore;
16
+ private debouncedNotify;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/watcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7D,KAAK,mBAAmB,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAExE,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,mBAAmB;IAMhE,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI;IAuClC,IAAI,IAAI,IAAI;IAaZ,SAAS,IAAI,OAAO;IAIpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,eAAe;CAexB"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileWatcher = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const logger_js_1 = require("../utils/logger.js");
40
+ class FileWatcher {
41
+ watchers = new Map();
42
+ pendingEvents = [];
43
+ debounceTimer = null;
44
+ debounceMs;
45
+ ignorePatterns;
46
+ callback;
47
+ running = false;
48
+ constructor(config, callback) {
49
+ this.debounceMs = config.watch.debounceMs;
50
+ this.ignorePatterns = config.watch.ignorePatterns;
51
+ this.callback = callback;
52
+ }
53
+ start(directories) {
54
+ if (this.running)
55
+ return;
56
+ this.running = true;
57
+ for (const dir of directories) {
58
+ const absoluteDir = path.resolve(dir);
59
+ if (!fs.existsSync(absoluteDir)) {
60
+ logger_js_1.logger.warn(`Watch directory does not exist: ${absoluteDir}`);
61
+ continue;
62
+ }
63
+ try {
64
+ const watcher = fs.watch(absoluteDir, { recursive: true }, (eventType, filename) => {
65
+ if (!filename)
66
+ return;
67
+ if (this.shouldIgnore(filename))
68
+ return;
69
+ const filePath = path.join(absoluteDir, filename);
70
+ this.pendingEvents.push({
71
+ filePath,
72
+ eventType: eventType,
73
+ timestamp: Date.now(),
74
+ });
75
+ this.debouncedNotify();
76
+ });
77
+ this.watchers.set(absoluteDir, watcher);
78
+ logger_js_1.logger.info(`Watching: ${absoluteDir}`);
79
+ }
80
+ catch (err) {
81
+ logger_js_1.logger.error(`Failed to watch ${absoluteDir}: ${err instanceof Error ? err.message : String(err)}`);
82
+ }
83
+ }
84
+ }
85
+ stop() {
86
+ this.running = false;
87
+ for (const [dir, watcher] of this.watchers) {
88
+ watcher.close();
89
+ logger_js_1.logger.debug(`Stopped watching: ${dir}`);
90
+ }
91
+ this.watchers.clear();
92
+ if (this.debounceTimer) {
93
+ clearTimeout(this.debounceTimer);
94
+ this.debounceTimer = null;
95
+ }
96
+ }
97
+ isRunning() {
98
+ return this.running;
99
+ }
100
+ shouldIgnore(filename) {
101
+ const normalized = filename.replace(/\\/g, '/');
102
+ return this.ignorePatterns.some((pattern) => {
103
+ return normalized.includes(pattern) || normalized.match(new RegExp(pattern.replace(/\*/g, '.*'))) !== null;
104
+ });
105
+ }
106
+ debouncedNotify() {
107
+ if (this.debounceTimer) {
108
+ clearTimeout(this.debounceTimer);
109
+ }
110
+ this.debounceTimer = setTimeout(() => {
111
+ const events = [...this.pendingEvents];
112
+ this.pendingEvents = [];
113
+ if (events.length > 0) {
114
+ this.callback(events).catch((err) => {
115
+ logger_js_1.logger.error(`Watch callback error: ${err instanceof Error ? err.message : String(err)}`);
116
+ });
117
+ }
118
+ }, this.debounceMs);
119
+ }
120
+ }
121
+ exports.FileWatcher = FileWatcher;
122
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/watcher/watcher.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@theihtisham/ai-testgen",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered test generator that creates comprehensive test suites from source code",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "ai-testgen": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/cli.js",
12
+ "dev": "ts-node src/cli.ts",
13
+ "test": "vitest run tests/config.test.ts tests/language.test.ts tests/file-utils.test.ts tests/generators.test.ts tests/coverage.test.ts tests/watcher.test.ts tests/incremental.test.ts tests/mutation.test.ts && vitest run tests/analyzer.test.ts",
14
+ "test:watch": "vitest",
15
+ "test:coverage": "vitest run --coverage",
16
+ "lint": "tsc --noEmit",
17
+ "clean": "rimraf dist",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": [
21
+ "testing",
22
+ "ai",
23
+ "test-generator",
24
+ "unit-tests",
25
+ "integration-tests",
26
+ "mutation-testing",
27
+ "code-coverage",
28
+ "jest",
29
+ "vitest",
30
+ "pytest",
31
+ "go-test"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "chalk": "^4.1.2",
37
+ "commander": "^12.1.0",
38
+ "fast-glob": "^3.3.2",
39
+ "js-yaml": "^4.1.0",
40
+ "openai": "^4.67.3",
41
+ "ora": "^5.4.1",
42
+ "ts-morph": "^21.0.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/js-yaml": "^4.0.9",
46
+ "@types/node": "^22.10.0",
47
+ "cross-env": "^10.1.0",
48
+ "rimraf": "^6.0.1",
49
+ "ts-node": "^10.9.2",
50
+ "typescript": "^5.7.2",
51
+ "vitest": "^2.1.8"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "https://github.com/theihtisham/ai-testgen"
62
+ }
63
+ }
@@ -0,0 +1,180 @@
1
+ import {
2
+ SourceAnalysis,
3
+ SupportedLanguage,
4
+ AnalyzedFunction,
5
+ TestCase,
6
+ MockDefinition,
7
+ } from '../types.js';
8
+ import { analyzeTsSource } from './js-ts-analyzer.js';
9
+ import { analyzePythonSource } from './python-analyzer.js';
10
+ import { analyzeGoSource } from './go-analyzer.js';
11
+
12
+ export function analyzeSource(filePath: string, language: SupportedLanguage): SourceAnalysis {
13
+ switch (language) {
14
+ case 'typescript':
15
+ case 'javascript':
16
+ return analyzeTsSource(filePath, language);
17
+ case 'python':
18
+ return analyzePythonSource(filePath);
19
+ case 'go':
20
+ return analyzeGoSource(filePath);
21
+ case 'rust':
22
+ throw new Error('Rust analysis is not yet supported. Use AI mode for Rust files.');
23
+ default:
24
+ throw new Error(`Unsupported language: ${language}`);
25
+ }
26
+ }
27
+
28
+ export function detectEdgeCases(fn: AnalyzedFunction): TestCase[] {
29
+ const cases: TestCase[] = [];
30
+
31
+ for (const param of fn.params) {
32
+ if (param.optional) {
33
+ cases.push({
34
+ name: `${fn.name} handles undefined ${param.name}`,
35
+ type: 'edge-case',
36
+ description: `Test that ${fn.name} gracefully handles undefined ${param.name}`,
37
+ code: '',
38
+ expectedBehavior: 'Should handle undefined parameter without throwing',
39
+ inputDescription: `${param.name} is undefined`,
40
+ tags: ['edge-case', 'undefined', param.name],
41
+ });
42
+ }
43
+
44
+ if (param.type && (param.type.includes('string') || param.type.includes('String'))) {
45
+ cases.push(
46
+ createEdgeCase(fn.name, param.name, 'empty string', '""', 'Should handle empty string input'),
47
+ createEdgeCase(fn.name, param.name, 'very long string', '"a".repeat(10000)', 'Should handle very long string'),
48
+ createEdgeCase(fn.name, param.name, 'string with special characters', '"\\n\\t\\0\\r"', 'Should handle special characters'),
49
+ );
50
+ }
51
+
52
+ if (param.type && (param.type.includes('number') || param.type.includes('Number'))) {
53
+ cases.push(
54
+ createEdgeCase(fn.name, param.name, 'zero', '0', 'Should handle zero value'),
55
+ createEdgeCase(fn.name, param.name, 'negative number', '-1', 'Should handle negative numbers'),
56
+ createEdgeCase(fn.name, param.name, 'very large number', 'Number.MAX_SAFE_INTEGER', 'Should handle large numbers'),
57
+ createEdgeCase(fn.name, param.name, 'NaN', 'NaN', 'Should handle NaN'),
58
+ createEdgeCase(fn.name, param.name, 'Infinity', 'Infinity', 'Should handle Infinity'),
59
+ );
60
+ }
61
+
62
+ if (param.type && (param.type.includes('Array') || param.type.includes('[]'))) {
63
+ cases.push(
64
+ createEdgeCase(fn.name, param.name, 'empty array', '[]', 'Should handle empty array'),
65
+ createEdgeCase(fn.name, param.name, 'array with single element', '[item]', 'Should handle single-element array'),
66
+ createEdgeCase(fn.name, param.name, 'very large array', 'Array(10000).fill(item)', 'Should handle large arrays'),
67
+ );
68
+ }
69
+
70
+ if (param.type && (param.type.includes('object') || param.type.includes('Object') || param.type.includes('{'))) {
71
+ cases.push(
72
+ createEdgeCase(fn.name, param.name, 'empty object', '{}', 'Should handle empty object'),
73
+ createEdgeCase(fn.name, param.name, 'null', 'null', 'Should handle null'),
74
+ );
75
+ }
76
+ }
77
+
78
+ if (fn.throws.length > 0) {
79
+ for (const throws of fn.throws) {
80
+ cases.push({
81
+ name: `${fn.name} throws for invalid input`,
82
+ type: 'edge-case',
83
+ description: `Test that ${fn.name} throws when given invalid input`,
84
+ code: '',
85
+ expectedBehavior: `Should throw ${throws}`,
86
+ inputDescription: 'Invalid input that triggers error',
87
+ tags: ['edge-case', 'error-path'],
88
+ });
89
+ }
90
+ }
91
+
92
+ if (fn.isAsync) {
93
+ cases.push({
94
+ name: `${fn.name} handles rejection`,
95
+ type: 'edge-case',
96
+ description: `Test that ${fn.name} properly handles promise rejection`,
97
+ code: '',
98
+ expectedBehavior: 'Should handle promise rejection gracefully',
99
+ inputDescription: 'Input that causes promise rejection',
100
+ tags: ['edge-case', 'async'],
101
+ });
102
+ }
103
+
104
+ return cases;
105
+ }
106
+
107
+ function createEdgeCase(
108
+ fnName: string,
109
+ paramName: string,
110
+ caseName: string,
111
+ value: string,
112
+ expected: string,
113
+ ): TestCase {
114
+ return {
115
+ name: `${fnName} handles ${caseName} for ${paramName}`,
116
+ type: 'edge-case',
117
+ description: `Test ${fnName} with ${caseName} for parameter ${paramName}`,
118
+ code: '',
119
+ expectedBehavior: expected,
120
+ inputDescription: `${paramName} = ${value}`,
121
+ tags: ['edge-case', caseName.replace(/\s+/g, '-')],
122
+ };
123
+ }
124
+
125
+ export function detectMocks(analysis: SourceAnalysis): MockDefinition[] {
126
+ const mocks: MockDefinition[] = [];
127
+
128
+ for (const imp of analysis.imports) {
129
+ if (imp.isTypeOnly) continue;
130
+
131
+ if (!imp.modulePath.startsWith('.') && imp.namedImports.length > 0) {
132
+ for (const named of imp.namedImports) {
133
+ const isLikelyDependency =
134
+ !imp.modulePath.startsWith('node:') &&
135
+ !['path', 'fs', 'os', 'util', 'events', 'stream', 'http', 'https', 'crypto'].includes(
136
+ imp.modulePath,
137
+ );
138
+
139
+ if (isLikelyDependency) {
140
+ mocks.push({
141
+ moduleName: imp.modulePath,
142
+ mockName: `mock${named.charAt(0).toUpperCase() + named.slice(1)}`,
143
+ setup: `jest.mock('${imp.modulePath}');`,
144
+ teardown: null,
145
+ implementations: {
146
+ [named]: `jest.fn()`,
147
+ },
148
+ });
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ return mocks;
155
+ }
156
+
157
+ export interface DependencyGraph {
158
+ files: Map<string, string[]>;
159
+ reverse: Map<string, string[]>;
160
+ }
161
+
162
+ export function buildDependencyGraph(analyses: SourceAnalysis[]): DependencyGraph {
163
+ const files = new Map<string, string[]>();
164
+ const reverse = new Map<string, string[]>();
165
+
166
+ for (const analysis of analyses) {
167
+ const deps: string[] = [];
168
+ for (const dep of analysis.dependencies) {
169
+ if (dep.startsWith('.')) {
170
+ deps.push(dep);
171
+ const rev = reverse.get(dep) ?? [];
172
+ rev.push(analysis.filePath);
173
+ reverse.set(dep, rev);
174
+ }
175
+ }
176
+ files.set(analysis.filePath, deps);
177
+ }
178
+
179
+ return { files, reverse };
180
+ }