= ({ notification, hostname }) => {
{reason.title}
{updatedAt}
+ {notification.subject?.linkedIssues?.length > 0 && (
+
+
+
+ )}
{notification.subject.reviews
? notification.subject.reviews.map((review) => {
const icon = getPullRequestReviewIcon(review);
diff --git a/src/components/__snapshots__/NotificationRow.test.tsx.snap b/src/components/__snapshots__/NotificationRow.test.tsx.snap
index 58f5668d1..6479765ad 100644
--- a/src/components/__snapshots__/NotificationRow.test.tsx.snap
+++ b/src/components/__snapshots__/NotificationRow.test.tsx.snap
@@ -1,5 +1,1143 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`components/NotificationRow.tsx linked issues/prs should render when linked to multiple issues/prs 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+ I am a robot and this is a test!
+
+
+
+
+
+
+
+
+ Updated
+
+
+ over 6 years ago
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ "container":
+
+
+
+
+ I am a robot and this is a test!
+
+
+
+
+
+
+
+
+ Updated
+
+
+ over 6 years ago
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
+
+exports[`components/NotificationRow.tsx linked issues/prs should render when linked to one issue/pr 1`] = `
+{
+ "asFragment": [Function],
+ "baseElement":
+
+
+
+
+
+ I am a robot and this is a test!
+
+
+
+
+
+
+
+
+ Updated
+
+
+ over 6 years ago
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ "container":
+
+
+
+
+ I am a robot and this is a test!
+
+
+
+
+
+
+
+
+ Updated
+
+
+ over 6 years ago
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
,
+ "debug": [Function],
+ "findAllByAltText": [Function],
+ "findAllByDisplayValue": [Function],
+ "findAllByLabelText": [Function],
+ "findAllByPlaceholderText": [Function],
+ "findAllByRole": [Function],
+ "findAllByTestId": [Function],
+ "findAllByText": [Function],
+ "findAllByTitle": [Function],
+ "findByAltText": [Function],
+ "findByDisplayValue": [Function],
+ "findByLabelText": [Function],
+ "findByPlaceholderText": [Function],
+ "findByRole": [Function],
+ "findByTestId": [Function],
+ "findByText": [Function],
+ "findByTitle": [Function],
+ "getAllByAltText": [Function],
+ "getAllByDisplayValue": [Function],
+ "getAllByLabelText": [Function],
+ "getAllByPlaceholderText": [Function],
+ "getAllByRole": [Function],
+ "getAllByTestId": [Function],
+ "getAllByText": [Function],
+ "getAllByTitle": [Function],
+ "getByAltText": [Function],
+ "getByDisplayValue": [Function],
+ "getByLabelText": [Function],
+ "getByPlaceholderText": [Function],
+ "getByRole": [Function],
+ "getByTestId": [Function],
+ "getByText": [Function],
+ "getByTitle": [Function],
+ "queryAllByAltText": [Function],
+ "queryAllByDisplayValue": [Function],
+ "queryAllByLabelText": [Function],
+ "queryAllByPlaceholderText": [Function],
+ "queryAllByRole": [Function],
+ "queryAllByTestId": [Function],
+ "queryAllByText": [Function],
+ "queryAllByTitle": [Function],
+ "queryByAltText": [Function],
+ "queryByDisplayValue": [Function],
+ "queryByLabelText": [Function],
+ "queryByPlaceholderText": [Function],
+ "queryByRole": [Function],
+ "queryByTestId": [Function],
+ "queryByText": [Function],
+ "queryByTitle": [Function],
+ "rerender": [Function],
+ "unmount": [Function],
+}
+`;
+
exports[`components/NotificationRow.tsx rendering for notification comments count should render when 1 comment 1`] = `
{
"asFragment": [Function],
diff --git a/src/typesGitHub.ts b/src/typesGitHub.ts
index 416b0164a..46f0ee943 100644
--- a/src/typesGitHub.ts
+++ b/src/typesGitHub.ts
@@ -264,6 +264,7 @@ export interface GitifySubject {
state?: StateType;
user?: SubjectUser;
reviews?: GitifyPullRequestReview[];
+ linkedIssues?: string[];
comments?: number;
}
diff --git a/src/utils/subject.test.ts b/src/utils/subject.test.ts
index 43419582b..9ea16cad2 100644
--- a/src/utils/subject.test.ts
+++ b/src/utils/subject.test.ts
@@ -17,6 +17,7 @@ import {
getGitifySubjectDetails,
getLatestReviewForReviewers,
getWorkflowRunAttributes,
+ parseLinkedIssuesFromPrBody,
} from './subject';
const mockAuthor = partialMockUser('some-author');
@@ -557,6 +558,7 @@ describe('utils/subject.ts', () => {
type: mockCommenter.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -589,6 +591,7 @@ describe('utils/subject.ts', () => {
type: mockCommenter.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -621,6 +624,7 @@ describe('utils/subject.ts', () => {
type: mockCommenter.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -653,6 +657,7 @@ describe('utils/subject.ts', () => {
type: mockCommenter.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -684,6 +689,7 @@ describe('utils/subject.ts', () => {
type: mockAuthor.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -714,6 +720,7 @@ describe('utils/subject.ts', () => {
type: mockAuthor.type,
},
reviews: null,
+ linkedIssues: [],
});
});
@@ -774,6 +781,19 @@ describe('utils/subject.ts', () => {
expect(result).toBeNull();
});
});
+
+ describe('Pull Request Reviews - Extract Linked Issues', () => {
+ it('returns empty if no pr body', () => {
+ const result = parseLinkedIssuesFromPrBody(null);
+ expect(result).toEqual([]);
+ });
+
+ it('returns linked issues', () => {
+ const mockPrBody = 'This PR is linked to #1, #2, and #3';
+ const result = parseLinkedIssuesFromPrBody(mockPrBody);
+ expect(result).toEqual(['#1', '#2', '#3']);
+ });
+ });
});
describe('Releases', () => {
diff --git a/src/utils/subject.ts b/src/utils/subject.ts
index ffcbe2c04..45085a41a 100644
--- a/src/utils/subject.ts
+++ b/src/utils/subject.ts
@@ -264,6 +264,7 @@ async function getGitifySubjectForPullRequest(
}
const reviews = await getLatestReviewForReviewers(notification);
+ const linkedIssues = parseLinkedIssuesFromPrBody(pr.body);
return {
state: prState,
@@ -275,6 +276,7 @@ async function getGitifySubjectForPullRequest(
},
reviews: reviews,
comments: pr.comments,
+ linkedIssues: linkedIssues,
};
}
@@ -329,6 +331,26 @@ export async function getLatestReviewForReviewers(
});
}
+export function parseLinkedIssuesFromPrBody(body: string): string[] {
+ const linkedIssues: string[] = [];
+
+ if (!body) {
+ return linkedIssues;
+ }
+
+ const regexPattern = /\s*#(\d+)\s*/gi;
+
+ const matches = body.matchAll(regexPattern);
+
+ for (const match of matches) {
+ if (match[0]) {
+ linkedIssues.push(match[0].trim());
+ }
+ }
+
+ return linkedIssues;
+}
+
async function getGitifySubjectForRelease(
notification: Notification,
): Promise
{