1
+ import React from 'react' ;
2
+ import { render , screen , fireEvent , waitFor } from '@testing-library/react' ;
3
+ import { PullRequestItem } from './pull-request' ;
4
+ import { vi , describe , it , expect , beforeEach } from 'vitest' ;
5
+ import { PullRequest , TestFile } from './types' ;
6
+ import { generateTestsResponseSchema } from "@/app/api/generate-tests/schema" ;
7
+
8
+ vi . mock ( '@/lib/github' , ( ) => ( {
9
+ getPullRequestInfo : vi . fn ( ) ,
10
+ commitChangesToPullRequest : vi . fn ( ) ,
11
+ } ) ) ;
12
+
13
+ vi . mock ( '@/hooks/use-toast' , ( ) => ( {
14
+ useToast : vi . fn ( ( ) => ( {
15
+ toast : vi . fn ( ) ,
16
+ } ) ) ,
17
+ } ) ) ;
18
+
19
+ vi . mock ( 'next/link' , ( ) => ( {
20
+ default : ( { children, href } : { children : React . ReactNode ; href : string } ) => (
21
+ < a href = { href } > { children } </ a >
22
+ ) ,
23
+ } ) ) ;
24
+
25
+ vi . mock ( 'react-diff-viewer' , ( ) => ( {
26
+ default : ( ) => < div data-testid = "react-diff-viewer" > Mocked Diff Viewer</ div > ,
27
+ } ) ) ;
28
+
29
+ describe ( 'PullRequestItem' , ( ) => {
30
+ const mockPullRequest : PullRequest = {
31
+ id : 1 ,
32
+ title : 'Test PR' ,
33
+ number : 123 ,
34
+ buildStatus : 'success' ,
35
+ isDraft : false ,
36
+ branchName : 'feature-branch' ,
37
+ repository : {
38
+ id : 1 ,
39
+ name : 'test-repo' ,
40
+ full_name : 'owner/test-repo' ,
41
+ owner : {
42
+ login : 'owner' ,
43
+ } ,
44
+ } ,
45
+ } ;
46
+
47
+ beforeEach ( ( ) => {
48
+ vi . clearAllMocks ( ) ;
49
+ global . fetch = vi . fn ( ) ;
50
+ } ) ;
51
+
52
+ it ( 'renders the pull request information correctly' , ( ) => {
53
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
54
+ expect ( screen . getByText ( 'Test PR' ) ) . toBeInTheDocument ( ) ;
55
+ expect ( screen . getByText ( '#123' ) ) . toBeInTheDocument ( ) ;
56
+ expect ( screen . getByText ( 'Build: success' ) ) . toBeInTheDocument ( ) ;
57
+ } ) ;
58
+
59
+ it ( 'handles "Write new tests" button click' , async ( ) => {
60
+ const { getPullRequestInfo } = await import ( '@/lib/github' ) ;
61
+ vi . mocked ( getPullRequestInfo ) . mockResolvedValue ( {
62
+ diff : 'mock diff' ,
63
+ testFiles : [ { name : 'test.ts' , content : 'test content' } ] ,
64
+ } ) ;
65
+
66
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
67
+ ok : true ,
68
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
69
+ } as Response ) ;
70
+
71
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
72
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
73
+ fireEvent . click ( writeTestsButton ) ;
74
+
75
+ await waitFor ( ( ) => {
76
+ expect ( screen . getByText ( 'Analyzing PR diff...' ) ) . toBeInTheDocument ( ) ;
77
+ } ) ;
78
+
79
+ await waitFor ( ( ) => {
80
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
81
+ expect ( screen . getByTestId ( 'react-diff-viewer' ) ) . toBeInTheDocument ( ) ;
82
+ } ) ;
83
+ } ) ;
84
+
85
+ it ( 'handles "Update tests to fix" button click' , async ( ) => {
86
+ const failedPR = { ...mockPullRequest , buildStatus : 'failure' } ;
87
+ const { getPullRequestInfo } = await import ( '@/lib/github' ) ;
88
+ vi . mocked ( getPullRequestInfo ) . mockResolvedValue ( {
89
+ diff : 'mock diff' ,
90
+ testFiles : [ { name : 'test.ts' , content : 'test content' } ] ,
91
+ } ) ;
92
+
93
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
94
+ ok : true ,
95
+ json : ( ) => Promise . resolve ( [ { name : 'fixed_test.ts' , content : 'fixed content' } ] ) ,
96
+ } as Response ) ;
97
+
98
+ render ( < PullRequestItem pullRequest = { failedPR } /> ) ;
99
+ const updateTestsButton = screen . getByText ( 'Update tests to fix' ) ;
100
+ fireEvent . click ( updateTestsButton ) ;
101
+
102
+ await waitFor ( ( ) => {
103
+ expect ( screen . getByText ( 'Analyzing PR diff...' ) ) . toBeInTheDocument ( ) ;
104
+ } ) ;
105
+
106
+ await waitFor ( ( ) => {
107
+ expect ( screen . getByText ( 'fixed_test.ts' ) ) . toBeInTheDocument ( ) ;
108
+ expect ( screen . getByTestId ( 'react-diff-viewer' ) ) . toBeInTheDocument ( ) ;
109
+ } ) ;
110
+ } ) ;
111
+
112
+ it ( 'handles errors when generating tests' , async ( ) => {
113
+ const { getPullRequestInfo } = await import ( '@/lib/github' ) ;
114
+ vi . mocked ( getPullRequestInfo ) . mockResolvedValue ( {
115
+ diff : 'mock diff' ,
116
+ testFiles : [ { name : 'test.ts' , content : 'test content' } ] ,
117
+ } ) ;
118
+
119
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
120
+ ok : false ,
121
+ } as Response ) ;
122
+
123
+ const { useToast } = await import ( '@/hooks/use-toast' ) ;
124
+ const mockToast = vi . fn ( ) ;
125
+ vi . mocked ( useToast ) . mockReturnValue ( { toast : mockToast } ) ;
126
+
127
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
128
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
129
+ fireEvent . click ( writeTestsButton ) ;
130
+
131
+ await waitFor ( ( ) => {
132
+ expect ( screen . getByText ( 'Failed to generate test files.' ) ) . toBeInTheDocument ( ) ;
133
+ } ) ;
134
+ } ) ;
135
+
136
+ it ( 'handles committing changes' , async ( ) => {
137
+ const { commitChangesToPullRequest } = await import ( '@/lib/github' ) ;
138
+ vi . mocked ( commitChangesToPullRequest ) . mockResolvedValue ( 'https://github.com/commit/123' ) ;
139
+
140
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
141
+ ok : true ,
142
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
143
+ } as Response ) ;
144
+
145
+ const { useToast } = await import ( '@/hooks/use-toast' ) ;
146
+ const mockToast = vi . fn ( ) ;
147
+ vi . mocked ( useToast ) . mockReturnValue ( { toast : mockToast } ) ;
148
+
149
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
150
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
151
+ fireEvent . click ( writeTestsButton ) ;
152
+
153
+ await waitFor ( ( ) => {
154
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
155
+ } ) ;
156
+
157
+ const commitButton = screen . getByText ( 'Commit changes' ) ;
158
+ fireEvent . click ( commitButton ) ;
159
+
160
+ await waitFor ( ( ) => {
161
+ expect ( commitChangesToPullRequest ) . toHaveBeenCalled ( ) ;
162
+ expect ( mockToast ) . toHaveBeenCalledWith ( expect . objectContaining ( {
163
+ title : 'Changes committed successfully' ,
164
+ } ) ) ;
165
+ } ) ;
166
+ } ) ;
167
+
168
+ it ( 'handles canceling changes' , async ( ) => {
169
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
170
+ ok : true ,
171
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
172
+ } as Response ) ;
173
+
174
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
175
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
176
+ fireEvent . click ( writeTestsButton ) ;
177
+
178
+ await waitFor ( ( ) => {
179
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
180
+ } ) ;
181
+
182
+ const cancelButton = screen . getByText ( 'Cancel' ) ;
183
+ fireEvent . click ( cancelButton ) ;
184
+
185
+ expect ( screen . queryByText ( 'generated_test.ts' ) ) . not . toBeInTheDocument ( ) ;
186
+ } ) ;
187
+
188
+ it ( 'handles file toggle' , async ( ) => {
189
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
190
+ ok : true ,
191
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
192
+ } as Response ) ;
193
+
194
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
195
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
196
+ fireEvent . click ( writeTestsButton ) ;
197
+
198
+ await waitFor ( ( ) => {
199
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
200
+ } ) ;
201
+
202
+ const checkbox = screen . getByRole ( 'checkbox' ) ;
203
+ fireEvent . click ( checkbox ) ;
204
+
205
+ expect ( checkbox ) . not . toBeChecked ( ) ;
206
+ } ) ;
207
+
208
+ it ( 'disables commit button when no files are selected' , async ( ) => {
209
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
210
+ ok : true ,
211
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
212
+ } as Response ) ;
213
+
214
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
215
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
216
+ fireEvent . click ( writeTestsButton ) ;
217
+
218
+ await waitFor ( ( ) => {
219
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
220
+ } ) ;
221
+
222
+ const checkbox = screen . getByRole ( 'checkbox' ) ;
223
+ fireEvent . click ( checkbox ) ;
224
+
225
+ const commitButton = screen . getByText ( 'Commit changes' ) ;
226
+ expect ( commitButton ) . toBeDisabled ( ) ;
227
+ } ) ;
228
+
229
+ it ( 'handles errors when committing changes' , async ( ) => {
230
+ const { commitChangesToPullRequest } = await import ( '@/lib/github' ) ;
231
+ vi . mocked ( commitChangesToPullRequest ) . mockRejectedValue ( new Error ( 'Commit failed' ) ) ;
232
+
233
+ vi . mocked ( global . fetch ) . mockResolvedValue ( {
234
+ ok : true ,
235
+ json : ( ) => Promise . resolve ( [ { name : 'generated_test.ts' , content : 'generated content' } ] ) ,
236
+ } as Response ) ;
237
+
238
+ const { useToast } = await import ( '@/hooks/use-toast' ) ;
239
+ const mockToast = vi . fn ( ) ;
240
+ vi . mocked ( useToast ) . mockReturnValue ( { toast : mockToast } ) ;
241
+
242
+ render ( < PullRequestItem pullRequest = { mockPullRequest } /> ) ;
243
+ const writeTestsButton = screen . getByText ( 'Write new tests' ) ;
244
+ fireEvent . click ( writeTestsButton ) ;
245
+
246
+ await waitFor ( ( ) => {
247
+ expect ( screen . getByText ( 'generated_test.ts' ) ) . toBeInTheDocument ( ) ;
248
+ } ) ;
249
+
250
+ const commitButton = screen . getByText ( 'Commit changes' ) ;
251
+ fireEvent . click ( commitButton ) ;
252
+
253
+ await waitFor ( ( ) => {
254
+ expect ( screen . getByText ( 'Failed to commit changes. Please try again.' ) ) . toBeInTheDocument ( ) ;
255
+ expect ( mockToast ) . toHaveBeenCalledWith ( expect . objectContaining ( {
256
+ title : 'Error' ,
257
+ description : 'Failed to commit changes. Please try again.' ,
258
+ variant : 'destructive' ,
259
+ } ) ) ;
260
+ } ) ;
261
+ } ) ;
262
+ } ) ;
0 commit comments