1
+ import { describe , it , expect , beforeAll } from 'bun:test'
2
+ import { readFile } from 'fs/promises'
3
+ import { join } from 'path'
4
+ import { generate } from '../src/generator'
5
+
6
+ const fixturesDir = join ( __dirname , 'fixtures' )
7
+ const inputDir = join ( fixturesDir , 'input' )
8
+ const expectedOutputDir = join ( fixturesDir , 'output' )
9
+ const actualOutputDir = join ( __dirname , 'temp-output' )
10
+
11
+ // List of test files to validate
12
+ const testFiles = [
13
+ 'class.ts' ,
14
+ 'enum.ts' ,
15
+ 'exports.ts' ,
16
+ 'function.ts' ,
17
+ 'interface.ts' ,
18
+ 'imports.ts' ,
19
+ 'module.ts' ,
20
+ 'type.ts' ,
21
+ 'variable.ts'
22
+ ]
23
+
24
+ describe ( 'DTS Generator' , ( ) => {
25
+ beforeAll ( async ( ) => {
26
+ // Generate all DTS files
27
+ await generate ( {
28
+ cwd : process . cwd ( ) ,
29
+ root : inputDir ,
30
+ entrypoints : testFiles ,
31
+ outdir : actualOutputDir ,
32
+ clean : true ,
33
+ keepComments : true ,
34
+ tsconfigPath : '' ,
35
+ verbose : false
36
+ } )
37
+ } )
38
+
39
+ // Test each file
40
+ testFiles . forEach ( testFile => {
41
+ const baseName = testFile . replace ( '.ts' , '' )
42
+
43
+ it ( `should generate correct output for ${ testFile } ` , async ( ) => {
44
+ const expectedPath = join ( expectedOutputDir , `${ baseName } .d.ts` )
45
+ const actualPath = join ( actualOutputDir , `${ baseName } .d.ts` )
46
+
47
+ const [ expectedContent , actualContent ] = await Promise . all ( [
48
+ readFile ( expectedPath , 'utf-8' ) ,
49
+ readFile ( actualPath , 'utf-8' )
50
+ ] )
51
+
52
+ // Normalize whitespace for comparison
53
+ const normalizeContent = ( content : string ) => {
54
+ return content
55
+ . split ( '\n' )
56
+ . map ( line => line . trimEnd ( ) ) // Remove trailing whitespace
57
+ . filter ( line => line !== '' ) // Remove empty lines
58
+ . join ( '\n' )
59
+ }
60
+
61
+ const normalizedExpected = normalizeContent ( expectedContent )
62
+ const normalizedActual = normalizeContent ( actualContent )
63
+
64
+ // For detailed error reporting, compare line by line
65
+ const expectedLines = normalizedExpected . split ( '\n' )
66
+ const actualLines = normalizedActual . split ( '\n' )
67
+
68
+ // First check if number of lines match
69
+ if ( expectedLines . length !== actualLines . length ) {
70
+ console . error ( `\n❌ ${ testFile } : Line count mismatch` )
71
+ console . error ( `Expected ${ expectedLines . length } lines, got ${ actualLines . length } lines` )
72
+ console . error ( '\nExpected:\n' , expectedContent )
73
+ console . error ( '\nActual:\n' , actualContent )
74
+ }
75
+
76
+ // Compare line by line for better error messages
77
+ expectedLines . forEach ( ( expectedLine , index ) => {
78
+ const actualLine = actualLines [ index ] || ''
79
+ if ( expectedLine !== actualLine ) {
80
+ console . error ( `\n❌ ${ testFile } : Mismatch at line ${ index + 1 } ` )
81
+ console . error ( `Expected: "${ expectedLine } "` )
82
+ console . error ( `Actual: "${ actualLine } "` )
83
+ }
84
+ } )
85
+
86
+ expect ( normalizedActual ) . toBe ( normalizedExpected )
87
+ } )
88
+ } )
89
+
90
+ // Test for narrowness - ensure types are as narrow or narrower than expected
91
+ describe ( 'Type Narrowness' , ( ) => {
92
+ it ( 'should infer literal types for const declarations' , async ( ) => {
93
+ const actualPath = join ( actualOutputDir , 'variable.d.ts' )
94
+ const actualContent = await readFile ( actualPath , 'utf-8' )
95
+
96
+ // Check for literal types
97
+ expect ( actualContent ) . toContain ( "export declare let test: 'test'" )
98
+ expect ( actualContent ) . toContain ( "export declare const someObject: {" )
99
+ expect ( actualContent ) . toContain ( "someString: 'Stacks'" )
100
+ expect ( actualContent ) . toContain ( "someNumber: 1000" )
101
+ expect ( actualContent ) . toContain ( "someBoolean: true" )
102
+ expect ( actualContent ) . toContain ( "someFalse: false" )
103
+ expect ( actualContent ) . toContain ( "readonly ['google', 'github']" )
104
+ } )
105
+
106
+ it ( 'should use broader types for let and var declarations' , async ( ) => {
107
+ const actualPath = join ( actualOutputDir , 'variable.d.ts' )
108
+ const actualContent = await readFile ( actualPath , 'utf-8' )
109
+
110
+ // Test file has: export let test = 'test'
111
+ // Should be: export declare let test: 'test' (according to expected output)
112
+ // But this seems to be a special case in the expected output
113
+
114
+ // Check that var gets broader type
115
+ expect ( actualContent ) . toContain ( "export declare var helloWorld: 'Hello World'" )
116
+ } )
117
+
118
+ it ( 'should handle function overloads correctly' , async ( ) => {
119
+ const actualPath = join ( actualOutputDir , 'function.d.ts' )
120
+ const actualContent = await readFile ( actualPath , 'utf-8' )
121
+
122
+ // Check that all overloads have 'declare'
123
+ const overloadLines = actualContent
124
+ . split ( '\n' )
125
+ . filter ( line => line . includes ( 'processData' ) )
126
+
127
+ // First 4 should be overload signatures, last should be implementation
128
+ expect ( overloadLines [ 0 ] ) . toContain ( 'export declare function processData(data: string): string' )
129
+ expect ( overloadLines [ 1 ] ) . toContain ( 'export declare function processData(data: number): number' )
130
+ expect ( overloadLines [ 2 ] ) . toContain ( 'export declare function processData(data: boolean): boolean' )
131
+ expect ( overloadLines [ 3 ] ) . toContain ( 'export declare function processData<T extends object>(data: T): T' )
132
+ expect ( overloadLines [ 4 ] ) . toContain ( 'export declare function processData(data: unknown): unknown' )
133
+ } )
134
+ } )
135
+ } )
0 commit comments