Skip to content

Commit 1fc9aa7

Browse files
authored
Merge branch 'master' into one_of-option-for-filter-and-validator
2 parents c421998 + 970ef68 commit 1fc9aa7

16 files changed

+211
-70
lines changed

__fixtures__/unit/helper.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const _ = require('lodash')
22
const yaml = require('js-yaml')
3-
const moment = require('moment-timezone')
43

54
const throwNotFound = () => {
65
const error = new Error('404 error')
@@ -18,11 +17,14 @@ module.exports = {
1817
action: 'opened',
1918
repository: {
2019
name: (options.repoName) ? options.repoName : 'repoName',
21-
full_name: 'name',
20+
full_name: 'fullRepoName',
2221
owner: {
2322
login: 'owner'
2423
}
2524
},
25+
sender: {
26+
login: 'initiator'
27+
},
2628
check_suite: {
2729
pull_requests: [
2830
{
@@ -37,8 +39,8 @@ module.exports = {
3739
title: (options.title) ? options.title : 'title',
3840
body: options.body,
3941
number: (options.number) ? options.number : 1,
40-
created_at: options.createdAt ? moment(options.createdAt) : moment(),
41-
updated_at: options.updatedAt ? moment(options.updatedAt) : moment(),
42+
created_at: (options.createdAt) ? options.createdAt : new Date().toISOString(),
43+
updated_at: (options.updatedAt) ? options.updatedAt : new Date().toISOString(),
4244
milestone: (options.milestone) ? options.milestone : null,
4345
requested_reviewers: options.requestedReviewers ? options.requestedReviewers : [],
4446
requested_teams: options.requestedTeams ? options.requestedTeams : [],
@@ -64,7 +66,13 @@ module.exports = {
6466
user: {
6567
login: 'creator'
6668
},
69+
title: (options.title) ? options.title : 'title',
70+
body: options.body,
6771
number: (options.number) ? options.number : 1,
72+
milestone: (options.milestone) ? options.milestone : null,
73+
created_at: (options.createdAt) ? options.createdAt : new Date().toISOString(),
74+
updated_at: (options.updatedAt) ? options.updatedAt : new Date().toISOString(),
75+
assignees: (options.assignees) ? options.assignees : [],
6876
pull_request: {}
6977
},
7078
comment: options.issueComment

__tests__/unit/actions/action.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,31 @@ describe('Action#getActionables', () => {
7373
).toBe(1)
7474
})
7575
})
76+
77+
describe('Action#getEventAttributes', () => {
78+
const action = new Action()
79+
80+
test('Extracts event properties from pull_request correctly', () => {
81+
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'pull_request' }))
82+
83+
expect(evt.action).toBe('opened')
84+
expect(evt.repository.full_name).toBe('fullRepoName')
85+
expect(evt.sender.login).toBe('initiator')
86+
})
87+
88+
test('Extracts event properties from issues correctly', () => {
89+
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'issues' }))
90+
91+
expect(evt.action).toBe('opened')
92+
expect(evt.repository.full_name).toBe('fullRepoName')
93+
expect(evt.sender.login).toBe('initiator')
94+
})
95+
96+
test('Defaults event properties on schedule event', () => {
97+
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'schedule' }))
98+
99+
expect(evt.action).toBe('')
100+
expect(evt.repository).toEqual({})
101+
expect(evt.sender).toEqual({})
102+
})
103+
})

__tests__/unit/actions/assign.test.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ test('check that assignees are added when afterValidate is called with proper pa
3232
expect(context.octokit.issues.addAssignees.mock.calls[0][0].assignees[1]).toBe('testuser2')
3333
})
3434

35-
test('check that creator is added when assignee is @author', async () => {
35+
test('check that creator is added when assignee is @author or @sender or @bot', async () => {
3636
const settings = {
37-
assignees: ['@author']
37+
assignees: ['@author', '@sender', '@bot']
3838
}
3939

4040
const assign = new Assign()
@@ -43,6 +43,8 @@ test('check that creator is added when assignee is @author', async () => {
4343
await assign.afterValidate(context, settings)
4444
expect(context.octokit.issues.addAssignees.mock.calls.length).toBe(1)
4545
expect(context.octokit.issues.addAssignees.mock.calls[0][0].assignees[0]).toBe('creator')
46+
expect(context.octokit.issues.addAssignees.mock.calls[0][0].assignees[1]).toBe('initiator')
47+
expect(context.octokit.issues.addAssignees.mock.calls[0][0].assignees[2]).toBe('Mergeable[bot]')
4648
})
4749

4850
test('check only authorized users are added as assignee ', async () => {

__tests__/unit/actions/checks.test.js

+38
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,44 @@ test('that afterValidate is called with properly and output is correct', async (
140140
expect(MetaData.exists(output.text)).toBe(false)
141141
})
142142

143+
test('that afterValidate is replacing special annotations in payload', async () => {
144+
const checks = new Checks()
145+
const context = createMockContext()
146+
const result = {
147+
status: 'pass',
148+
validations: [{
149+
status: 'pass',
150+
name: 'Label'
151+
}],
152+
completed_at: '2024-06-15T19:14:00Z'
153+
}
154+
const settings = {
155+
payload: {
156+
title: '@author @sender @bot @repository @action {{formatDate completed_at}} , completed!',
157+
summary: '@author @sender @bot @repository @action {{formatDate completed_at}} , summary',
158+
text: '@author @sender @bot @repository @action {{formatDate completed_at}} , text'
159+
}
160+
}
161+
162+
const name = undefined
163+
164+
checks.checkRunResult = new Map()
165+
166+
checks.checkRunResult.set(name, {
167+
data: {
168+
id: '3'
169+
}
170+
})
171+
172+
await checks.afterValidate(context, settings, name, result)
173+
const output = context.octokit.checks.update.mock.calls[0][0].output
174+
expect(context.octokit.checks.update.mock.calls.length).toBe(1)
175+
expect(output.title).toBe('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , completed!')
176+
expect(output.summary).toBe('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , summary')
177+
expect(output.text).toContain('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , text')
178+
expect(MetaData.exists(output.text)).toBe(true)
179+
})
180+
143181
test('that afterValidate is correct when validation fails', async () => {
144182
const checks = new Checks()
145183
const context = createMockContext()

__tests__/unit/actions/comment.test.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ test.each([
4040

4141
test('check that comment created when afterValidate is called with proper parameter', async () => {
4242
const comment = new Comment()
43-
const context = createMockContext()
43+
const context = createMockContext([])
4444

4545
const result = {
4646
status: 'pass',
@@ -257,23 +257,24 @@ test('remove Error comment fail gracefully if payload does not exists', async ()
257257
expect(context.octokit.issues.deleteComment.mock.calls.length).toBe(0)
258258
})
259259

260-
test('error handling includes removing old error comments and creating new error comment', async () => {
260+
test('special annotations are replaced', async () => {
261261
const comment = new Comment()
262-
const context = createMockContext()
262+
const context = createMockContext([])
263263
const settings = {
264264
payload: {
265-
body: '@author , do something!'
265+
body: '@author @sender @bot @repository @action {{formatDate created_at}} , do something!'
266266
}
267267
}
268268

269269
await comment.afterValidate(context, settings, '', result)
270270
await Helper.flushPromises()
271271

272-
expect(context.octokit.issues.createComment.mock.calls[0][0].body).toBe('creator , do something!')
272+
expect(context.octokit.issues.createComment.mock.calls[0][0].body).toBe('creator initiator Mergeable[bot] fullRepoName opened Jun 15, 2024, 7:14 PM , do something!')
273273
})
274274

275275
const createMockContext = (comments, eventName = undefined, event = undefined) => {
276-
const context = Helper.mockContext({ comments, eventName, event })
276+
const createdAt = '2024-06-15T19:14:00Z'
277+
const context = Helper.mockContext({ comments, eventName, createdAt, event })
277278

278279
context.octokit.issues.createComment = jest.fn()
279280
context.octokit.issues.deleteComment = jest.fn()
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,69 @@
11
const searchAndReplaceSpecialAnnotations = require('../../../../lib/actions/lib/searchAndReplaceSpecialAnnotation')
22

33
describe('searchAndReplaceSpecialAnnotations', () => {
4-
test('does not affect input if no special annotations are found', () => {
5-
const payload = {
6-
user: {
7-
login: 'creator'
8-
}
9-
}
10-
expect(searchAndReplaceSpecialAnnotations('no special annotations', payload)).toBe('no special annotations')
11-
})
12-
13-
test('special annotation at the beginning of string works properly', () => {
14-
const payload = {
15-
user: {
16-
login: 'creator'
17-
}
18-
}
19-
expect(searchAndReplaceSpecialAnnotations('@author says hello!', payload)).toBe('creator says hello!')
20-
})
21-
22-
test('escape character works properly', () => {
23-
const payload = {
24-
user: {
25-
login: 'creator'
26-
}
4+
const SPECIAL_ANNOTATION = {
5+
'@author': 'creator',
6+
'@action': 'created',
7+
'@sender': 'initiator',
8+
'@bot': 'Mergeable[bot]',
9+
'@repository': 'botrepo'
10+
}
11+
const tests = [
12+
{
13+
name: 'does not affect input if no special annotations are found',
14+
message: 'no special annotations',
15+
expected: 'no special annotations'
16+
},
17+
{
18+
name: 'special annotation at the beginning of string works properly',
19+
message: '$annotation$ says hello!',
20+
expected: '$annotation$ says hello!'
21+
},
22+
{
23+
name: 'escape character works properly',
24+
message: 'this is \\@author',
25+
expected: 'this is @author'
26+
},
27+
{
28+
name: 'special annotation at the end of string works properly',
29+
message: 'this is $annotation$',
30+
expected: 'this is $annotation$'
31+
},
32+
{
33+
name: '@@annotation is replaced, prepending @ remains',
34+
message: 'this is @$annotation$',
35+
expected: 'this is @$annotation$'
36+
},
37+
{
38+
name: 'replaces special annotation anywhere in the text',
39+
message: 'this is something$annotation$ speaking',
40+
expected: 'this is something$annotation$ speaking'
2741
}
28-
expect(searchAndReplaceSpecialAnnotations('this is \\@author', payload)).toBe('this is @author')
29-
})
42+
]
3043

31-
test('@author is replaced by payload.user.login', () => {
32-
const payload = {
33-
user: {
34-
login: 'creator'
44+
test.each(tests)(
45+
'$name',
46+
async ({ message, expected }) => {
47+
const payload = {
48+
user: {
49+
login: 'creator'
50+
}
3551
}
36-
}
37-
expect(searchAndReplaceSpecialAnnotations('this is @author', payload)).toBe('this is creator')
38-
})
39-
40-
test('@@author is replaced by @payload.user.login', () => {
41-
const payload = {
42-
user: {
43-
login: 'creator'
52+
const evt = {
53+
action: 'created',
54+
repository: {
55+
full_name: 'botrepo'
56+
},
57+
sender: {
58+
login: 'initiator'
59+
}
4460
}
45-
}
46-
expect(searchAndReplaceSpecialAnnotations('this is @@author', payload)).toBe('this is @creator')
47-
})
4861

49-
test('replaces annotation anywhere in the text', () => {
50-
const payload = {
51-
user: {
52-
login: 'creator'
62+
for (const annotation of Object.keys(SPECIAL_ANNOTATION)) {
63+
const messageWithAnnotation = message.replace('$annotation$', annotation)
64+
const messageWithReplacement = expected.replace('$annotation$', SPECIAL_ANNOTATION[annotation])
65+
expect(searchAndReplaceSpecialAnnotations(messageWithAnnotation, payload, evt)).toBe(messageWithReplacement)
5366
}
5467
}
55-
expect(searchAndReplaceSpecialAnnotations('this is something@author speaking', payload)).toBe('this is somethingcreator speaking')
56-
})
68+
)
5769
})

docs/actions/assign.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ You can assign specific people to a pull request or issue.
66
::
77

88
- do: assign
9-
assignees: [ 'shine2lay', 'jusx', '@author' ] # only array accepted, use @author for PR/Issue author
9+
assignees: [ 'shine2lay', 'jusx', '@author' ] # only array accepted, use @author for PR/Issue author, use @sender for event initiator, use @bot for Mergable bot
1010

1111
Supported Events:
1212
::

docs/actions/comment.rst

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ You can add a comment to a pull request or issue.
99
payload:
1010
body: >
1111
Your very long comment can go here.
12+
Annotations are replaced:
13+
- @author
14+
- @sender
15+
- @bot
16+
- @repository
17+
- @action
18+
- {{formatDate}} # today's date and time
19+
- {{formatDate created_at}} # PR/issue creation date and time
20+
- {{title}} # PR/issue title
21+
- {{body}} # PR/issue description
1222
leave_old_comment: true # Optional, by default old comments are deleted, if true, old comments will be left alone
1323

1424
Supported Events:

docs/annotations.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ To bypass the annotation, use ``\`` prefix. (i.e ``\@author`` will be replaced w
88

99
::
1010

11-
@author : replace with the login of creator of issues/PR
11+
@author : replaced with the login of creator of issues/PR
12+
@sender : replaced with the login of initiator of the ocurred event
13+
@bot : replaced with the name of the Mergeable bot
14+
@repository : replaced with the name of repository of issues/PR
15+
@action : replaced with action of the ocurred event
1216

1317

1418
Actions supported:

docs/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
CHANGELOG
22
=====================================
33
| June 20, 2024: feat: Add options 'one_of' and 'none_of'. Support in filters `payload`, `author`, and in action `lastComment` to filter comments authors `#757 <https://github.com/mergeability/mergeable/pull/757>`_
4+
| June 20, 2024: feat: Add annotations @sender @bot @repository @action and template helper {{formatDate}} `#756 <https://github.com/mergeability/mergeable/pull/756>`_
45
| June 20, 2024: fix: Comments on Issues should not trigger `checks` action `#759 <https://github.com/mergeability/mergeable/pull/759>`_
56
| June 20, 2024: fix: Respect all comments in lastComment validator and comment action `#755 <https://github.com/mergeability/mergeable/pull/755>`_
67
| June 12, 2024: feat: Support `issue_comment` event as trigger for actions `#754 <https://github.com/mergeability/mergeable/pull/754>`_

lib/actions/assign.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ class Assign extends Action {
1515
async beforeValidate () {}
1616

1717
async afterValidate (context, settings, name, results) {
18+
const evt = this.getEventAttributes(context)
1819
const payload = this.getPayload(context)
1920
const issueNumber = payload.number
20-
const assignees = settings.assignees.map(assignee => searchAndReplaceSpecialAnnotations(assignee, payload))
21+
const assignees = settings.assignees.map(assignee => searchAndReplaceSpecialAnnotations(assignee, payload, evt))
2122
const checkResults = await Promise.all(assignees.map(
2223
assignee => assignee === payload.user.login
2324
? assignee

lib/actions/checks.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class Checks extends Action {
123123
populatePayloadWithResult (settings, results, context) {
124124
const output = {}
125125
Object.keys(settings).forEach(key => {
126-
output[key] = populateTemplate(settings[key], results, this.getPayload(context))
126+
output[key] = populateTemplate(settings[key], results, this.getPayload(context), this.getEventAttributes(context))
127127
})
128128

129129
return output

lib/actions/comment.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,15 @@ class Comment extends Action {
6060

6161
async afterValidate (context, settings, name, results) {
6262
const commentables = this.getActionables(context, results)
63+
const evt = this.getEventAttributes(context)
6364

6465
return Promise.all(
6566
// eslint-disable-next-line array-callback-return
6667
commentables.map(issue => {
6768
updateItemWithComment(
6869
context,
6970
issue.number,
70-
populateTemplate(settings.payload.body, results, issue),
71+
populateTemplate(settings.payload.body, results, issue, evt),
7172
settings.leave_old_comment,
7273
this
7374
)

0 commit comments

Comments
 (0)