smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
800 lines • 33 kB
JavaScript
"use strict";
/**
* Framework-Specific Analyzer
* Phase 4: Multi-Language & Framework Support
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FrameworkSpecificAnalyzer = void 0;
class FrameworkSpecificAnalyzer {
constructor() {
this.frameworks = new Map();
this.metadata = this.initializeMetadata();
this.initializeFrameworks();
}
initializeMetadata() {
return {
language: 'javascript',
framework: 'react',
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
initializeFrameworks() {
// React Framework
this.frameworks.set('react', {
framework: 'react',
version: '18.0.0',
patterns: [
{
id: 'react-hooks',
name: 'React Hooks Pattern',
description: 'Use React hooks for state management and side effects',
type: 'behavioral',
category: 'state-management',
severity: 'medium',
priority: 3,
pattern: /useState|useEffect|useContext|useReducer|useMemo|useCallback/gi,
confidence: 0.9,
examples: [
'const [state, setState] = useState(initialValue);',
'useEffect(() => { /* side effect */ }, [dependencies]);'
],
documentation: ['React Hooks Documentation'],
resources: ['React Hooks Guide'],
metadata: this.createPatternMetadata('react')
},
{
id: 'react-components',
name: 'React Components Pattern',
description: 'Use functional components with TypeScript',
type: 'structural',
category: 'component-architecture',
severity: 'high',
priority: 4,
pattern: /const\s+\w+\s*=\s*\(\s*[^)]*\s*\)\s*=>\s*{/gi,
confidence: 0.8,
examples: [
'const MyComponent = ({ prop1, prop2 }: Props) => {',
'const Button = ({ onClick, children }: ButtonProps) => {'
],
documentation: ['React Components Documentation'],
resources: ['React Components Guide'],
metadata: this.createPatternMetadata('react')
}
],
conventions: [
{
id: 'react-naming',
name: 'React Naming Convention',
description: 'Use PascalCase for components and camelCase for functions',
type: 'naming',
category: 'naming-conventions',
severity: 'medium',
priority: 2,
rule: 'Components: PascalCase, Functions: camelCase',
confidence: 0.9,
examples: [
'const MyComponent = () => {};',
'const handleClick = () => {};'
],
documentation: ['React Naming Conventions'],
resources: ['React Style Guide'],
metadata: this.createConventionMetadata('react')
}
],
bestPractices: [
{
id: 'react-performance',
name: 'React Performance Best Practice',
description: 'Use React.memo and useMemo for performance optimization',
type: 'performance',
category: 'performance-optimization',
severity: 'high',
priority: 4,
practice: 'Use React.memo for component memoization and useMemo for expensive calculations',
confidence: 0.8,
examples: [
'const MemoizedComponent = React.memo(Component);',
'const expensiveValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);'
],
documentation: ['React Performance Documentation'],
resources: ['React Performance Guide'],
metadata: this.createBestPracticeMetadata('react')
}
],
antiPatterns: [
{
id: 'react-direct-dom',
name: 'Direct DOM Manipulation Anti-Pattern',
description: 'Avoid direct DOM manipulation in React components',
type: 'maintainability',
category: 'anti-patterns',
severity: 'high',
priority: 4,
pattern: /document\.getElementById|document\.querySelector|element\.innerHTML/gi,
confidence: 0.9,
examples: [
'document.getElementById("myElement").innerHTML = "Hello";',
'document.querySelector(".my-class").style.display = "none";'
],
documentation: ['React Anti-Patterns'],
resources: ['React Best Practices'],
metadata: this.createAntiPatternMetadata('react')
}
],
transformations: [
{
id: 'react-class-to-hooks',
name: 'Class Component to Hooks Transformation',
description: 'Transform class components to functional components with hooks',
type: 'migrate',
from: 'class MyComponent extends React.Component',
to: 'const MyComponent = () => {',
confidence: 0.8,
effort: 'high',
impact: 'high',
risk: 'medium',
code: '// Transform class component to functional component with hooks',
validation: '// Validation: Check if transformation was applied correctly',
rollback: '// Rollback: Revert to class component',
metadata: this.createTransformationMetadata('react')
}
],
metadata: this.createFrameworkMetadata('react')
});
// Angular Framework
this.frameworks.set('angular', {
framework: 'angular',
version: '15.0.0',
patterns: [
{
id: 'angular-components',
name: 'Angular Components Pattern',
description: 'Use Angular components with decorators',
type: 'structural',
category: 'component-architecture',
severity: 'high',
priority: 4,
pattern: /@Component\s*\(\s*{[^}]*}\s*\)/gi,
confidence: 0.9,
examples: [
'@Component({ selector: "app-root", templateUrl: "./app.component.html" })',
'@Component({ selector: "my-component", template: "<div>Hello</div>" })'
],
documentation: ['Angular Components Documentation'],
resources: ['Angular Components Guide'],
metadata: this.createPatternMetadata('angular')
}
],
conventions: [
{
id: 'angular-naming',
name: 'Angular Naming Convention',
description: 'Use kebab-case for selectors and PascalCase for classes',
type: 'naming',
category: 'naming-conventions',
severity: 'medium',
priority: 2,
rule: 'Selectors: kebab-case, Classes: PascalCase',
confidence: 0.9,
examples: [
'selector: "app-root"',
'export class MyComponent { }'
],
documentation: ['Angular Naming Conventions'],
resources: ['Angular Style Guide'],
metadata: this.createConventionMetadata('angular')
}
],
bestPractices: [
{
id: 'angular-onpush',
name: 'Angular OnPush Strategy',
description: 'Use OnPush change detection strategy for better performance',
type: 'performance',
category: 'performance-optimization',
severity: 'high',
priority: 4,
practice: 'Use OnPush change detection strategy to reduce unnecessary change detection cycles',
confidence: 0.8,
examples: [
'changeDetection: ChangeDetectionStrategy.OnPush',
'changeDetection: ChangeDetectionStrategy.OnPush'
],
documentation: ['Angular Change Detection Documentation'],
resources: ['Angular Performance Guide'],
metadata: this.createBestPracticeMetadata('angular')
}
],
antiPatterns: [
{
id: 'angular-subscribe-in-template',
name: 'Subscribe in Template Anti-Pattern',
description: 'Avoid subscribing to observables directly in templates',
type: 'maintainability',
category: 'anti-patterns',
severity: 'high',
priority: 4,
pattern: /\([^)]*\)\s*=>\s*[^}]*\.subscribe/gi,
confidence: 0.9,
examples: [
'(data$ | async).subscribe(data => { /* handle data */ })',
'(user$ | async).subscribe(user => { /* handle user */ })'
],
documentation: ['Angular Anti-Patterns'],
resources: ['Angular Best Practices'],
metadata: this.createAntiPatternMetadata('angular')
}
],
transformations: [
{
id: 'angular-standalone',
name: 'Angular Standalone Components',
description: 'Transform to standalone components for better tree-shaking',
type: 'migrate',
from: 'NgModule',
to: 'standalone: true',
confidence: 0.7,
effort: 'high',
impact: 'high',
risk: 'high',
code: '// Transform to standalone components',
validation: '// Validation: Check if transformation was applied correctly',
rollback: '// Rollback: Revert to NgModule',
metadata: this.createTransformationMetadata('angular')
}
],
metadata: this.createFrameworkMetadata('angular')
});
// Vue Framework
this.frameworks.set('vue', {
framework: 'vue',
version: '3.0.0',
patterns: [
{
id: 'vue-composition-api',
name: 'Vue Composition API Pattern',
description: 'Use Vue 3 Composition API for better logic reuse',
type: 'behavioral',
category: 'state-management',
severity: 'high',
priority: 4,
pattern: /import\s*{\s*ref\s*,\s*reactive\s*,\s*computed\s*,\s*watch\s*}/gi,
confidence: 0.9,
examples: [
'import { ref, reactive, computed, watch } from "vue";',
'const count = ref(0); const state = reactive({ name: "Vue" });'
],
documentation: ['Vue Composition API Documentation'],
resources: ['Vue Composition API Guide'],
metadata: this.createPatternMetadata('vue')
}
],
conventions: [
{
id: 'vue-naming',
name: 'Vue Naming Convention',
description: 'Use PascalCase for components and camelCase for functions',
type: 'naming',
category: 'naming-conventions',
severity: 'medium',
priority: 2,
rule: 'Components: PascalCase, Functions: camelCase',
confidence: 0.9,
examples: [
'const MyComponent = defineComponent({});',
'const handleClick = () => {};'
],
documentation: ['Vue Naming Conventions'],
resources: ['Vue Style Guide'],
metadata: this.createConventionMetadata('vue')
}
],
bestPractices: [
{
id: 'vue-performance',
name: 'Vue Performance Best Practice',
description: 'Use v-memo for performance optimization',
type: 'performance',
category: 'performance-optimization',
severity: 'high',
priority: 4,
practice: 'Use v-memo for expensive template calculations',
confidence: 0.8,
examples: [
'<div v-memo="[valueA, valueB]">',
'<template v-memo="[expensiveValue]">'
],
documentation: ['Vue Performance Documentation'],
resources: ['Vue Performance Guide'],
metadata: this.createBestPracticeMetadata('vue')
}
],
antiPatterns: [
{
id: 'vue-mutating-props',
name: 'Mutating Props Anti-Pattern',
description: 'Avoid mutating props directly in child components',
type: 'maintainability',
category: 'anti-patterns',
severity: 'high',
priority: 4,
pattern: /props\.\w+\s*=/gi,
confidence: 0.9,
examples: [
'props.name = "new name";',
'props.count = 42;'
],
documentation: ['Vue Anti-Patterns'],
resources: ['Vue Best Practices'],
metadata: this.createAntiPatternMetadata('vue')
}
],
transformations: [
{
id: 'vue-options-to-composition',
name: 'Options API to Composition API',
description: 'Transform Options API to Composition API',
type: 'migrate',
from: 'export default { data() { return {} } }',
to: 'import { ref } from "vue"; const data = ref({});',
confidence: 0.8,
effort: 'high',
impact: 'high',
risk: 'medium',
code: '// Transform Options API to Composition API',
validation: '// Validation: Check if transformation was applied correctly',
rollback: '// Rollback: Revert to Options API',
metadata: this.createTransformationMetadata('vue')
}
],
metadata: this.createFrameworkMetadata('vue')
});
// Cypress Framework
this.frameworks.set('cypress', {
framework: 'cypress',
version: '12.0.0',
patterns: [
{
id: 'cypress-commands',
name: 'Cypress Commands Pattern',
description: 'Use Cypress commands for testing',
type: 'testing',
category: 'test-commands',
severity: 'high',
priority: 4,
pattern: /cy\.\w+\(/gi,
confidence: 0.9,
examples: [
'cy.visit("/");',
'cy.get("[data-testid=button]").click();'
],
documentation: ['Cypress Commands Documentation'],
resources: ['Cypress Commands Guide'],
metadata: this.createPatternMetadata('cypress')
}
],
conventions: [
{
id: 'cypress-naming',
name: 'Cypress Naming Convention',
description: 'Use descriptive test names and data-testid attributes',
type: 'naming',
category: 'naming-conventions',
severity: 'medium',
priority: 2,
rule: 'Tests: descriptive names, Elements: data-testid attributes',
confidence: 0.9,
examples: [
'it("should display user profile when logged in", () => {});',
'cy.get("[data-testid=user-profile]");'
],
documentation: ['Cypress Naming Conventions'],
resources: ['Cypress Style Guide'],
metadata: this.createConventionMetadata('cypress')
}
],
bestPractices: [
{
id: 'cypress-page-objects',
name: 'Cypress Page Objects Best Practice',
description: 'Use page objects for better test organization',
type: 'maintainability',
category: 'test-organization',
severity: 'high',
priority: 4,
practice: 'Use page objects to encapsulate page-specific logic and selectors',
confidence: 0.8,
examples: [
'class LoginPage { get username() { return cy.get("[data-testid=username]"); } }',
'const loginPage = new LoginPage();'
],
documentation: ['Cypress Page Objects Documentation'],
resources: ['Cypress Best Practices'],
metadata: this.createBestPracticeMetadata('cypress')
}
],
antiPatterns: [
{
id: 'cypress-hardcoded-selectors',
name: 'Hardcoded Selectors Anti-Pattern',
description: 'Avoid hardcoded CSS selectors in tests',
type: 'maintainability',
category: 'anti-patterns',
severity: 'high',
priority: 4,
pattern: /cy\.get\("\.\w+"\)|cy\.get\("#\w+"\)/gi,
confidence: 0.9,
examples: [
'cy.get(".my-button");',
'cy.get("#submit-form");'
],
documentation: ['Cypress Anti-Patterns'],
resources: ['Cypress Best Practices'],
metadata: this.createAntiPatternMetadata('cypress')
}
],
transformations: [
{
id: 'cypress-visual-testing',
name: 'Cypress Visual Testing',
description: 'Add visual testing to Cypress tests',
type: 'migrate',
from: 'cy.get("[data-testid=element]")',
to: 'cy.get("[data-testid=element]").percySnapshot("Element Snapshot");',
confidence: 0.9,
effort: 'low',
impact: 'high',
risk: 'low',
code: '// Add visual testing to Cypress tests',
validation: '// Validation: Check if visual testing was added',
rollback: '// Rollback: Remove visual testing',
metadata: this.createTransformationMetadata('cypress')
}
],
metadata: this.createFrameworkMetadata('cypress')
});
// Playwright Framework
this.frameworks.set('playwright', {
framework: 'playwright',
version: '1.40.0',
patterns: [
{
id: 'playwright-page-objects',
name: 'Playwright Page Objects Pattern',
description: 'Use Playwright page objects for testing',
type: 'testing',
category: 'test-architecture',
severity: 'high',
priority: 4,
pattern: /class\s+\w+Page\s*{/gi,
confidence: 0.9,
examples: [
'class LoginPage { async login(username, password) { await this.page.fill("[data-testid=username]", username); } }',
'class HomePage { async navigate() { await this.page.goto("/"); } }'
],
documentation: ['Playwright Page Objects Documentation'],
resources: ['Playwright Page Objects Guide'],
metadata: this.createPatternMetadata('playwright')
}
],
conventions: [
{
id: 'playwright-naming',
name: 'Playwright Naming Convention',
description: 'Use descriptive test names and page object methods',
type: 'naming',
category: 'naming-conventions',
severity: 'medium',
priority: 2,
rule: 'Tests: descriptive names, Methods: descriptive action names',
confidence: 0.9,
examples: [
'test("should login with valid credentials", async ({ page }) => {});',
'async clickLoginButton() { await this.page.click("[data-testid=login-button]"); }'
],
documentation: ['Playwright Naming Conventions'],
resources: ['Playwright Style Guide'],
metadata: this.createConventionMetadata('playwright')
}
],
bestPractices: [
{
id: 'playwright-parallel-execution',
name: 'Playwright Parallel Execution',
description: 'Use parallel execution for better test performance',
type: 'performance',
category: 'test-performance',
severity: 'high',
priority: 4,
practice: 'Use parallel execution to run tests faster',
confidence: 0.8,
examples: [
'test.describe.configure({ mode: "parallel" });',
'test.describe.parallel("Login Tests", () => {});'
],
documentation: ['Playwright Parallel Execution Documentation'],
resources: ['Playwright Performance Guide'],
metadata: this.createBestPracticeMetadata('playwright')
}
],
antiPatterns: [
{
id: 'playwright-sleep',
name: 'Sleep Anti-Pattern',
description: 'Avoid using sleep in tests',
type: 'reliability',
category: 'anti-patterns',
severity: 'high',
priority: 4,
pattern: /await\s+page\.waitForTimeout\(\d+\)/gi,
confidence: 0.9,
examples: [
'await page.waitForTimeout(1000);',
'await page.waitForTimeout(5000);'
],
documentation: ['Playwright Anti-Patterns'],
resources: ['Playwright Best Practices'],
metadata: this.createAntiPatternMetadata('playwright')
}
],
transformations: [
{
id: 'playwright-visual-testing',
name: 'Playwright Visual Testing',
description: 'Add visual testing to Playwright tests',
type: 'migrate',
from: 'await page.screenshot();',
to: 'await page.screenshot({ path: "screenshot.png" }); await expect(page).toHaveScreenshot("screenshot.png");',
confidence: 0.9,
effort: 'low',
impact: 'high',
risk: 'low',
code: '// Add visual testing to Playwright tests',
validation: '// Validation: Check if visual testing was added',
rollback: '// Rollback: Remove visual testing',
metadata: this.createTransformationMetadata('playwright')
}
],
metadata: this.createFrameworkMetadata('playwright')
});
}
analyze(ast) {
const framework = ast.framework;
if (!framework || !this.frameworks.has(framework)) {
return null;
}
const analysis = this.frameworks.get(framework);
return {
...analysis,
metadata: {
...analysis.metadata,
language: ast.language,
platform: ast.platform || null,
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0
}
};
}
getSupportedFrameworks() {
return Array.from(this.frameworks.keys());
}
isFrameworkSupported(framework) {
return this.frameworks.has(framework);
}
getFramework(framework) {
return this.frameworks.get(framework);
}
createPatternMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createConventionMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createBestPracticeMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createAntiPatternMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createTransformationMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createFrameworkMetadata(framework) {
return {
language: 'javascript',
framework,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
}
exports.FrameworkSpecificAnalyzer = FrameworkSpecificAnalyzer;
//# sourceMappingURL=FrameworkSpecificAnalyzer.js.map