Skip to content

Commit f81ebf9

Browse files
committed
feat: add tests for collect utils
1 parent 528ecc5 commit f81ebf9

File tree

2 files changed

+566
-0
lines changed

2 files changed

+566
-0
lines changed
Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
import { collectSourceFiles, FileMap, CollectOptions } from '../src/collect';
2+
3+
describe('collectSourceFiles', () => {
4+
describe('basic functionality', () => {
5+
it('should collect a single file with no imports', () => {
6+
const files = [{ path: 'main.ts', content: 'const x = 1;' }];
7+
8+
const result = collectSourceFiles('main.ts', files);
9+
10+
expect(result).toEqual({
11+
'main.ts': 'const x = 1;',
12+
});
13+
});
14+
15+
it('should return empty object when entry file not found', () => {
16+
const files = [{ path: 'other.ts', content: 'const x = 1;' }];
17+
18+
const result = collectSourceFiles('main.ts', files);
19+
20+
expect(result).toEqual({});
21+
});
22+
});
23+
24+
describe('import following', () => {
25+
it('should follow import statements', () => {
26+
const files = [
27+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
28+
{ path: 'helper.ts', content: 'export const foo = 1;' },
29+
];
30+
31+
const result = collectSourceFiles('main.ts', files);
32+
33+
expect(result).toEqual({
34+
'main.ts': 'import { foo } from "./helper";',
35+
'helper.ts': 'export const foo = 1;',
36+
});
37+
});
38+
39+
it('should follow nested imports', () => {
40+
const files = [
41+
{ path: 'main.ts', content: 'import { foo } from "./utils/helper";' },
42+
{
43+
path: 'utils/helper.ts',
44+
content: 'import { bar } from "./shared"; export const foo = bar;',
45+
},
46+
{ path: 'utils/shared.ts', content: 'export const bar = 1;' },
47+
];
48+
49+
const result = collectSourceFiles('main.ts', files);
50+
51+
expect(result).toEqual({
52+
'main.ts': 'import { foo } from "./utils/helper";',
53+
'utils/helper.ts': 'import { bar } from "./shared"; export const foo = bar;',
54+
'utils/shared.ts': 'export const bar = 1;',
55+
});
56+
});
57+
58+
it('should handle relative imports with ../', () => {
59+
const files = [
60+
{ path: 'src/main.ts', content: 'import { foo } from "../utils/helper";' },
61+
{ path: 'utils/helper.ts', content: 'export const foo = 1;' },
62+
];
63+
64+
const result = collectSourceFiles('src/main.ts', files);
65+
66+
expect(result).toEqual({
67+
'src/main.ts': 'import { foo } from "../utils/helper";',
68+
'utils/helper.ts': 'export const foo = 1;',
69+
});
70+
});
71+
});
72+
73+
describe('import patterns', () => {
74+
it('should handle different import syntaxes', () => {
75+
const files = [
76+
{
77+
path: 'main.ts',
78+
content: `
79+
import { foo } from './helper1';
80+
import * as helper2 from "./helper2";
81+
import helper3, { bar } from './helper3';
82+
import('./helper4');
83+
const helper5 = require('./helper5');
84+
export { baz } from './helper6';
85+
`,
86+
},
87+
{ path: 'helper1.ts', content: 'export const foo = 1;' },
88+
{ path: 'helper2.ts', content: 'export const x = 1;' },
89+
{ path: 'helper3.ts', content: 'export const bar = 1; export default 1;' },
90+
{ path: 'helper4.ts', content: 'export const y = 1;' },
91+
{ path: 'helper5.ts', content: 'module.exports = { z: 1 };' },
92+
{ path: 'helper6.ts', content: 'export const baz = 1;' },
93+
];
94+
95+
const result = collectSourceFiles('main.ts', files);
96+
const keys = Object.keys(result);
97+
98+
expect(keys).toHaveLength(7);
99+
expect(keys.includes('main.ts')).toBe(true);
100+
expect(keys.includes('helper1.ts')).toBe(true);
101+
expect(keys.includes('helper2.ts')).toBe(true);
102+
expect(keys.includes('helper3.ts')).toBe(true);
103+
expect(keys.includes('helper4.ts')).toBe(true);
104+
expect(keys.includes('helper5.ts')).toBe(true);
105+
expect(keys.includes('helper6.ts')).toBe(true);
106+
});
107+
108+
it('should ignore external package imports', () => {
109+
const files = [
110+
{
111+
path: 'main.ts',
112+
content: `
113+
import React from 'react';
114+
import { lodash } from 'lodash';
115+
import { foo } from './helper';
116+
`,
117+
},
118+
{ path: 'helper.ts', content: 'export const foo = 1;' },
119+
];
120+
121+
const result = collectSourceFiles('main.ts', files);
122+
const keys = Object.keys(result);
123+
124+
expect(keys).toHaveLength(2);
125+
expect(keys.includes('main.ts')).toBe(true);
126+
expect(keys.includes('helper.ts')).toBe(true);
127+
expect(result['helper.ts']).toBe('export const foo = 1;');
128+
});
129+
});
130+
131+
describe('extension resolution', () => {
132+
it('should resolve imports without extensions using default extensions', () => {
133+
const files = [
134+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
135+
{ path: 'helper.ts', content: 'export const foo = 1;' },
136+
];
137+
138+
const result = collectSourceFiles('main.ts', files);
139+
140+
expect(result).toEqual({
141+
'main.ts': 'import { foo } from "./helper";',
142+
'helper.ts': 'export const foo = 1;',
143+
});
144+
});
145+
146+
it('should try multiple extensions in order', () => {
147+
const files = [
148+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
149+
{ path: 'helper.jsx', content: 'export const foo = 1;' },
150+
];
151+
152+
const result = collectSourceFiles('main.ts', files);
153+
154+
expect(result).toEqual({
155+
'main.ts': 'import { foo } from "./helper";',
156+
'helper.jsx': 'export const foo = 1;',
157+
});
158+
});
159+
160+
it('should use custom extensions when provided', () => {
161+
const files = [
162+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
163+
{ path: 'helper.custom', content: 'export const foo = 1;' },
164+
];
165+
166+
const options: CollectOptions = {
167+
extensions: ['.custom', '.ts'],
168+
};
169+
170+
const result = collectSourceFiles('main.ts', files, options);
171+
172+
expect(result).toEqual({
173+
'main.ts': 'import { foo } from "./helper";',
174+
'helper.custom': 'export const foo = 1;',
175+
});
176+
});
177+
178+
it('should prefer exact path match over extension resolution', () => {
179+
const files = [
180+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
181+
{ path: 'helper', content: 'export const foo = "exact";' },
182+
{ path: 'helper.ts', content: 'export const foo = "with-ext";' },
183+
];
184+
185+
const result = collectSourceFiles('main.ts', files);
186+
187+
expect(result['helper']).toBe('export const foo = "exact";');
188+
});
189+
});
190+
191+
describe('circular dependencies', () => {
192+
it('should handle circular dependencies without infinite loop', () => {
193+
const files = [
194+
{ path: 'a.ts', content: 'import { b } from "./b"; export const a = 1;' },
195+
{ path: 'b.ts', content: 'import { a } from "./a"; export const b = 2;' },
196+
];
197+
198+
const result = collectSourceFiles('a.ts', files);
199+
200+
expect(result).toEqual({
201+
'a.ts': 'import { b } from "./b"; export const a = 1;',
202+
'b.ts': 'import { a } from "./a"; export const b = 2;',
203+
});
204+
});
205+
206+
it('should handle self-imports', () => {
207+
const files = [
208+
{ path: 'main.ts', content: 'import { foo } from "./main"; export const foo = 1;' },
209+
];
210+
211+
const result = collectSourceFiles('main.ts', files);
212+
213+
expect(result).toEqual({
214+
'main.ts': 'import { foo } from "./main"; export const foo = 1;',
215+
});
216+
});
217+
});
218+
219+
describe('options', () => {
220+
it('should exclude content when includeContent is false', () => {
221+
const files = [
222+
{ path: 'main.ts', content: 'import { foo } from "./helper";' },
223+
{ path: 'helper.ts', content: 'export const foo = 1;' },
224+
];
225+
226+
const options: CollectOptions = {
227+
includeContent: false,
228+
};
229+
230+
const result = collectSourceFiles('main.ts', files, options);
231+
232+
expect(result).toEqual({
233+
'main.ts': '',
234+
'helper.ts': '',
235+
});
236+
});
237+
238+
it('should use default options when not provided', () => {
239+
const files = [{ path: 'main.ts', content: 'const x = 1;' }];
240+
241+
const result1 = collectSourceFiles('main.ts', files);
242+
const result2 = collectSourceFiles('main.ts', files, {});
243+
244+
expect(result1).toEqual(result2);
245+
});
246+
});
247+
248+
describe('edge cases', () => {
249+
it('should handle empty file content', () => {
250+
const files = [{ path: 'main.ts', content: '' }];
251+
252+
const result = collectSourceFiles('main.ts', files);
253+
254+
expect(result).toEqual({
255+
'main.ts': '',
256+
});
257+
});
258+
259+
it('should handle files with only comments', () => {
260+
const files = [
261+
{ path: 'main.ts', content: '// This is a comment\n/* Block comment */' },
262+
];
263+
264+
const result = collectSourceFiles('main.ts', files);
265+
266+
expect(result).toEqual({
267+
'main.ts': '// This is a comment\n/* Block comment */',
268+
});
269+
});
270+
271+
it('should handle malformed import statements', () => {
272+
const files = [
273+
{
274+
path: 'main.ts',
275+
content: `
276+
import from './missing-specifier';
277+
import { from './missing-closing-brace';
278+
import { foo } from;
279+
import { foo } from "./helper";
280+
`,
281+
},
282+
{ path: 'helper.ts', content: 'export const foo = 1;' },
283+
];
284+
285+
const result = collectSourceFiles('main.ts', files);
286+
const keys = Object.keys(result);
287+
288+
expect(keys).toHaveLength(2);
289+
expect(keys.includes('main.ts')).toBe(true);
290+
expect(keys.includes('helper.ts')).toBe(true);
291+
});
292+
293+
it('should handle imports in strings and comments', () => {
294+
const files = [
295+
{
296+
path: 'main.ts',
297+
content: `
298+
// import { fake } from "./fake";
299+
/* import { fake } from "./fake"; */
300+
const str = 'import { fake } from "./fake";';
301+
import { foo } from "./helper";
302+
`,
303+
},
304+
{ path: 'helper.ts', content: 'export const foo = 1;' },
305+
];
306+
307+
const result = collectSourceFiles('main.ts', files);
308+
const keys = Object.keys(result);
309+
310+
// Should only collect the real import, not the ones in comments/strings
311+
expect(keys).toHaveLength(2);
312+
expect(keys.includes('main.ts')).toBe(true);
313+
expect(keys.includes('helper.ts')).toBe(true);
314+
expect(result['helper.ts']).toBe('export const foo = 1;');
315+
});
316+
317+
it('should handle absolute path imports that start with /', () => {
318+
const files = [
319+
{ path: 'main.ts', content: 'import { foo } from "/absolute/helper";' },
320+
{ path: 'absolute/helper.ts', content: 'export const foo = 1;' },
321+
];
322+
323+
const result = collectSourceFiles('main.ts', files);
324+
325+
expect(result).toEqual({
326+
'main.ts': 'import { foo } from "/absolute/helper";',
327+
'absolute/helper.ts': 'export const foo = 1;',
328+
});
329+
});
330+
331+
it('should handle missing intermediate directories', () => {
332+
const files = [{ path: 'main.ts', content: 'import { foo } from "./missing/helper";' }];
333+
334+
const result = collectSourceFiles('main.ts', files);
335+
336+
expect(result).toEqual({
337+
'main.ts': 'import { foo } from "./missing/helper";',
338+
});
339+
});
340+
});
341+
342+
describe('complex scenarios', () => {
343+
it('should handle deep directory structures', () => {
344+
const files = [
345+
{
346+
path: 'src/components/Button/index.ts',
347+
content: 'import { styles } from "./styles"; export { Button } from "./Button";',
348+
},
349+
{
350+
path: 'src/components/Button/Button.tsx',
351+
content: 'import { Props } from "../types"; export const Button = () => {};',
352+
},
353+
{ path: 'src/components/Button/styles.ts', content: 'export const styles = {};' },
354+
{ path: 'src/components/types.ts', content: 'export interface Props {}' },
355+
];
356+
357+
const result = collectSourceFiles('src/components/Button/index.ts', files);
358+
const keys = Object.keys(result);
359+
360+
expect(keys).toHaveLength(4);
361+
expect(keys.includes('src/components/Button/index.ts')).toBe(true);
362+
expect(keys.includes('src/components/Button/Button.tsx')).toBe(true);
363+
expect(keys.includes('src/components/Button/styles.ts')).toBe(true);
364+
expect(keys.includes('src/components/types.ts')).toBe(true);
365+
});
366+
367+
it('should handle mixed import styles in single file', () => {
368+
const files = [
369+
{
370+
path: 'main.ts',
371+
content: `
372+
import React from 'react';
373+
import { Component } from './components';
374+
const utils = require('./utils');
375+
import('./dynamic');
376+
export { helper } from './helper';
377+
`,
378+
},
379+
{ path: 'components.ts', content: 'export const Component = {};' },
380+
{ path: 'utils.js', content: 'module.exports = {};' },
381+
{ path: 'dynamic.ts', content: 'export default {};' },
382+
{ path: 'helper.ts', content: 'export const helper = {};' },
383+
];
384+
385+
const result = collectSourceFiles('main.ts', files);
386+
const keys = Object.keys(result);
387+
388+
expect(keys).toHaveLength(5);
389+
expect(keys.includes('main.ts')).toBe(true);
390+
expect(keys.includes('components.ts')).toBe(true);
391+
expect(keys.includes('utils.js')).toBe(true);
392+
expect(keys.includes('dynamic.ts')).toBe(true);
393+
expect(keys.includes('helper.ts')).toBe(true);
394+
});
395+
});
396+
});

0 commit comments

Comments
 (0)