1
+ import { jest } from '@jest/globals'
2
+ import { attachComment , buildCommentIdentifier } from '../src/annotator.js'
3
+ import * as core from '@actions/core'
4
+
5
+ /**
6
+ * Copyright 2024 Mike Penz
7
+ */
8
+ jest . setTimeout ( 30000 )
9
+
10
+ // Mock the context object
11
+ jest . mock ( '@actions/github/lib/utils.js' , ( ) => ( {
12
+ context : {
13
+ issue : { number : undefined } ,
14
+ repo : { owner : 'test-owner' , repo : 'test-repo' }
15
+ }
16
+ } ) )
17
+
18
+ describe ( 'attachComment' , ( ) => {
19
+ let mockOctokit : any
20
+ let mockWarning : jest . SpiedFunction < typeof core . warning >
21
+ let mockContext : any
22
+
23
+ beforeEach ( ( ) => {
24
+ // Import context after mocking
25
+ const { context} = require ( '@actions/github/lib/utils.js' )
26
+ mockContext = context
27
+
28
+ // Mock core.warning
29
+ mockWarning = jest . spyOn ( core , 'warning' ) . mockImplementation ( ( ) => { } )
30
+
31
+ // Mock octokit
32
+ mockOctokit = {
33
+ paginate : jest . fn ( ) ,
34
+ rest : {
35
+ issues : {
36
+ listComments : jest . fn ( ) ,
37
+ createComment : jest . fn ( ) ,
38
+ updateComment : jest . fn ( )
39
+ }
40
+ }
41
+ }
42
+ } )
43
+
44
+ afterEach ( ( ) => {
45
+ jest . restoreAllMocks ( )
46
+ } )
47
+
48
+ it ( 'should use pr_id when provided and context.issue.number is not available' , async ( ) => {
49
+ // Setup: no context issue number
50
+ mockContext . issue . number = undefined
51
+
52
+ mockOctokit . paginate . mockResolvedValue ( [ ] )
53
+
54
+ const checkName = [ 'Test Check' ]
55
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Passed' ] ]
56
+ const prId = '123'
57
+
58
+ await attachComment ( mockOctokit , checkName , false , table , [ ] , [ ] , [ ] , prId )
59
+
60
+ // Verify comment was created with correct issue number
61
+ expect ( mockOctokit . rest . issues . createComment ) . toHaveBeenCalledWith ( {
62
+ owner : 'test-owner' ,
63
+ repo : 'test-repo' ,
64
+ issue_number : 123 ,
65
+ body : expect . stringContaining ( 'Example Test' )
66
+ } )
67
+
68
+ expect ( mockWarning ) . not . toHaveBeenCalled ( )
69
+ } )
70
+
71
+ it ( 'should fall back to context.issue.number when pr_id is not provided' , async ( ) => {
72
+ // Setup: context issue number available
73
+ mockContext . issue . number = 456
74
+
75
+ mockOctokit . paginate . mockResolvedValue ( [ ] )
76
+
77
+ const checkName = [ 'Test Check' ]
78
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Passed' ] ]
79
+
80
+ await attachComment ( mockOctokit , checkName , false , table , [ ] , [ ] , [ ] )
81
+
82
+ // Verify comment was created with context issue number
83
+ expect ( mockOctokit . rest . issues . createComment ) . toHaveBeenCalledWith ( {
84
+ owner : 'test-owner' ,
85
+ repo : 'test-repo' ,
86
+ issue_number : 456 ,
87
+ body : expect . stringContaining ( 'Example Test' )
88
+ } )
89
+
90
+ expect ( mockWarning ) . not . toHaveBeenCalled ( )
91
+ } )
92
+
93
+ it ( 'should warn and return early when no issue number is available' , async ( ) => {
94
+ // Setup: no context issue number and no pr_id
95
+ mockContext . issue . number = undefined
96
+
97
+ const checkName = [ 'Test Check' ]
98
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Passed' ] ]
99
+
100
+ await attachComment ( mockOctokit , checkName , false , table , [ ] , [ ] , [ ] )
101
+
102
+ // Verify warning was called and no comment was created
103
+ expect ( mockWarning ) . toHaveBeenCalledWith (
104
+ expect . stringContaining ( 'Action requires a valid issue number (PR reference) or pr_id input' )
105
+ )
106
+ expect ( mockOctokit . rest . issues . createComment ) . not . toHaveBeenCalled ( )
107
+ } )
108
+
109
+ it ( 'should update existing comment when updateComment is true' , async ( ) => {
110
+ // Setup: context issue number available
111
+ mockContext . issue . number = 456
112
+
113
+ const existingComment = {
114
+ id : 999 ,
115
+ body : 'Existing comment <!-- Summary comment for ["Test Check"] by mikepenz/action-junit-report -->'
116
+ }
117
+ mockOctokit . paginate . mockResolvedValue ( [ existingComment ] )
118
+
119
+ const checkName = [ 'Test Check' ]
120
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Updated' ] ]
121
+
122
+ await attachComment ( mockOctokit , checkName , true , table , [ ] , [ ] , [ ] )
123
+
124
+ // Verify comment was updated
125
+ expect ( mockOctokit . rest . issues . updateComment ) . toHaveBeenCalledWith ( {
126
+ owner : 'test-owner' ,
127
+ repo : 'test-repo' ,
128
+ comment_id : 999 ,
129
+ body : expect . stringContaining ( 'Example Test' )
130
+ } )
131
+ expect ( mockOctokit . rest . issues . createComment ) . not . toHaveBeenCalled ( )
132
+ } )
133
+ it ( 'should warn and return early when pr_id is invalid' , async ( ) => {
134
+ // Setup: no context issue number and invalid pr_id
135
+ mockContext . issue . number = undefined
136
+
137
+ const checkName = [ 'Test Check' ]
138
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Passed' ] ]
139
+ const prId = 'invalid-number'
140
+
141
+ await attachComment ( mockOctokit , checkName , false , table , [ ] , [ ] , [ ] , prId )
142
+
143
+ // Verify warning was called and no comment was created
144
+ expect ( mockWarning ) . toHaveBeenCalledWith (
145
+ expect . stringContaining ( 'Action requires a valid issue number (PR reference) or pr_id input' )
146
+ )
147
+ expect ( mockOctokit . rest . issues . createComment ) . not . toHaveBeenCalled ( )
148
+ } )
149
+
150
+ it ( 'should handle pr_id with leading/trailing whitespace' , async ( ) => {
151
+ // Setup: no context issue number
152
+ mockContext . issue . number = undefined
153
+
154
+ mockOctokit . paginate . mockResolvedValue ( [ ] )
155
+
156
+ const checkName = [ 'Test Check' ]
157
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Passed' ] ]
158
+ const prId = ' 123 '
159
+
160
+ await attachComment ( mockOctokit , checkName , false , table , [ ] , [ ] , [ ] , prId )
161
+
162
+ // Verify comment was created with correct issue number (whitespace trimmed)
163
+ expect ( mockOctokit . rest . issues . createComment ) . toHaveBeenCalledWith ( {
164
+ owner : 'test-owner' ,
165
+ repo : 'test-repo' ,
166
+ issue_number : 123 ,
167
+ body : expect . stringContaining ( 'Example Test' )
168
+ } )
169
+
170
+ expect ( mockWarning ) . not . toHaveBeenCalled ( )
171
+ } )
172
+
173
+ it ( 'should update existing comment when pr_id is provided and updateComment is true' , async ( ) => {
174
+ // Setup: no context issue number but pr_id provided
175
+ mockContext . issue . number = undefined
176
+
177
+ const existingComment = {
178
+ id : 888 ,
179
+ body : 'Existing comment <!-- Summary comment for ["Test Check"] by mikepenz/action-junit-report -->'
180
+ }
181
+ mockOctokit . paginate . mockResolvedValue ( [ existingComment ] )
182
+
183
+ const checkName = [ 'Test Check' ]
184
+ const table = [ [ 'Test' , 'Result' ] , [ 'Example Test' , 'Updated' ] ]
185
+ const prId = '789'
186
+
187
+ await attachComment ( mockOctokit , checkName , true , table , [ ] , [ ] , [ ] , prId )
188
+
189
+ // Verify paginate was called with correct issue number
190
+ expect ( mockOctokit . paginate ) . toHaveBeenCalledWith (
191
+ mockOctokit . rest . issues . listComments ,
192
+ {
193
+ owner : 'test-owner' ,
194
+ repo : 'test-repo' ,
195
+ issue_number : 789
196
+ }
197
+ )
198
+
199
+ // Verify comment was updated
200
+ expect ( mockOctokit . rest . issues . updateComment ) . toHaveBeenCalledWith ( {
201
+ owner : 'test-owner' ,
202
+ repo : 'test-repo' ,
203
+ comment_id : 888 ,
204
+ body : expect . stringContaining ( 'Example Test' )
205
+ } )
206
+ expect ( mockOctokit . rest . issues . createComment ) . not . toHaveBeenCalled ( )
207
+ } )
208
+
209
+ } )
210
+
211
+ describe ( 'buildCommentIdentifier' , ( ) => {
212
+ it ( 'should build correct identifier' , ( ) => {
213
+ const checkName = [ 'Test Check' ]
214
+ const identifier = buildCommentIdentifier ( checkName )
215
+ expect ( identifier ) . toBe ( '<!-- Summary comment for ["Test Check"] by mikepenz/action-junit-report -->' )
216
+ } )
217
+ } )
0 commit comments