ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
387 lines (316 loc) • 13.4 kB
JavaScript
/**
* Unit tests for diagnostic utilities
*/
const { expect, describe, test, beforeEach } = require('@jest/globals');
const path = require('path');
const fs = require('fs');
// Mock fs and child_process
jest.mock('fs');
jest.mock('child_process');
// Create mock implementation of diagnostics module
const mockDiagnostics = {
checkInstallation: jest.fn(),
fixCommonIssues: jest.fn(),
checkFrameworkCompatibility: jest.fn(),
runComprehensiveScan: jest.fn(),
checkFrameworkSupport: jest.fn()
};
// Mock the module
jest.mock('../../../../src/utils/diagnostics', () => mockDiagnostics);
describe('Diagnostics Utilities', () => {
let diagnostics;
let mockExecSync;
beforeEach(() => {
// Reset mocks before each test
jest.clearAllMocks();
// Setup fs mock
fs.existsSync = jest.fn().mockReturnValue(true);
fs.readFileSync = jest.fn().mockReturnValue('{}');
fs.writeFileSync = jest.fn();
fs.appendFileSync = jest.fn();
fs.copyFileSync = jest.fn();
fs.mkdirSync = jest.fn();
fs.symlinkSync = jest.fn();
// Setup execSync mock
mockExecSync = jest.fn().mockReturnValue('1.2.0');
const childProcess = require('child_process');
childProcess.execSync = mockExecSync;
// Set diagnostics to our mock implementation
diagnostics = mockDiagnostics;
// Setup default mock responses
diagnostics.checkInstallation.mockReturnValue({
isInstalled: true,
issues: [],
missingFiles: []
});
diagnostics.fixCommonIssues.mockReturnValue({
repairAttempted: false,
repairsPerformed: [],
successful: true
});
diagnostics.checkFrameworkCompatibility.mockReturnValue({
detectedFramework: 'unknown',
isCompatible: true,
optimizationNeeded: false,
issues: []
});
diagnostics.runComprehensiveScan.mockReturnValue({
installation: { isInstalled: true, issues: [] },
framework: { detectedFramework: 'unknown', isCompatible: true },
environment: { nodeVersion: '16.0.0', npmVersion: '8.0.0' },
recommendedFixes: []
});
diagnostics.checkFrameworkSupport.mockReturnValue({
isSupported: false,
level: 'unsupported',
setupCommands: ['npm install'],
notes: []
});
});
describe('checkInstallation', () => {
test('should detect a correctly installed package', () => {
// Setup mock response for a healthy installation
diagnostics.checkInstallation.mockReturnValue({
isInstalled: true,
issues: [],
missingFiles: []
});
const result = diagnostics.checkInstallation('/test/project');
expect(result.isInstalled).toBe(true);
expect(result.issues).toHaveLength(0);
// Verify the function was called with the correct argument
expect(diagnostics.checkInstallation).toHaveBeenCalledWith('/test/project');
});
test('should detect missing files', () => {
// Setup mock response for missing files
diagnostics.checkInstallation.mockReturnValue({
isInstalled: false,
issues: ['Missing files'],
missingFiles: ['node_modules/ctrlshiftleft/vscode-ext-test/generate-tests.js']
});
const result = diagnostics.checkInstallation('/test/project');
expect(result.isInstalled).toBe(false);
expect(result.issues.length).toBeGreaterThan(0);
expect(result.missingFiles.length).toBeGreaterThan(0);
// Verify the function was called
expect(diagnostics.checkInstallation).toHaveBeenCalled();
});
test('should handle command execution errors', () => {
// Setup mock response for command errors
diagnostics.checkInstallation.mockReturnValue({
isInstalled: false,
issues: ['Command execution failed: Command failed'],
suggestion: 'Reinstall the package using "npm install --save-dev ctrlshiftleft"'
});
const result = diagnostics.checkInstallation('/test/project');
expect(result.issues.length).toBeGreaterThan(0);
expect(result.suggestion).toBeTruthy();
// Verify the function was called
expect(diagnostics.checkInstallation).toHaveBeenCalled();
});
});
describe('fixCommonIssues', () => {
test('should not attempt repairs on a healthy installation', () => {
// Setup mock response for no repairs needed
diagnostics.fixCommonIssues.mockReturnValue({
repairAttempted: false,
repairsPerformed: [],
successful: true
});
const result = diagnostics.fixCommonIssues('/test/project');
expect(result.repairAttempted).toBe(false);
expect(result.successful).toBe(true);
// Verify the function was called with the correct argument
expect(diagnostics.fixCommonIssues).toHaveBeenCalledWith('/test/project');
});
test('should create missing directories when needed', () => {
// Setup mock response for successful repairs
diagnostics.fixCommonIssues.mockReturnValue({
repairAttempted: true,
repairsPerformed: [
'Created missing directory: vscode-ext-test',
'Created CLI links'
],
successful: true
});
const result = diagnostics.fixCommonIssues('/test/project');
expect(result.repairAttempted).toBe(true);
expect(result.repairsPerformed.length).toBeGreaterThan(0);
expect(result.successful).toBe(true);
// Verify the function was called
expect(diagnostics.fixCommonIssues).toHaveBeenCalled();
});
test('should try to copy files from global installation', () => {
// Setup mock response for global copy repairs
diagnostics.fixCommonIssues.mockReturnValue({
repairAttempted: true,
repairsPerformed: [
'Copied bin/ctrlshiftleft from global installation',
'Created CLI links'
],
successful: true
});
const result = diagnostics.fixCommonIssues('/test/project');
expect(result.repairAttempted).toBe(true);
expect(result.repairsPerformed.length).toBeGreaterThan(0);
expect(result.successful).toBe(true);
// Verify the function was called
expect(diagnostics.fixCommonIssues).toHaveBeenCalled();
});
test('should handle errors during repair', () => {
// Setup mock response for failed repairs
diagnostics.fixCommonIssues.mockReturnValue({
repairAttempted: true,
repairsPerformed: [
'Created missing directory: vscode-ext-test'
],
successful: false
});
const result = diagnostics.fixCommonIssues('/test/project');
expect(result.repairAttempted).toBe(true);
expect(result.successful).toBe(false);
// Verify the function was called
expect(diagnostics.fixCommonIssues).toHaveBeenCalled();
});
});
describe('checkFrameworkCompatibility', () => {
test('should detect Next.js project', () => {
// Setup mock response for Next.js
diagnostics.checkFrameworkCompatibility.mockReturnValue({
detectedFramework: 'next.js',
isCompatible: true,
optimizationNeeded: false,
issues: []
});
const result = diagnostics.checkFrameworkCompatibility('/test/project');
expect(result.detectedFramework).toBe('next.js');
expect(result.isCompatible).toBe(true);
// Verify the function was called with the correct argument
expect(diagnostics.checkFrameworkCompatibility).toHaveBeenCalledWith('/test/project');
});
test('should detect React project', () => {
// Setup mock response for React
diagnostics.checkFrameworkCompatibility.mockReturnValue({
detectedFramework: 'react',
isCompatible: true,
optimizationNeeded: false,
issues: []
});
const result = diagnostics.checkFrameworkCompatibility('/test/project');
expect(result.detectedFramework).toBe('react');
expect(result.isCompatible).toBe(true);
// Verify the function was called
expect(diagnostics.checkFrameworkCompatibility).toHaveBeenCalled();
});
test('should detect optimization needs', () => {
// Setup mock response for Next.js with optimization needs
diagnostics.checkFrameworkCompatibility.mockReturnValue({
detectedFramework: 'next.js',
isCompatible: true,
optimizationNeeded: true,
issues: ['Missing jest.config.js', 'Missing test directory']
});
const result = diagnostics.checkFrameworkCompatibility('/test/project');
expect(result.detectedFramework).toBe('next.js');
expect(result.optimizationNeeded).toBe(true);
expect(result.issues.length).toBeGreaterThan(0);
// Verify the function was called
expect(diagnostics.checkFrameworkCompatibility).toHaveBeenCalled();
});
});
describe('runComprehensiveScan', () => {
test('should perform all checks and compile results', () => {
// Setup mock response for comprehensive scan
diagnostics.runComprehensiveScan.mockReturnValue({
installation: { isInstalled: true, issues: [] },
framework: {
detectedFramework: 'next.js',
isCompatible: true,
optimizationNeeded: false,
issues: []
},
environment: {
nodeVersion: '16.0.0',
npmVersion: '8.0.0',
hasOpenAIKey: true
},
recommendedFixes: []
});
const result = diagnostics.runComprehensiveScan('/test/project');
expect(result).toHaveProperty('installation');
expect(result).toHaveProperty('framework');
expect(result).toHaveProperty('environment');
expect(result).toHaveProperty('recommendedFixes');
// Verify the function was called with the correct argument
expect(diagnostics.runComprehensiveScan).toHaveBeenCalledWith('/test/project');
});
test('should generate appropriate fix recommendations', () => {
// Setup mock response for scan with issues
diagnostics.runComprehensiveScan.mockReturnValue({
installation: { isInstalled: false, issues: ['Missing files'] },
framework: {
detectedFramework: 'next.js',
isCompatible: true,
optimizationNeeded: true,
issues: ['Missing jest config']
},
environment: {
nodeVersion: '16.0.0',
npmVersion: '8.0.0',
hasOpenAIKey: false
},
recommendedFixes: [
'Run repair to fix installation issues',
'Run framework-specific setup for next.js',
'Set OPENAI_API_KEY environment variable for AI-enhanced features'
]
});
const result = diagnostics.runComprehensiveScan('/test/project');
expect(result.recommendedFixes.length).toBeGreaterThan(1); // Should have multiple recommendations
expect(result.recommendedFixes.some(fix => fix.includes('repair'))).toBe(true);
expect(result.recommendedFixes.some(fix => fix.includes('next.js'))).toBe(true);
expect(result.recommendedFixes.some(fix => fix.includes('OPENAI_API_KEY'))).toBe(true);
// Verify the function was called
expect(diagnostics.runComprehensiveScan).toHaveBeenCalled();
});
});
describe('checkFrameworkSupport', () => {
test('should report full support for Next.js', () => {
// Setup mock response for Next.js support
diagnostics.checkFrameworkSupport.mockReturnValue({
isSupported: true,
level: 'full',
setupCommands: [
'npx ctrlshiftleft-setup-nextjs',
'npm install --save-dev @testing-library/react'
],
notes: [
'Full support for both App Router and Pages Router',
'Specialized path resolution for Next.js imports'
]
});
const result = diagnostics.checkFrameworkSupport('next.js');
expect(result.isSupported).toBe(true);
expect(result.level).toBe('full');
expect(result.setupCommands.length).toBeGreaterThan(0);
expect(result.notes.length).toBeGreaterThan(0);
// Verify the function was called with correct argument
expect(diagnostics.checkFrameworkSupport).toHaveBeenCalledWith('next.js');
});
test('should handle unsupported frameworks gracefully', () => {
// Setup mock response for unsupported framework
diagnostics.checkFrameworkSupport.mockReturnValue({
isSupported: false,
level: 'unsupported',
setupCommands: ['npm install --save-dev ctrlshiftleft'],
notes: ['Not officially supported, but generic testing may work']
});
const result = diagnostics.checkFrameworkSupport('unknown-framework');
expect(result.isSupported).toBe(false);
expect(result.notes.length).toBeGreaterThan(0);
expect(result.setupCommands.length).toBeGreaterThan(0); // Should still provide generic setup
// Verify the function was called with correct argument
expect(diagnostics.checkFrameworkSupport).toHaveBeenCalledWith('unknown-framework');
});
});
});