Skip to content

Commit 42ed3d8

Browse files
unhappychoiceclaude
andcommitted
test: add comprehensive JavaScript language tests
Add integration tests for JavaScript code extraction covering: - Function declarations and arrow functions - Class declarations and methods - Export statements and modules - Object method assignments - Mixed JavaScript patterns Tests currently failing due to tree-sitter pattern issues. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f6ef381 commit 42ed3d8

File tree

2 files changed

+352
-0
lines changed

2 files changed

+352
-0
lines changed
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
use gittype::extractor::{ChunkType, CodeExtractor, ExtractionOptions};
2+
use std::fs;
3+
use tempfile::TempDir;
4+
5+
#[test]
6+
fn test_javascript_function_extraction() {
7+
let temp_dir = TempDir::new().unwrap();
8+
let file_path = temp_dir.path().join("test.js");
9+
10+
let js_code = r#"
11+
function calculateSum(a, b) {
12+
return a + b;
13+
}
14+
15+
function greetUser(name) {
16+
return `Hello, ${name}!`;
17+
}
18+
19+
async function fetchUserData(userId) {
20+
const response = await fetch(`/api/users/${userId}`);
21+
return response.json();
22+
}
23+
"#;
24+
fs::write(&file_path, js_code).unwrap();
25+
26+
let mut extractor = CodeExtractor::new().unwrap();
27+
let chunks = extractor
28+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
29+
.unwrap();
30+
31+
assert_eq!(chunks.len(), 3);
32+
33+
let function_chunks: Vec<_> = chunks
34+
.iter()
35+
.filter(|c| matches!(c.chunk_type, ChunkType::Function))
36+
.collect();
37+
assert_eq!(function_chunks.len(), 3);
38+
39+
let function_names: Vec<&String> = function_chunks.iter().map(|c| &c.name).collect();
40+
assert!(function_names.contains(&&"calculateSum".to_string()));
41+
assert!(function_names.contains(&&"greetUser".to_string()));
42+
assert!(function_names.contains(&&"fetchUserData".to_string()));
43+
}
44+
45+
#[test]
46+
fn test_javascript_arrow_function_extraction() {
47+
let temp_dir = TempDir::new().unwrap();
48+
let file_path = temp_dir.path().join("test.js");
49+
50+
let js_code = r#"
51+
const add = (a, b) => a + b;
52+
53+
const multiply = (x, y) => {
54+
return x * y;
55+
};
56+
57+
const processData = async (data) => {
58+
const processed = data.map(item => item.value);
59+
return processed;
60+
};
61+
"#;
62+
fs::write(&file_path, js_code).unwrap();
63+
64+
let mut extractor = CodeExtractor::new().unwrap();
65+
let chunks = extractor
66+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
67+
.unwrap();
68+
69+
assert_eq!(chunks.len(), 3);
70+
71+
let function_chunks: Vec<_> = chunks
72+
.iter()
73+
.filter(|c| matches!(c.chunk_type, ChunkType::Function))
74+
.collect();
75+
assert_eq!(function_chunks.len(), 3);
76+
77+
let function_names: Vec<&String> = function_chunks.iter().map(|c| &c.name).collect();
78+
assert!(function_names.contains(&&"add".to_string()));
79+
assert!(function_names.contains(&&"multiply".to_string()));
80+
assert!(function_names.contains(&&"processData".to_string()));
81+
}
82+
83+
#[test]
84+
fn test_javascript_class_extraction() {
85+
let temp_dir = TempDir::new().unwrap();
86+
let file_path = temp_dir.path().join("test.js");
87+
88+
let js_code = r#"
89+
class UserManager {
90+
constructor(apiKey) {
91+
this.apiKey = apiKey;
92+
this.users = [];
93+
}
94+
95+
async loadUsers() {
96+
try {
97+
const response = await fetchData('/users', {
98+
headers: { 'Authorization': `Bearer ${this.apiKey}` }
99+
});
100+
this.users = response.data;
101+
return this.users;
102+
} catch (error) {
103+
console.error('Failed to load users:', error);
104+
throw error;
105+
}
106+
}
107+
108+
findUser = (id) => {
109+
return this.users.find(user => user.id === id);
110+
};
111+
}
112+
113+
class EventEmitter {
114+
constructor() {
115+
this.events = {};
116+
}
117+
118+
on(eventName, callback) {
119+
if (!this.events[eventName]) {
120+
this.events[eventName] = [];
121+
}
122+
this.events[eventName].push(callback);
123+
}
124+
125+
emit(eventName, data) {
126+
if (this.events[eventName]) {
127+
this.events[eventName].forEach(callback => callback(data));
128+
}
129+
}
130+
}
131+
"#;
132+
fs::write(&file_path, js_code).unwrap();
133+
134+
let mut extractor = CodeExtractor::new().unwrap();
135+
let chunks = extractor
136+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
137+
.unwrap();
138+
139+
// Should find 2 classes + their methods
140+
let class_chunks: Vec<_> = chunks
141+
.iter()
142+
.filter(|c| matches!(c.chunk_type, ChunkType::Class))
143+
.collect();
144+
assert_eq!(class_chunks.len(), 2);
145+
146+
let class_names: Vec<&String> = class_chunks.iter().map(|c| &c.name).collect();
147+
assert!(class_names.contains(&&"UserManager".to_string()));
148+
assert!(class_names.contains(&&"EventEmitter".to_string()));
149+
}
150+
151+
#[test]
152+
fn test_javascript_export_extraction() {
153+
let temp_dir = TempDir::new().unwrap();
154+
let file_path = temp_dir.path().join("test.js");
155+
156+
let js_code = r#"
157+
export function validateEmail(email) {
158+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
159+
return emailRegex.test(email);
160+
}
161+
162+
export class ApiClient {
163+
constructor(baseUrl) {
164+
this.baseUrl = baseUrl;
165+
}
166+
167+
async get(endpoint) {
168+
const response = await fetch(`${this.baseUrl}${endpoint}`);
169+
return response.json();
170+
}
171+
}
172+
173+
export const config = {
174+
apiUrl: process.env.API_URL || 'http://localhost:3000',
175+
timeout: 5000
176+
};
177+
178+
export default class UserService {
179+
constructor(apiClient) {
180+
this.apiClient = apiClient;
181+
}
182+
183+
async getUser(id) {
184+
return this.apiClient.get(`/users/${id}`);
185+
}
186+
}
187+
"#;
188+
fs::write(&file_path, js_code).unwrap();
189+
190+
let mut extractor = CodeExtractor::new().unwrap();
191+
let chunks = extractor
192+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
193+
.unwrap();
194+
195+
// Should find exported functions and classes
196+
let function_chunks: Vec<_> = chunks
197+
.iter()
198+
.filter(|c| matches!(c.chunk_type, ChunkType::Function))
199+
.collect();
200+
let class_chunks: Vec<_> = chunks
201+
.iter()
202+
.filter(|c| matches!(c.chunk_type, ChunkType::Class))
203+
.collect();
204+
205+
assert!(!function_chunks.is_empty());
206+
assert!(!class_chunks.is_empty());
207+
208+
let all_names: Vec<&String> = chunks.iter().map(|c| &c.name).collect();
209+
assert!(all_names.contains(&&"validateEmail".to_string()));
210+
assert!(all_names.contains(&&"ApiClient".to_string()));
211+
assert!(all_names.contains(&&"UserService".to_string()));
212+
}
213+
214+
#[test]
215+
fn test_javascript_object_method_extraction() {
216+
let temp_dir = TempDir::new().unwrap();
217+
let file_path = temp_dir.path().join("test.js");
218+
219+
let js_code = r#"
220+
const utils = {
221+
formatDate: (date) => {
222+
return date.toLocaleDateString();
223+
},
224+
225+
calculateAge: function(birthDate) {
226+
const today = new Date();
227+
return today.getFullYear() - birthDate.getFullYear();
228+
}
229+
};
230+
231+
const eventHandlers = {
232+
handleClick: (event) => {
233+
console.log('Button clicked:', event.target);
234+
},
235+
236+
handleSubmit: async function(formData) {
237+
try {
238+
const response = await fetch('/api/submit', {
239+
method: 'POST',
240+
body: formData
241+
});
242+
return response.json();
243+
} catch (error) {
244+
console.error('Submit failed:', error);
245+
}
246+
}
247+
};
248+
"#;
249+
fs::write(&file_path, js_code).unwrap();
250+
251+
let mut extractor = CodeExtractor::new().unwrap();
252+
let chunks = extractor
253+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
254+
.unwrap();
255+
256+
// Should find method assignments
257+
let method_chunks: Vec<_> = chunks
258+
.iter()
259+
.filter(|c| matches!(c.chunk_type, ChunkType::Method))
260+
.collect();
261+
262+
if !method_chunks.is_empty() {
263+
let method_names: Vec<&String> = method_chunks.iter().map(|c| &c.name).collect();
264+
// These might be detected as methods if the parser can handle object property assignments
265+
println!("Found methods: {:?}", method_names);
266+
}
267+
}
268+
269+
#[test]
270+
fn test_javascript_mixed_patterns() {
271+
let temp_dir = TempDir::new().unwrap();
272+
let file_path = temp_dir.path().join("test.js");
273+
274+
let js_code = r#"
275+
import { fetchData } from './api.js';
276+
277+
class UserManager {
278+
constructor(apiKey) {
279+
this.apiKey = apiKey;
280+
this.users = [];
281+
}
282+
283+
async loadUsers() {
284+
try {
285+
const response = await fetchData('/users', {
286+
headers: { 'Authorization': `Bearer ${this.apiKey}` }
287+
});
288+
this.users = response.data;
289+
return this.users;
290+
} catch (error) {
291+
console.error('Failed to load users:', error);
292+
throw error;
293+
}
294+
}
295+
296+
findUser = (id) => {
297+
return this.users.find(user => user.id === id);
298+
};
299+
}
300+
301+
function processUsers(users) {
302+
return users.map(user => ({
303+
...user,
304+
displayName: `${user.firstName} ${user.lastName}`
305+
}));
306+
}
307+
308+
const filterActiveUsers = (users) => {
309+
return users.filter(user => user.isActive);
310+
};
311+
312+
const userService = {
313+
async createUser(userData) {
314+
const response = await fetch('/api/users', {
315+
method: 'POST',
316+
headers: { 'Content-Type': 'application/json' },
317+
body: JSON.stringify(userData)
318+
});
319+
return response.json();
320+
}
321+
};
322+
323+
export default UserManager;
324+
"#;
325+
fs::write(&file_path, js_code).unwrap();
326+
327+
let mut extractor = CodeExtractor::new().unwrap();
328+
let chunks = extractor
329+
.extract_chunks(temp_dir.path(), ExtractionOptions::default())
330+
.unwrap();
331+
332+
// Should find at least the class and functions
333+
assert!(!chunks.is_empty());
334+
335+
let class_count = chunks
336+
.iter()
337+
.filter(|c| matches!(c.chunk_type, ChunkType::Class))
338+
.count();
339+
let function_count = chunks
340+
.iter()
341+
.filter(|c| matches!(c.chunk_type, ChunkType::Function))
342+
.count();
343+
344+
assert!(class_count >= 1, "Should find at least 1 class");
345+
assert!(function_count >= 2, "Should find at least 2 functions");
346+
347+
let chunk_names: Vec<&String> = chunks.iter().map(|c| &c.name).collect();
348+
assert!(chunk_names.contains(&&"UserManager".to_string()));
349+
assert!(chunk_names.contains(&&"processUsers".to_string()));
350+
assert!(chunk_names.contains(&&"filterActiveUsers".to_string()));
351+
}

tests/integration/languages/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod go;
22
pub mod java;
3+
pub mod javascript;
34
pub mod kotlin;
45
pub mod php;
56
pub mod python;

0 commit comments

Comments
 (0)