Skip to content

Commit

Permalink
feat: Add options 'one_of' and 'none_of' for filters and validators
Browse files Browse the repository at this point in the history
  • Loading branch information
andekande committed Jun 20, 2024
1 parent 2ad0b2a commit d93bc55
Show file tree
Hide file tree
Showing 35 changed files with 1,002 additions and 116 deletions.
19 changes: 14 additions & 5 deletions __fixtures__/unit/helper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const _ = require('lodash')
const yaml = require('js-yaml')
const moment = require('moment-timezone')

const throwNotFound = () => {
const error = new Error('404 error')
Expand All @@ -18,11 +17,14 @@ module.exports = {
action: 'opened',
repository: {
name: (options.repoName) ? options.repoName : 'repoName',
full_name: 'name',
full_name: 'fullRepoName',
owner: {
login: 'owner'
}
},
sender: {
login: 'initiator'
},
check_suite: {
pull_requests: [
{
Expand All @@ -37,8 +39,8 @@ module.exports = {
title: (options.title) ? options.title : 'title',
body: options.body,
number: (options.number) ? options.number : 1,
created_at: options.createdAt ? moment(options.createdAt) : moment(),
updated_at: options.updatedAt ? moment(options.updatedAt) : moment(),
created_at: (options.createdAt) ? options.createdAt : new Date().toISOString(),
updated_at: (options.updatedAt) ? options.updatedAt : new Date().toISOString(),
milestone: (options.milestone) ? options.milestone : null,
requested_reviewers: options.requestedReviewers ? options.requestedReviewers : [],
requested_teams: options.requestedTeams ? options.requestedTeams : [],
Expand All @@ -64,9 +66,16 @@ module.exports = {
user: {
login: 'creator'
},
title: (options.title) ? options.title : 'title',
body: options.body,
number: (options.number) ? options.number : 1,
milestone: (options.milestone) ? options.milestone : null,
created_at: (options.createdAt) ? options.createdAt : new Date().toISOString(),
updated_at: (options.updatedAt) ? options.updatedAt : new Date().toISOString(),
assignees: (options.assignees) ? options.assignees : [],
pull_request: {}
}
},
comment: options.issueComment
},
log: {
child: (s) => {
Expand Down
28 changes: 28 additions & 0 deletions __tests__/unit/actions/action.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,31 @@ describe('Action#getActionables', () => {
).toBe(1)
})
})

describe('Action#getEventAttributes', () => {
const action = new Action()

test('Extracts event properties from pull_request correctly', () => {
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'pull_request' }))

expect(evt.action).toBe('opened')
expect(evt.repository.full_name).toBe('fullRepoName')
expect(evt.sender.login).toBe('initiator')
})

test('Extracts event properties from issues correctly', () => {
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'issues' }))

expect(evt.action).toBe('opened')
expect(evt.repository.full_name).toBe('fullRepoName')
expect(evt.sender.login).toBe('initiator')
})

test('Defaults event properties on schedule event', () => {
const evt = action.getEventAttributes(Helper.mockContext({ eventName: 'schedule' }))

expect(evt.action).toBe('')
expect(evt.repository).toEqual({})
expect(evt.sender).toEqual({})
})
})
6 changes: 4 additions & 2 deletions __tests__/unit/actions/assign.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ test('check that assignees are added when afterValidate is called with proper pa
expect(context.octokit.issues.addAssignees.mock.calls[0][0].assignees[1]).toBe('testuser2')
})

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

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

test('check only authorized users are added as assignee ', async () => {
Expand Down
38 changes: 38 additions & 0 deletions __tests__/unit/actions/checks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,44 @@ test('that afterValidate is called with properly and output is correct', async (
expect(MetaData.exists(output.text)).toBe(false)
})

test('that afterValidate is replacing special annotations in payload', async () => {
const checks = new Checks()
const context = createMockContext()
const result = {
status: 'pass',
validations: [{
status: 'pass',
name: 'Label'
}],
completed_at: '2024-06-15T19:14:00Z'
}
const settings = {
payload: {
title: '@author @sender @bot @repository @action {{formatDate completed_at}} , completed!',
summary: '@author @sender @bot @repository @action {{formatDate completed_at}} , summary',
text: '@author @sender @bot @repository @action {{formatDate completed_at}} , text'
}
}

const name = undefined

checks.checkRunResult = new Map()

checks.checkRunResult.set(name, {
data: {
id: '3'
}
})

await checks.afterValidate(context, settings, name, result)
const output = context.octokit.checks.update.mock.calls[0][0].output
expect(context.octokit.checks.update.mock.calls.length).toBe(1)
expect(output.title).toBe('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , completed!')
expect(output.summary).toBe('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , summary')
expect(output.text).toContain('creator initiator Mergeable[bot] fullRepoName actionName Jun 15, 2024, 7:14 PM , text')
expect(MetaData.exists(output.text)).toBe(true)
})

test('that afterValidate is correct when validation fails', async () => {
const checks = new Checks()
const context = createMockContext()
Expand Down
13 changes: 7 additions & 6 deletions __tests__/unit/actions/comment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ test.each([

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

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

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

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

expect(context.octokit.issues.createComment.mock.calls[0][0].body).toBe('creator , do something!')
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!')
})

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

context.octokit.issues.createComment = jest.fn()
context.octokit.issues.deleteComment = jest.fn()
Expand Down
104 changes: 58 additions & 46 deletions __tests__/unit/actions/lib/searchAndReplaceSpecialAnnotation.test.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,69 @@
const searchAndReplaceSpecialAnnotations = require('../../../../lib/actions/lib/searchAndReplaceSpecialAnnotation')

describe('searchAndReplaceSpecialAnnotations', () => {
test('does not affect input if no special annotations are found', () => {
const payload = {
user: {
login: 'creator'
}
}
expect(searchAndReplaceSpecialAnnotations('no special annotations', payload)).toBe('no special annotations')
})

test('special annotation at the beginning of string works properly', () => {
const payload = {
user: {
login: 'creator'
}
}
expect(searchAndReplaceSpecialAnnotations('@author says hello!', payload)).toBe('creator says hello!')
})

test('escape character works properly', () => {
const payload = {
user: {
login: 'creator'
}
const SPECIAL_ANNOTATION = {
'@author': 'creator',
'@action': 'created',
'@sender': 'initiator',
'@bot': 'Mergeable[bot]',
'@repository': 'botrepo'
}
const tests = [
{
name: 'does not affect input if no special annotations are found',
message: 'no special annotations',
expected: 'no special annotations'
},
{
name: 'special annotation at the beginning of string works properly',
message: '$annotation$ says hello!',
expected: '$annotation$ says hello!'
},
{
name: 'escape character works properly',
message: 'this is \\@author',
expected: 'this is @author'
},
{
name: 'special annotation at the end of string works properly',
message: 'this is $annotation$',
expected: 'this is $annotation$'
},
{
name: '@@annotation is replaced, prepending @ remains',
message: 'this is @$annotation$',
expected: 'this is @$annotation$'
},
{
name: 'replaces special annotation anywhere in the text',
message: 'this is something$annotation$ speaking',
expected: 'this is something$annotation$ speaking'
}
expect(searchAndReplaceSpecialAnnotations('this is \\@author', payload)).toBe('this is @author')
})
]

test('@author is replaced by payload.user.login', () => {
const payload = {
user: {
login: 'creator'
test.each(tests)(
'$name',
async ({ message, expected }) => {
const payload = {
user: {
login: 'creator'
}
}
}
expect(searchAndReplaceSpecialAnnotations('this is @author', payload)).toBe('this is creator')
})

test('@@author is replaced by @payload.user.login', () => {
const payload = {
user: {
login: 'creator'
const evt = {
action: 'created',
repository: {
full_name: 'botrepo'
},
sender: {
login: 'initiator'
}
}
}
expect(searchAndReplaceSpecialAnnotations('this is @@author', payload)).toBe('this is @creator')
})

test('replaces annotation anywhere in the text', () => {
const payload = {
user: {
login: 'creator'
for (const annotation of Object.keys(SPECIAL_ANNOTATION)) {
const messageWithAnnotation = message.replace('$annotation$', annotation)
const messageWithReplacement = expected.replace('$annotation$', SPECIAL_ANNOTATION[annotation])
expect(searchAndReplaceSpecialAnnotations(messageWithAnnotation, payload, evt)).toBe(messageWithReplacement)
}
}
expect(searchAndReplaceSpecialAnnotations('this is something@author speaking', payload)).toBe('this is somethingcreator speaking')
})
)
})
Loading

0 comments on commit d93bc55

Please sign in to comment.