diff --git a/src/utils/__snapshots__/api-requests.test.ts.snap b/src/utils/__snapshots__/api-requests.test.ts.snap index 86a0ee8e5..420db2eb8 100644 --- a/src/utils/__snapshots__/api-requests.test.ts.snap +++ b/src/utils/__snapshots__/api-requests.test.ts.snap @@ -1,34 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`apiRequest should make a request with the correct parameters 1`] = ` +exports[`apiRequestAuth should make an authenticated request with the correct parameters 1`] = ` { "Accept": "application/json", + "Authorization": "token yourAuthToken", "Cache-Control": "no-cache", "Content-Type": "application/json", } `; -exports[`apiRequest should make a request with the correct parameters and default data 1`] = ` +exports[`apiRequestAuth should make an authenticated request with the correct parameters and default data 1`] = ` { "Accept": "application/json", + "Authorization": "token yourAuthToken", "Cache-Control": "no-cache", "Content-Type": "application/json", } `; -exports[`apiRequestAuth should make an authenticated request with the correct parameters 1`] = ` +exports[`utils/api-requests.ts should make a request with the correct parameters 1`] = ` { "Accept": "application/json", - "Authorization": "token yourAuthToken", "Cache-Control": "no-cache", "Content-Type": "application/json", } `; -exports[`apiRequestAuth should make an authenticated request with the correct parameters and default data 1`] = ` +exports[`utils/api-requests.ts should make a request with the correct parameters and default data 1`] = ` { "Accept": "application/json", - "Authorization": "token yourAuthToken", "Cache-Control": "no-cache", "Content-Type": "application/json", } diff --git a/src/utils/api-requests.test.ts b/src/utils/api-requests.test.ts index 9bb716488..7d0bc6637 100644 --- a/src/utils/api-requests.test.ts +++ b/src/utils/api-requests.test.ts @@ -6,7 +6,11 @@ jest.mock('axios'); const url = 'https://example.com'; const method = 'get'; -describe('apiRequest', () => { +describe('utils/api-requests.ts', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + it('should make a request with the correct parameters', async () => { const data = { key: 'value' }; @@ -38,6 +42,10 @@ describe('apiRequest', () => { describe('apiRequestAuth', () => { const token = 'yourAuthToken'; + afterEach(() => { + jest.resetAllMocks(); + }); + it('should make an authenticated request with the correct parameters', async () => { const data = { key: 'value' }; diff --git a/src/utils/api-requests.ts b/src/utils/api-requests.ts index 8938d52b7..47e0066e4 100644 --- a/src/utils/api-requests.ts +++ b/src/utils/api-requests.ts @@ -4,7 +4,7 @@ export function apiRequest( url: string, method: Method, data = {}, -): AxiosPromise { +): AxiosPromise | null { axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.common['Content-Type'] = 'application/json'; axios.defaults.headers.common['Cache-Control'] = 'no-cache'; @@ -16,7 +16,7 @@ export function apiRequestAuth( method: Method, token: string, data = {}, -): AxiosPromise { +): AxiosPromise | null { axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.common['Authorization'] = `token ${token}`; axios.defaults.headers.common['Cache-Control'] = 'no-cache'; diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts index bea3101cb..dfe3f6e77 100644 --- a/src/utils/helpers.test.ts +++ b/src/utils/helpers.test.ts @@ -445,6 +445,34 @@ describe('utils/helpers.ts', () => { `https://github.com/manosim/notifications-test/discussions/612?${mockedNotificationReferrer}#discussioncomment-2300902`, ); }); + + it('default to base discussions url when graphql query fails', async () => { + const subject = { + title: '1.16.0', + url: null, + latest_comment_url: null, + type: 'Discussion' as SubjectType, + }; + + const requestPromise = new Promise((resolve) => + resolve(null as AxiosResponse), + ) as AxiosPromise; + + apiRequestAuthMock.mockResolvedValue(requestPromise); + + const result = await generateGitHubWebUrl( + { + ...mockedSingleNotification, + subject: subject, + }, + mockAccounts, + ); + + expect(apiRequestAuthMock).toHaveBeenCalledTimes(1); + expect(result).toBe( + `https://github.com/manosim/notifications-test/discussions?${mockedNotificationReferrer}`, + ); + }); }); it('Repository Invitation url', async () => { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6da0b2525..016605e88 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -136,8 +136,9 @@ async function getDiscussionUrl( let latestCommentId: string | number; - if (comments?.length) { - latestCommentId = getLatestDiscussionComment(comments)?.databaseId; + latestCommentId = getLatestDiscussionComment(comments)?.databaseId; + + if (latestCommentId) { url += `#discussioncomment-${latestCommentId}`; } } @@ -218,9 +219,13 @@ export async function fetchDiscussion( export function getLatestDiscussionComment( comments: DiscussionCommentNode[], ): DiscussionSubcommentNode | null { + if (!comments || comments.length == 0) { + return null; + } + return comments .flatMap((comment) => comment.replies.nodes) - .concat([comments.at(-1)]) + .concat([comments[comments.length - 1]]) .reduce((a, b) => (a.createdAt > b.createdAt ? a : b)); } diff --git a/src/utils/subject.test.ts b/src/utils/subject.test.ts index b2dc8597a..77f3c2ddd 100644 --- a/src/utils/subject.test.ts +++ b/src/utils/subject.test.ts @@ -134,461 +134,503 @@ describe('utils/subject.ts', () => { }); }); - describe('getGitifySubjectForDiscussion', () => { - it('answered discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is an answered discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is an answered discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: null, - isAnswered: true, - comments: { - nodes: [], //TODO - Update this to have real data + describe('getGitifySubjectDetails', () => { + describe('Discussions', () => { + it('answered discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is an answered discussion', + type: 'Discussion' as SubjectType, + }, + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is an answered discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: null, + isAnswered: true, + comments: { + nodes: [], //TODO - Update this to have real data + }, }, - }, - ], + ], + }, }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('ANSWERED'); + expect(result.user).toBe(null); + }); + + it('duplicate discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is a duplicate discussion', + type: 'Discussion' as SubjectType, }, - }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('ANSWERED'); - expect(result.user).toBe(null); - }); - - it('duplicate discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is a duplicate discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is a duplicate discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: 'DUPLICATE', - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is a duplicate discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: 'DUPLICATE', + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, }, - }, - ], + ], + }, }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('DUPLICATE'); + expect(result.user).toBe(null); + }); + + it('open discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is an open discussion', + type: 'Discussion' as SubjectType, }, - }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('DUPLICATE'); - expect(result.user).toBe(null); - }); - - it('open discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is an open discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is an open discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: null, - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is an open discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: null, + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, }, - }, - ], + ], + }, }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('OPEN'); + expect(result.user).toBe(null); + }); + + it('outdated discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is an outdated discussion', + type: 'Discussion' as SubjectType, }, - }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('OPEN'); - expect(result.user).toBe(null); - }); - - it('outdated discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is an outdated discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is an outdated discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: 'OUTDATED', - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is an outdated discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: 'OUTDATED', + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, }, - }, - ], + ], + }, }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('OUTDATED'); + expect(result.user).toBe(null); + }); + + it('reopened discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is a reopened discussion', + type: 'Discussion' as SubjectType, }, - }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('OUTDATED'); - expect(result.user).toBe(null); - }); - - it('reopened discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is a reopened discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is a reopened discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: 'REOPENED', - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is a reopened discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: 'REOPENED', + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, }, - }, - ], + ], + }, }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('REOPENED'); + expect(result.user).toBe(null); + }); + + it('resolved discussion state', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is a resolved discussion', + type: 'Discussion' as SubjectType, }, - }); + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is a resolved discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: 'RESOLVED', + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, + }, + ], + }, + }, + }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('RESOLVED'); + expect(result.user).toBe(null); + }); + + it('filtered response by subscribed', async () => { + const mockNotification = { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + title: 'This is a discussion', + type: 'Discussion' as SubjectType, + }, + }; + + nock('https://api.github.com') + .post('/graphql') + .reply(200, { + data: { + search: { + nodes: [ + { + title: 'This is a discussion', + viewerSubscription: 'SUBSCRIBED', + stateReason: null, + isAnswered: false, + comments: { + nodes: [], //TODO - Update this to have real data + }, + }, + { + title: 'This is a discussion', + viewerSubscription: 'IGNORED', + stateReason: null, + isAnswered: true, + comments: { + nodes: [], //TODO - Update this to have real data + }, + }, + ], + }, + }, + }); - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); - expect(result.state).toBe('REOPENED'); - expect(result.user).toBe(null); + expect(result.state).toBe('OPEN'); + expect(result.user).toBe(null); + }); }); - it('resolved discussion state', async () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - title: 'This is a resolved discussion', - type: 'Discussion' as SubjectType, - }, - }; - - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is a resolved discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: 'RESOLVED', - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data - }, - }, - ], + describe('Issues', () => { + it('open issue state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open' }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockedSingleNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('open'); + expect(result.user).toBe('some-user'); + }); + + it('closed issue state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'closed' }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockedSingleNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('closed'); + expect(result.user).toBe('some-user'); + }); + + it('completed issue state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'closed', state_reason: 'completed' }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockedSingleNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('completed'); + expect(result.user).toBe('some-user'); + }); + + it('not_planned issue state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', state_reason: 'not_planned' }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockedSingleNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('not_planned'); + expect(result.user).toBe('some-user'); + }); + + it('reopened issue state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', state_reason: 'reopened' }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockedSingleNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('reopened'); + expect(result.user).toBe('some-user'); + }); + + it('handle issues without latest_comment_url', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', draft: false, merged: false }); + + const result = await getGitifySubjectDetails( + { + ...mockedSingleNotification, + subject: { + ...mockedSingleNotification.subject, + latest_comment_url: null, }, }, - }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); + mockAccounts.token, + ); - expect(result.state).toBe('RESOLVED'); - expect(result.user).toBe(null); + expect(result.state).toBe('open'); + expect(result.user).toBeNull(); + }); }); - it('filtered response by subscribed', async () => { + describe('Pull Requests', () => { const mockNotification = { ...mockedSingleNotification, subject: { ...mockedSingleNotification.subject, - title: 'This is a discussion', - type: 'Discussion' as SubjectType, + type: 'PullRequest' as SubjectType, }, }; - nock('https://api.github.com') - .post('/graphql') - .reply(200, { - data: { - search: { - nodes: [ - { - title: 'This is a discussion', - viewerSubscription: 'SUBSCRIBED', - stateReason: null, - isAnswered: false, - comments: { - nodes: [], //TODO - Update this to have real data - }, - }, - { - title: 'This is a discussion', - viewerSubscription: 'IGNORED', - stateReason: null, - isAnswered: true, - comments: { - nodes: [], //TODO - Update this to have real data - }, - }, - ], + it('closed pull request state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'closed', draft: false, merged: false }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('closed'); + expect(result.user).toBe('some-user'); + }); + + it('draft pull request state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', draft: true, merged: false }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('draft'); + expect(result.user).toBe('some-user'); + }); + + it('merged pull request state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', draft: false, merged: true }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('merged'); + expect(result.user).toBe('some-user'); + }); + + it('open pull request state', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', draft: false, merged: false }); + + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/comments/302888448') + .reply(200, { user: { login: 'some-user' } }); + + const result = await getGitifySubjectDetails( + mockNotification, + mockAccounts.token, + ); + + expect(result.state).toBe('open'); + expect(result.user).toBe('some-user'); + }); + + it('handle pull request without latest_comment_url', async () => { + nock('https://api.github.com') + .get('/repos/manosim/notifications-test/issues/1') + .reply(200, { state: 'open', draft: false, merged: false }); + + const result = await getGitifySubjectDetails( + { + ...mockNotification, + subject: { + ...mockNotification.subject, + latest_comment_url: null, }, }, - }); + mockAccounts.token, + ); - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('OPEN'); - expect(result.user).toBe(null); - }); - }); - - describe('getGitifySubjectForIssue', () => { - it('open issue state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open' }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockedSingleNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('open'); - expect(result.user).toBe('some-user'); - }); - - it('closed issue state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'closed' }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockedSingleNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('closed'); - expect(result.user).toBe('some-user'); - }); - - it('completed issue state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'closed', state_reason: 'completed' }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockedSingleNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('completed'); - expect(result.user).toBe('some-user'); - }); - - it('not_planned issue state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open', state_reason: 'not_planned' }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockedSingleNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('not_planned'); - expect(result.user).toBe('some-user'); - }); - - it('reopened issue state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open', state_reason: 'reopened' }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockedSingleNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('reopened'); - expect(result.user).toBe('some-user'); - }); - }); - - describe('getGitifySubjectForPullRequest', () => { - const mockNotification = { - ...mockedSingleNotification, - subject: { - ...mockedSingleNotification.subject, - type: 'PullRequest' as SubjectType, - }, - }; - - it('closed pull request state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'closed', draft: false, merged: false }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('closed'); - expect(result.user).toBe('some-user'); - }); - - it('draft pull request state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open', draft: true, merged: false }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('draft'); - expect(result.user).toBe('some-user'); - }); - - it('merged pull request state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open', draft: false, merged: true }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('merged'); - expect(result.user).toBe('some-user'); - }); - - it('open pull request state', async () => { - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/1') - .reply(200, { state: 'open', draft: false, merged: false }); - - nock('https://api.github.com') - .get('/repos/manosim/notifications-test/issues/comments/302888448') - .reply(200, { user: { login: 'some-user' } }); - - const result = await getGitifySubjectDetails( - mockNotification, - mockAccounts.token, - ); - - expect(result.state).toBe('open'); - expect(result.user).toBe('some-user'); + expect(result.state).toBe('open'); + expect(result.user).toBeNull(); + }); }); }); diff --git a/src/utils/subject.ts b/src/utils/subject.ts index e4d2a051a..96969a129 100644 --- a/src/utils/subject.ts +++ b/src/utils/subject.ts @@ -107,16 +107,12 @@ async function getGitifySubjectForDiscussion( } } - let comments = discussion.comments.nodes; - let discussionUser = null; - - if (comments?.length) { - discussionUser = getLatestDiscussionComment(comments)?.author.login; - } + const discussionUser = getLatestDiscussionComment(discussion.comments.nodes) + ?.author.login; return { state: discussionState, - user: discussionUser, + user: discussionUser ?? null, }; } @@ -132,7 +128,7 @@ async function getGitifySubjectForIssue( return { state: issue.state_reason ?? issue.state, - user: issueCommentUser.login, + user: issueCommentUser?.login ?? null, }; } @@ -144,8 +140,6 @@ async function getGitifySubjectForPullRequest( await apiRequestAuth(notification.subject.url, 'GET', token) ).data; - const prCommentUser = await getLatestCommentUser(notification, token); - let prState: PullRequestStateType = pr.state; if (pr.merged) { prState = 'merged'; @@ -153,9 +147,11 @@ async function getGitifySubjectForPullRequest( prState = 'draft'; } + const prCommentUser = await getLatestCommentUser(notification, token); + return { state: prState, - user: prCommentUser.login, + user: prCommentUser?.login ?? null, }; } @@ -217,12 +213,16 @@ function getWorkflowRunStatus(statusDisplayName: string): CheckSuiteStatus { async function getLatestCommentUser( notification: Notification, token: string, -): Promise { +): Promise | null { + if (!notification.subject.latest_comment_url) { + return null; + } + const response: IssueComments | ReleaseComments = ( await apiRequestAuth(notification.subject.latest_comment_url, 'GET', token) - ).data; + )?.data; return ( - (response as IssueComments).user ?? (response as ReleaseComments).author + (response as IssueComments)?.user ?? (response as ReleaseComments).author ); }