Skip to main content
Glama
languageServer.test.ts6.78 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { LanguageServerService } from '../../src/languageServerService' import * as fs from 'fs/promises' import * as path from 'path' import { mkdtemp, rm } from 'fs/promises' import { tmpdir } from 'os' describe('LanguageServer Integration Tests', () => { let service: LanguageServerService let testDir: string beforeAll(async () => { // Create a temporary test directory testDir = await mkdtemp(path.join(tmpdir(), 'langserver-test-')) service = new LanguageServerService() // Create a simple TypeScript project structure await fs.mkdir(path.join(testDir, 'src'), { recursive: true }) // Create tsconfig.json await fs.writeFile( path.join(testDir, 'tsconfig.json'), JSON.stringify( { compilerOptions: { target: 'es2020', module: 'commonjs', strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, }, include: ['src/**/*'], }, null, 2 ) ) // Create package.json await fs.writeFile( path.join(testDir, 'package.json'), JSON.stringify( { name: 'test-project', version: '1.0.0', type: 'commonjs', }, null, 2 ) ) // Create a test TypeScript file await fs.writeFile( path.join(testDir, 'src', 'test.ts'), `interface User { id: number; name: string; email: string; } class UserService { private users: User[] = []; addUser(user: User): void { this.users.push(user); } getUser(id: number): User | undefined { return this.users.find(u => u.id === id); } getAllUsers(): User[] { return this.users; } } const service = new UserService(); const testUser: User = { id: 1, name: "Test User", email: "test@example.com" }; service.addUser(testUser); ` ) // Create another file for testing references await fs.writeFile( path.join(testDir, 'src', 'consumer.ts'), `import { UserService } from './test'; const myService = new UserService(); myService.addUser({ id: 2, name: "Another User", email: "another@example.com" }); ` ) }) afterAll(async () => { // Clean up test directory await rm(testDir, { recursive: true, force: true }) }) describe('TypeScript Language Server Operations', () => { it('should initialize and discover the test project', async () => { const result = await service.initialize(testDir, true) expect(result.projects).toHaveLength(1) expect(result.projects[0]?.name).toBe(path.basename(testDir)) expect(result.projects[0]?.type).toBe('node') }) it('should provide diagnostics for TypeScript errors', async () => { // Add a file with errors const errorFile = path.join(testDir, 'src', 'errors.ts') await fs.writeFile( errorFile, `const x: string = 123; // Type error const y = undefined.property; // Runtime error ` ) await service.initialize(testDir, true) const diagnostics = service.getDiagnostics({ file: errorFile }) expect(diagnostics).toHaveLength(1) expect(diagnostics[0]?.diagnostics.length).toBeGreaterThan(0) const errors = diagnostics[0]?.diagnostics.filter((d) => d.severity === 'error') || [] expect(errors.length).toBeGreaterThan(0) }) it('should handle operations on non-initialized service gracefully', () => { const newService = new LanguageServerService() // All operations should return empty/undefined when not initialized expect(newService.getDiagnostics({})).toEqual([]) expect(newService.getCompletions('/test/file.ts', 1, 1)).toEqual({ completions: [] }) expect(newService.getHover('/test/file.ts', 1, 1)).toBeUndefined() expect(newService.getDefinition('/test/file.ts', 1, 1)).toBeUndefined() expect(newService.findReferences('/test/file.ts', 1, 1)).toEqual({ references: [] }) expect(newService.renameSymbol('/test/file.ts', 1, 1, 'newName')).toEqual({ changes: {}, }) expect(newService.getTypeInfo('/test/file.ts', 1, 1)).toBeUndefined() }) it('should provide completions after initialization', async () => { await service.initialize(testDir, true) const testFile = path.join(testDir, 'src', 'test.ts') // We just check that we get some completions back const completions = service.getCompletions(testFile, 30, 1) expect(completions.completions.length).toBeGreaterThan(0) }) it('should find and rename symbols', async () => { await service.initialize(testDir, true) const testFile = path.join(testDir, 'src', 'test.ts') // Try to rename something - the actual behavior depends on TypeScript const renameResult = service.renameSymbol(testFile, 8, 13, 'userList') // We expect some result, even if it's empty expect(renameResult).toBeDefined() expect(renameResult.changes).toBeDefined() }) it('should handle multi-project scenarios', async () => { // Create a second project const project2Dir = path.join(testDir, 'project2') await fs.mkdir(path.join(project2Dir, 'src'), { recursive: true }) await fs.writeFile( path.join(project2Dir, 'tsconfig.json'), JSON.stringify({ compilerOptions: { target: 'es2020', module: 'esnext', }, }) ) await fs.writeFile( path.join(project2Dir, 'package.json'), JSON.stringify({ name: 'project2', type: 'module', }) ) const result = await service.initialize(testDir, true) // Should discover both projects expect(result.projects.length).toBeGreaterThanOrEqual(2) }) }) })

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/currentspace/shortcut_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server