Skip to content

Commit 898ca21

Browse files
setchyafonsojramos
authored andcommitted
feat: link checksuite notifications to actions (gitify-app#851)
Co-authored-by: Afonso Jorge Ramos <[email protected]>
1 parent bede307 commit 898ca21

File tree

2 files changed

+235
-47
lines changed

2 files changed

+235
-47
lines changed

src/utils/helpers.test.ts

+204-46
Original file line numberDiff line numberDiff line change
@@ -230,62 +230,220 @@ describe('utils/helpers.ts', () => {
230230
expect(result).toBe(`${mockedHtmlUrl}?${mockedNotificationReferrer}`);
231231
});
232232

233-
it('Discussions: when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
234-
const subject = {
235-
title: 'generate github web url unit tests',
236-
url: null,
237-
latest_comment_url: null,
238-
type: 'Discussion' as SubjectType,
239-
};
233+
describe('Check Suite URLs', () => {
234+
it('successful workflow', async () => {
235+
const subject = {
236+
title: 'Demo workflow run succeeded for main branch',
237+
url: null,
238+
latest_comment_url: null,
239+
type: 'CheckSuite' as SubjectType,
240+
};
240241

241-
const requestPromise = new Promise((resolve) =>
242-
resolve({
243-
data: {},
244-
} as AxiosResponse),
245-
) as AxiosPromise;
242+
const result = await generateGitHubWebUrl(
243+
{
244+
...mockedSingleNotification,
245+
subject: subject,
246+
},
247+
mockAccounts,
248+
);
246249

247-
apiRequestAuthMock.mockResolvedValue(requestPromise);
250+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
251+
expect(result).toBe(
252+
`https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Asuccess+branch%3Amain&${mockedNotificationReferrer}`,
253+
);
254+
});
248255

249-
const result = await generateGitHubWebUrl(
250-
{
251-
...mockedSingleNotification,
252-
subject: subject,
253-
},
254-
mockAccounts,
255-
);
256+
it('failed workflow', async () => {
257+
const subject = {
258+
title: 'Demo workflow run failed for main branch',
259+
url: null,
260+
latest_comment_url: null,
261+
type: 'CheckSuite' as SubjectType,
262+
};
256263

257-
expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
258-
expect(result).toBe(
259-
`${mockedSingleNotification.repository.html_url}/discussions?${mockedNotificationReferrer}`,
260-
);
264+
const result = await generateGitHubWebUrl(
265+
{
266+
...mockedSingleNotification,
267+
subject: subject,
268+
},
269+
mockAccounts,
270+
);
271+
272+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
273+
expect(result).toBe(
274+
`https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockedNotificationReferrer}`,
275+
);
276+
});
277+
278+
it('failed workflow multiple attempts', async () => {
279+
const subject = {
280+
title: 'Demo workflow run, Attempt #3 failed for main branch',
281+
url: null,
282+
latest_comment_url: null,
283+
type: 'CheckSuite' as SubjectType,
284+
};
285+
286+
const result = await generateGitHubWebUrl(
287+
{
288+
...mockedSingleNotification,
289+
subject: subject,
290+
},
291+
mockAccounts,
292+
);
293+
294+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
295+
expect(result).toBe(
296+
`https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockedNotificationReferrer}`,
297+
);
298+
});
299+
300+
it('skipped workflow', async () => {
301+
const subject = {
302+
title: 'Demo workflow run skipped for main branch',
303+
url: null,
304+
latest_comment_url: null,
305+
type: 'CheckSuite' as SubjectType,
306+
};
307+
308+
const result = await generateGitHubWebUrl(
309+
{
310+
...mockedSingleNotification,
311+
subject: subject,
312+
},
313+
mockAccounts,
314+
);
315+
316+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
317+
expect(result).toBe(
318+
`https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Askipped+branch%3Amain&${mockedNotificationReferrer}`,
319+
);
320+
});
321+
322+
it('unhandled workflow scenario', async () => {
323+
const subject = {
324+
title: 'unhandled workflow scenario',
325+
url: null,
326+
latest_comment_url: null,
327+
type: 'CheckSuite' as SubjectType,
328+
};
329+
330+
const result = await generateGitHubWebUrl(
331+
{
332+
...mockedSingleNotification,
333+
subject: subject,
334+
},
335+
mockAccounts,
336+
);
337+
338+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
339+
expect(result).toBe(
340+
`https://github.com/manosim/notifications-test/actions?${mockedNotificationReferrer}`,
341+
);
342+
});
343+
344+
it('unhandled status scenario', async () => {
345+
const subject = {
346+
title: 'Demo workflow run unhandled-status for main branch',
347+
url: null,
348+
latest_comment_url: null,
349+
type: 'CheckSuite' as SubjectType,
350+
};
351+
352+
const result = await generateGitHubWebUrl(
353+
{
354+
...mockedSingleNotification,
355+
subject: subject,
356+
},
357+
mockAccounts,
358+
);
359+
360+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
361+
expect(result).toBe(
362+
`https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+branch%3Amain&${mockedNotificationReferrer}`,
363+
);
364+
});
365+
366+
it('unhandled check suite scenario', async () => {
367+
const subject = {
368+
title: 'Unhandled scenario',
369+
url: null,
370+
latest_comment_url: null,
371+
type: 'CheckSuite' as SubjectType,
372+
};
373+
374+
const result = await generateGitHubWebUrl(
375+
{
376+
...mockedSingleNotification,
377+
subject: subject,
378+
},
379+
mockAccounts,
380+
);
381+
382+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
383+
expect(result).toBe(
384+
`https://github.com/manosim/notifications-test/actions?${mockedNotificationReferrer}`,
385+
);
386+
});
261387
});
262388

263-
it('Discussions: when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
264-
const subject = {
265-
title: '1.16.0',
266-
url: null,
267-
latest_comment_url: null,
268-
type: 'Discussion' as SubjectType,
269-
};
389+
describe('Discussions URLs', () => {
390+
it('when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
391+
const subject = {
392+
title: 'generate github web url unit tests',
393+
url: null,
394+
latest_comment_url: null,
395+
type: 'Discussion' as SubjectType,
396+
};
270397

271-
const requestPromise = new Promise((resolve) =>
272-
resolve(mockedGraphQLResponse as AxiosResponse),
273-
) as AxiosPromise;
398+
const requestPromise = new Promise((resolve) =>
399+
resolve({
400+
data: {},
401+
} as AxiosResponse),
402+
) as AxiosPromise;
274403

275-
apiRequestAuthMock.mockResolvedValue(requestPromise);
404+
apiRequestAuthMock.mockResolvedValue(requestPromise);
276405

277-
const result = await generateGitHubWebUrl(
278-
{
279-
...mockedSingleNotification,
280-
subject: subject,
281-
},
282-
mockAccounts,
283-
);
406+
const result = await generateGitHubWebUrl(
407+
{
408+
...mockedSingleNotification,
409+
subject: subject,
410+
},
411+
mockAccounts,
412+
);
284413

285-
expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
286-
expect(result).toBe(
287-
`https://github.com/manosim/notifications-test/discussions/612?${mockedNotificationReferrer}#discussioncomment-2300902`,
288-
);
414+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
415+
expect(result).toBe(
416+
`${mockedSingleNotification.repository.html_url}/discussions?${mockedNotificationReferrer}`,
417+
);
418+
});
419+
420+
it('when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
421+
const subject = {
422+
title: '1.16.0',
423+
url: null,
424+
latest_comment_url: null,
425+
type: 'Discussion' as SubjectType,
426+
};
427+
428+
const requestPromise = new Promise((resolve) =>
429+
resolve(mockedGraphQLResponse as AxiosResponse),
430+
) as AxiosPromise;
431+
432+
apiRequestAuthMock.mockResolvedValue(requestPromise);
433+
434+
const result = await generateGitHubWebUrl(
435+
{
436+
...mockedSingleNotification,
437+
subject: subject,
438+
},
439+
mockAccounts,
440+
);
441+
442+
expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
443+
expect(result).toBe(
444+
`https://github.com/manosim/notifications-test/discussions/612?${mockedNotificationReferrer}#discussioncomment-2300902`,
445+
);
446+
});
289447
});
290448

291449
it('Repository Invitation url', async () => {

src/utils/helpers.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
import { apiRequestAuth } from '../utils/api-requests';
99
import { openExternalLink } from '../utils/comms';
1010
import { Constants } from './constants';
11-
import { getWorkflowRunAttributes } from './state';
11+
import { getWorkflowRunAttributes, getCheckSuiteAttributes } from './state';
1212

1313
export function getEnterpriseAccountToken(
1414
hostname: string,
@@ -71,6 +71,33 @@ export async function getHtmlUrl(url: string, token: string): Promise<string> {
7171
return response.data.html_url;
7272
}
7373

74+
export function getCheckSuiteUrl(notification: Notification) {
75+
let url = `${notification.repository.html_url}/actions`;
76+
let filters = [];
77+
78+
const checkSuiteAttributes = getCheckSuiteAttributes(notification);
79+
80+
if (checkSuiteAttributes?.workflowName) {
81+
filters.push(
82+
`workflow:"${checkSuiteAttributes.workflowName.replaceAll(' ', '+')}"`,
83+
);
84+
}
85+
86+
if (checkSuiteAttributes?.status) {
87+
filters.push(`is:${checkSuiteAttributes.status}`);
88+
}
89+
90+
if (checkSuiteAttributes?.branchName) {
91+
filters.push(`branch:${checkSuiteAttributes.branchName}`);
92+
}
93+
94+
if (filters.length > 0) {
95+
url += `?query=${filters.join('+')}`;
96+
}
97+
98+
return url;
99+
}
100+
74101
export function getWorkflowRunUrl(notification: Notification) {
75102
let url = `${notification.repository.html_url}/actions`;
76103
let filters = [];
@@ -175,6 +202,9 @@ export async function generateGitHubWebUrl(
175202
} else {
176203
// Perform any specific notification type handling (only required for a few special notification scenarios)
177204
switch (notification.subject.type) {
205+
case 'CheckSuite':
206+
url = getCheckSuiteUrl(notification);
207+
break;
178208
case 'Discussion':
179209
url = await getDiscussionUrl(notification, accounts.token);
180210
break;

0 commit comments

Comments
 (0)