UNPKG

@dkoul/auto-testid-cli

Version:

Command-line interface for React and Vue.js custom attribute generation

238 lines • 10.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateCommand = generateCommand; const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const auto_testid_core_1 = require("@dkoul/auto-testid-core"); const path = __importStar(require("path")); const fs = __importStar(require("fs/promises")); async function generateCommand(targetPath, options) { const spinner = (0, ora_1.default)('Initializing Auto-TestID...').start(); try { // Parse options const config = { frameworks: options.framework ? [options.framework] : ['react'], namingStrategy: { type: options.naming || 'kebab-case' }, prefix: options.prefix || 'test', attributeName: options.attributeName || 'data-testid', maxIdLength: parseInt(options.maxLength || '50'), exclude: options.exclude || [], preserveExisting: true, }; // Load custom config if specified if (options.config) { try { const configContent = await fs.readFile(options.config, 'utf-8'); const customConfig = JSON.parse(configContent); Object.assign(config, customConfig); spinner.succeed(`Configuration loaded from ${options.config}`); } catch (error) { spinner.warn(`Could not load config from ${options.config}, using defaults`); } } else { spinner.succeed('Using default configuration'); } // Initialize the core API const autoTestID = (0, auto_testid_core_1.createAutoTestID)(config); // Resolve target path const resolvedPath = path.resolve(targetPath); // Check if path exists try { await fs.access(resolvedPath); } catch { spinner.fail(`Path does not exist: ${resolvedPath}`); process.exit(1); } // Determine if processing file or directory const stats = await fs.stat(resolvedPath); let filePaths = []; if (stats.isFile()) { filePaths = [resolvedPath]; spinner.succeed(`Processing single file: ${path.basename(resolvedPath)}`); } else if (stats.isDirectory()) { spinner.text = 'Scanning directory for files...'; const scanOptions = { include: options.include || ['**/*.{js,jsx,ts,tsx,vue,html}'], exclude: options.exclude || [ '**/node_modules/**', '**/dist/**', '**/build/**', '**/*.test.*', '**/*.spec.*' ], frameworks: config.frameworks || ['react'], recursive: true, }; filePaths = await autoTestID.scanDirectory(resolvedPath, scanOptions); if (filePaths.length === 0) { spinner.warn('No supported files found in directory'); process.exit(0); } spinner.succeed(`Found ${filePaths.length} files to process`); } // Progress tracking let processedFiles = 0; let totalElements = 0; let totalTransformed = 0; let totalErrors = 0; const progressSpinner = (0, ora_1.default)('Processing files...').start(); const onProgress = (progress) => { const percentage = Math.round((progress.completed / progress.total) * 100); progressSpinner.text = `Processing ${progress.stage}: ${path.basename(progress.current)} (${percentage}%)`; }; // Process files const results = await autoTestID.processFiles(filePaths, { dryRun: options.dryRun, backup: options.backup, config, onProgress, }); progressSpinner.stop(); // Analyze results let hasErrors = false; const successfulResults = []; const failedResults = []; results.forEach(result => { processedFiles++; totalElements += result.metrics.elementsFound; totalTransformed += result.metrics.elementsTransformed; totalErrors += result.errors.length; if (result.success) { successfulResults.push(result); } else { failedResults.push(result); hasErrors = true; } }); // Display results console.log(chalk_1.default.cyan.bold('\nšŸŽÆ Auto-TestID Generation Results\n')); if (options.dryRun) { console.log(chalk_1.default.yellow('šŸ“‹ Dry Run Mode - No files were modified\n')); } // Summary statistics console.log(chalk_1.default.white('šŸ“Š Summary:')); console.log(` ${chalk_1.default.green('Files processed:')} ${processedFiles}`); console.log(` ${chalk_1.default.blue('Elements found:')} ${totalElements}`); console.log(` ${chalk_1.default.green('Test IDs generated:')} ${totalTransformed}`); if (totalErrors > 0) { console.log(` ${chalk_1.default.red('Errors:')} ${totalErrors}`); } // Show successful transformations if (successfulResults.length > 0) { console.log(chalk_1.default.green.bold('\nāœ… Successfully processed:')); successfulResults.forEach(result => { const fileName = path.basename(result.filePath); const transformCount = result.transformations.length; const timeMs = result.metrics.processingTime; console.log(` ${chalk_1.default.green('āœ“')} ${fileName} (${transformCount} test IDs, ${timeMs}ms)`); // Show transformations in dry-run mode if (options.dryRun && result.transformations.length > 0) { result.transformations.slice(0, 3).forEach(transform => { const element = transform.element; const preview = element.content ? ` "${element.content}"` : ''; console.log(` ${chalk_1.default.dim('+')} ${element.tag}${preview} → ${transform.attribute}="${transform.value}"`); }); if (result.transformations.length > 3) { console.log(` ${chalk_1.default.dim(`... and ${result.transformations.length - 3} more`)}`); } } }); } // Show errors if (failedResults.length > 0) { console.log(chalk_1.default.red.bold('\nāŒ Failed to process:')); failedResults.forEach(result => { const fileName = path.basename(result.filePath); console.log(` ${chalk_1.default.red('āœ—')} ${fileName}`); result.errors.forEach(error => { console.log(` ${chalk_1.default.red('→')} ${error.message}`); }); }); } // Performance metrics const totalTime = results.reduce((sum, r) => sum + r.metrics.processingTime, 0); const avgTimePerFile = totalTime / results.length; console.log(chalk_1.default.blue.bold('\n⚔ Performance:')); console.log(` ${chalk_1.default.blue('Total time:')} ${totalTime}ms`); console.log(` ${chalk_1.default.blue('Average per file:')} ${avgTimePerFile.toFixed(1)}ms`); // Recommendations if (totalTransformed === 0 && totalElements > 0) { console.log(chalk_1.default.yellow.bold('\nšŸ’” Recommendations:')); console.log(' • Elements were found but no test IDs were generated'); console.log(' • Check your include/exclude patterns'); console.log(' • Verify the framework setting matches your files'); } if (options.dryRun && totalTransformed > 0) { console.log(chalk_1.default.yellow.bold('\nšŸ’” Next steps:')); console.log(` • Run without --dry-run to apply ${totalTransformed} changes`); console.log(' • Use --backup to create backup files'); } // Exit with appropriate code process.exit(hasErrors ? 1 : 0); } catch (error) { spinner.fail(`Generation failed: ${error.message}`); if (options.logLevel === 'debug') { console.error(chalk_1.default.red('Stack trace:'), error.stack); } process.exit(1); } } // Helper function to format file size function formatFileSize(bytes) { const sizes = ['B', 'KB', 'MB']; if (bytes === 0) return '0 B'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`; } // Helper function to format duration function formatDuration(ms) { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`; } //# sourceMappingURL=generate.js.map