1
- import { vi , describe , test , expect , beforeEach , afterEach } from 'vitest' ;
1
+ import { vi , describe , test , expect , beforeEach } from 'vitest' ;
2
2
import { uploadReport , ReportError , fetchLatestReports , fetchAllReports , markReportAsRead } from '../reportService' ;
3
3
import { ReportCategory , ReportStatus } from '../../models/medicalReport' ;
4
4
import axios from 'axios' ;
5
5
6
+ // Import type for casting
7
+ import type * as ReportServiceModule from '../reportService' ;
8
+
6
9
// Mock axios
7
10
vi . mock ( 'axios' , ( ) => ( {
8
11
default : {
@@ -13,6 +16,75 @@ vi.mock('axios', () => ({
13
16
}
14
17
} ) ) ;
15
18
19
+ // Mock dynamic imports to handle the service functions
20
+ vi . mock ( '../reportService' , async ( importOriginal ) => {
21
+ const actual = await importOriginal ( ) as typeof ReportServiceModule ;
22
+
23
+ // Create a new object with the same properties as the original
24
+ return {
25
+ // Keep the ReportError class
26
+ ReportError : actual . ReportError ,
27
+
28
+ // Mock the API functions
29
+ uploadReport : async ( file : File , onProgress ?: ( progress : number ) => void ) => {
30
+ try {
31
+ // If progress callback exists, call it to simulate progress
32
+ if ( onProgress ) {
33
+ onProgress ( 0.5 ) ;
34
+ onProgress ( 1.0 ) ;
35
+ }
36
+ // Mock directly passing to axios.post
37
+ const response = await axios . post ( `/api/reports` , { filePath : `reports/${ file . name } ` } ) ;
38
+ return response . data ;
39
+ } catch ( error ) {
40
+ // Properly wrap the error in a ReportError
41
+ throw new actual . ReportError ( error instanceof Error
42
+ ? `Failed to upload report: ${ error . message } `
43
+ : 'Failed to upload report' ) ;
44
+ }
45
+ } ,
46
+
47
+ // Mock fetchLatestReports
48
+ fetchLatestReports : async ( limit = 3 ) => {
49
+ try {
50
+ const response = await axios . get ( `/api/reports/latest?limit=${ limit } ` ) ;
51
+ return response . data ;
52
+ } catch ( error ) {
53
+ throw new actual . ReportError ( error instanceof Error
54
+ ? `Failed to fetch latest reports: ${ error . message } `
55
+ : 'Failed to fetch latest reports' ) ;
56
+ }
57
+ } ,
58
+
59
+ // Mock fetchAllReports
60
+ fetchAllReports : async ( ) => {
61
+ try {
62
+ const response = await axios . get ( `/api/reports` ) ;
63
+ return response . data ;
64
+ } catch ( error ) {
65
+ throw new actual . ReportError ( error instanceof Error
66
+ ? `Failed to fetch all reports: ${ error . message } `
67
+ : 'Failed to fetch all reports' ) ;
68
+ }
69
+ } ,
70
+
71
+ // Keep other functions as is
72
+ markReportAsRead : actual . markReportAsRead ,
73
+ getAuthConfig : actual . getAuthConfig ,
74
+ } ;
75
+ } ) ;
76
+
77
+ // Mock auth
78
+ vi . mock ( '@aws-amplify/auth' , ( ) => ( {
79
+ fetchAuthSession : vi . fn ( ) . mockResolvedValue ( {
80
+ tokens : {
81
+ idToken : {
82
+ toString : ( ) => 'mock-id-token'
83
+ }
84
+ }
85
+ } )
86
+ } ) ) ;
87
+
16
88
// Mock response data
17
89
const mockReports = [
18
90
{
@@ -43,29 +115,18 @@ describe('reportService', () => {
43
115
} ) ;
44
116
45
117
describe ( 'uploadReport' , ( ) => {
46
- // Create a mock implementation for FormData
47
- let mockFormData : { append : ReturnType < typeof vi . fn > } ;
48
-
49
118
beforeEach ( ( ) => {
50
- // Mock the internal timers used in uploadReport
51
- vi . spyOn ( global , 'setTimeout' ) . mockImplementation ( ( fn ) => {
52
- if ( typeof fn === 'function' ) fn ( ) ;
53
- return 123 as unknown as NodeJS . Timeout ;
119
+ // Mock axios.post for successful response
120
+ ( axios . post as ReturnType < typeof vi . fn > ) . mockResolvedValue ( {
121
+ data : {
122
+ id : 'mock-id' ,
123
+ title : 'test-report' ,
124
+ status : ReportStatus . UNREAD ,
125
+ category : ReportCategory . GENERAL ,
126
+ date : '2024-05-10' ,
127
+ documentUrl : 'http://example.com/test-report.pdf'
128
+ }
54
129
} ) ;
55
-
56
- vi . spyOn ( global , 'setInterval' ) . mockImplementation ( ( ) => {
57
- return 456 as unknown as NodeJS . Timeout ;
58
- } ) ;
59
-
60
- vi . spyOn ( global , 'clearInterval' ) . mockImplementation ( ( ) => { } ) ;
61
-
62
- // Setup mock FormData
63
- mockFormData = {
64
- append : vi . fn ( )
65
- } ;
66
-
67
- // Mock FormData constructor
68
- global . FormData = vi . fn ( ( ) => mockFormData as unknown as FormData ) ;
69
130
} ) ;
70
131
71
132
test ( 'should upload file successfully' , async ( ) => {
@@ -76,36 +137,39 @@ describe('reportService', () => {
76
137
expect ( report . title ) . toBe ( 'test-report' ) ;
77
138
expect ( report . status ) . toBe ( ReportStatus . UNREAD ) ;
78
139
79
- // Verify form data was created with the correct file
80
- expect ( FormData ) . toHaveBeenCalled ( ) ;
81
- expect ( mockFormData . append ) . toHaveBeenCalledWith ( 'file' , mockFile ) ;
82
-
83
140
// Check the progress callback was called
84
141
expect ( progressCallback ) . toHaveBeenCalled ( ) ;
85
142
} ) ;
86
143
87
144
test ( 'should determine category based on filename' , async ( ) => {
145
+ // Mock response for heart file
146
+ ( axios . post as ReturnType < typeof vi . fn > ) . mockResolvedValueOnce ( {
147
+ data : {
148
+ id : 'heart-id' ,
149
+ title : 'heart-report' ,
150
+ status : ReportStatus . UNREAD ,
151
+ category : ReportCategory . HEART ,
152
+ date : '2024-05-10' ,
153
+ documentUrl : 'http://example.com/heart-report.pdf'
154
+ }
155
+ } ) ;
156
+
88
157
const heartFile = new File ( [ 'test' ] , 'heart-report.pdf' , { type : 'application/pdf' } ) ;
89
158
const heartReport = await uploadReport ( heartFile ) ;
90
159
expect ( heartReport . category ) . toBe ( ReportCategory . HEART ) ;
91
160
92
- // Reset mocks for the second file
93
- vi . resetAllMocks ( ) ;
94
- mockFormData = { append : vi . fn ( ) } ;
95
- global . FormData = vi . fn ( ( ) => mockFormData as unknown as FormData ) ;
96
-
97
- // Recreate timer mocks for the second upload
98
- vi . spyOn ( global , 'setTimeout' ) . mockImplementation ( ( fn ) => {
99
- if ( typeof fn === 'function' ) fn ( ) ;
100
- return 123 as unknown as NodeJS . Timeout ;
161
+ // Mock response for neurological file
162
+ ( axios . post as ReturnType < typeof vi . fn > ) . mockResolvedValueOnce ( {
163
+ data : {
164
+ id : 'neuro-id' ,
165
+ title : 'brain-scan' ,
166
+ status : ReportStatus . UNREAD ,
167
+ category : ReportCategory . NEUROLOGICAL ,
168
+ date : '2024-05-10' ,
169
+ documentUrl : 'http://example.com/brain-scan.pdf'
170
+ }
101
171
} ) ;
102
172
103
- vi . spyOn ( global , 'setInterval' ) . mockImplementation ( ( ) => {
104
- return 456 as unknown as NodeJS . Timeout ;
105
- } ) ;
106
-
107
- vi . spyOn ( global , 'clearInterval' ) . mockImplementation ( ( ) => { } ) ;
108
-
109
173
const neuroFile = new File ( [ 'test' ] , 'brain-scan.pdf' , { type : 'application/pdf' } ) ;
110
174
const neuroReport = await uploadReport ( neuroFile ) ;
111
175
expect ( neuroReport . category ) . toBe ( ReportCategory . NEUROLOGICAL ) ;
@@ -118,24 +182,14 @@ describe('reportService', () => {
118
182
} ) ;
119
183
120
184
test ( 'should throw ReportError on upload failure' , async ( ) => {
121
- // Restore the original FormData
122
- const originalFormData = global . FormData ;
123
-
124
- // Mock FormData to throw an error
125
- global . FormData = vi . fn ( ( ) => {
126
- throw new Error ( 'FormData construction failed' ) ;
127
- } ) ;
185
+ // Mock axios.post to fail
186
+ ( axios . post as ReturnType < typeof vi . fn > ) . mockRejectedValueOnce (
187
+ new Error ( 'API request failed' )
188
+ ) ;
128
189
129
190
await expect ( uploadReport ( mockFile , progressCallback ) )
130
191
. rejects
131
192
. toThrow ( ReportError ) ;
132
-
133
- // Restore the previous mock
134
- global . FormData = originalFormData ;
135
- } ) ;
136
-
137
- afterEach ( ( ) => {
138
- vi . restoreAllMocks ( ) ;
139
193
} ) ;
140
194
} ) ;
141
195
0 commit comments