testgenius-ai
Version:
š TestGenius AI - The Ultimate E2E Testing Framework for Everyone | No Coding Required ⢠AI-Powered Automation ⢠Beautiful Reports ⢠Zero Complexity
626 lines (580 loc) ⢠19.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomToolsManager = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const ConfigManager_1 = require("./ConfigManager");
class CustomToolsManager {
constructor() {
this.configManager = new ConfigManager_1.ConfigManager();
this.toolsDir = path_1.default.join(process.cwd(), 'tools');
this.templatesDir = path_1.default.join(process.cwd(), 'tool-templates');
}
/**
* Initialize custom tools system
*/
async initialize() {
await fs_extra_1.default.ensureDir(this.toolsDir);
await fs_extra_1.default.ensureDir(this.templatesDir);
// Create default templates
await this.createDefaultTemplates();
}
/**
* Create a new custom tool
*/
async createTool(toolId, options = {}) {
const config = await this.configManager.loadConfig();
const tool = {
id: toolId,
name: options.name || toolId,
description: options.description || `Custom tool: ${toolId}`,
version: '1.0.0',
author: options.author || 'Unknown',
category: options.category || 'custom',
template: options.template || 'basic',
dependencies: [],
config: {},
enabled: true,
path: path_1.default.join(this.toolsDir, toolId)
};
// Create tool directory
await fs_extra_1.default.ensureDir(tool.path);
// Copy template files
await this.copyTemplateFiles(tool.template, tool.path);
// Create tool configuration
await this.createToolConfig(tool);
// Create tool implementation
await this.createToolImplementation(tool);
console.log(chalk_1.default.green(`ā
Custom tool "${toolId}" created successfully!`));
console.log(chalk_1.default.blue(`š Location: ${tool.path}`));
console.log(chalk_1.default.yellow(`š” Next steps:`));
console.log(chalk_1.default.yellow(` 1. Edit ${tool.path}/index.ts`));
console.log(chalk_1.default.yellow(` 2. Configure ${tool.path}/config.json`));
console.log(chalk_1.default.yellow(` 3. Run: testgenius validate tools`));
return tool;
}
/**
* List all custom tools
*/
async listTools(verbose = false) {
const tools = [];
if (!await fs_extra_1.default.pathExists(this.toolsDir)) {
return tools;
}
const toolDirs = await fs_extra_1.default.readdir(this.toolsDir);
for (const toolDir of toolDirs) {
const toolPath = path_1.default.join(this.toolsDir, toolDir);
const stats = await fs_extra_1.default.stat(toolPath);
if (stats.isDirectory()) {
try {
const configPath = path_1.default.join(toolPath, 'config.json');
const config = await fs_extra_1.default.readJson(configPath);
const tool = {
id: toolDir,
name: config.name || toolDir,
description: config.description || '',
version: config.version || '1.0.0',
author: config.author || 'Unknown',
category: config.category || 'custom',
template: config.template || 'basic',
dependencies: config.dependencies || [],
config: config.config || {},
enabled: config.enabled !== false,
path: toolPath
};
tools.push(tool);
if (verbose) {
console.log(chalk_1.default.blue(`\nš§ Tool: ${tool.name} (${tool.id})`));
console.log(chalk_1.default.gray(` Description: ${tool.description}`));
console.log(chalk_1.default.gray(` Category: ${tool.category}`));
console.log(chalk_1.default.gray(` Version: ${tool.version}`));
console.log(chalk_1.default.gray(` Author: ${tool.author}`));
console.log(chalk_1.default.gray(` Status: ${tool.enabled ? 'ā
Enabled' : 'ā Disabled'}`));
console.log(chalk_1.default.gray(` Path: ${tool.path}`));
}
}
catch (error) {
console.log(chalk_1.default.yellow(`ā ļø Invalid tool: ${toolDir}`));
}
}
}
return tools;
}
/**
* Validate all custom tools
*/
async validateTools() {
const tools = await this.listTools();
const valid = [];
const invalid = [];
for (const tool of tools) {
try {
await this.validateTool(tool);
valid.push(tool);
}
catch (error) {
invalid.push(`${tool.id}: ${error.message}`);
}
}
return { valid, invalid };
}
/**
* Validate a single tool
*/
async validateTool(tool) {
// Check required files
const requiredFiles = ['index.ts', 'config.json', 'README.md'];
for (const file of requiredFiles) {
const filePath = path_1.default.join(tool.path, file);
if (!await fs_extra_1.default.pathExists(filePath)) {
throw new Error(`Missing required file: ${file}`);
}
}
// Check TypeScript compilation
try {
// This would require TypeScript compilation check
// For now, just check if files exist
}
catch (error) {
throw new Error(`TypeScript compilation failed: ${error.message}`);
}
// Check configuration
if (!tool.name || !tool.description) {
throw new Error('Invalid configuration: name and description are required');
}
}
/**
* Enable/disable a tool
*/
async toggleTool(toolId, enabled) {
const configPath = path_1.default.join(this.toolsDir, toolId, 'config.json');
if (!await fs_extra_1.default.pathExists(configPath)) {
throw new Error(`Tool not found: ${toolId}`);
}
const config = await fs_extra_1.default.readJson(configPath);
config.enabled = enabled;
await fs_extra_1.default.writeJson(configPath, config, { spaces: 2 });
console.log(chalk_1.default.green(`ā
Tool "${toolId}" ${enabled ? 'enabled' : 'disabled'}`));
}
/**
* Delete a tool
*/
async deleteTool(toolId) {
const toolPath = path_1.default.join(this.toolsDir, toolId);
if (!await fs_extra_1.default.pathExists(toolPath)) {
throw new Error(`Tool not found: ${toolId}`);
}
await fs_extra_1.default.remove(toolPath);
console.log(chalk_1.default.green(`ā
Tool "${toolId}" deleted successfully`));
}
/**
* Get available templates
*/
async getTemplates() {
const templates = [
{
name: 'basic',
description: 'Basic tool template with minimal setup',
category: 'custom',
template: 'basic',
dependencies: [],
configSchema: {
name: { type: 'string', required: true },
description: { type: 'string', required: true }
}
},
{
name: 'api',
description: 'API testing tool template',
category: 'api',
template: 'api',
dependencies: ['axios'],
configSchema: {
baseUrl: { type: 'string', required: true },
headers: { type: 'object', required: false }
}
},
{
name: 'ui',
description: 'UI interaction tool template',
category: 'ui',
template: 'ui',
dependencies: ['webdriverio'],
configSchema: {
selectors: { type: 'object', required: true },
timeouts: { type: 'object', required: false }
}
},
{
name: 'database',
description: 'Database testing tool template',
category: 'database',
template: 'database',
dependencies: ['mysql2', 'pg'],
configSchema: {
connection: { type: 'object', required: true },
queries: { type: 'object', required: false }
}
}
];
return templates;
}
/**
* Create default templates
*/
async createDefaultTemplates() {
const templates = await this.getTemplates();
for (const template of templates) {
await this.createTemplateFiles(template);
}
}
/**
* Create template files
*/
async createTemplateFiles(template) {
const templateDir = path_1.default.join(this.templatesDir, template.name);
await fs_extra_1.default.ensureDir(templateDir);
// Create template files based on type
switch (template.template) {
case 'basic':
await this.createBasicTemplate(templateDir);
break;
case 'api':
await this.createApiTemplate(templateDir);
break;
case 'ui':
await this.createUiTemplate(templateDir);
break;
case 'database':
await this.createDatabaseTemplate(templateDir);
break;
}
}
/**
* Create basic template files
*/
async createBasicTemplate(templateDir) {
const files = {
'index.ts': `import { CustomTool } from '../../framework/core/CustomToolsManager';
export class BasicTool {
private config: any;
constructor(config: any) {
this.config = config;
}
async execute(params: any): Promise<any> {
// Your tool logic here
console.log('Basic tool executed with params:', params);
return { success: true, result: 'Basic tool completed' };
}
}`,
'config.json': `{
"name": "Basic Tool",
"description": "A basic custom tool",
"version": "1.0.0",
"author": "Your Name",
"category": "custom",
"template": "basic",
"dependencies": [],
"config": {},
"enabled": true
}`,
'README.md': `# Basic Tool
A basic custom tool template.
## Usage
\`\`\`typescript
const tool = new BasicTool(config);
const result = await tool.execute(params);
\`\`\`
## Configuration
Edit \`config.json\` to customize the tool behavior.
`
};
for (const [filename, content] of Object.entries(files)) {
await fs_extra_1.default.writeFile(path_1.default.join(templateDir, filename), content);
}
}
/**
* Create API template files
*/
async createApiTemplate(templateDir) {
const files = {
'index.ts': `import axios from 'axios';
import { CustomTool } from '../../framework/core/CustomToolsManager';
export class ApiTool {
private config: any;
private client: any;
constructor(config: any) {
this.config = config;
this.client = axios.create({
baseURL: config.baseUrl,
headers: config.headers || {}
});
}
async execute(params: any): Promise<any> {
const { method = 'GET', endpoint, data } = params;
try {
const response = await this.client.request({
method,
url: endpoint,
data
});
return {
success: true,
status: response.status,
data: response.data
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
}`,
'config.json': `{
"name": "API Tool",
"description": "API testing tool",
"version": "1.0.0",
"author": "Your Name",
"category": "api",
"template": "api",
"dependencies": ["axios"],
"config": {
"baseUrl": "https://api.example.com",
"headers": {
"Content-Type": "application/json"
}
},
"enabled": true
}`,
'README.md': `# API Tool
API testing tool for making HTTP requests.
## Usage
\`\`\`typescript
const tool = new ApiTool(config);
const result = await tool.execute({
method: 'POST',
endpoint: '/users',
data: { name: 'John' }
});
\`\`\`
## Configuration
- \`baseUrl\`: Base URL for API requests
- \`headers\`: Default headers to include in requests
`
};
for (const [filename, content] of Object.entries(files)) {
await fs_extra_1.default.writeFile(path_1.default.join(templateDir, filename), content);
}
}
/**
* Create UI template files
*/
async createUiTemplate(templateDir) {
const files = {
'index.ts': `import { Browser } from 'webdriverio';
import { CustomTool } from '../../framework/core/CustomToolsManager';
export class UiTool {
private config: any;
private browser: Browser | null = null;
constructor(config: any) {
this.config = config;
}
setBrowser(browser: Browser): void {
this.browser = browser;
}
async execute(params: any): Promise<any> {
if (!this.browser) {
throw new Error('Browser not initialized');
}
const { action, selector, value } = params;
try {
switch (action) {
case 'click':
await this.browser.$(selector).click();
break;
case 'type':
await this.browser.$(selector).setValue(value);
break;
case 'getText':
const text = await this.browser.$(selector).getText();
return { success: true, text };
default:
throw new Error(\`Unknown action: \${action}\`);
}
return { success: true };
} catch (error) {
return {
success: false,
error: error.message
};
}
}
}`,
'config.json': `{
"name": "UI Tool",
"description": "UI interaction tool",
"version": "1.0.0",
"author": "Your Name",
"category": "ui",
"template": "ui",
"dependencies": ["webdriverio"],
"config": {
"selectors": {
"loginButton": "#login-btn",
"usernameField": "#username",
"passwordField": "#password"
},
"timeouts": {
"implicit": 10000,
"explicit": 5000
}
},
"enabled": true
}`,
'README.md': `# UI Tool
UI interaction tool for WebDriverIO automation.
## Usage
\`\`\`typescript
const tool = new UiTool(config);
tool.setBrowser(browser);
const result = await tool.execute({
action: 'click',
selector: '#login-btn'
});
\`\`\`
## Actions
- \`click\`: Click an element
- \`type\`: Type text into an element
- \`getText\`: Get text from an element
## Configuration
- \`selectors\`: Common selectors used by the tool
- \`timeouts\`: Timeout configurations
`
};
for (const [filename, content] of Object.entries(files)) {
await fs_extra_1.default.writeFile(path_1.default.join(templateDir, filename), content);
}
}
/**
* Create database template files
*/
async createDatabaseTemplate(templateDir) {
const files = {
'index.ts': `import mysql from 'mysql2/promise';
import { CustomTool } from '../../framework/core/CustomToolsManager';
export class DatabaseTool {
private config: any;
private connection: any;
constructor(config: any) {
this.config = config;
}
async connect(): Promise<void> {
this.connection = await mysql.createConnection(this.config.connection);
}
async execute(params: any): Promise<any> {
if (!this.connection) {
await this.connect();
}
const { query, values } = params;
try {
const [rows] = await this.connection.execute(query, values);
return {
success: true,
rows
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
async disconnect(): Promise<void> {
if (this.connection) {
await this.connection.end();
}
}
}`,
'config.json': `{
"name": "Database Tool",
"description": "Database testing tool",
"version": "1.0.0",
"author": "Your Name",
"category": "database",
"template": "database",
"dependencies": ["mysql2"],
"config": {
"connection": {
"host": "localhost",
"user": "root",
"password": "password",
"database": "testdb"
},
"queries": {
"getUser": "SELECT * FROM users WHERE id = ?",
"createUser": "INSERT INTO users (name, email) VALUES (?, ?)"
}
},
"enabled": true
}`,
'README.md': `# Database Tool
Database testing tool for MySQL operations.
## Usage
\`\`\`typescript
const tool = new DatabaseTool(config);
const result = await tool.execute({
query: "SELECT * FROM users WHERE id = ?",
values: [1]
});
\`\`\`
## Configuration
- \`connection\`: Database connection parameters
- \`queries\`: Common queries used by the tool
`
};
for (const [filename, content] of Object.entries(files)) {
await fs_extra_1.default.writeFile(path_1.default.join(templateDir, filename), content);
}
}
/**
* Copy template files to tool directory
*/
async copyTemplateFiles(templateName, toolPath) {
const templatePath = path_1.default.join(this.templatesDir, templateName);
if (!await fs_extra_1.default.pathExists(templatePath)) {
throw new Error(`Template not found: ${templateName}`);
}
await fs_extra_1.default.copy(templatePath, toolPath);
}
/**
* Create tool configuration
*/
async createToolConfig(tool) {
const configPath = path_1.default.join(tool.path, 'config.json');
await fs_extra_1.default.writeJson(configPath, {
name: tool.name,
description: tool.description,
version: tool.version,
author: tool.author,
category: tool.category,
template: tool.template,
dependencies: tool.dependencies,
config: tool.config,
enabled: tool.enabled
}, { spaces: 2 });
}
/**
* Create tool implementation
*/
async createToolImplementation(tool) {
// The implementation file is already copied from template
// This method can be used for additional customization if needed
}
}
exports.CustomToolsManager = CustomToolsManager;
//# sourceMappingURL=CustomToolsManager.js.map