smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
951 lines (808 loc) ⢠31 kB
JavaScript
const { Command, Flags } = require('@oclif/core');
const chalk = require('chalk');
const { ASCIILogos } = require('../utils/ascii-logos');
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
class PatternRecognition extends Command {
static description = 'Advanced pattern recognition and analysis for visual testing migration';
static flags = {
path: Flags.string({ char: 'p', description: 'Project path to analyze' }),
platform: Flags.string({ char: 'f', description: 'Target platform (percy|applitools|sauce|smartui)' }),
framework: Flags.string({ char: 'w', description: 'Target framework (cypress|playwright|selenium|jest)' }),
language: Flags.string({ char: 'l', description: 'Target language (javascript|typescript|python|java|csharp)' }),
'confidence-threshold': Flags.integer({ description: 'Minimum confidence threshold (0-100)', default: 70 }),
'include-context': Flags.boolean({ description: 'Include context analysis in results' }),
'export-patterns': Flags.boolean({ description: 'Export detected patterns to file' }),
output: Flags.string({ char: 'o', description: 'Output file for pattern analysis results' }),
format: Flags.string({ description: 'Output format (json|yaml|table)', default: 'table' })
};
async run() {
console.log(ASCIILogos.getMinimalLogo());
console.log(chalk.cyan.bold('\nš Advanced Pattern Recognition Engine\n'));
const { flags } = await this.parse(PatternRecognition);
const projectPath = flags.path || process.cwd();
console.log(chalk.yellow.bold('š Analyzing Project:'));
console.log(chalk.white(projectPath));
try {
// Initialize pattern recognition engine
const engine = this.initializePatternEngine();
// Perform comprehensive pattern analysis
const analysis = await this.performPatternAnalysis(projectPath, engine, flags);
// Display results
this.displayPatternResults(analysis, flags);
// Export patterns if requested
if (flags['export-patterns']) {
await this.exportPatterns(analysis, projectPath);
}
// Save results if output specified
if (flags.output) {
await this.saveAnalysisResults(analysis, flags.output, flags.format);
}
} catch (error) {
console.error(chalk.red.bold('\nā Pattern recognition failed:'));
console.error(chalk.red(error.message));
this.exit(1);
}
}
initializePatternEngine() {
return {
visualTestingPatterns: this.getVisualTestingPatterns(),
frameworkPatterns: this.getFrameworkPatterns(),
configurationPatterns: this.getConfigurationPatterns(),
migrationPatterns: this.getMigrationPatterns(),
contextAnalyzer: this.createContextAnalyzer()
};
}
async performPatternAnalysis(projectPath, engine, flags) {
const analysis = {
projectPath,
timestamp: new Date().toISOString(),
patterns: {
visualTesting: [],
frameworks: [],
configurations: [],
migrations: []
},
context: {},
statistics: {},
recommendations: []
};
console.log(chalk.blue(' š Scanning for visual testing patterns...'));
analysis.patterns.visualTesting = await this.analyzeVisualTestingPatterns(projectPath, engine, flags);
console.log(chalk.blue(' šļø Detecting framework patterns...'));
analysis.patterns.frameworks = await this.analyzeFrameworkPatterns(projectPath, engine, flags);
console.log(chalk.blue(' āļø Analyzing configuration patterns...'));
analysis.patterns.configurations = await this.analyzeConfigurationPatterns(projectPath, engine, flags);
console.log(chalk.blue(' š Identifying migration patterns...'));
analysis.patterns.migrations = await this.analyzeMigrationPatterns(projectPath, engine, flags);
// Context analysis if requested
if (flags['include-context']) {
console.log(chalk.blue(' š§ Performing context analysis...'));
analysis.context = await this.performContextAnalysis(projectPath, analysis.patterns, engine);
}
// Calculate statistics
analysis.statistics = this.calculatePatternStatistics(analysis.patterns);
// Generate recommendations
analysis.recommendations = this.generatePatternRecommendations(analysis.patterns, flags);
return analysis;
}
async analyzeVisualTestingPatterns(projectPath, engine, flags) {
const patterns = [];
const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true });
for (const file of files.slice(0, 50)) { // Limit for performance
try {
const content = await fs.readFile(file, 'utf8');
const filePatterns = this.findVisualTestingPatterns(content, file, engine.visualTestingPatterns);
patterns.push(...filePatterns);
} catch (error) {
// Skip files that can't be read
}
}
return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100);
}
async analyzeFrameworkPatterns(projectPath, engine, flags) {
const patterns = [];
const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true });
for (const file of files.slice(0, 50)) {
try {
const content = await fs.readFile(file, 'utf8');
const filePatterns = this.findFrameworkPatterns(content, file, engine.frameworkPatterns);
patterns.push(...filePatterns);
} catch (error) {
// Skip files that can't be read
}
}
return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100);
}
async analyzeConfigurationPatterns(projectPath, engine, flags) {
const patterns = [];
const configFiles = glob.sync(`${projectPath}/**/*.{json,js,ts,yml,yaml,config}`, { nodir: true });
for (const file of configFiles.slice(0, 20)) {
try {
const content = await fs.readFile(file, 'utf8');
const filePatterns = this.findConfigurationPatterns(content, file, engine.configurationPatterns);
patterns.push(...filePatterns);
} catch (error) {
// Skip files that can't be read
}
}
return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100);
}
async analyzeMigrationPatterns(projectPath, engine, flags) {
const patterns = [];
const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true });
for (const file of files.slice(0, 50)) {
try {
const content = await fs.readFile(file, 'utf8');
const filePatterns = this.findMigrationPatterns(content, file, engine.migrationPatterns);
patterns.push(...filePatterns);
} catch (error) {
// Skip files that can't be read
}
}
return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100);
}
async performContextAnalysis(projectPath, patterns, engine) {
return {
projectStructure: await this.analyzeProjectStructure(projectPath),
codeComplexity: this.analyzeCodeComplexity(patterns),
migrationReadiness: this.analyzeMigrationReadiness(patterns),
riskAssessment: this.assessMigrationRisks(patterns),
dependencies: await this.analyzeDependencies(projectPath)
};
}
findVisualTestingPatterns(content, filePath, patterns) {
const matches = [];
for (const pattern of patterns) {
const regex = new RegExp(pattern.regex, 'gi');
const matches_found = content.match(regex);
if (matches_found) {
matches.push({
type: 'visual-testing',
pattern: pattern.name,
platform: pattern.platform,
framework: pattern.framework,
file: filePath,
matches: matches_found.length,
confidence: pattern.confidence,
context: this.extractContext(content, matches_found[0], 3),
suggestion: pattern.suggestion,
migration: pattern.migration
});
}
}
return matches;
}
findFrameworkPatterns(content, filePath, patterns) {
const matches = [];
for (const pattern of patterns) {
const regex = new RegExp(pattern.regex, 'gi');
const matches_found = content.match(regex);
if (matches_found) {
matches.push({
type: 'framework',
pattern: pattern.name,
framework: pattern.framework,
language: pattern.language,
file: filePath,
matches: matches_found.length,
confidence: pattern.confidence,
context: this.extractContext(content, matches_found[0], 3),
suggestion: pattern.suggestion
});
}
}
return matches;
}
findConfigurationPatterns(content, filePath, patterns) {
const matches = [];
for (const pattern of patterns) {
const regex = new RegExp(pattern.regex, 'gi');
const matches_found = content.match(regex);
if (matches_found) {
matches.push({
type: 'configuration',
pattern: pattern.name,
configType: pattern.configType,
file: filePath,
matches: matches_found.length,
confidence: pattern.confidence,
context: this.extractContext(content, matches_found[0], 3),
suggestion: pattern.suggestion
});
}
}
return matches;
}
findMigrationPatterns(content, filePath, patterns) {
const matches = [];
for (const pattern of patterns) {
const regex = new RegExp(pattern.regex, 'gi');
const matches_found = content.match(regex);
if (matches_found) {
matches.push({
type: 'migration',
pattern: pattern.name,
sourcePlatform: pattern.sourcePlatform,
targetPlatform: pattern.targetPlatform,
file: filePath,
matches: matches_found.length,
confidence: pattern.confidence,
context: this.extractContext(content, matches_found[0], 3),
suggestion: pattern.suggestion,
transformation: pattern.transformation
});
}
}
return matches;
}
extractContext(content, match, lines) {
const lines_array = content.split('\n');
const matchLine = lines_array.findIndex(line => line.includes(match));
if (matchLine === -1) return '';
const start = Math.max(0, matchLine - lines);
const end = Math.min(lines_array.length, matchLine + lines + 1);
return lines_array.slice(start, end).join('\n');
}
filterByConfidence(patterns, threshold) {
return patterns.filter(pattern => pattern.confidence >= threshold);
}
calculatePatternStatistics(patterns) {
const stats = {
total: 0,
byType: {},
byPlatform: {},
byFramework: {},
confidenceDistribution: { high: 0, medium: 0, low: 0 },
fileDistribution: {}
};
const allPatterns = [
...patterns.visualTesting,
...patterns.frameworks,
...patterns.configurations,
...patterns.migrations
];
stats.total = allPatterns.length;
allPatterns.forEach(pattern => {
// By type
stats.byType[pattern.type] = (stats.byType[pattern.type] || 0) + 1;
// By platform
if (pattern.platform) {
stats.byPlatform[pattern.platform] = (stats.byPlatform[pattern.platform] || 0) + 1;
}
// By framework
if (pattern.framework) {
stats.byFramework[pattern.framework] = (stats.byFramework[pattern.framework] || 0) + 1;
}
// Confidence distribution
if (pattern.confidence >= 0.8) stats.confidenceDistribution.high++;
else if (pattern.confidence >= 0.6) stats.confidenceDistribution.medium++;
else stats.confidenceDistribution.low++;
// File distribution
const file = path.basename(pattern.file);
stats.fileDistribution[file] = (stats.fileDistribution[file] || 0) + 1;
});
return stats;
}
generatePatternRecommendations(patterns, flags) {
const recommendations = [];
// Visual testing migration recommendations
const visualPatterns = patterns.visualTesting;
const outdatedPlatforms = visualPatterns.filter(p =>
['percy', 'applitools', 'sauce-labs'].includes(p.platform)
);
if (outdatedPlatforms.length > 0) {
recommendations.push({
type: 'migration',
priority: 'high',
message: `Found ${outdatedPlatforms.length} outdated visual testing patterns. Consider migrating to SmartUI.`,
patterns: outdatedPlatforms.map(p => p.pattern),
confidence: 0.9
});
}
// Framework recommendations
const frameworkPatterns = patterns.frameworks;
if (frameworkPatterns.length === 0) {
recommendations.push({
type: 'framework',
priority: 'medium',
message: 'No test framework patterns detected. Consider adding a testing framework.',
confidence: 0.7
});
}
// Configuration recommendations
const configPatterns = patterns.configurations;
const smartuiConfig = configPatterns.find(p => p.pattern.includes('smartui'));
if (!smartuiConfig) {
recommendations.push({
type: 'configuration',
priority: 'high',
message: 'No SmartUI configuration found. Add smartui.config.js for optimal setup.',
confidence: 0.95
});
}
return recommendations;
}
async analyzeProjectStructure(projectPath) {
const structure = {
totalFiles: 0,
testFiles: 0,
configFiles: 0,
sourceFiles: 0,
languages: new Set(),
frameworks: new Set()
};
const files = glob.sync(`${projectPath}/**/*`, { nodir: true });
structure.totalFiles = files.length;
files.forEach(file => {
const ext = path.extname(file).toLowerCase();
const basename = path.basename(file).toLowerCase();
if (basename.includes('test') || basename.includes('spec')) {
structure.testFiles++;
}
if (basename.includes('config') || ext === '.json' || ext === '.yml' || ext === '.yaml') {
structure.configFiles++;
}
if (['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs'].includes(ext)) {
structure.sourceFiles++;
// Detect language
if (['.js', '.jsx'].includes(ext)) structure.languages.add('javascript');
if (['.ts', '.tsx'].includes(ext)) structure.languages.add('typescript');
if (ext === '.py') structure.languages.add('python');
if (ext === '.java') structure.languages.add('java');
if (ext === '.cs') structure.languages.add('csharp');
}
});
return {
...structure,
languages: Array.from(structure.languages),
frameworks: Array.from(structure.frameworks)
};
}
analyzeCodeComplexity(patterns) {
const complexity = {
score: 0,
factors: []
};
const allPatterns = [
...patterns.visualTesting,
...patterns.frameworks,
...patterns.configurations,
...patterns.migrations
];
// Calculate complexity based on pattern density and types
const patternDensity = allPatterns.length / 100; // patterns per 100 lines
complexity.score = Math.min(100, patternDensity * 20);
if (patternDensity > 5) {
complexity.factors.push('High pattern density detected');
}
const migrationPatterns = patterns.migrations.length;
if (migrationPatterns > 10) {
complexity.factors.push('Complex migration requirements');
}
return complexity;
}
analyzeMigrationReadiness(patterns) {
const readiness = {
score: 0,
factors: []
};
const visualPatterns = patterns.visualTesting;
const smartuiPatterns = visualPatterns.filter(p => p.platform === 'smartui');
const outdatedPatterns = visualPatterns.filter(p =>
['percy', 'applitools', 'sauce-labs'].includes(p.platform)
);
// Calculate readiness score
if (smartuiPatterns.length > 0) {
readiness.score += 40;
readiness.factors.push('SmartUI patterns already present');
}
if (outdatedPatterns.length === 0) {
readiness.score += 30;
readiness.factors.push('No outdated patterns to migrate');
} else {
readiness.score += 20;
readiness.factors.push(`${outdatedPatterns.length} patterns need migration`);
}
if (patterns.frameworks.length > 0) {
readiness.score += 20;
readiness.factors.push('Test framework detected');
}
if (patterns.configurations.length > 0) {
readiness.score += 10;
readiness.factors.push('Configuration files present');
}
return readiness;
}
assessMigrationRisks(patterns) {
const risks = {
level: 'low',
factors: []
};
const allPatterns = [
...patterns.visualTesting,
...patterns.frameworks,
...patterns.configurations,
...patterns.migrations
];
const highConfidencePatterns = allPatterns.filter(p => p.confidence >= 0.8);
const lowConfidencePatterns = allPatterns.filter(p => p.confidence < 0.6);
if (lowConfidencePatterns.length > highConfidencePatterns.length) {
risks.level = 'high';
risks.factors.push('Many low-confidence pattern matches');
} else if (lowConfidencePatterns.length > 0) {
risks.level = 'medium';
risks.factors.push('Some low-confidence pattern matches');
}
const migrationPatterns = patterns.migrations.length;
if (migrationPatterns > 20) {
risks.level = 'high';
risks.factors.push('Complex migration requirements');
}
return risks;
}
async analyzeDependencies(projectPath) {
try {
const packageJsonPath = path.join(projectPath, 'package.json');
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
return {
dependencies: Object.keys(packageJson.dependencies || {}),
devDependencies: Object.keys(packageJson.devDependencies || {}),
hasSmartUI: !!(packageJson.dependencies?.['@lambdatest/smartui-cli'] ||
packageJson.devDependencies?.['@lambdatest/smartui-cli'])
};
}
} catch (error) {
// Ignore errors
}
return { dependencies: [], devDependencies: [], hasSmartUI: false };
}
displayPatternResults(analysis, flags) {
console.log(chalk.yellow.bold('\nš Pattern Recognition Results:'));
// Statistics
console.log(chalk.blue('\nš Statistics:'));
console.log(chalk.white(` ⢠Total patterns: ${analysis.statistics.total}`));
console.log(chalk.white(` ⢠High confidence: ${analysis.statistics.confidenceDistribution.high}`));
console.log(chalk.white(` ⢠Medium confidence: ${analysis.statistics.confidenceDistribution.medium}`));
console.log(chalk.white(` ⢠Low confidence: ${analysis.statistics.confidenceDistribution.low}`));
// Pattern breakdown
console.log(chalk.blue('\nš Pattern Breakdown:'));
Object.entries(analysis.statistics.byType).forEach(([type, count]) => {
console.log(chalk.white(` ⢠${type}: ${count}`));
});
// Platform breakdown
if (Object.keys(analysis.statistics.byPlatform).length > 0) {
console.log(chalk.blue('\nš¢ Platform Breakdown:'));
Object.entries(analysis.statistics.byPlatform).forEach(([platform, count]) => {
console.log(chalk.white(` ⢠${platform}: ${count}`));
});
}
// Framework breakdown
if (Object.keys(analysis.statistics.byFramework).length > 0) {
console.log(chalk.blue('\nšļø Framework Breakdown:'));
Object.entries(analysis.statistics.byFramework).forEach(([framework, count]) => {
console.log(chalk.white(` ⢠${framework}: ${count}`));
});
}
// Context analysis
if (flags['include-context'] && analysis.context) {
console.log(chalk.blue('\nš§ Context Analysis:'));
if (analysis.context.projectStructure) {
const structure = analysis.context.projectStructure;
console.log(chalk.white(` ⢠Total files: ${structure.totalFiles}`));
console.log(chalk.white(` ⢠Test files: ${structure.testFiles}`));
console.log(chalk.white(` ⢠Config files: ${structure.configFiles}`));
console.log(chalk.white(` ⢠Languages: ${structure.languages.join(', ')}`));
}
if (analysis.context.codeComplexity) {
const complexity = analysis.context.codeComplexity;
console.log(chalk.white(` ⢠Complexity score: ${complexity.score.toFixed(1)}/100`));
if (complexity.factors.length > 0) {
console.log(chalk.white(` ⢠Factors: ${complexity.factors.join(', ')}`));
}
}
if (analysis.context.migrationReadiness) {
const readiness = analysis.context.migrationReadiness;
console.log(chalk.white(` ⢠Migration readiness: ${readiness.score}/100`));
if (readiness.factors.length > 0) {
console.log(chalk.white(` ⢠Factors: ${readiness.factors.join(', ')}`));
}
}
if (analysis.context.riskAssessment) {
const risk = analysis.context.riskAssessment;
const riskColor = risk.level === 'high' ? chalk.red : risk.level === 'medium' ? chalk.yellow : chalk.green;
console.log(riskColor(` ⢠Risk level: ${risk.level.toUpperCase()}`));
if (risk.factors.length > 0) {
console.log(chalk.white(` ⢠Factors: ${risk.factors.join(', ')}`));
}
}
}
// Recommendations
if (analysis.recommendations.length > 0) {
console.log(chalk.blue('\nš” Recommendations:'));
analysis.recommendations.forEach((rec, index) => {
const priorityColor = {
'high': chalk.red,
'medium': chalk.yellow,
'low': chalk.green
};
console.log(chalk.white(` ${index + 1}. ${rec.message}`));
console.log(priorityColor[rec.priority](` Priority: ${rec.priority.toUpperCase()}`));
console.log(chalk.gray(` Confidence: ${(rec.confidence * 100).toFixed(1)}%`));
});
}
}
async exportPatterns(analysis, projectPath) {
const exportPath = path.join(projectPath, 'smartui-patterns.json');
const exportData = {
timestamp: analysis.timestamp,
projectPath: analysis.projectPath,
patterns: analysis.patterns,
statistics: analysis.statistics
};
await fs.writeJson(exportPath, exportData, { spaces: 2 });
console.log(chalk.green(`\nā
Patterns exported to: ${exportPath}`));
}
async saveAnalysisResults(analysis, outputPath, format) {
let content;
if (format === 'json') {
content = JSON.stringify(analysis, null, 2);
} else if (format === 'yaml') {
const yaml = require('js-yaml');
content = yaml.dump(analysis);
} else {
content = JSON.stringify(analysis, null, 2);
}
await fs.writeFile(outputPath, content);
console.log(chalk.green(`\nā
Analysis results saved to: ${outputPath}`));
}
getVisualTestingPatterns() {
return [
// Percy patterns
{
name: 'percy-snapshot',
regex: 'percy\\.snapshot',
platform: 'percy',
framework: 'cypress',
confidence: 0.95,
suggestion: 'Replace with SmartUI visual check',
migration: 'cy.visualCheck()'
},
{
name: 'percy-visual',
regex: 'percy\\.visual',
platform: 'percy',
framework: 'cypress',
confidence: 0.95,
suggestion: 'Replace with SmartUI visual check',
migration: 'cy.visualCheck()'
},
// Applitools patterns
{
name: 'applitools-eyes',
regex: 'eyes\\.',
platform: 'applitools',
framework: 'cypress',
confidence: 0.9,
suggestion: 'Replace with SmartUI visual check',
migration: 'cy.visualCheck()'
},
{
name: 'applitools-check',
regex: 'checkWindow',
platform: 'applitools',
framework: 'cypress',
confidence: 0.9,
suggestion: 'Replace with SmartUI visual check',
migration: 'cy.visualCheck()'
},
// Sauce Labs patterns
{
name: 'sauce-visual',
regex: 'sauce.*visual',
platform: 'sauce-labs',
framework: 'selenium',
confidence: 0.85,
suggestion: 'Replace with SmartUI visual check',
migration: 'smartui.visualCheck()'
},
// SmartUI patterns
{
name: 'smartui-visual',
regex: 'smartui\\.visual',
platform: 'smartui',
framework: 'cypress',
confidence: 0.95,
suggestion: 'SmartUI pattern detected',
migration: 'Already migrated'
}
];
}
getFrameworkPatterns() {
return [
// Cypress patterns
{
name: 'cypress-commands',
regex: 'cy\\.',
framework: 'cypress',
language: 'javascript',
confidence: 0.9,
suggestion: 'Cypress framework detected'
},
{
name: 'cypress-describe',
regex: 'describe\\(.*cy\\.',
framework: 'cypress',
language: 'javascript',
confidence: 0.95,
suggestion: 'Cypress test structure detected'
},
// Playwright patterns
{
name: 'playwright-page',
regex: 'page\\.',
framework: 'playwright',
language: 'javascript',
confidence: 0.9,
suggestion: 'Playwright framework detected'
},
{
name: 'playwright-test',
regex: 'test\\(.*page\\.',
framework: 'playwright',
language: 'javascript',
confidence: 0.95,
suggestion: 'Playwright test structure detected'
},
// Selenium patterns
{
name: 'selenium-webdriver',
regex: 'webdriver',
framework: 'selenium',
language: 'javascript',
confidence: 0.8,
suggestion: 'Selenium WebDriver detected'
}
];
}
getConfigurationPatterns() {
return [
{
name: 'cypress-config',
regex: 'cypress\\.config',
configType: 'cypress',
confidence: 0.9,
suggestion: 'Cypress configuration detected'
},
{
name: 'playwright-config',
regex: 'playwright\\.config',
configType: 'playwright',
confidence: 0.9,
suggestion: 'Playwright configuration detected'
},
{
name: 'smartui-config',
regex: 'smartui\\.config',
configType: 'smartui',
confidence: 0.95,
suggestion: 'SmartUI configuration detected'
},
{
name: 'package-json',
regex: 'package\\.json',
configType: 'npm',
confidence: 0.8,
suggestion: 'Package.json configuration detected'
}
];
}
getMigrationPatterns() {
return [
{
name: 'percy-to-smartui',
regex: 'percy\\.(snapshot|visual)',
sourcePlatform: 'percy',
targetPlatform: 'smartui',
confidence: 0.9,
suggestion: 'Migrate from Percy to SmartUI',
transformation: 'percy.snapshot() ā cy.visualCheck()'
},
{
name: 'applitools-to-smartui',
regex: 'eyes\\.(checkWindow|check)',
sourcePlatform: 'applitools',
targetPlatform: 'smartui',
confidence: 0.9,
suggestion: 'Migrate from Applitools to SmartUI',
transformation: 'eyes.checkWindow() ā cy.visualCheck()'
},
{
name: 'sauce-to-smartui',
regex: 'sauce.*visual',
sourcePlatform: 'sauce-labs',
targetPlatform: 'smartui',
confidence: 0.85,
suggestion: 'Migrate from Sauce Labs to SmartUI',
transformation: 'sauce.visualCheck() ā smartui.visualCheck()'
}
];
}
createContextAnalyzer() {
return {
analyzeScope: (content, pattern) => this.analyzeScope(content, pattern),
analyzeDependencies: (content, pattern) => this.analyzeDependencies(content, pattern),
analyzeComplexity: (content, pattern) => this.analyzeComplexity(content, pattern)
};
}
analyzeScope(content, pattern) {
// Analyze the scope where the pattern appears
const lines = content.split('\n');
const patternLine = lines.findIndex(line => line.includes(pattern));
if (patternLine === -1) return { scope: 'unknown', context: '' };
// Determine if it's in a function, class, or global scope
let scope = 'global';
let context = '';
for (let i = patternLine; i >= 0; i--) {
const line = lines[i];
if (line.includes('function ') || line.includes('=>')) {
scope = 'function';
context = line.trim();
break;
}
if (line.includes('class ')) {
scope = 'class';
context = line.trim();
break;
}
}
return { scope, context };
}
analyzeDependencies(content, pattern) {
// Analyze dependencies related to the pattern
const dependencies = [];
if (pattern.includes('percy')) {
dependencies.push('@percy/cli', '@percy/cypress');
}
if (pattern.includes('applitools')) {
dependencies.push('@applitools/eyes-cypress');
}
if (pattern.includes('sauce')) {
dependencies.push('sauce-connect');
}
if (pattern.includes('smartui')) {
dependencies.push('@lambdatest/smartui-cli');
}
return dependencies;
}
analyzeComplexity(content, pattern) {
// Analyze the complexity of the code around the pattern
const lines = content.split('\n');
const patternLine = lines.findIndex(line => line.includes(pattern));
if (patternLine === -1) return { complexity: 'low', factors: [] };
const factors = [];
let complexity = 'low';
// Check for nested structures
const contextLines = lines.slice(Math.max(0, patternLine - 5), patternLine + 6);
const nestingLevel = contextLines.reduce((max, line) => {
const indent = line.match(/^\s*/)[0].length;
return Math.max(max, indent);
}, 0);
if (nestingLevel > 8) {
factors.push('High nesting level');
complexity = 'high';
} else if (nestingLevel > 4) {
factors.push('Medium nesting level');
complexity = 'medium';
}
// Check for async/await patterns
if (contextLines.some(line => line.includes('async') || line.includes('await'))) {
factors.push('Async operations present');
if (complexity === 'low') complexity = 'medium';
}
return { complexity, factors };
}
}
module.exports.default = PatternRecognition;