diff --git a/tests/projectTypeDetector.test.ts b/tests/projectTypeDetector.test.ts new file mode 100644 index 0000000..8f05181 --- /dev/null +++ b/tests/projectTypeDetector.test.ts @@ -0,0 +1,205 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { ProjectTypeDetector } from '../src/analyzers/projectTypeDetector'; + +describe('ProjectTypeDetector', () => { + let detector: ProjectTypeDetector; + let testDir: string; + + beforeEach(async () => { + detector = new ProjectTypeDetector(); + testDir = path.join(__dirname, 'test-projects', `test-${Date.now()}`); + await fs.promises.mkdir(testDir, { recursive: true }); + }); + + afterEach(async () => { + if (await fs.promises.stat(testDir).catch(() => null)) { + await fs.promises.rm(testDir, { recursive: true }); + } + }); + + describe('detect', () => { + it('should detect TypeScript project with tsconfig.json', async () => { + const tsconfig = { compilerOptions: { target: 'ES2020' } }; + await fs.promises.writeFile( + path.join(testDir, 'tsconfig.json'), + JSON.stringify(tsconfig) + ); + await fs.promises.writeFile( + path.join(testDir, 'index.ts'), + 'const x: string = "hello";' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('TypeScript'); + expect(result.primaryLanguage).toBe('TypeScript'); + }); + + it('should detect JavaScript project with package.json', async () => { + const packageJson = { dependencies: { express: '^4.0.0' } }; + await fs.promises.writeFile( + path.join(testDir, 'package.json'), + JSON.stringify(packageJson) + ); + await fs.promises.writeFile( + path.join(testDir, 'index.js'), + 'const express = require("express");' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('JavaScript'); + expect(result.primaryLanguage).toBe('JavaScript'); + }); + + it('should detect Python project with requirements.txt', async () => { + await fs.promises.writeFile( + path.join(testDir, 'requirements.txt'), + 'django>=3.0.0\nflask>=1.0.0' + ); + await fs.promises.writeFile( + path.join(testDir, 'main.py'), + 'import django\nfrom flask import Flask' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('Python'); + }); + + it('should detect Go project with go.mod', async () => { + await fs.promises.writeFile( + path.join(testDir, 'go.mod'), + 'module example.com/mymodule\n\ngo 1.21\n\nrequire github.com/gin-gonic/gin v1.9.0' + ); + await fs.promises.writeFile( + path.join(testDir, 'main.go'), + 'package main\nimport "fmt"' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('Go'); + expect(result.frameworks).toContain('Gin'); + }); + + it('should detect Rust project with Cargo.toml', async () => { + await fs.promises.writeFile( + path.join(testDir, 'Cargo.toml'), + '[package]\nname = "myproject"\nversion = "0.1.0"\n\n[dependencies]\nserde = { version = "1.0", features = ["derive"] }' + ); + await fs.promises.writeFile( + path.join(testDir, 'main.rs'), + 'fn main() { println!("Hello"); }' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('Rust'); + expect(result.buildTools).toContain('cargo'); + }); + + it('should detect React framework from package.json', async () => { + const packageJson = { + dependencies: { react: '^18.0.0', 'react-dom': '^18.0.0' }, + devDependencies: { '@types/react': '^18.0.0' } + }; + await fs.promises.writeFile( + path.join(testDir, 'package.json'), + JSON.stringify(packageJson) + ); + await fs.promises.writeFile( + path.join(testDir, 'App.tsx'), + 'import React from "react";' + ); + + const result = await detector.detect(testDir); + + expect(result.languages).toContain('TypeScript'); + expect(result.frameworks).toContain('React'); + }); + + it('should detect Next.js framework', async () => { + const packageJson = { + dependencies: { + next: '^14.0.0', + react: '^18.0.0', + 'react-dom': '^18.0.0' + } + }; + await fs.promises.writeFile( + path.join(testDir, 'package.json'), + JSON.stringify(packageJson) + ); + await fs.promises.writeFile( + path.join(testDir, 'next.config.js'), + 'module.exports = {}' + ); + await fs.promises.mkdir(path.join(testDir, 'pages'), { recursive: true }); + await fs.promises.writeFile( + path.join(testDir, 'pages', 'index.tsx'), + 'export default function() { return null; }' + ); + + const result = await detector.detect(testDir); + + expect(result.frameworks).toContain('Next.js'); + }); + + it('should detect Django framework', async () => { + await fs.promises.writeFile( + path.join(testDir, 'requirements.txt'), + 'django>=4.0.0\ndjangorestframework>=3.0.0' + ); + + const result = await detector.detect(testDir); + + expect(result.frameworks).toContain('Django'); + }); + + it('should detect FastAPI framework', async () => { + await fs.promises.writeFile( + path.join(testDir, 'requirements.txt'), + 'fastapi>=0.100.0\nuvicorn>=0.23.0' + ); + + const result = await detector.detect(testDir); + + expect(result.frameworks).toContain('FastAPI'); + }); + + it('should detect build tools from config files', async () => { + await fs.promises.writeFile( + path.join(testDir, 'package.json'), + JSON.stringify({ dependencies: {} }) + ); + await fs.promises.writeFile(path.join(testDir, 'Makefile'), 'all:\n\techo "build"'); + + const result = await detector.detect(testDir); + + expect(result.buildTools).toContain('npm'); + expect(result.buildTools).toContain('make'); + }); + + it('should detect multiple languages in same project', async () => { + await fs.promises.writeFile( + path.join(testDir, 'package.json'), + JSON.stringify({ dependencies: {} }) + ); + await fs.promises.writeFile(path.join(testDir, 'index.js'), 'console.log("js");'); + await fs.promises.writeFile(path.join(testDir, 'main.py'), 'print("python")'); + + const result = await detector.detect(testDir); + + expect(result.languages.length).toBeGreaterThan(1); + }); + + it('should return Unknown for empty directory', async () => { + const result = await detector.detect(testDir); + + expect(result.primaryLanguage).toBe('Unknown'); + expect(result.languages).toHaveLength(0); + }); + }); +});