diff --git a/src/renderer/utils/notifications/handlers/checkSuite.test.ts b/src/renderer/utils/notifications/handlers/checkSuite.test.ts index 8f8da018b..7687cb3f0 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.test.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.test.ts @@ -3,6 +3,7 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; +import { IconColor } from '../../../types'; import { checkSuiteHandler, getCheckSuiteAttributes } from './checkSuite'; describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { @@ -182,6 +183,38 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { ).toBe('CheckIcon'); }); + it('iconColor', () => { + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: 'success' }), + ), + ).toBe(IconColor.GREEN); + + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: 'failure' }), + ), + ).toBe(IconColor.RED); + + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: 'cancelled' }), + ), + ).toBe(IconColor.GRAY); + + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: 'skipped' }), + ), + ).toBe(IconColor.GRAY); + + expect( + checkSuiteHandler.iconColor( + createMockSubject({ type: 'CheckSuite', state: null }), + ), + ).toBe(IconColor.GRAY); + }); + describe('getCheckSuiteState', () => { it('cancelled check suite state', async () => { const mockNotification = createPartialMockNotification({ diff --git a/src/renderer/utils/notifications/handlers/checkSuite.ts b/src/renderer/utils/notifications/handlers/checkSuite.ts index e4a6fcf36..dbeac0ae8 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.ts @@ -10,6 +10,7 @@ import { } from '@primer/octicons-react'; import type { SettingsState } from '../../../types'; +import { IconColor } from '../../../types'; import type { CheckSuiteAttributes, CheckSuiteStatus, @@ -17,7 +18,7 @@ import type { Notification, Subject, } from '../../../typesGitHub'; -import { DefaultHandler } from './default'; +import { DefaultHandler, defaultHandler } from './default'; class CheckSuiteHandler extends DefaultHandler { readonly type = 'CheckSuite'; @@ -52,6 +53,17 @@ class CheckSuiteHandler extends DefaultHandler { return RocketIcon; } } + + iconColor(subject: Subject): IconColor { + switch (subject.state) { + case 'success': + return IconColor.GREEN; + case 'failure': + return IconColor.RED; + default: + return defaultHandler.iconColor(subject); + } + } } export const checkSuiteHandler = new CheckSuiteHandler(); diff --git a/src/renderer/utils/notifications/handlers/default.test.ts b/src/renderer/utils/notifications/handlers/default.test.ts index caebed99b..3d1abebc8 100644 --- a/src/renderer/utils/notifications/handlers/default.test.ts +++ b/src/renderer/utils/notifications/handlers/default.test.ts @@ -32,28 +32,17 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { }); describe('iconColor', () => { - const cases: Array<[StateType | null, IconColor]> = [ - ['open' as StateType, IconColor.GREEN], - ['reopened' as StateType, IconColor.GREEN], - ['ANSWERED' as StateType, IconColor.GREEN], - ['success' as StateType, IconColor.GREEN], - ['closed' as StateType, IconColor.RED], - ['failure' as StateType, IconColor.RED], - ['completed' as StateType, IconColor.PURPLE], - ['RESOLVED' as StateType, IconColor.PURPLE], - ['merged' as StateType, IconColor.PURPLE], - ['not_planned' as StateType, IconColor.GRAY], - ['draft' as StateType, IconColor.GRAY], - ['skipped' as StateType, IconColor.GRAY], - ['cancelled' as StateType, IconColor.GRAY], - ['unknown' as StateType, IconColor.GRAY], - [null, IconColor.GRAY], - [undefined, IconColor.GRAY], - ]; + it('returns GRAY for any state (fallback behavior)', () => { + const states: Array = [ + 'unknown' as StateType, + null, + undefined, + ]; - it.each(cases)('returns correct color for state %s', (state, expected) => { - const subject = createMockSubject({ state }); - expect(defaultHandler.iconColor(subject)).toBe(expected); + states.forEach((state) => { + const subject = createMockSubject({ state }); + expect(defaultHandler.iconColor(subject)).toBe(IconColor.GRAY); + }); }); }); diff --git a/src/renderer/utils/notifications/handlers/default.ts b/src/renderer/utils/notifications/handlers/default.ts index 99f66b474..4b8bba274 100644 --- a/src/renderer/utils/notifications/handlers/default.ts +++ b/src/renderer/utils/notifications/handlers/default.ts @@ -28,23 +28,8 @@ export class DefaultHandler implements NotificationTypeHandler { return QuestionIcon; } - iconColor(subject: Subject): IconColor { - switch (subject.state) { - case 'open': - case 'reopened': - case 'ANSWERED': - case 'success': - return IconColor.GREEN; - case 'closed': - case 'failure': - return IconColor.RED; - case 'completed': - case 'RESOLVED': - case 'merged': - return IconColor.PURPLE; - default: - return IconColor.GRAY; - } + iconColor(_subject: Subject): IconColor { + return IconColor.GRAY; } formattedNotificationType(notification: Notification): string { diff --git a/src/renderer/utils/notifications/handlers/discussion.test.ts b/src/renderer/utils/notifications/handlers/discussion.test.ts index 5f28fb321..0b54c684e 100644 --- a/src/renderer/utils/notifications/handlers/discussion.test.ts +++ b/src/renderer/utils/notifications/handlers/discussion.test.ts @@ -6,7 +6,7 @@ import { createPartialMockNotification, } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import type { Link } from '../../../types'; +import { IconColor, type Link } from '../../../types'; import type { Owner, Repository } from '../../../typesGitHub'; import { type AuthorFieldsFragment, @@ -325,6 +325,38 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { ).displayName, ).toBe('DiscussionClosedIcon'); }); + + it('iconColor', () => { + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: 'ANSWERED' }), + ), + ).toBe(IconColor.GREEN); + + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: 'RESOLVED' }), + ), + ).toBe(IconColor.PURPLE); + + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: 'DUPLICATE' }), + ), + ).toBe(IconColor.GRAY); + + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: 'OUTDATED' }), + ), + ).toBe(IconColor.GRAY); + + expect( + discussionHandler.iconColor( + createMockSubject({ type: 'Discussion', state: 'OPEN' }), + ), + ).toBe(IconColor.GRAY); + }); }); function mockDiscussionNode( diff --git a/src/renderer/utils/notifications/handlers/discussion.ts b/src/renderer/utils/notifications/handlers/discussion.ts index cf66a8de7..eee06cbcf 100644 --- a/src/renderer/utils/notifications/handlers/discussion.ts +++ b/src/renderer/utils/notifications/handlers/discussion.ts @@ -11,6 +11,7 @@ import { import { differenceInMilliseconds } from 'date-fns'; import type { SettingsState } from '../../../types'; +import { IconColor } from '../../../types'; import type { DiscussionStateType, GitifySubject, @@ -24,7 +25,7 @@ import type { FetchDiscussionByNumberQuery, } from '../../api/graphql/generated/graphql'; import { isStateFilteredOut } from '../filters/filter'; -import { DefaultHandler } from './default'; +import { DefaultHandler, defaultHandler } from './default'; type DiscussionComment = NonNullable< NonNullable< @@ -110,6 +111,17 @@ class DiscussionHandler extends DefaultHandler { return CommentDiscussionIcon; } } + + iconColor(subject: Subject): IconColor { + switch (subject.state) { + case 'ANSWERED': + return IconColor.GREEN; + case 'RESOLVED': + return IconColor.PURPLE; + default: + return defaultHandler.iconColor(subject); + } + } } export const discussionHandler = new DiscussionHandler(); diff --git a/src/renderer/utils/notifications/handlers/issue.test.ts b/src/renderer/utils/notifications/handlers/issue.test.ts index 0a5bde114..7e8b2cfb2 100644 --- a/src/renderer/utils/notifications/handlers/issue.test.ts +++ b/src/renderer/utils/notifications/handlers/issue.test.ts @@ -7,7 +7,7 @@ import { } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; -import type { Link } from '../../../types'; +import { IconColor, type Link } from '../../../types'; import type { Notification } from '../../../typesGitHub'; import { issueHandler } from './issue'; @@ -341,4 +341,42 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { ).displayName, ).toBe('IssueReopenedIcon'); }); + + it('iconColor', () => { + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'open' }), + ), + ).toBe(IconColor.GREEN); + + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'reopened' }), + ), + ).toBe(IconColor.GREEN); + + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'closed' }), + ), + ).toBe(IconColor.RED); + + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'completed' }), + ), + ).toBe(IconColor.PURPLE); + + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'draft' }), + ), + ).toBe(IconColor.GRAY); + + expect( + issueHandler.iconColor( + createMockSubject({ type: 'Issue', state: 'not_planned' }), + ), + ).toBe(IconColor.GRAY); + }); }); diff --git a/src/renderer/utils/notifications/handlers/issue.ts b/src/renderer/utils/notifications/handlers/issue.ts index c351b7a5c..868bbe3bb 100644 --- a/src/renderer/utils/notifications/handlers/issue.ts +++ b/src/renderer/utils/notifications/handlers/issue.ts @@ -10,6 +10,7 @@ import { } from '@primer/octicons-react'; import type { SettingsState } from '../../../types'; +import { IconColor } from '../../../types'; import type { GitifySubject, Notification, @@ -18,7 +19,7 @@ import type { } from '../../../typesGitHub'; import { getIssue, getIssueOrPullRequestComment } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; -import { DefaultHandler } from './default'; +import { DefaultHandler, defaultHandler } from './default'; import { getSubjectUser } from './utils'; class IssueHandler extends DefaultHandler { @@ -76,6 +77,20 @@ class IssueHandler extends DefaultHandler { return IssueOpenedIcon; } } + + iconColor(subject: Subject): IconColor { + switch (subject.state) { + case 'open': + case 'reopened': + return IconColor.GREEN; + case 'closed': + return IconColor.RED; + case 'completed': + return IconColor.PURPLE; + default: + return defaultHandler.iconColor(subject); + } + } } export const issueHandler = new IssueHandler(); diff --git a/src/renderer/utils/notifications/handlers/pullRequest.test.ts b/src/renderer/utils/notifications/handlers/pullRequest.test.ts index c251d6fe2..494267779 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.test.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.test.ts @@ -7,7 +7,7 @@ import { } from '../../../__mocks__/notifications-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { createPartialMockUser } from '../../../__mocks__/user-mocks'; -import type { Link } from '../../../types'; +import { IconColor, type Link } from '../../../types'; import type { Notification, PullRequest } from '../../../typesGitHub'; import { getLatestReviewForReviewers, @@ -472,6 +472,38 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { ).toBe('GitMergeIcon'); }); + it('iconColor', () => { + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: 'open' }), + ), + ).toBe(IconColor.GREEN); + + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: 'reopened' }), + ), + ).toBe(IconColor.GREEN); + + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: 'closed' }), + ), + ).toBe(IconColor.RED); + + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: 'merged' }), + ), + ).toBe(IconColor.PURPLE); + + expect( + pullRequestHandler.iconColor( + createMockSubject({ type: 'PullRequest', state: 'draft' }), + ), + ).toBe(IconColor.GRAY); + }); + describe('Pull Request Reviews - Latest Reviews By Reviewer', () => { it('returns latest review state per reviewer', async () => { nock('https://api.github.com') diff --git a/src/renderer/utils/notifications/handlers/pullRequest.ts b/src/renderer/utils/notifications/handlers/pullRequest.ts index f58eccd98..739c475c8 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.ts @@ -9,6 +9,7 @@ import { } from '@primer/octicons-react'; import type { Link, SettingsState } from '../../../types'; +import { IconColor } from '../../../types'; import type { GitifyPullRequestReview, GitifySubject, @@ -25,7 +26,7 @@ import { getPullRequestReviews, } from '../../api/client'; import { isStateFilteredOut, isUserFilteredOut } from '../filters/filter'; -import { DefaultHandler } from './default'; +import { DefaultHandler, defaultHandler } from './default'; import { getSubjectUser } from './utils'; class PullRequestHandler extends DefaultHandler { @@ -99,6 +100,20 @@ class PullRequestHandler extends DefaultHandler { return GitPullRequestIcon; } } + + iconColor(subject: Subject): IconColor { + switch (subject.state) { + case 'open': + case 'reopened': + return IconColor.GREEN; + case 'closed': + return IconColor.RED; + case 'merged': + return IconColor.PURPLE; + default: + return defaultHandler.iconColor(subject); + } + } } export const pullRequestHandler = new PullRequestHandler();