diff --git a/src/components/NotificationRow.test.tsx b/src/components/NotificationRow.test.tsx index 71be711e6..1f9e6f0c8 100644 --- a/src/components/NotificationRow.test.tsx +++ b/src/components/NotificationRow.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { shell } from 'electron'; import { mockAuth, mockSettings } from '../__mocks__/state-mocks'; import { AppContext } from '../context/App'; -import type { UserType } from '../typesGitHub'; +import type { Milestone, UserType } from '../typesGitHub'; import { mockSingleNotification } from '../utils/api/__mocks__/response-mocks'; import * as helpers from '../utils/helpers'; import { NotificationRow } from './NotificationRow'; @@ -64,111 +64,155 @@ describe('components/NotificationRow.tsx', () => { expect(tree).toMatchSnapshot(); }); - describe('rendering for notification comments count', () => { - it('should render when no comments', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); - - const mockNotification = mockSingleNotification; - mockNotification.subject.comments = null; - - const props = { - notification: mockNotification, - hostname: 'github.com', - }; - - const tree = render(); - expect(tree).toMatchSnapshot(); + describe('notification pills / metrics', () => { + describe('linked issue pills', () => { + it('should render issues pill when linked to one issue/pr', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.linkedIssues = ['#1']; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); + + it('should render issues pill when linked to multiple issues/prs', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.linkedIssues = ['#1', '#2']; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); }); - it('should render when 1 comment', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); - - const mockNotification = mockSingleNotification; - mockNotification.subject.comments = 1; - - const props = { - notification: mockNotification, - hostname: 'github.com', - }; - - const tree = render(); - expect(tree).toMatchSnapshot(); + describe('comment pills', () => { + it('should render when no comments', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.comments = null; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); + + it('should render when 1 comment', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.comments = 1; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); + + it('should render when more than 1 comments', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.comments = 2; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); }); - it('should render when more than 1 comments', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); + describe('label pills', () => { + it('should render labels pill', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); - const mockNotification = mockSingleNotification; - mockNotification.subject.comments = 2; + const mockNotification = mockSingleNotification; + mockNotification.subject.labels = ['enhancement', 'good-first-issue']; - const props = { - notification: mockNotification, - hostname: 'github.com', - }; + const props = { + notification: mockNotification, + hostname: 'github.com', + }; - const tree = render(); - expect(tree).toMatchSnapshot(); + const tree = render(); + expect(tree).toMatchSnapshot(); + }); }); - }); - - describe('notification labels', () => { - it('should render labels metric when available', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); - - const mockNotification = mockSingleNotification; - mockNotification.subject.labels = ['enhancement', 'good-first-issue']; - - const props = { - notification: mockNotification, - hostname: 'github.com', - }; - - const tree = render(); - expect(tree).toMatchSnapshot(); - }); - }); - - describe('linked issues/prs', () => { - it('should render when linked to one issue/pr', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); - - const mockNotification = mockSingleNotification; - mockNotification.subject.linkedIssues = ['#1']; - - const props = { - notification: mockNotification, - hostname: 'github.com', - }; - - const tree = render(); - expect(tree).toMatchSnapshot(); - }); - - it('should render when linked to multiple issues/prs', async () => { - jest - .spyOn(global.Date, 'now') - .mockImplementation(() => new Date('2024').valueOf()); - - const mockNotification = mockSingleNotification; - mockNotification.subject.linkedIssues = ['#1', '#2']; - - const props = { - notification: mockNotification, - hostname: 'github.com', - }; - const tree = render(); - expect(tree).toMatchSnapshot(); + describe('milestone pills', () => { + it('should render open milestone pill', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.milestone = { + title: 'Milestone 1', + state: 'open', + } as Milestone; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); + + it('should render closed milestone pill', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('2024').valueOf()); + + const mockNotification = mockSingleNotification; + mockNotification.subject.milestone = { + title: 'Milestone 1', + state: 'closed', + } as Milestone; + + const props = { + notification: mockNotification, + hostname: 'github.com', + }; + + const tree = render(); + expect(tree).toMatchSnapshot(); + }); }); }); diff --git a/src/components/NotificationRow.tsx b/src/components/NotificationRow.tsx index 1a5af4379..1ff46aa17 100644 --- a/src/components/NotificationRow.tsx +++ b/src/components/NotificationRow.tsx @@ -4,6 +4,7 @@ import { CommentIcon, FeedPersonIcon, IssueClosedIcon, + MilestoneIcon, ReadIcon, TagIcon, } from '@primer/octicons-react'; @@ -218,6 +219,24 @@ export const NotificationRow: FC = ({ notification, hostname }) => { )} + {notification.subject.milestone && ( + + + + + + )} diff --git a/src/components/__snapshots__/NotificationRow.test.tsx.snap b/src/components/__snapshots__/NotificationRow.test.tsx.snap index c2cac717f..1829920b0 100644 --- a/src/components/__snapshots__/NotificationRow.test.tsx.snap +++ b/src/components/__snapshots__/NotificationRow.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`components/NotificationRow.tsx linked issues/prs should render when linked to multiple issues/prs 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics comment pills should render when 1 comment 1`] = ` { "asFragment": [Function], "baseElement": @@ -162,14 +162,14 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin - 2 - - - - - - - - 2 + 1 @@ -446,14 +418,14 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin - 2 - - - - - - - - 2 + 1 @@ -625,7 +569,7 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin } `; -exports[`components/NotificationRow.tsx linked issues/prs should render when linked to one issue/pr 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics comment pills should render when more than 1 comments 1`] = ` { "asFragment": [Function], "baseElement": @@ -706,14 +650,14 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin - 1 + 2 - - - - - - 2 - - @@ -990,14 +906,14 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin - 1 + 2 - - - - - - 2 - - @@ -1250,7 +1138,7 @@ exports[`components/NotificationRow.tsx linked issues/prs should render when lin } `; -exports[`components/NotificationRow.tsx notification labels should render labels metric when available 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics comment pills should render when no comments 1`] = ` { "asFragment": [Function], "baseElement": @@ -1331,14 +1219,14 @@ exports[`components/NotificationRow.tsx notification labels should render labels - - 1 - - - - - - 1 + 2 - 2 + 1 - 2 + 1 @@ -1586,14 +1449,14 @@ exports[`components/NotificationRow.tsx notification labels should render labels - - 1 - - - - - - 1 + 2 - 2 + 1 - 2 + 1 @@ -1817,7 +1655,7 @@ exports[`components/NotificationRow.tsx notification labels should render labels } `; -exports[`components/NotificationRow.tsx rendering for notification comments count should render when 1 comment 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics label pills should render labels pill 1`] = ` { "asFragment": [Function], "baseElement": @@ -1898,14 +1736,14 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun + - 1 + 2 + + + + + + + + 1 - 1 + 2 + + + + + + + + 2 @@ -2123,6 +2018,35 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun > over 6 years ago + + + + + + + 2 + + - 1 + 2 + + + + + + + + 2 @@ -2328,7 +2280,7 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun } `; -exports[`components/NotificationRow.tsx rendering for notification comments count should render when more than 1 comments 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics linked issue pills should render issues pill when linked to multiple issues/prs 1`] = ` { "asFragment": [Function], "baseElement": @@ -2409,14 +2361,14 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun + - 1 + 2 1 @@ -2461,15 +2416,15 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun - 2 + 1 @@ -2636,14 +2591,14 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun + - 1 + 2 1 @@ -2688,15 +2646,15 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun - 2 + 1 @@ -2839,7 +2797,7 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun } `; -exports[`components/NotificationRow.tsx rendering for notification comments count should render when no comments 1`] = ` +exports[`components/NotificationRow.tsx notification pills / metrics linked issue pills should render issues pill when linked to one issue/pr 1`] = ` { "asFragment": [Function], "baseElement": @@ -2918,6 +2876,35 @@ exports[`components/NotificationRow.tsx rendering for notification comments coun > over 6 years ago + + + + + + + 1 + + over 6 years ago + + + + + + + 1 + + + + + + + + + + + + + I am a robot and this is a test! + + + + + + + + + + + Updated + + + over 6 years ago + + + + + + + + 2 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + "container": + + + + + + + + + + I am a robot and this is a test! + + + + + + + + + + + Updated + + + over 6 years ago + + + + + + + + 2 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + "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 notification pills / metrics milestone pills should render open milestone pill 1`] = ` +{ + "asFragment": [Function], + "baseElement": + + + + + + + + + + + I am a robot and this is a test! + + + + + + + + + + + Updated + + + over 6 years ago + + + + + + + + 2 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + "container": + + + + + + + + + + I am a robot and this is a test! + + + + + + + + + + + Updated + + + over 6 years ago + + + + + + + + 2 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 2 + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + , + "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 should render itself & its children 1`] = ` { "asFragment": [Function], diff --git a/src/typesGitHub.ts b/src/typesGitHub.ts index 316baee7c..17f0404f0 100644 --- a/src/typesGitHub.ts +++ b/src/typesGitHub.ts @@ -267,6 +267,7 @@ export interface GitifySubject { linkedIssues?: string[]; comments?: number; labels?: string[]; + milestone?: Milestone; } export interface PullRequest { @@ -289,6 +290,7 @@ export interface PullRequest { merged_at: string | null; merge_commit_sha: string | null; labels: Labels[]; + milestone: Milestone | null; draft: boolean; commits_url: string; review_comments_url: string; @@ -421,6 +423,8 @@ export interface Issue { user: User; state: IssueStateType; locked: boolean; + labels: Labels[]; + milestone: Milestone | null; comments: number; created_at: string; updated_at: string; @@ -428,7 +432,6 @@ export interface Issue { author_association: string; body: string; state_reason: IssueStateReasonType | null; - labels: Labels[]; } export interface IssueOrPullRequestComment { @@ -443,6 +446,27 @@ export interface IssueOrPullRequestComment { body: string; } +export interface Milestone { + url: string; + html_url: string; + labels_url: string; + id: number; + node_id: string; + number: number; + title: string; + description: string; + creator: User; + open_issues: number; + closed_issues: number; + state: MilestoneStateType; + created_at: string; + updated_at: string; + due_on: string | null; + closed_at: string | null; +} + +type MilestoneStateType = 'open' | 'closed'; + export interface Release { url: string; assets_url: string; diff --git a/src/utils/subject.ts b/src/utils/subject.ts index ffbc3736d..b0293189a 100644 --- a/src/utils/subject.ts +++ b/src/utils/subject.ts @@ -233,6 +233,7 @@ async function getGitifySubjectForIssue( }, comments: issue.comments, labels: issue.labels?.map((label) => label.name) ?? [], + milestone: issue.milestone, }; } @@ -280,6 +281,7 @@ async function getGitifySubjectForPullRequest( comments: pr.comments, labels: pr.labels?.map((label) => label.name) ?? [], linkedIssues: linkedIssues, + milestone: pr.milestone, }; }