wdyt 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/chat.ts +65 -3
- package/src/context/hints.test.ts +135 -0
- package/src/context/hints.ts +264 -0
- package/src/context/index.ts +48 -0
- package/src/context/references.test.ts +341 -0
- package/src/context/references.ts +232 -0
- package/src/context/rereview.test.ts +135 -0
- package/src/context/rereview.ts +204 -0
- package/src/context/symbols.test.ts +550 -0
- package/src/context/symbols.ts +234 -0
- package/src/flow/index.ts +18 -0
- package/src/flow/specs.test.ts +260 -0
- package/src/flow/specs.ts +255 -0
- package/src/git/diff.test.ts +311 -0
- package/src/git/diff.ts +205 -0
- package/src/integration.test.ts +538 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for symbol extraction module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, test, expect } from "bun:test";
|
|
6
|
+
import {
|
|
7
|
+
extractSymbols,
|
|
8
|
+
extractSymbolsFromFile,
|
|
9
|
+
isSupported,
|
|
10
|
+
getSupportedExtensions,
|
|
11
|
+
type Symbol,
|
|
12
|
+
} from "./symbols";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { writeFile, rm, mkdir } from "fs/promises";
|
|
15
|
+
|
|
16
|
+
// Test fixtures directory
|
|
17
|
+
const FIXTURES_DIR = join(import.meta.dir, "..", "..", ".test-fixtures");
|
|
18
|
+
|
|
19
|
+
describe("Symbol extraction", () => {
|
|
20
|
+
describe("TypeScript/JavaScript", () => {
|
|
21
|
+
test("extracts function declarations", () => {
|
|
22
|
+
const content = `
|
|
23
|
+
function hello() {}
|
|
24
|
+
export function goodbye() {}
|
|
25
|
+
async function asyncFn() {}
|
|
26
|
+
export async function exportAsyncFn() {}
|
|
27
|
+
`;
|
|
28
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
29
|
+
|
|
30
|
+
expect(symbols).toHaveLength(4);
|
|
31
|
+
expect(symbols[0]).toEqual({ name: "hello", type: "function", line: 2 });
|
|
32
|
+
expect(symbols[1]).toEqual({ name: "goodbye", type: "function", line: 3 });
|
|
33
|
+
expect(symbols[2]).toEqual({ name: "asyncFn", type: "function", line: 4 });
|
|
34
|
+
expect(symbols[3]).toEqual({
|
|
35
|
+
name: "exportAsyncFn",
|
|
36
|
+
type: "function",
|
|
37
|
+
line: 5,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("extracts class declarations", () => {
|
|
42
|
+
const content = `
|
|
43
|
+
class MyClass {}
|
|
44
|
+
export class ExportedClass extends BaseClass {}
|
|
45
|
+
`;
|
|
46
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
47
|
+
|
|
48
|
+
expect(symbols).toHaveLength(2);
|
|
49
|
+
expect(symbols[0]).toEqual({ name: "MyClass", type: "class", line: 2 });
|
|
50
|
+
expect(symbols[1]).toEqual({
|
|
51
|
+
name: "ExportedClass",
|
|
52
|
+
type: "class",
|
|
53
|
+
line: 3,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("extracts type and interface declarations", () => {
|
|
58
|
+
const content = `
|
|
59
|
+
type MyType = string;
|
|
60
|
+
export type ExportedType = number;
|
|
61
|
+
interface MyInterface {}
|
|
62
|
+
export interface ExportedInterface {}
|
|
63
|
+
`;
|
|
64
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
65
|
+
|
|
66
|
+
expect(symbols).toHaveLength(4);
|
|
67
|
+
expect(symbols[0]).toEqual({ name: "MyType", type: "type", line: 2 });
|
|
68
|
+
expect(symbols[1]).toEqual({
|
|
69
|
+
name: "ExportedType",
|
|
70
|
+
type: "type",
|
|
71
|
+
line: 3,
|
|
72
|
+
});
|
|
73
|
+
expect(symbols[2]).toEqual({
|
|
74
|
+
name: "MyInterface",
|
|
75
|
+
type: "interface",
|
|
76
|
+
line: 4,
|
|
77
|
+
});
|
|
78
|
+
expect(symbols[3]).toEqual({
|
|
79
|
+
name: "ExportedInterface",
|
|
80
|
+
type: "interface",
|
|
81
|
+
line: 5,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("extracts const declarations", () => {
|
|
86
|
+
const content = `
|
|
87
|
+
const MY_CONST = 42;
|
|
88
|
+
export const EXPORTED_CONST = "hello";
|
|
89
|
+
`;
|
|
90
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
91
|
+
|
|
92
|
+
expect(symbols).toHaveLength(2);
|
|
93
|
+
expect(symbols[0]).toEqual({ name: "MY_CONST", type: "const", line: 2 });
|
|
94
|
+
expect(symbols[1]).toEqual({
|
|
95
|
+
name: "EXPORTED_CONST",
|
|
96
|
+
type: "const",
|
|
97
|
+
line: 3,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("handles .js, .jsx, .tsx, .mjs, .cjs extensions", () => {
|
|
102
|
+
const content = "function test() {}";
|
|
103
|
+
|
|
104
|
+
for (const ext of [".js", ".jsx", ".tsx", ".mjs", ".cjs"]) {
|
|
105
|
+
const symbols = extractSymbols(content, `file${ext}`);
|
|
106
|
+
expect(symbols).toHaveLength(1);
|
|
107
|
+
expect(symbols[0].name).toBe("test");
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("handles mixed TypeScript file", () => {
|
|
112
|
+
const content = `
|
|
113
|
+
import { something } from 'module';
|
|
114
|
+
|
|
115
|
+
export interface Config {
|
|
116
|
+
name: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type Status = 'pending' | 'done';
|
|
120
|
+
|
|
121
|
+
export class Service {
|
|
122
|
+
async init(): Promise<void> {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function main(config: Config): Promise<void> {
|
|
126
|
+
const service = new Service();
|
|
127
|
+
await service.init();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const DEFAULT_CONFIG: Config = { name: 'default' };
|
|
131
|
+
`;
|
|
132
|
+
const symbols = extractSymbols(content, "app.ts");
|
|
133
|
+
|
|
134
|
+
const names = symbols.map((s) => s.name);
|
|
135
|
+
expect(names).toContain("Config");
|
|
136
|
+
expect(names).toContain("Status");
|
|
137
|
+
expect(names).toContain("Service");
|
|
138
|
+
expect(names).toContain("main");
|
|
139
|
+
expect(names).toContain("DEFAULT_CONFIG");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("Python", () => {
|
|
144
|
+
test("extracts function definitions", () => {
|
|
145
|
+
const content = `
|
|
146
|
+
def hello():
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
async def async_hello():
|
|
150
|
+
pass
|
|
151
|
+
`;
|
|
152
|
+
const symbols = extractSymbols(content, "test.py");
|
|
153
|
+
|
|
154
|
+
expect(symbols).toHaveLength(2);
|
|
155
|
+
expect(symbols[0]).toEqual({ name: "hello", type: "function", line: 2 });
|
|
156
|
+
expect(symbols[1]).toEqual({
|
|
157
|
+
name: "async_hello",
|
|
158
|
+
type: "function",
|
|
159
|
+
line: 5,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("extracts class definitions", () => {
|
|
164
|
+
const content = `
|
|
165
|
+
class MyClass:
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
class DerivedClass(BaseClass):
|
|
169
|
+
pass
|
|
170
|
+
`;
|
|
171
|
+
const symbols = extractSymbols(content, "test.py");
|
|
172
|
+
|
|
173
|
+
expect(symbols).toHaveLength(2);
|
|
174
|
+
expect(symbols[0]).toEqual({ name: "MyClass", type: "class", line: 2 });
|
|
175
|
+
expect(symbols[1]).toEqual({
|
|
176
|
+
name: "DerivedClass",
|
|
177
|
+
type: "class",
|
|
178
|
+
line: 5,
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("handles mixed Python file", () => {
|
|
183
|
+
const content = `
|
|
184
|
+
import os
|
|
185
|
+
from typing import Optional
|
|
186
|
+
|
|
187
|
+
class Config:
|
|
188
|
+
def __init__(self):
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
def validate(self):
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
async def main():
|
|
195
|
+
config = Config()
|
|
196
|
+
|
|
197
|
+
def helper():
|
|
198
|
+
pass
|
|
199
|
+
`;
|
|
200
|
+
const symbols = extractSymbols(content, "app.py");
|
|
201
|
+
|
|
202
|
+
const names = symbols.map((s) => s.name);
|
|
203
|
+
expect(names).toContain("Config");
|
|
204
|
+
expect(names).toContain("__init__");
|
|
205
|
+
expect(names).toContain("validate");
|
|
206
|
+
expect(names).toContain("main");
|
|
207
|
+
expect(names).toContain("helper");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe("Go", () => {
|
|
212
|
+
test("extracts function declarations", () => {
|
|
213
|
+
const content = `
|
|
214
|
+
func Hello() {
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
func (s *Service) Method() {
|
|
218
|
+
}
|
|
219
|
+
`;
|
|
220
|
+
const symbols = extractSymbols(content, "test.go");
|
|
221
|
+
|
|
222
|
+
expect(symbols).toHaveLength(2);
|
|
223
|
+
expect(symbols[0]).toEqual({ name: "Hello", type: "function", line: 2 });
|
|
224
|
+
expect(symbols[1]).toEqual({ name: "Method", type: "function", line: 5 });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("extracts struct and interface declarations", () => {
|
|
228
|
+
const content = `
|
|
229
|
+
type Config struct {
|
|
230
|
+
Name string
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
type Handler interface {
|
|
234
|
+
Handle() error
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
237
|
+
const symbols = extractSymbols(content, "test.go");
|
|
238
|
+
|
|
239
|
+
expect(symbols).toHaveLength(2);
|
|
240
|
+
expect(symbols[0]).toEqual({ name: "Config", type: "class", line: 2 });
|
|
241
|
+
expect(symbols[1]).toEqual({
|
|
242
|
+
name: "Handler",
|
|
243
|
+
type: "interface",
|
|
244
|
+
line: 6,
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("handles mixed Go file", () => {
|
|
249
|
+
const content = `
|
|
250
|
+
package main
|
|
251
|
+
|
|
252
|
+
import "fmt"
|
|
253
|
+
|
|
254
|
+
type Server struct {
|
|
255
|
+
Port int
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
type Logger interface {
|
|
259
|
+
Log(msg string)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
func NewServer(port int) *Server {
|
|
263
|
+
return &Server{Port: port}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
func (s *Server) Start() error {
|
|
267
|
+
fmt.Println("Starting server")
|
|
268
|
+
return nil
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
func main() {
|
|
272
|
+
server := NewServer(8080)
|
|
273
|
+
server.Start()
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
276
|
+
const symbols = extractSymbols(content, "main.go");
|
|
277
|
+
|
|
278
|
+
const names = symbols.map((s) => s.name);
|
|
279
|
+
expect(names).toContain("Server");
|
|
280
|
+
expect(names).toContain("Logger");
|
|
281
|
+
expect(names).toContain("NewServer");
|
|
282
|
+
expect(names).toContain("Start");
|
|
283
|
+
expect(names).toContain("main");
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe("Rust", () => {
|
|
288
|
+
test("extracts function declarations", () => {
|
|
289
|
+
const content = `
|
|
290
|
+
fn hello() {
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
pub fn public_hello() {
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
pub async fn async_hello() {
|
|
297
|
+
}
|
|
298
|
+
`;
|
|
299
|
+
const symbols = extractSymbols(content, "test.rs");
|
|
300
|
+
|
|
301
|
+
expect(symbols).toHaveLength(3);
|
|
302
|
+
expect(symbols[0]).toEqual({ name: "hello", type: "function", line: 2 });
|
|
303
|
+
expect(symbols[1]).toEqual({
|
|
304
|
+
name: "public_hello",
|
|
305
|
+
type: "function",
|
|
306
|
+
line: 5,
|
|
307
|
+
});
|
|
308
|
+
expect(symbols[2]).toEqual({
|
|
309
|
+
name: "async_hello",
|
|
310
|
+
type: "function",
|
|
311
|
+
line: 8,
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("extracts struct and trait declarations", () => {
|
|
316
|
+
const content = `
|
|
317
|
+
struct Config {
|
|
318
|
+
name: String,
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub struct PublicConfig {
|
|
322
|
+
name: String,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
trait Handler {
|
|
326
|
+
fn handle(&self);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
pub trait PublicHandler {
|
|
330
|
+
fn handle(&self);
|
|
331
|
+
}
|
|
332
|
+
`;
|
|
333
|
+
const symbols = extractSymbols(content, "test.rs");
|
|
334
|
+
|
|
335
|
+
const structs = symbols.filter((s) => s.type === "class");
|
|
336
|
+
const traits = symbols.filter((s) => s.type === "interface");
|
|
337
|
+
|
|
338
|
+
expect(structs).toHaveLength(2);
|
|
339
|
+
expect(traits).toHaveLength(2);
|
|
340
|
+
expect(structs.map((s) => s.name)).toContain("Config");
|
|
341
|
+
expect(structs.map((s) => s.name)).toContain("PublicConfig");
|
|
342
|
+
expect(traits.map((s) => s.name)).toContain("Handler");
|
|
343
|
+
expect(traits.map((s) => s.name)).toContain("PublicHandler");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("extracts type alias declarations", () => {
|
|
347
|
+
const content = `
|
|
348
|
+
type Result<T> = std::result::Result<T, Error>;
|
|
349
|
+
pub type BoxedError = Box<dyn std::error::Error>;
|
|
350
|
+
`;
|
|
351
|
+
const symbols = extractSymbols(content, "test.rs");
|
|
352
|
+
|
|
353
|
+
const types = symbols.filter((s) => s.type === "type");
|
|
354
|
+
expect(types).toHaveLength(2);
|
|
355
|
+
expect(types.map((s) => s.name)).toContain("Result");
|
|
356
|
+
expect(types.map((s) => s.name)).toContain("BoxedError");
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("handles mixed Rust file", () => {
|
|
360
|
+
const content = `
|
|
361
|
+
use std::io;
|
|
362
|
+
|
|
363
|
+
pub struct Server {
|
|
364
|
+
port: u16,
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
pub trait Logger {
|
|
368
|
+
fn log(&self, msg: &str);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
impl Server {
|
|
372
|
+
pub fn new(port: u16) -> Self {
|
|
373
|
+
Server { port }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
pub async fn start(&self) -> io::Result<()> {
|
|
377
|
+
Ok(())
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
fn main() {
|
|
382
|
+
let server = Server::new(8080);
|
|
383
|
+
}
|
|
384
|
+
`;
|
|
385
|
+
const symbols = extractSymbols(content, "main.rs");
|
|
386
|
+
|
|
387
|
+
const names = symbols.map((s) => s.name);
|
|
388
|
+
expect(names).toContain("Server");
|
|
389
|
+
expect(names).toContain("Logger");
|
|
390
|
+
expect(names).toContain("new");
|
|
391
|
+
expect(names).toContain("start");
|
|
392
|
+
expect(names).toContain("main");
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
describe("Unsupported languages", () => {
|
|
397
|
+
test("returns empty array for unsupported extensions", () => {
|
|
398
|
+
const content = "some content";
|
|
399
|
+
|
|
400
|
+
expect(extractSymbols(content, "file.c")).toEqual([]);
|
|
401
|
+
expect(extractSymbols(content, "file.cpp")).toEqual([]);
|
|
402
|
+
expect(extractSymbols(content, "file.java")).toEqual([]);
|
|
403
|
+
expect(extractSymbols(content, "file.txt")).toEqual([]);
|
|
404
|
+
expect(extractSymbols(content, "file")).toEqual([]);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
describe("Line numbers", () => {
|
|
409
|
+
test("correctly calculates line numbers", () => {
|
|
410
|
+
const content = `line 1
|
|
411
|
+
line 2
|
|
412
|
+
function onLine3() {}
|
|
413
|
+
line 4
|
|
414
|
+
function onLine5() {}`;
|
|
415
|
+
|
|
416
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
417
|
+
|
|
418
|
+
expect(symbols[0].line).toBe(3);
|
|
419
|
+
expect(symbols[1].line).toBe(5);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("handles Windows line endings", () => {
|
|
423
|
+
const content = "line 1\r\nline 2\r\nfunction onLine3() {}";
|
|
424
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
425
|
+
|
|
426
|
+
// Note: \r\n counts as 2 chars but only 1 newline for line counting
|
|
427
|
+
expect(symbols[0].line).toBe(3);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe("isSupported", () => {
|
|
432
|
+
test("returns true for supported extensions", () => {
|
|
433
|
+
expect(isSupported("file.ts")).toBe(true);
|
|
434
|
+
expect(isSupported("file.tsx")).toBe(true);
|
|
435
|
+
expect(isSupported("file.js")).toBe(true);
|
|
436
|
+
expect(isSupported("file.jsx")).toBe(true);
|
|
437
|
+
expect(isSupported("file.py")).toBe(true);
|
|
438
|
+
expect(isSupported("file.go")).toBe(true);
|
|
439
|
+
expect(isSupported("file.rs")).toBe(true);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("returns false for unsupported extensions", () => {
|
|
443
|
+
expect(isSupported("file.c")).toBe(false);
|
|
444
|
+
expect(isSupported("file.java")).toBe(false);
|
|
445
|
+
expect(isSupported("file.txt")).toBe(false);
|
|
446
|
+
expect(isSupported("file")).toBe(false);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("handles case-insensitive extensions", () => {
|
|
450
|
+
expect(isSupported("file.TS")).toBe(true);
|
|
451
|
+
expect(isSupported("file.PY")).toBe(true);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe("getSupportedExtensions", () => {
|
|
456
|
+
test("returns all supported extensions", () => {
|
|
457
|
+
const extensions = getSupportedExtensions();
|
|
458
|
+
|
|
459
|
+
expect(extensions).toContain(".ts");
|
|
460
|
+
expect(extensions).toContain(".tsx");
|
|
461
|
+
expect(extensions).toContain(".js");
|
|
462
|
+
expect(extensions).toContain(".jsx");
|
|
463
|
+
expect(extensions).toContain(".py");
|
|
464
|
+
expect(extensions).toContain(".go");
|
|
465
|
+
expect(extensions).toContain(".rs");
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
describe("extractSymbolsFromFile", () => {
|
|
470
|
+
test("extracts symbols from actual file", async () => {
|
|
471
|
+
// Create test fixtures directory
|
|
472
|
+
await mkdir(FIXTURES_DIR, { recursive: true });
|
|
473
|
+
|
|
474
|
+
const testFile = join(FIXTURES_DIR, "test.ts");
|
|
475
|
+
await writeFile(
|
|
476
|
+
testFile,
|
|
477
|
+
`
|
|
478
|
+
export function greet(name: string): string {
|
|
479
|
+
return \`Hello, \${name}!\`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export class Greeter {
|
|
483
|
+
constructor(private name: string) {}
|
|
484
|
+
greet(): string {
|
|
485
|
+
return \`Hello, \${this.name}!\`;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
`
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
const symbols = await extractSymbolsFromFile(testFile);
|
|
493
|
+
|
|
494
|
+
expect(symbols.length).toBeGreaterThan(0);
|
|
495
|
+
expect(symbols.map((s) => s.name)).toContain("greet");
|
|
496
|
+
expect(symbols.map((s) => s.name)).toContain("Greeter");
|
|
497
|
+
} finally {
|
|
498
|
+
await rm(FIXTURES_DIR, { recursive: true, force: true });
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test("returns empty array for non-existent file", async () => {
|
|
503
|
+
const symbols = await extractSymbolsFromFile("/nonexistent/file.ts");
|
|
504
|
+
expect(symbols).toEqual([]);
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
describe("Edge cases", () => {
|
|
509
|
+
test("handles empty content", () => {
|
|
510
|
+
expect(extractSymbols("", "test.ts")).toEqual([]);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test("handles content with no symbols", () => {
|
|
514
|
+
const content = `
|
|
515
|
+
// Just comments
|
|
516
|
+
/* and more comments */
|
|
517
|
+
import { something } from 'module';
|
|
518
|
+
`;
|
|
519
|
+
expect(extractSymbols(content, "test.ts")).toEqual([]);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("symbols are sorted by line number", () => {
|
|
523
|
+
const content = `
|
|
524
|
+
const z = 1;
|
|
525
|
+
function a() {}
|
|
526
|
+
class M {}
|
|
527
|
+
`;
|
|
528
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
529
|
+
|
|
530
|
+
expect(symbols[0].line).toBeLessThan(symbols[1].line);
|
|
531
|
+
expect(symbols[1].line).toBeLessThan(symbols[2].line);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
test("handles deeply nested content", () => {
|
|
535
|
+
const content = `
|
|
536
|
+
export class Outer {
|
|
537
|
+
inner = class Inner {
|
|
538
|
+
method() {
|
|
539
|
+
const nested = () => {};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
`;
|
|
544
|
+
const symbols = extractSymbols(content, "test.ts");
|
|
545
|
+
|
|
546
|
+
// Should at least find Outer class
|
|
547
|
+
expect(symbols.map((s) => s.name)).toContain("Outer");
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
});
|