ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
129 lines • 5.61 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestGenerator = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const glob_1 = require("glob");
const llmService_1 = require("./llmService");
const fileUtils_1 = require("../utils/fileUtils");
const playwrightTemplate_1 = require("../templates/playwrightTemplate");
const seleniumTemplate_1 = require("../templates/seleniumTemplate");
class TestGenerator {
constructor(options) {
this.options = {
format: options.format || 'playwright',
timeout: options.timeout || 60
};
this.llmService = new llmService_1.LLMService();
}
/**
* Generates end-to-end tests from source code
* @param sourcePath Path to source file or directory
* @param outputDir Output directory for generated tests
* @returns Generation result containing test count and file paths
*/
async generateTests(sourcePath, outputDir) {
// Ensure output directory exists
await (0, fileUtils_1.ensureDirectoryExists)(outputDir);
// Get all source files if sourcePath is a directory
const sourceFiles = await this.getSourceFiles(sourcePath);
const result = {
testCount: 0,
files: []
};
// Generate tests for each source file
for (const sourceFile of sourceFiles) {
const sourceCode = await promises_1.default.readFile(sourceFile, 'utf8');
const relativeSourcePath = path_1.default.relative(process.cwd(), sourceFile);
// Skip files that don't need tests (like test files themselves)
if (this.shouldSkipFile(sourceFile)) {
continue;
}
// Analyze source code with LLM to extract test scenarios
const testScenarios = await this.llmService.extractTestScenarios(sourceCode, relativeSourcePath);
if (testScenarios.length === 0) {
continue;
}
// Generate test file path
const testFileName = await this.getTestFileName(sourceFile, outputDir);
// Generate test template based on format
const testTemplate = this.generateTestTemplate(testScenarios, relativeSourcePath);
// Write test file
await promises_1.default.writeFile(testFileName, testTemplate.content, 'utf8');
result.testCount += testScenarios.length;
result.files.push(path_1.default.relative(process.cwd(), testFileName));
}
return result;
}
/**
* Get all source files from a path (file or directory)
*/
async getSourceFiles(sourcePath) {
const stats = await promises_1.default.stat(sourcePath);
if (stats.isFile()) {
return [sourcePath];
}
if (stats.isDirectory()) {
// Find all JS/TS files but exclude test files and node_modules
return (0, glob_1.glob)(`${sourcePath}/**/*.{js,jsx,ts,tsx}`, {
ignore: [
'**/node_modules/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/*.spec.{js,jsx,ts,tsx}',
'**/test/**',
'**/tests/**',
'**/dist/**',
'**/build/**'
]
});
}
return [];
}
/**
* Determine if a file should be skipped for test generation
*/
shouldSkipFile(filePath) {
const filename = path_1.default.basename(filePath).toLowerCase();
return filename.includes('.test.') ||
filename.includes('.spec.') ||
filename.endsWith('.d.ts');
}
/**
* Generate test file name based on source file
*/
async getTestFileName(sourceFile, outputDir) {
// Check if outputDir is actually a file path rather than a directory
if (path_1.default.extname(outputDir) !== '') {
// If outputDir has a file extension, use it directly as the test file
await (0, fileUtils_1.ensureDirectoryExists)(path_1.default.dirname(outputDir));
return outputDir;
}
const sourceFileName = path_1.default.basename(sourceFile);
const sourceDir = path_1.default.dirname(sourceFile);
// Keep subdirectory structure relative to source root
const relativeDir = path_1.default.relative(process.cwd(), sourceDir);
const targetDir = path_1.default.join(outputDir, relativeDir);
// Replace extension with .test.ts or .test.js
let testFileName = sourceFileName.replace(/\.(js|jsx|ts|tsx)$/, '');
testFileName = `${testFileName}.test.${this.options.format === 'playwright' ? 'ts' : 'js'}`;
// Create subdirectory if needed
await (0, fileUtils_1.ensureDirectoryExists)(targetDir);
return path_1.default.join(targetDir, testFileName);
}
/**
* Generate test template based on format
*/
generateTestTemplate(testScenarios, sourcePath) {
if (this.options.format === 'playwright') {
return (0, playwrightTemplate_1.generatePlaywrightTest)(testScenarios, sourcePath);
}
else {
return (0, seleniumTemplate_1.generateSeleniumTest)(testScenarios, sourcePath);
}
}
}
exports.TestGenerator = TestGenerator;
//# sourceMappingURL=testGenerator.js.map