UNPKG

smartui-migration-tool

Version:

Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI

459 lines (455 loc) 18 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ChangeTracker = void 0; const path = __importStar(require("path")); const fs = __importStar(require("fs/promises")); class ChangeTracker { constructor() { this.changes = []; this.fileChanges = new Map(); this.dependencyChanges = []; this.scriptChanges = []; this.validationResults = []; this.recommendations = []; this.startTime = new Date(); } /** * Track a file change */ trackFileChange(filePath, changeType, originalContent, newContent, description, lineNumber, snapshotCount, dependencyCount) { const change = { filePath, changeType, lineNumber, originalContent, newContent, description: description || this.generateDescription(changeType, filePath), timestamp: new Date(), riskLevel: this.assessRiskLevel(changeType, filePath, originalContent, newContent) }; this.changes.push(change); this.updateFileChange(filePath, change, snapshotCount, dependencyCount); } /** * Track a dependency change */ trackDependencyChange(type, packageName, oldVersion, newVersion, reason, filePath) { const change = { type, package: packageName, oldVersion, newVersion, reason: reason || this.generateDependencyReason(type, packageName), filePath: filePath || 'package.json', timestamp: new Date() }; this.dependencyChanges.push(change); } /** * Track a script change */ trackScriptChange(filePath, scriptName, changeType, oldScript, newScript, description) { const change = { filePath, scriptName, oldScript, newScript, changeType, description: description || this.generateScriptDescription(changeType, scriptName), timestamp: new Date() }; this.scriptChanges.push(change); } /** * Add validation result */ addValidationResult(type, message, filePath, suggestion) { const result = { type, message, filePath, suggestion, timestamp: new Date() }; this.validationResults.push(result); } /** * Add recommendation */ addRecommendation(type, title, description, priority, action) { const recommendation = { type, title, description, priority, action }; this.recommendations.push(recommendation); } /** * Mark migration as complete */ completeMigration() { this.endTime = new Date(); } /** * Generate comprehensive migration report */ generateReport(projectPath, platform, framework, language) { const migrationTime = this.endTime ? this.endTime.getTime() - this.startTime.getTime() : Date.now() - this.startTime.getTime(); const summary = { totalFiles: this.fileChanges.size, filesModified: Array.from(this.fileChanges.values()).filter(fc => fc.changeType === 'MODIFY').length, filesCreated: Array.from(this.fileChanges.values()).filter(fc => fc.changeType === 'ADD').length, filesDeleted: Array.from(this.fileChanges.values()).filter(fc => fc.changeType === 'DELETE').length, snapshotsMigrated: Array.from(this.fileChanges.values()).reduce((sum, fc) => sum + (fc.snapshotCount || 0), 0), dependenciesChanged: this.dependencyChanges.length, scriptsChanged: this.scriptChanges.length, validationErrors: this.validationResults.filter(vr => vr.type === 'ERROR').length, validationWarnings: this.validationResults.filter(vr => vr.type === 'WARNING').length, migrationTime, successRate: this.calculateSuccessRate() }; const metadata = { toolVersion: '1.1.3', migrationDate: this.startTime, projectPath, platform, framework, language, totalChanges: this.changes.length }; return { summary, fileChanges: Array.from(this.fileChanges.values()), dependencyChanges: this.dependencyChanges, scriptChanges: this.scriptChanges, validationResults: this.validationResults, recommendations: this.recommendations, metadata }; } /** * Export report to JSON */ async exportToJSON(report, outputPath) { const jsonContent = JSON.stringify(report, null, 2); await fs.writeFile(outputPath, jsonContent, 'utf-8'); } /** * Export report to Markdown */ async exportToMarkdown(report, outputPath) { const markdown = this.generateMarkdownReport(report); await fs.writeFile(outputPath, markdown, 'utf-8'); } /** * Export report to HTML */ async exportToHTML(report, outputPath) { const html = this.generateHTMLReport(report); await fs.writeFile(outputPath, html, 'utf-8'); } /** * Get all changes for a specific file */ getFileChanges(filePath) { return this.fileChanges.get(filePath); } /** * Get all changes */ getAllChanges() { return [...this.changes]; } /** * Clear all tracked changes */ clear() { this.changes = []; this.fileChanges.clear(); this.dependencyChanges = []; this.scriptChanges = []; this.validationResults = []; this.recommendations = []; this.startTime = new Date(); this.endTime = undefined; } // Private helper methods updateFileChange(filePath, change, snapshotCount, dependencyCount) { const existing = this.fileChanges.get(filePath); if (existing) { existing.changes.push(change); existing.riskLevel = this.getHighestRiskLevel(existing.riskLevel, change.riskLevel); if (snapshotCount) existing.snapshotCount = (existing.snapshotCount || 0) + snapshotCount; if (dependencyCount) existing.dependencyCount = (existing.dependencyCount || 0) + dependencyCount; } else { const fileChange = { filePath, changeType: change.changeType, changes: [change], summary: this.generateFileSummary(change), riskLevel: change.riskLevel, snapshotCount, dependencyCount }; this.fileChanges.set(filePath, fileChange); } } generateDescription(changeType, filePath) { const fileName = path.basename(filePath); const fileExt = path.extname(filePath); switch (changeType) { case 'ADD': return `Created new file: ${fileName}`; case 'MODIFY': return `Modified file: ${fileName}`; case 'DELETE': return `Deleted file: ${fileName}`; case 'RENAME': return `Renamed file: ${fileName}`; default: return `Changed file: ${fileName}`; } } generateDependencyReason(type, packageName) { switch (type) { case 'ADD': return `Added SmartUI dependency: ${packageName}`; case 'REMOVE': return `Removed old dependency: ${packageName}`; case 'UPDATE': return `Updated dependency: ${packageName}`; default: return `Modified dependency: ${packageName}`; } } generateScriptDescription(changeType, scriptName) { switch (changeType) { case 'ADD': return `Added new script: ${scriptName}`; case 'MODIFY': return `Modified script: ${scriptName}`; case 'DELETE': return `Deleted script: ${scriptName}`; default: return `Changed script: ${scriptName}`; } } generateFileSummary(change) { return `${change.changeType} - ${change.description}`; } assessRiskLevel(changeType, filePath, originalContent, newContent) { const fileName = path.basename(filePath); const fileExt = path.extname(filePath); // High risk: Core configuration files if (['package.json', 'pom.xml', 'requirements.txt', '.env', 'config.json'].includes(fileName)) { return 'HIGH'; } // Medium risk: Test files and scripts if (fileExt === '.java' || fileExt === '.js' || fileExt === '.ts' || fileName.includes('test')) { return 'MEDIUM'; } // Low risk: Documentation and other files return 'LOW'; } getHighestRiskLevel(level1, level2) { const levels = { 'LOW': 1, 'MEDIUM': 2, 'HIGH': 3 }; const level1Value = levels[level1] || 1; const level2Value = levels[level2] || 1; return level1Value >= level2Value ? level1 : level2; } calculateSuccessRate() { const totalValidations = this.validationResults.length; if (totalValidations === 0) return 100; const errors = this.validationResults.filter(vr => vr.type === 'ERROR').length; return Math.round(((totalValidations - errors) / totalValidations) * 100); } generateMarkdownReport(report) { const { summary, fileChanges, dependencyChanges, scriptChanges, validationResults, recommendations, metadata } = report; let markdown = `# SmartUI Migration Report\n\n`; markdown += `**Migration Date:** ${metadata.migrationDate.toISOString()}\n`; markdown += `**Tool Version:** ${metadata.toolVersion}\n`; markdown += `**Project:** ${metadata.projectPath}\n`; markdown += `**Platform:** ${metadata.platform}\n`; markdown += `**Framework:** ${metadata.framework}\n`; markdown += `**Language:** ${metadata.language}\n\n`; markdown += `## Summary\n\n`; markdown += `- **Total Files:** ${summary.totalFiles}\n`; markdown += `- **Files Modified:** ${summary.filesModified}\n`; markdown += `- **Files Created:** ${summary.filesCreated}\n`; markdown += `- **Snapshots Migrated:** ${summary.snapshotsMigrated}\n`; markdown += `- **Dependencies Changed:** ${summary.dependenciesChanged}\n`; markdown += `- **Migration Time:** ${Math.round(summary.migrationTime / 1000)}s\n`; markdown += `- **Success Rate:** ${summary.successRate}%\n\n`; if (fileChanges.length > 0) { markdown += `## File Changes\n\n`; fileChanges.forEach(fileChange => { markdown += `### ${fileChange.filePath}\n`; markdown += `- **Type:** ${fileChange.changeType}\n`; markdown += `- **Risk Level:** ${fileChange.riskLevel}\n`; if (fileChange.snapshotCount) markdown += `- **Snapshots:** ${fileChange.snapshotCount}\n`; markdown += `- **Changes:** ${fileChange.changes.length}\n\n`; }); } if (dependencyChanges.length > 0) { markdown += `## Dependency Changes\n\n`; dependencyChanges.forEach(depChange => { markdown += `- **${depChange.type}:** ${depChange.package}`; if (depChange.oldVersion && depChange.newVersion) { markdown += ` (${depChange.oldVersion}${depChange.newVersion})`; } markdown += `\n`; }); markdown += `\n`; } if (validationResults.length > 0) { markdown += `## Validation Results\n\n`; validationResults.forEach(result => { markdown += `- **${result.type}:** ${result.message}\n`; if (result.suggestion) markdown += ` - *Suggestion:* ${result.suggestion}\n`; }); markdown += `\n`; } if (recommendations.length > 0) { markdown += `## Recommendations\n\n`; recommendations.forEach(rec => { markdown += `### ${rec.title} (${rec.priority})\n`; markdown += `${rec.description}\n\n`; }); } return markdown; } generateHTMLReport(report) { const { summary, fileChanges, dependencyChanges, validationResults, recommendations, metadata } = report; return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SmartUI Migration Report</title> <style> body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; } .header { background: #f4f4f4; padding: 20px; border-radius: 5px; margin-bottom: 30px; } .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; } .summary-item { background: #e8f4fd; padding: 15px; border-radius: 5px; text-align: center; } .summary-item h3 { margin: 0; color: #2c5aa0; } .summary-item p { margin: 5px 0 0 0; font-size: 24px; font-weight: bold; color: #1a365d; } .section { margin: 30px 0; } .file-change { background: #f9f9f9; padding: 15px; margin: 10px 0; border-radius: 5px; border-left: 4px solid #4299e1; } .validation-success { color: #38a169; } .validation-warning { color: #d69e2e; } .validation-error { color: #e53e3e; } .recommendation { background: #fff5cd; padding: 15px; margin: 10px 0; border-radius: 5px; border-left: 4px solid #d69e2e; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } th { background-color: #f2f2f2; } </style> </head> <body> <div class="header"> <h1>SmartUI Migration Report</h1> <p><strong>Migration Date:</strong> ${metadata.migrationDate.toISOString()}</p> <p><strong>Tool Version:</strong> ${metadata.toolVersion}</p> <p><strong>Project:</strong> ${metadata.projectPath}</p> <p><strong>Platform:</strong> ${metadata.platform} | <strong>Framework:</strong> ${metadata.framework} | <strong>Language:</strong> ${metadata.language}</p> </div> <div class="summary"> <div class="summary-item"> <h3>Total Files</h3> <p>${summary.totalFiles}</p> </div> <div class="summary-item"> <h3>Files Modified</h3> <p>${summary.filesModified}</p> </div> <div class="summary-item"> <h3>Snapshots Migrated</h3> <p>${summary.snapshotsMigrated}</p> </div> <div class="summary-item"> <h3>Success Rate</h3> <p>${summary.successRate}%</p> </div> </div> ${fileChanges.length > 0 ? ` <div class="section"> <h2>File Changes</h2> ${fileChanges.map(fc => ` <div class="file-change"> <h3>${fc.filePath}</h3> <p><strong>Type:</strong> ${fc.changeType} | <strong>Risk:</strong> ${fc.riskLevel} | <strong>Changes:</strong> ${fc.changes.length}</p> ${fc.snapshotCount ? `<p><strong>Snapshots:</strong> ${fc.snapshotCount}</p>` : ''} </div> `).join('')} </div> ` : ''} ${validationResults.length > 0 ? ` <div class="section"> <h2>Validation Results</h2> ${validationResults.map(vr => ` <p class="validation-${vr.type.toLowerCase()}"> <strong>${vr.type}:</strong> ${vr.message} ${vr.suggestion ? `<br><em>Suggestion:</em> ${vr.suggestion}` : ''} </p> `).join('')} </div> ` : ''} ${recommendations.length > 0 ? ` <div class="section"> <h2>Recommendations</h2> ${recommendations.map(rec => ` <div class="recommendation"> <h3>${rec.title} (${rec.priority})</h3> <p>${rec.description}</p> </div> `).join('')} </div> ` : ''} </body> </html>`; } } exports.ChangeTracker = ChangeTracker; //# sourceMappingURL=ChangeTracker.js.map