Repository: gitify-app/gitify
Pull request #851 (2024-03-03 14:46:39)

feat: link checksuite notifications to actions
Closes https://github.com/gitify-app/gitify/issues/381


Patch:
diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts
index 64cd9335..322fb628 100644
--- a/src/utils/helpers.ts
+++ b/src/utils/helpers.ts
@@ -8,7 +8,7 @@ import {
 import { apiRequestAuth } from '../utils/api-requests';
 import { openExternalLink } from '../utils/comms';
 import { Constants } from './constants';
-import { getWorkflowRunAttributes } from './state';
+import { getWorkflowRunAttributes, getCheckSuiteAttributes } from './state';
 
 export function getEnterpriseAccountToken(
   hostname: string,
@@ -71,6 +71,33 @@ export async function getHtmlUrl(url: string, token: string): Promise<string> {
   return response.data.html_url;
 }
 
+export function getCheckSuiteUrl(notification: Notification) {
+  let url = `${notification.repository.html_url}/actions`;
+  let filters = [];
+
+  const checkSuiteAttributes = getCheckSuiteAttributes(notification);
+
+  if (checkSuiteAttributes?.workflowName) {
+    filters.push(
+      `workflow:"${checkSuiteAttributes.workflowName.replaceAll(' ', '+')}"`,
+    );
+  }
+
+  if (checkSuiteAttributes?.status) {
+    filters.push(`is:${checkSuiteAttributes.status}`);
+  }
+
+  if (checkSuiteAttributes?.branchName) {
+    filters.push(`branch:${checkSuiteAttributes.branchName}`);
+  }
+
+  if (filters.length > 0) {
+    url += `?query=${filters.join('+')}`;
+  }
+
+  return url;
+}
+
 export function getWorkflowRunUrl(notification: Notification) {
   let url = `${notification.repository.html_url}/actions`;
   let filters = [];
@@ -175,6 +202,9 @@ export async function generateGitHubWebUrl(
   } else {
     // Perform any specific notification type handling (only required for a few special notification scenarios)
     switch (notification.subject.type) {
+      case 'CheckSuite':
+        url = getCheckSuiteUrl(notification);
+        break;
       case 'Discussion':
         url = await getDiscussionUrl(notification, accounts.token);
         break;


Test patch:
diff --git a/src/utils/helpers.test.ts b/src/utils/helpers.test.ts
index c4473f22..3f5e3625 100644
--- a/src/utils/helpers.test.ts
+++ b/src/utils/helpers.test.ts
@@ -230,62 +230,220 @@ describe('utils/helpers.ts', () => {
       expect(result).toBe(`${mockedHtmlUrl}?${mockedNotificationReferrer}`);
     });
 
-    it('Discussions: when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
-      const subject = {
-        title: 'generate github web url unit tests',
-        url: null,
-        latest_comment_url: null,
-        type: 'Discussion' as SubjectType,
-      };
+    describe('Check Suite URLs', () => {
+      it('successful workflow', async () => {
+        const subject = {
+          title: 'Demo workflow run succeeded for main branch',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
 
-      const requestPromise = new Promise((resolve) =>
-        resolve({
-          data: {},
-        } as AxiosResponse),
-      ) as AxiosPromise;
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
 
-      apiRequestAuthMock.mockResolvedValue(requestPromise);
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Asuccess+branch%3Amain&${mockedNotificationReferrer}`,
+        );
+      });
 
-      const result = await generateGitHubWebUrl(
-        {
-          ...mockedSingleNotification,
-          subject: subject,
-        },
-        mockAccounts,
-      );
+      it('failed workflow', async () => {
+        const subject = {
+          title: 'Demo workflow run failed for main branch',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
 
-      expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
-      expect(result).toBe(
-        `${mockedSingleNotification.repository.html_url}/discussions?${mockedNotificationReferrer}`,
-      );
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('failed workflow multiple attempts', async () => {
+        const subject = {
+          title: 'Demo workflow run, Attempt #3 failed for main branch',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
+
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Afailure+branch%3Amain&${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('skipped workflow', async () => {
+        const subject = {
+          title: 'Demo workflow run skipped for main branch',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
+
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+is%3Askipped+branch%3Amain&${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('unhandled workflow scenario', async () => {
+        const subject = {
+          title: 'unhandled workflow scenario',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
+
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('unhandled status scenario', async () => {
+        const subject = {
+          title: 'Demo workflow run unhandled-status for main branch',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
+
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?query=workflow%3A%22Demo%22+branch%3Amain&${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('unhandled check suite scenario', async () => {
+        const subject = {
+          title: 'Unhandled scenario',
+          url: null,
+          latest_comment_url: null,
+          type: 'CheckSuite' as SubjectType,
+        };
+
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
+
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(0);
+        expect(result).toBe(
+          `https://github.com/manosim/notifications-test/actions?${mockedNotificationReferrer}`,
+        );
+      });
     });
 
-    it('Discussions: when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
-      const subject = {
-        title: '1.16.0',
-        url: null,
-        latest_comment_url: null,
-        type: 'Discussion' as SubjectType,
-      };
+    describe('Discussions URLs', () => {
+      it('when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
+        const subject = {
+          title: 'generate github web url unit tests',
+          url: null,
+          latest_comment_url: null,
+          type: 'Discussion' as SubjectType,
+        };
 
-      const requestPromise = new Promise((resolve) =>
-        resolve(mockedGraphQLResponse as AxiosResponse),
-      ) as AxiosPromise;
+        const requestPromise = new Promise((resolve) =>
+          resolve({
+            data: {},
+          } as AxiosResponse),
+        ) as AxiosPromise;
 
-      apiRequestAuthMock.mockResolvedValue(requestPromise);
+        apiRequestAuthMock.mockResolvedValue(requestPromise);
 
-      const result = await generateGitHubWebUrl(
-        {
-          ...mockedSingleNotification,
-          subject: subject,
-        },
-        mockAccounts,
-      );
+        const result = await generateGitHubWebUrl(
+          {
+            ...mockedSingleNotification,
+            subject: subject,
+          },
+          mockAccounts,
+        );
 
-      expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
-      expect(result).toBe(
-        `https://github.com/manosim/notifications-test/discussions/612?${mockedNotificationReferrer}#discussioncomment-2300902`,
-      );
+        expect(apiRequestAuthMock).toHaveBeenCalledTimes(1);
+        expect(result).toBe(
+          `${mockedSingleNotification.repository.html_url}/discussions?${mockedNotificationReferrer}`,
+        );
+      });
+
+      it('when no subject urls and no discussions found via query, default to linking to repository discussions', async () => {
+        const subject = {
+          title: '1.16.0',
+          url: null,
+          latest_comment_url: null,
+          type: 'Discussion' as SubjectType,
+        };
+
+        const requestPromise = new Promise((resolve) =>
+          resolve(mockedGraphQLResponse 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/612?${mockedNotificationReferrer}#discussioncomment-2300902`,
+        );
+      });
     });
 
     it('Repository Invitation url', async () => {