From fdbbad65e5f264fb2e876cb3a70d59c3824c8f93 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sun, 9 Nov 2025 18:04:40 -0500 Subject: [PATCH 1/3] refactor(handlers): use notification instead of subject Signed-off-by: Adam Setch --- src/renderer/__mocks__/notifications-mocks.ts | 27 +++---- .../notifications/NotificationRow.tsx | 4 +- .../utils/notifications/group.test.ts | 52 ++++++------- .../notifications/handlers/checkSuite.test.ts | 12 +-- .../notifications/handlers/checkSuite.ts | 5 +- .../notifications/handlers/commit.test.ts | 5 +- .../utils/notifications/handlers/commit.ts | 3 +- .../notifications/handlers/default.test.ts | 10 +-- .../utils/notifications/handlers/default.ts | 7 +- .../notifications/handlers/discussion.test.ts | 13 ++-- .../notifications/handlers/discussion.ts | 5 +- .../notifications/handlers/issue.test.ts | 15 ++-- .../utils/notifications/handlers/issue.ts | 11 +-- .../handlers/pullRequest.test.ts | 13 ++-- .../notifications/handlers/pullRequest.ts | 5 +- .../notifications/handlers/release.test.ts | 4 +- .../utils/notifications/handlers/release.ts | 3 +- .../repositoryDependabotAlertsThread.test.ts | 4 +- .../repositoryDependabotAlertsThread.ts | 4 +- .../handlers/repositoryInvitation.test.ts | 4 +- .../handlers/repositoryInvitation.ts | 4 +- .../repositoryVulnerabilityAlert.test.ts | 4 +- .../handlers/repositoryVulnerabilityAlert.ts | 4 +- .../utils/notifications/handlers/types.ts | 5 +- .../handlers/workflowRun.test.ts | 4 +- .../notifications/handlers/workflowRun.ts | 3 +- .../utils/notifications/notifications.test.ts | 14 ++-- .../utils/notifications/utils.test.ts | 78 +++++++++---------- 28 files changed, 155 insertions(+), 167 deletions(-) diff --git a/src/renderer/__mocks__/notifications-mocks.ts b/src/renderer/__mocks__/notifications-mocks.ts index f4dadb94b..a113761db 100644 --- a/src/renderer/__mocks__/notifications-mocks.ts +++ b/src/renderer/__mocks__/notifications-mocks.ts @@ -1,10 +1,5 @@ import type { AccountNotifications, GitifyError } from '../types'; -import type { - Notification, - StateType, - Subject, - SubjectType, -} from '../typesGitHub'; +import type { Notification, StateType, SubjectType } from '../typesGitHub'; import { mockEnterpriseNotifications, mockGitHubNotifications, @@ -36,18 +31,20 @@ export const mockSingleAccountNotifications: AccountNotifications[] = [ }, ]; -export function createSubjectMock(mocks: { +export function mockNotificationWithSubject(mocks: { title?: string; type?: SubjectType; state?: StateType; -}): Subject { +}): Notification { return { - title: mocks.title ?? 'Mock Subject', - type: mocks.type ?? ('Unknown' as SubjectType), - state: mocks.state ?? ('Unknown' as StateType), - url: null, - latest_comment_url: null, - }; + subject: { + title: mocks.title ?? 'Mock Subject', + type: mocks.type ?? ('Unknown' as SubjectType), + state: mocks.state ?? ('Unknown' as StateType), + url: null, + latest_comment_url: null, + }, + } as Notification; } export function mockAccountWithError(error: GitifyError): AccountNotifications { @@ -58,7 +55,7 @@ export function mockAccountWithError(error: GitifyError): AccountNotifications { }; } -export function createMockNotificationForRepoName( +export function mockNotificationWithRepoName( id: string, repoFullName: string | null, ): Notification { diff --git a/src/renderer/components/notifications/NotificationRow.tsx b/src/renderer/components/notifications/NotificationRow.tsx index bf4404f48..1f7867529 100644 --- a/src/renderer/components/notifications/NotificationRow.tsx +++ b/src/renderer/components/notifications/NotificationRow.tsx @@ -63,8 +63,8 @@ export const NotificationRow: FC = ({ }; const handler = createNotificationHandler(notification); - const NotificationIcon = handler.iconType(notification.subject); - const iconColor = handler.iconColor(notification.subject); + const NotificationIcon = handler.iconType(notification); + const iconColor = handler.iconColor(notification); const notificationType = handler.formattedNotificationType(notification); const notificationNumber = handler.formattedNotificationNumber(notification); const notificationTitle = handler.formattedNotificationTitle(notification); diff --git a/src/renderer/utils/notifications/group.test.ts b/src/renderer/utils/notifications/group.test.ts index ba9d39642..88ec55865 100644 --- a/src/renderer/utils/notifications/group.test.ts +++ b/src/renderer/utils/notifications/group.test.ts @@ -1,4 +1,4 @@ -import { createMockNotificationForRepoName } from '../../__mocks__/notifications-mocks'; +import { mockNotificationWithRepoName } from '../../__mocks__/notifications-mocks'; import { mockSettings } from '../../__mocks__/state-mocks'; import { GroupBy } from '../../types'; import type { Notification } from '../../typesGitHub'; @@ -24,9 +24,9 @@ describe('renderer/utils/notifications/group.ts', () => { describe('groupNotificationsByRepository', () => { it('groups notifications by repository full_name', () => { const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-a'), - createMockNotificationForRepoName('2', 'owner/repo-b'), - createMockNotificationForRepoName('3', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-a'), + mockNotificationWithRepoName('2', 'owner/repo-b'), + mockNotificationWithRepoName('3', 'owner/repo-a'), ]; const result = groupNotificationsByRepository(notifications); @@ -38,10 +38,10 @@ describe('renderer/utils/notifications/group.ts', () => { it('preserves first-seen repository order', () => { const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-c'), - createMockNotificationForRepoName('2', 'owner/repo-a'), - createMockNotificationForRepoName('3', 'owner/repo-b'), - createMockNotificationForRepoName('4', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-c'), + mockNotificationWithRepoName('2', 'owner/repo-a'), + mockNotificationWithRepoName('3', 'owner/repo-b'), + mockNotificationWithRepoName('4', 'owner/repo-a'), ]; const result = groupNotificationsByRepository(notifications); @@ -52,9 +52,9 @@ describe('renderer/utils/notifications/group.ts', () => { it('skips notifications without repository data', () => { const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-a'), - createMockNotificationForRepoName('2', null), - createMockNotificationForRepoName('3', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-a'), + mockNotificationWithRepoName('2', null), + mockNotificationWithRepoName('3', 'owner/repo-a'), ]; const result = groupNotificationsByRepository(notifications); @@ -73,8 +73,8 @@ describe('renderer/utils/notifications/group.ts', () => { it('returns empty map when all notifications lack repository data', () => { const notifications: Notification[] = [ - createMockNotificationForRepoName('1', null), - createMockNotificationForRepoName('2', null), + mockNotificationWithRepoName('1', null), + mockNotificationWithRepoName('2', null), ]; const result = groupNotificationsByRepository(notifications); @@ -87,10 +87,10 @@ describe('renderer/utils/notifications/group.ts', () => { it('returns repository-grouped order when groupBy is REPOSITORY', () => { const settings = { ...mockSettings, groupBy: GroupBy.REPOSITORY }; const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-b'), - createMockNotificationForRepoName('2', 'owner/repo-a'), - createMockNotificationForRepoName('3', 'owner/repo-b'), - createMockNotificationForRepoName('4', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-b'), + mockNotificationWithRepoName('2', 'owner/repo-a'), + mockNotificationWithRepoName('3', 'owner/repo-b'), + mockNotificationWithRepoName('4', 'owner/repo-a'), ]; const result = getFlattenedNotificationsByRepo(notifications, settings); @@ -102,9 +102,9 @@ describe('renderer/utils/notifications/group.ts', () => { it('returns natural account order when groupBy is DATE', () => { const settings = { ...mockSettings, groupBy: GroupBy.DATE }; const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-b'), - createMockNotificationForRepoName('2', 'owner/repo-a'), - createMockNotificationForRepoName('3', 'owner/repo-b'), + mockNotificationWithRepoName('1', 'owner/repo-b'), + mockNotificationWithRepoName('2', 'owner/repo-a'), + mockNotificationWithRepoName('3', 'owner/repo-b'), ]; const result = getFlattenedNotificationsByRepo(notifications, settings); @@ -125,9 +125,9 @@ describe('renderer/utils/notifications/group.ts', () => { it('handles notifications without repository data when grouped', () => { const settings = { ...mockSettings, groupBy: GroupBy.REPOSITORY }; const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-a'), - createMockNotificationForRepoName('2', null), - createMockNotificationForRepoName('3', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-a'), + mockNotificationWithRepoName('2', null), + mockNotificationWithRepoName('3', 'owner/repo-a'), ]; const result = getFlattenedNotificationsByRepo(notifications, settings); @@ -139,9 +139,9 @@ describe('renderer/utils/notifications/group.ts', () => { it('preserves notifications without repository data when not grouped', () => { const settings = { ...mockSettings, groupBy: GroupBy.DATE }; const notifications: Notification[] = [ - createMockNotificationForRepoName('1', 'owner/repo-a'), - createMockNotificationForRepoName('2', null), - createMockNotificationForRepoName('3', 'owner/repo-a'), + mockNotificationWithRepoName('1', 'owner/repo-a'), + mockNotificationWithRepoName('2', null), + mockNotificationWithRepoName('3', 'owner/repo-a'), ]; const result = getFlattenedNotificationsByRepo(notifications, settings); diff --git a/src/renderer/utils/notifications/handlers/checkSuite.test.ts b/src/renderer/utils/notifications/handlers/checkSuite.test.ts index 8f50f8af1..777af4f5e 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.test.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.test.ts @@ -1,4 +1,4 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { checkSuiteHandler, getCheckSuiteAttributes } from './checkSuite'; @@ -139,13 +139,13 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { it('iconType', () => { expect( checkSuiteHandler.iconType( - createSubjectMock({ type: 'CheckSuite', state: null }), + mockNotificationWithSubject({ type: 'CheckSuite', state: null }), ).displayName, ).toBe('RocketIcon'); expect( checkSuiteHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'CheckSuite', state: 'cancelled', }), @@ -154,7 +154,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect( checkSuiteHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'CheckSuite', state: 'failure', }), @@ -163,7 +163,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect( checkSuiteHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'CheckSuite', state: 'skipped', }), @@ -172,7 +172,7 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { expect( checkSuiteHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'CheckSuite', state: 'success', }), diff --git a/src/renderer/utils/notifications/handlers/checkSuite.ts b/src/renderer/utils/notifications/handlers/checkSuite.ts index e4a6fcf36..704bd991f 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.ts @@ -15,7 +15,6 @@ import type { CheckSuiteStatus, GitifySubject, Notification, - Subject, } from '../../../typesGitHub'; import { DefaultHandler } from './default'; @@ -38,8 +37,8 @@ class CheckSuiteHandler extends DefaultHandler { return null; } - iconType(subject: Subject): FC | null { - switch (subject.state) { + iconType(notification: Notification): FC | null { + switch (notification.subject.state) { case 'cancelled': return StopIcon; case 'failure': diff --git a/src/renderer/utils/notifications/handlers/commit.test.ts b/src/renderer/utils/notifications/handlers/commit.test.ts index bf9d26ba9..5788ccef6 100644 --- a/src/renderer/utils/notifications/handlers/commit.test.ts +++ b/src/renderer/utils/notifications/handlers/commit.test.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import nock from 'nock'; -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification, partialMockUser, @@ -99,7 +99,8 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { it('iconType', () => { expect( - commitHandler.iconType(createSubjectMock({ type: 'Commit' })).displayName, + commitHandler.iconType(mockNotificationWithSubject({ type: 'Commit' })) + .displayName, ).toBe('GitCommitIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/commit.ts b/src/renderer/utils/notifications/handlers/commit.ts index 882134f03..8a083bc8c 100644 --- a/src/renderer/utils/notifications/handlers/commit.ts +++ b/src/renderer/utils/notifications/handlers/commit.ts @@ -8,7 +8,6 @@ import type { GitifySubject, Notification, StateType, - Subject, User, } from '../../../typesGitHub'; import { getCommit, getCommitComment } from '../../api/client'; @@ -55,7 +54,7 @@ class CommitHandler extends DefaultHandler { }; } - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return GitCommitIcon; } } diff --git a/src/renderer/utils/notifications/handlers/default.test.ts b/src/renderer/utils/notifications/handlers/default.test.ts index d1abc5f7c..74db4b001 100644 --- a/src/renderer/utils/notifications/handlers/default.test.ts +++ b/src/renderer/utils/notifications/handlers/default.test.ts @@ -1,4 +1,4 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { IconColor } from '../../../types'; @@ -24,9 +24,9 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { }); it('iconType', () => { - expect(defaultHandler.iconType(createSubjectMock({})).displayName).toBe( - 'QuestionIcon', - ); + expect( + defaultHandler.iconType(mockNotificationWithSubject({})).displayName, + ).toBe('QuestionIcon'); }); describe('iconColor', () => { @@ -50,7 +50,7 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { ]; it.each(cases)('returns correct color for state %s', (state, expected) => { - const subject = createSubjectMock({ state }); + const subject = mockNotificationWithSubject({ state }); expect(defaultHandler.iconColor(subject)).toBe(expected); }); }); diff --git a/src/renderer/utils/notifications/handlers/default.ts b/src/renderer/utils/notifications/handlers/default.ts index 99f66b474..b490e6ce7 100644 --- a/src/renderer/utils/notifications/handlers/default.ts +++ b/src/renderer/utils/notifications/handlers/default.ts @@ -8,7 +8,6 @@ import { IconColor } from '../../../types'; import type { GitifySubject, Notification, - Subject, SubjectType, } from '../../../typesGitHub'; import type { NotificationTypeHandler } from './types'; @@ -24,12 +23,12 @@ export class DefaultHandler implements NotificationTypeHandler { return null; } - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return QuestionIcon; } - iconColor(subject: Subject): IconColor { - switch (subject.state) { + iconColor(notification: Notification): IconColor { + switch (notification.subject.state) { case 'open': case 'reopened': case 'ANSWERED': diff --git a/src/renderer/utils/notifications/handlers/discussion.test.ts b/src/renderer/utils/notifications/handlers/discussion.test.ts index e81878886..fdcf1eb92 100644 --- a/src/renderer/utils/notifications/handlers/discussion.test.ts +++ b/src/renderer/utils/notifications/handlers/discussion.test.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import nock from 'nock'; -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import type { Link } from '../../../types'; @@ -281,25 +281,26 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { it('iconType', () => { expect( - discussionHandler.iconType(createSubjectMock({ type: 'Discussion' })) - .displayName, + discussionHandler.iconType( + mockNotificationWithSubject({ type: 'Discussion' }), + ).displayName, ).toBe('CommentDiscussionIcon'); expect( discussionHandler.iconType( - createSubjectMock({ type: 'Discussion', state: 'DUPLICATE' }), + mockNotificationWithSubject({ type: 'Discussion', state: 'DUPLICATE' }), ).displayName, ).toBe('DiscussionDuplicateIcon'); expect( discussionHandler.iconType( - createSubjectMock({ type: 'Discussion', state: 'OUTDATED' }), + mockNotificationWithSubject({ type: 'Discussion', state: 'OUTDATED' }), ).displayName, ).toBe('DiscussionOutdatedIcon'); expect( discussionHandler.iconType( - createSubjectMock({ type: 'Discussion', state: 'RESOLVED' }), + mockNotificationWithSubject({ type: 'Discussion', state: 'RESOLVED' }), ).displayName, ).toBe('DiscussionClosedIcon'); }); diff --git a/src/renderer/utils/notifications/handlers/discussion.ts b/src/renderer/utils/notifications/handlers/discussion.ts index 255cd68b1..5ef9788fb 100644 --- a/src/renderer/utils/notifications/handlers/discussion.ts +++ b/src/renderer/utils/notifications/handlers/discussion.ts @@ -16,7 +16,6 @@ import type { DiscussionStateType, GitifySubject, Notification, - Subject, SubjectUser, } from '../../../typesGitHub'; import { getLatestDiscussion } from '../../api/client'; @@ -77,8 +76,8 @@ class DiscussionHandler extends DefaultHandler { }; } - iconType(subject: Subject): FC | null { - switch (subject.state) { + iconType(notification: Notification): FC | null { + switch (notification.subject.state) { case 'DUPLICATE': return DiscussionDuplicateIcon; case 'OUTDATED': diff --git a/src/renderer/utils/notifications/handlers/issue.test.ts b/src/renderer/utils/notifications/handlers/issue.test.ts index 511aaeee6..fc3b5d47f 100644 --- a/src/renderer/utils/notifications/handlers/issue.test.ts +++ b/src/renderer/utils/notifications/handlers/issue.test.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import nock from 'nock'; -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification, partialMockUser, @@ -296,18 +296,19 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { it('iconType', () => { expect( - issueHandler.iconType(createSubjectMock({ type: 'Issue' })).displayName, + issueHandler.iconType(mockNotificationWithSubject({ type: 'Issue' })) + .displayName, ).toBe('IssueOpenedIcon'); expect( issueHandler.iconType( - createSubjectMock({ type: 'Issue', state: 'draft' }), + mockNotificationWithSubject({ type: 'Issue', state: 'draft' }), ).displayName, ).toBe('IssueDraftIcon'); expect( issueHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'Issue', state: 'closed', }), @@ -316,7 +317,7 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { expect( issueHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'Issue', state: 'completed', }), @@ -325,7 +326,7 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { expect( issueHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'Issue', state: 'not_planned', }), @@ -334,7 +335,7 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { expect( issueHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'Issue', state: 'reopened', }), diff --git a/src/renderer/utils/notifications/handlers/issue.ts b/src/renderer/utils/notifications/handlers/issue.ts index 74b637aad..89b9f75f1 100644 --- a/src/renderer/utils/notifications/handlers/issue.ts +++ b/src/renderer/utils/notifications/handlers/issue.ts @@ -10,12 +10,7 @@ import { } from '@primer/octicons-react'; import type { SettingsState } from '../../../types'; -import type { - GitifySubject, - Notification, - Subject, - User, -} from '../../../typesGitHub'; +import type { GitifySubject, Notification, User } from '../../../typesGitHub'; import { getIssue, getIssueOrPullRequestComment } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; @@ -61,8 +56,8 @@ class IssueHandler extends DefaultHandler { }; } - iconType(subject: Subject): FC | null { - switch (subject.state) { + iconType(notification: Notification): FC | null { + switch (notification.subject.state) { case 'draft': return IssueDraftIcon; case 'closed': diff --git a/src/renderer/utils/notifications/handlers/pullRequest.test.ts b/src/renderer/utils/notifications/handlers/pullRequest.test.ts index c70f264bd..2e2462369 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.test.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.test.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import nock from 'nock'; -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification, partialMockUser, @@ -440,13 +440,14 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { it('iconType', () => { expect( - pullRequestHandler.iconType(createSubjectMock({ type: 'PullRequest' })) - .displayName, + pullRequestHandler.iconType( + mockNotificationWithSubject({ type: 'PullRequest' }), + ).displayName, ).toBe('GitPullRequestIcon'); expect( pullRequestHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'PullRequest', state: 'draft', }), @@ -455,7 +456,7 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect( pullRequestHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'PullRequest', state: 'closed', }), @@ -464,7 +465,7 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { expect( pullRequestHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'PullRequest', state: 'merged', }), diff --git a/src/renderer/utils/notifications/handlers/pullRequest.ts b/src/renderer/utils/notifications/handlers/pullRequest.ts index f58eccd98..7a67ed07c 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.ts @@ -16,7 +16,6 @@ import type { PullRequest, PullRequestReview, PullRequestStateType, - Subject, User, } from '../../../typesGitHub'; import { @@ -87,8 +86,8 @@ class PullRequestHandler extends DefaultHandler { }; } - iconType(subject: Subject): FC | null { - switch (subject.state) { + iconType(notification: Notification): FC | null { + switch (notification.subject.state) { case 'draft': return GitPullRequestDraftIcon; case 'closed': diff --git a/src/renderer/utils/notifications/handlers/release.test.ts b/src/renderer/utils/notifications/handlers/release.test.ts index 49e6f87a4..d9fc1c288 100644 --- a/src/renderer/utils/notifications/handlers/release.test.ts +++ b/src/renderer/utils/notifications/handlers/release.test.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import nock from 'nock'; -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification, partialMockUser, @@ -70,7 +70,7 @@ describe('renderer/utils/notifications/handlers/release.ts', () => { it('iconType', () => { expect( releaseHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'Release', }), ).displayName, diff --git a/src/renderer/utils/notifications/handlers/release.ts b/src/renderer/utils/notifications/handlers/release.ts index 9caadc1a7..6a635963c 100644 --- a/src/renderer/utils/notifications/handlers/release.ts +++ b/src/renderer/utils/notifications/handlers/release.ts @@ -8,7 +8,6 @@ import type { GitifySubject, Notification, StateType, - Subject, } from '../../../typesGitHub'; import { getRelease } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; @@ -39,7 +38,7 @@ class ReleaseHandler extends DefaultHandler { }; } - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return TagIcon; } } diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts index b6fd92706..3b0e497e5 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts @@ -1,11 +1,11 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; describe('renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts', () => { it('iconType', () => { expect( repositoryDependabotAlertsThreadHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'RepositoryDependabotAlertsThread', }), ).displayName, diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts index cd06771be..334ed26c5 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts @@ -3,13 +3,13 @@ import type { FC } from 'react'; import type { OcticonProps } from '@primer/octicons-react'; import { AlertIcon } from '@primer/octicons-react'; -import type { Subject } from '../../../typesGitHub'; +import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; class RepositoryDependabotAlertsThreadHandler extends DefaultHandler { readonly type = 'RepositoryDependabotAlertsThread'; - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return AlertIcon; } } diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts index 69062a277..e5e249a6a 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts @@ -1,11 +1,11 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { repositoryInvitationHandler } from './repositoryInvitation'; describe('renderer/utils/notifications/handlers/repositoryInvitation.ts', () => { it('iconType', () => { expect( repositoryInvitationHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'RepositoryInvitation', }), ).displayName, diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts index 38bf30470..7e499ffe4 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts @@ -2,13 +2,13 @@ import type { FC } from 'react'; import { MailIcon, type OcticonProps } from '@primer/octicons-react'; -import type { Subject } from '../../../typesGitHub'; +import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; class RepositoryInvitationHandler extends DefaultHandler { readonly type = 'RepositoryInvitation'; - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return MailIcon; } } diff --git a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts index f6055ed3d..153fbb712 100644 --- a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts @@ -1,11 +1,11 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; describe('renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts', () => { it('iconType', () => { expect( repositoryVulnerabilityAlertHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'RepositoryVulnerabilityAlert', }), ).displayName, diff --git a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts index f3b8e77e7..471e55b85 100644 --- a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts +++ b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts @@ -2,13 +2,13 @@ import type { FC } from 'react'; import { AlertIcon, type OcticonProps } from '@primer/octicons-react'; -import type { Subject } from '../../../typesGitHub'; +import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; class RepositoryVulnerabilityAlertHandler extends DefaultHandler { readonly type = 'RepositoryVulnerabilityAlert'; - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return AlertIcon; } } diff --git a/src/renderer/utils/notifications/handlers/types.ts b/src/renderer/utils/notifications/handlers/types.ts index 686e3ef50..0f15e91b5 100644 --- a/src/renderer/utils/notifications/handlers/types.ts +++ b/src/renderer/utils/notifications/handlers/types.ts @@ -6,7 +6,6 @@ import type { SettingsState } from '../../../types'; import type { GitifySubject, Notification, - Subject, SubjectType, } from '../../../typesGitHub'; @@ -24,12 +23,12 @@ export interface NotificationTypeHandler { /** * Return the icon component for this notification type. */ - iconType(subject: Subject): FC | null; + iconType(notification: Notification): FC | null; /** * Return the icon color for this notification type. */ - iconColor(subject: Subject): string | undefined; + iconColor(notification: Notification): string | undefined; /** * Return the formatted notification type for this notification. diff --git a/src/renderer/utils/notifications/handlers/workflowRun.test.ts b/src/renderer/utils/notifications/handlers/workflowRun.test.ts index 5d843cf1d..e8a2ebd1b 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.test.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.test.ts @@ -1,4 +1,4 @@ -import { createSubjectMock } from '../../../__mocks__/notifications-mocks'; +import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { getWorkflowRunAttributes, workflowRunHandler } from './workflowRun'; @@ -55,7 +55,7 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { it('iconType', () => { expect( workflowRunHandler.iconType( - createSubjectMock({ + mockNotificationWithSubject({ type: 'WorkflowRun', }), ).displayName, diff --git a/src/renderer/utils/notifications/handlers/workflowRun.ts b/src/renderer/utils/notifications/handlers/workflowRun.ts index 9dfd7df2e..d3e21c5ad 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.ts @@ -8,7 +8,6 @@ import type { CheckSuiteStatus, GitifySubject, Notification, - Subject, WorkflowRunAttributes, } from '../../../typesGitHub'; import { DefaultHandler } from './default'; @@ -32,7 +31,7 @@ class WorkflowRunHandler extends DefaultHandler { return null; } - iconType(_subject: Subject): FC | null { + iconType(_notification: Notification): FC | null { return RocketIcon; } } diff --git a/src/renderer/utils/notifications/notifications.test.ts b/src/renderer/utils/notifications/notifications.test.ts index 8bed45437..4b98f1b28 100644 --- a/src/renderer/utils/notifications/notifications.test.ts +++ b/src/renderer/utils/notifications/notifications.test.ts @@ -2,7 +2,7 @@ import axios from 'axios'; import nock from 'nock'; import { - createMockNotificationForRepoName, + mockNotificationWithRepoName, mockSingleAccountNotifications, } from '../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../__mocks__/partial-mocks'; @@ -83,9 +83,9 @@ describe('renderer/utils/notifications/notifications.ts', () => { const acc1: AccountNotifications = { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('a1', 'owner/repo-1'), - createMockNotificationForRepoName('a2', 'owner/repo-2'), - createMockNotificationForRepoName('a3', 'owner/repo-1'), + mockNotificationWithRepoName('a1', 'owner/repo-1'), + mockNotificationWithRepoName('a2', 'owner/repo-2'), + mockNotificationWithRepoName('a3', 'owner/repo-1'), ], error: null, }; @@ -93,9 +93,9 @@ describe('renderer/utils/notifications/notifications.ts', () => { const acc2: AccountNotifications = { account: mockGitHubEnterpriseServerAccount, notifications: [ - createMockNotificationForRepoName('b1', 'owner/repo-3'), - createMockNotificationForRepoName('b2', 'owner/repo-4'), - createMockNotificationForRepoName('b3', 'owner/repo-3'), + mockNotificationWithRepoName('b1', 'owner/repo-3'), + mockNotificationWithRepoName('b2', 'owner/repo-4'), + mockNotificationWithRepoName('b3', 'owner/repo-3'), ], error: null, }; diff --git a/src/renderer/utils/notifications/utils.test.ts b/src/renderer/utils/notifications/utils.test.ts index c16b6c3e8..44446ef1d 100644 --- a/src/renderer/utils/notifications/utils.test.ts +++ b/src/renderer/utils/notifications/utils.test.ts @@ -1,4 +1,4 @@ -import { createMockNotificationForRepoName } from '../../__mocks__/notifications-mocks'; +import { mockNotificationWithRepoName } from '../../__mocks__/notifications-mocks'; import { mockGitHubCloudAccount, mockGitHubEnterpriseServerAccount, @@ -13,9 +13,9 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), - createMockNotificationForRepoName('3', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), + mockNotificationWithRepoName('3', 'some/repo'), ], error: null, }, @@ -31,7 +31,7 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, ]; @@ -52,8 +52,8 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), ], error: null, }, @@ -63,9 +63,9 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('2', 'some/repo'), - createMockNotificationForRepoName('3', 'some/repo'), - createMockNotificationForRepoName('4', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), + mockNotificationWithRepoName('3', 'some/repo'), + mockNotificationWithRepoName('4', 'some/repo'), ], error: null, }, @@ -85,9 +85,9 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), - createMockNotificationForRepoName('3', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), + mockNotificationWithRepoName('3', 'some/repo'), ], error: null, }, @@ -97,9 +97,9 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), - createMockNotificationForRepoName('3', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), + mockNotificationWithRepoName('3', 'some/repo'), ], error: null, }, @@ -117,12 +117,12 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, { account: mockGitHubEnterpriseServerAccount, - notifications: [createMockNotificationForRepoName('10', 'some/repo')], + notifications: [mockNotificationWithRepoName('10', 'some/repo')], error: null, }, ]; @@ -131,16 +131,16 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), ], error: null, }, { account: mockGitHubEnterpriseServerAccount, notifications: [ - createMockNotificationForRepoName('10', 'some/repo'), - createMockNotificationForRepoName('11', 'some/repo'), + mockNotificationWithRepoName('10', 'some/repo'), + mockNotificationWithRepoName('11', 'some/repo'), ], error: null, }, @@ -159,7 +159,7 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, ]; @@ -167,14 +167,14 @@ describe('renderer/utils/notifications/utils.ts', () => { const newNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, { account: mockGitHubEnterpriseServerAccount, notifications: [ - createMockNotificationForRepoName('10', 'some/repo'), - createMockNotificationForRepoName('11', 'some/repo'), + mockNotificationWithRepoName('10', 'some/repo'), + mockNotificationWithRepoName('11', 'some/repo'), ], error: null, }, @@ -193,7 +193,7 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, ]; @@ -218,7 +218,7 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, ]; @@ -227,9 +227,9 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('5', 'some/repo'), - createMockNotificationForRepoName('3', 'some/repo'), - createMockNotificationForRepoName('4', 'some/repo'), + mockNotificationWithRepoName('5', 'some/repo'), + mockNotificationWithRepoName('3', 'some/repo'), + mockNotificationWithRepoName('4', 'some/repo'), ], error: null, }, @@ -248,12 +248,12 @@ describe('renderer/utils/notifications/utils.ts', () => { const previousNotifications: AccountNotifications[] = [ { account: mockGitHubCloudAccount, - notifications: [createMockNotificationForRepoName('1', 'some/repo')], + notifications: [mockNotificationWithRepoName('1', 'some/repo')], error: null, }, { account: mockGitHubEnterpriseServerAccount, - notifications: [createMockNotificationForRepoName('10', 'some/repo')], + notifications: [mockNotificationWithRepoName('10', 'some/repo')], error: null, }, ]; @@ -262,8 +262,8 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), ], error: null, }, @@ -286,16 +286,16 @@ describe('renderer/utils/notifications/utils.ts', () => { { account: mockGitHubCloudAccount, notifications: [ - createMockNotificationForRepoName('1', 'some/repo'), - createMockNotificationForRepoName('2', 'some/repo'), + mockNotificationWithRepoName('1', 'some/repo'), + mockNotificationWithRepoName('2', 'some/repo'), ], error: null, }, { account: mockGitHubEnterpriseServerAccount, notifications: [ - createMockNotificationForRepoName('10', 'some/repo'), - createMockNotificationForRepoName('11', 'some/repo'), + mockNotificationWithRepoName('10', 'some/repo'), + mockNotificationWithRepoName('11', 'some/repo'), ], error: null, }, From 86932c9640e17bf75e9978f48b9529848ade29b7 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sun, 9 Nov 2025 18:20:31 -0500 Subject: [PATCH 2/3] refactor(handlers): use notification instead of subject Signed-off-by: Adam Setch --- .../notifications/NotificationRow.tsx | 10 ++-- .../notifications/handlers/checkSuite.ts | 18 +++--- .../utils/notifications/handlers/commit.ts | 25 ++++---- .../utils/notifications/handlers/default.ts | 42 ++++++++------ .../notifications/handlers/discussion.ts | 20 ++++--- .../utils/notifications/handlers/index.ts | 58 +++++++------------ .../utils/notifications/handlers/issue.ts | 27 +++++---- .../notifications/handlers/pullRequest.ts | 32 +++++----- .../utils/notifications/handlers/release.ts | 19 +++--- .../repositoryDependabotAlertsThread.ts | 10 +++- .../handlers/repositoryInvitation.ts | 9 ++- .../handlers/repositoryVulnerabilityAlert.ts | 10 +++- .../utils/notifications/handlers/types.ts | 16 +++-- .../notifications/handlers/workflowRun.ts | 16 ++--- 14 files changed, 171 insertions(+), 141 deletions(-) diff --git a/src/renderer/components/notifications/NotificationRow.tsx b/src/renderer/components/notifications/NotificationRow.tsx index 1f7867529..718045d03 100644 --- a/src/renderer/components/notifications/NotificationRow.tsx +++ b/src/renderer/components/notifications/NotificationRow.tsx @@ -63,11 +63,11 @@ export const NotificationRow: FC = ({ }; const handler = createNotificationHandler(notification); - const NotificationIcon = handler.iconType(notification); - const iconColor = handler.iconColor(notification); - const notificationType = handler.formattedNotificationType(notification); - const notificationNumber = handler.formattedNotificationNumber(notification); - const notificationTitle = handler.formattedNotificationTitle(notification); + const NotificationIcon = handler.iconType(); + const iconColor = handler.iconColor(); + const notificationType = handler.formattedNotificationType(); + const notificationNumber = handler.formattedNotificationNumber(); + const notificationTitle = handler.formattedNotificationTitle(); const groupByDate = settings.groupBy === GroupBy.DATE; diff --git a/src/renderer/utils/notifications/handlers/checkSuite.ts b/src/renderer/utils/notifications/handlers/checkSuite.ts index 704bd991f..39787cfaa 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.ts @@ -17,15 +17,13 @@ import type { Notification, } from '../../../typesGitHub'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class CheckSuiteHandler extends DefaultHandler { readonly type = 'CheckSuite'; - async enrich( - notification: Notification, - _settings: SettingsState, - ): Promise { - const state = getCheckSuiteAttributes(notification)?.status; + async enrich(_settings: SettingsState): Promise { + const state = getCheckSuiteAttributes(this.notification)?.status; if (state) { return { @@ -37,8 +35,8 @@ class CheckSuiteHandler extends DefaultHandler { return null; } - iconType(notification: Notification): FC | null { - switch (notification.subject.state) { + iconType(): FC | null { + switch (this.notification.subject.state) { case 'cancelled': return StopIcon; case 'failure': @@ -53,7 +51,11 @@ class CheckSuiteHandler extends DefaultHandler { } } -export const checkSuiteHandler = new CheckSuiteHandler(); +export function createCheckSuiteHandler( + notification: Notification, +): NotificationTypeHandler { + return new CheckSuiteHandler(notification); +} /** * Ideally we would be using a GitHub API to fetch the CheckSuite / WorkflowRun state, diff --git a/src/renderer/utils/notifications/handlers/commit.ts b/src/renderer/utils/notifications/handlers/commit.ts index 8a083bc8c..bcea66cc3 100644 --- a/src/renderer/utils/notifications/handlers/commit.ts +++ b/src/renderer/utils/notifications/handlers/commit.ts @@ -13,15 +13,13 @@ import type { import { getCommit, getCommitComment } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; import { getSubjectUser } from './utils'; class CommitHandler extends DefaultHandler { readonly type = 'Commit'; - async enrich( - notification: Notification, - settings: SettingsState, - ): Promise { + async enrich(settings: SettingsState): Promise { const commitState: StateType = null; // Commit notifications are stateless // Return early if this notification would be hidden by filters @@ -31,18 +29,21 @@ class CommitHandler extends DefaultHandler { let user: User; - if (notification.subject.latest_comment_url) { + if (this.notification.subject.latest_comment_url) { const commitComment = ( await getCommitComment( - notification.subject.latest_comment_url, - notification.account.token, + this.notification.subject.latest_comment_url, + this.notification.account.token, ) ).data; user = commitComment.user; } else { const commit = ( - await getCommit(notification.subject.url, notification.account.token) + await getCommit( + this.notification.subject.url, + this.notification.account.token, + ) ).data; user = commit.author; @@ -54,9 +55,13 @@ class CommitHandler extends DefaultHandler { }; } - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return GitCommitIcon; } } -export const commitHandler = new CommitHandler(); +export function createCommitHandler( + notification: Notification, +): NotificationTypeHandler { + return new CommitHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/default.ts b/src/renderer/utils/notifications/handlers/default.ts index b490e6ce7..4e5953832 100644 --- a/src/renderer/utils/notifications/handlers/default.ts +++ b/src/renderer/utils/notifications/handlers/default.ts @@ -15,20 +15,22 @@ import { formatForDisplay } from './utils'; export class DefaultHandler implements NotificationTypeHandler { type?: SubjectType; + notification: Notification; - async enrich( - _notification: Notification, - _settings: SettingsState, - ): Promise { + constructor(notification: Notification) { + this.notification = notification; + } + + async enrich(_settings: SettingsState): Promise { return null; } - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return QuestionIcon; } - iconColor(notification: Notification): IconColor { - switch (notification.subject.state) { + iconColor(): IconColor { + switch (this.notification.subject.state) { case 'open': case 'reopened': case 'ANSWERED': @@ -46,25 +48,29 @@ export class DefaultHandler implements NotificationTypeHandler { } } - formattedNotificationType(notification: Notification): string { + formattedNotificationType(): string { return formatForDisplay([ - notification.subject.state, - notification.subject.type, + this.notification.subject.state, + this.notification.subject.type, ]); } - formattedNotificationNumber(notification: Notification): string { - return notification.subject?.number - ? `#${notification.subject.number}` + formattedNotificationNumber(): string { + return this.notification.subject?.number + ? `#${this.notification.subject.number}` : ''; } - formattedNotificationTitle(notification: Notification): string { - let title = notification.subject.title; + formattedNotificationTitle(): string { + let title = this.notification.subject.title; - if (notification.subject?.number) { - title = `${title} [${this.formattedNotificationNumber(notification)}]`; + if (this.notification.subject?.number) { + title = `${title} [${this.formattedNotificationNumber()}]`; } return title; } } -export const defaultHandler = new DefaultHandler(); +export function createDefaultHandler( + notification: Notification, +): NotificationTypeHandler { + return new DefaultHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/discussion.ts b/src/renderer/utils/notifications/handlers/discussion.ts index 5ef9788fb..af61434a5 100644 --- a/src/renderer/utils/notifications/handlers/discussion.ts +++ b/src/renderer/utils/notifications/handlers/discussion.ts @@ -21,15 +21,13 @@ import type { import { getLatestDiscussion } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class DiscussionHandler extends DefaultHandler { readonly type = 'Discussion'; - async enrich( - notification: Notification, - settings: SettingsState, - ): Promise { - const discussion = await getLatestDiscussion(notification); + async enrich(settings: SettingsState): Promise { + const discussion = await getLatestDiscussion(this.notification); let discussionState: DiscussionStateType = 'OPEN'; if (discussion) { @@ -48,7 +46,7 @@ class DiscussionHandler extends DefaultHandler { } const latestDiscussionComment = getClosestDiscussionCommentOrReply( - notification, + this.notification, discussion.comments.nodes, ); @@ -76,8 +74,8 @@ class DiscussionHandler extends DefaultHandler { }; } - iconType(notification: Notification): FC | null { - switch (notification.subject.state) { + iconType(): FC | null { + switch (this.notification.subject.state) { case 'DUPLICATE': return DiscussionDuplicateIcon; case 'OUTDATED': @@ -90,7 +88,11 @@ class DiscussionHandler extends DefaultHandler { } } -export const discussionHandler = new DiscussionHandler(); +export function createDiscussionHandler( + notification: Notification, +): NotificationTypeHandler { + return new DiscussionHandler(notification); +} export function getClosestDiscussionCommentOrReply( notification: Notification, diff --git a/src/renderer/utils/notifications/handlers/index.ts b/src/renderer/utils/notifications/handlers/index.ts index b6e5d539f..babf7e1b5 100644 --- a/src/renderer/utils/notifications/handlers/index.ts +++ b/src/renderer/utils/notifications/handlers/index.ts @@ -1,56 +1,42 @@ import type { Notification } from '../../../typesGitHub'; -import { checkSuiteHandler } from './checkSuite'; -import { commitHandler } from './commit'; -import { defaultHandler } from './default'; -import { discussionHandler } from './discussion'; -import { issueHandler } from './issue'; -import { pullRequestHandler } from './pullRequest'; -import { releaseHandler } from './release'; -import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; -import { repositoryInvitationHandler } from './repositoryInvitation'; -import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; +import { createCheckSuiteHandler } from './checkSuite'; +import { createCommitHandler } from './commit'; +import { createDefaultHandler } from './default'; +import { createDiscussionHandler } from './discussion'; +import { createIssueHandler } from './issue'; +import { createPullRequestHandler } from './pullRequest'; +import { createReleaseHandler } from './release'; +import { createRepositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; +import { createRepositoryInvitationHandler } from './repositoryInvitation'; +import { createRepositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; import type { NotificationTypeHandler } from './types'; -import { workflowRunHandler } from './workflowRun'; +import { createWorkflowRunHandler } from './workflowRun'; export function createNotificationHandler( notification: Notification, ): NotificationTypeHandler { switch (notification.subject.type) { case 'CheckSuite': - return checkSuiteHandler; + return createCheckSuiteHandler(notification); case 'Commit': - return commitHandler; + return createCommitHandler(notification); case 'Discussion': - return discussionHandler; + return createDiscussionHandler(notification); case 'Issue': - return issueHandler; + return createIssueHandler(notification); case 'PullRequest': - return pullRequestHandler; + return createPullRequestHandler(notification); case 'Release': - return releaseHandler; + return createReleaseHandler(notification); case 'RepositoryDependabotAlertsThread': - return repositoryDependabotAlertsThreadHandler; + return createRepositoryDependabotAlertsThreadHandler(notification); case 'RepositoryInvitation': - return repositoryInvitationHandler; + return createRepositoryInvitationHandler(notification); case 'RepositoryVulnerabilityAlert': - return repositoryVulnerabilityAlertHandler; + return createRepositoryVulnerabilityAlertHandler(notification); case 'WorkflowRun': - return workflowRunHandler; + return createWorkflowRunHandler(notification); default: - return defaultHandler; + return createDefaultHandler(notification); } } - -export const handlers = { - checkSuiteHandler, - commitHandler, - discussionHandler, - issueHandler, - pullRequestHandler, - releaseHandler, - repositoryDependabotAlertsThreadHandler, - repositoryInvitationHandler, - repositoryVulnerabilityAlertHandler, - workflowRunHandler, - defaultHandler, -}; diff --git a/src/renderer/utils/notifications/handlers/issue.ts b/src/renderer/utils/notifications/handlers/issue.ts index 89b9f75f1..14ef8c1af 100644 --- a/src/renderer/utils/notifications/handlers/issue.ts +++ b/src/renderer/utils/notifications/handlers/issue.ts @@ -14,17 +14,18 @@ import type { GitifySubject, Notification, User } from '../../../typesGitHub'; import { getIssue, getIssueOrPullRequestComment } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; import { getSubjectUser } from './utils'; class IssueHandler extends DefaultHandler { readonly type = 'Issue'; - async enrich( - notification: Notification, - settings: SettingsState, - ): Promise { + async enrich(settings: SettingsState): Promise { const issue = ( - await getIssue(notification.subject.url, notification.account.token) + await getIssue( + this.notification.subject.url, + this.notification.account.token, + ) ).data; const issueState = issue.state_reason ?? issue.state; @@ -36,11 +37,11 @@ class IssueHandler extends DefaultHandler { let issueCommentUser: User; - if (notification.subject.latest_comment_url) { + if (this.notification.subject.latest_comment_url) { const issueComment = ( await getIssueOrPullRequestComment( - notification.subject.latest_comment_url, - notification.account.token, + this.notification.subject.latest_comment_url, + this.notification.account.token, ) ).data; issueCommentUser = issueComment.user; @@ -56,8 +57,8 @@ class IssueHandler extends DefaultHandler { }; } - iconType(notification: Notification): FC | null { - switch (notification.subject.state) { + iconType(): FC | null { + switch (this.notification.subject.state) { case 'draft': return IssueDraftIcon; case 'closed': @@ -73,4 +74,8 @@ class IssueHandler extends DefaultHandler { } } -export const issueHandler = new IssueHandler(); +export function createIssueHandler( + notification: Notification, +): NotificationTypeHandler { + return new IssueHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/pullRequest.ts b/src/renderer/utils/notifications/handlers/pullRequest.ts index 7a67ed07c..72a2c2f16 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.ts @@ -25,17 +25,18 @@ import { } from '../../api/client'; import { isStateFilteredOut, isUserFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; import { getSubjectUser } from './utils'; class PullRequestHandler extends DefaultHandler { readonly type = 'PullRequest' as const; - async enrich( - notification: Notification, - settings: SettingsState, - ): Promise { + async enrich(settings: SettingsState): Promise { const pr = ( - await getPullRequest(notification.subject.url, notification.account.token) + await getPullRequest( + this.notification.subject.url, + this.notification.account.token, + ) ).data; let prState: PullRequestStateType = pr.state; @@ -52,13 +53,14 @@ class PullRequestHandler extends DefaultHandler { let prCommentUser: User; if ( - notification.subject.latest_comment_url && - notification.subject.latest_comment_url !== notification.subject.url + this.notification.subject.latest_comment_url && + this.notification.subject.latest_comment_url !== + this.notification.subject.url ) { const prComment = ( await getIssueOrPullRequestComment( - notification.subject.latest_comment_url, - notification.account.token, + this.notification.subject.latest_comment_url, + this.notification.account.token, ) ).data; prCommentUser = prComment.user; @@ -71,7 +73,7 @@ class PullRequestHandler extends DefaultHandler { return null; } - const reviews = await getLatestReviewForReviewers(notification); + const reviews = await getLatestReviewForReviewers(this.notification); const linkedIssues = parseLinkedIssuesFromPr(pr); return { @@ -86,8 +88,8 @@ class PullRequestHandler extends DefaultHandler { }; } - iconType(notification: Notification): FC | null { - switch (notification.subject.state) { + iconType(): FC | null { + switch (this.notification.subject.state) { case 'draft': return GitPullRequestDraftIcon; case 'closed': @@ -100,7 +102,11 @@ class PullRequestHandler extends DefaultHandler { } } -export const pullRequestHandler = new PullRequestHandler(); +export function createPullRequestHandler( + notification: Notification, +): NotificationTypeHandler { + return new PullRequestHandler(notification); +} export async function getLatestReviewForReviewers( notification: Notification, diff --git a/src/renderer/utils/notifications/handlers/release.ts b/src/renderer/utils/notifications/handlers/release.ts index 6a635963c..a7651d6a5 100644 --- a/src/renderer/utils/notifications/handlers/release.ts +++ b/src/renderer/utils/notifications/handlers/release.ts @@ -12,15 +12,13 @@ import type { import { getRelease } from '../../api/client'; import { isStateFilteredOut } from '../filters/filter'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; import { getSubjectUser } from './utils'; class ReleaseHandler extends DefaultHandler { readonly type = 'Release'; - async enrich( - notification: Notification, - settings: SettingsState, - ): Promise { + async enrich(settings: SettingsState): Promise { const releaseState: StateType = null; // Release notifications are stateless // Return early if this notification would be hidden by filters @@ -29,7 +27,10 @@ class ReleaseHandler extends DefaultHandler { } const release = ( - await getRelease(notification.subject.url, notification.account.token) + await getRelease( + this.notification.subject.url, + this.notification.account.token, + ) ).data; return { @@ -38,9 +39,13 @@ class ReleaseHandler extends DefaultHandler { }; } - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return TagIcon; } } -export const releaseHandler = new ReleaseHandler(); +export function createReleaseHandler( + notification: Notification, +): NotificationTypeHandler { + return new ReleaseHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts index 334ed26c5..3fc934140 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts @@ -5,14 +5,18 @@ import { AlertIcon } from '@primer/octicons-react'; import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class RepositoryDependabotAlertsThreadHandler extends DefaultHandler { readonly type = 'RepositoryDependabotAlertsThread'; - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return AlertIcon; } } -export const repositoryDependabotAlertsThreadHandler = - new RepositoryDependabotAlertsThreadHandler(); +export function createRepositoryDependabotAlertsThreadHandler( + notification: Notification, +): NotificationTypeHandler { + return new RepositoryDependabotAlertsThreadHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts index 7e499ffe4..2a97c79f2 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.ts @@ -4,13 +4,18 @@ import { MailIcon, type OcticonProps } from '@primer/octicons-react'; import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class RepositoryInvitationHandler extends DefaultHandler { readonly type = 'RepositoryInvitation'; - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return MailIcon; } } -export const repositoryInvitationHandler = new RepositoryInvitationHandler(); +export function createRepositoryInvitationHandler( + notification: Notification, +): NotificationTypeHandler { + return new RepositoryInvitationHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts index 471e55b85..f01760183 100644 --- a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts +++ b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts @@ -4,14 +4,18 @@ import { AlertIcon, type OcticonProps } from '@primer/octicons-react'; import type { Notification } from '../../../typesGitHub'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class RepositoryVulnerabilityAlertHandler extends DefaultHandler { readonly type = 'RepositoryVulnerabilityAlert'; - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return AlertIcon; } } -export const repositoryVulnerabilityAlertHandler = - new RepositoryVulnerabilityAlertHandler(); +export function createRepositoryVulnerabilityAlertHandler( + notification: Notification, +): NotificationTypeHandler { + return new RepositoryVulnerabilityAlertHandler(notification); +} diff --git a/src/renderer/utils/notifications/handlers/types.ts b/src/renderer/utils/notifications/handlers/types.ts index 0f15e91b5..4e4ada71a 100644 --- a/src/renderer/utils/notifications/handlers/types.ts +++ b/src/renderer/utils/notifications/handlers/types.ts @@ -11,37 +11,35 @@ import type { export interface NotificationTypeHandler { readonly type?: SubjectType; + readonly notification: Notification; /** * Enrich a notification. Settings may be unused for some handlers. */ - enrich( - notification: Notification, - settings: SettingsState, - ): Promise; + enrich(settings: SettingsState): Promise; /** * Return the icon component for this notification type. */ - iconType(notification: Notification): FC | null; + iconType(): FC | null; /** * Return the icon color for this notification type. */ - iconColor(notification: Notification): string | undefined; + iconColor(): string | undefined; /** * Return the formatted notification type for this notification. */ - formattedNotificationType(notification: Notification): string; + formattedNotificationType(): string; /** * Return the formatted notification number for this notification. */ - formattedNotificationNumber(notification: Notification): string; + formattedNotificationNumber(): string; /** * Return the formatted notification title for this notification. */ - formattedNotificationTitle(notification: Notification): string; + formattedNotificationTitle(): string; } diff --git a/src/renderer/utils/notifications/handlers/workflowRun.ts b/src/renderer/utils/notifications/handlers/workflowRun.ts index d3e21c5ad..3f4929a52 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.ts @@ -11,15 +11,13 @@ import type { WorkflowRunAttributes, } from '../../../typesGitHub'; import { DefaultHandler } from './default'; +import type { NotificationTypeHandler } from './types'; class WorkflowRunHandler extends DefaultHandler { readonly type = 'WorkflowRun'; - async enrich( - notification: Notification, - _settings: SettingsState, - ): Promise { - const state = getWorkflowRunAttributes(notification)?.status; + async enrich(_settings: SettingsState): Promise { + const state = getWorkflowRunAttributes(this.notification)?.status; if (state) { return { @@ -31,12 +29,16 @@ class WorkflowRunHandler extends DefaultHandler { return null; } - iconType(_notification: Notification): FC | null { + iconType(): FC | null { return RocketIcon; } } -export const workflowRunHandler = new WorkflowRunHandler(); +export function createWorkflowRunHandler( + notification: Notification, +): NotificationTypeHandler { + return new WorkflowRunHandler(notification); +} /** * Ideally we would be using a GitHub API to fetch the CheckSuite / WorkflowRun state, From 463790c09700aedf1893a34b6cac91a242e45d6e Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sun, 9 Nov 2025 22:38:14 -0500 Subject: [PATCH 3/3] refactor(handlers): use notification instead of subject Signed-off-by: Adam Setch --- .../notifications/handlers/checkSuite.test.ts | 109 ++++++----------- .../notifications/handlers/commit.test.ts | 22 ++-- .../notifications/handlers/default.test.ts | 59 +++++----- .../notifications/handlers/discussion.test.ts | 87 ++++++-------- .../notifications/handlers/index.test.ts | 57 +++++---- .../notifications/handlers/issue.test.ts | 101 ++++++---------- .../handlers/pullRequest.test.ts | 110 +++++++----------- .../notifications/handlers/release.test.ts | 25 ++-- .../repositoryDependabotAlertsThread.test.ts | 16 +-- .../handlers/repositoryInvitation.test.ts | 16 +-- .../repositoryVulnerabilityAlert.test.ts | 16 +-- .../handlers/workflowRun.test.ts | 37 +++--- .../utils/notifications/notifications.ts | 2 +- 13 files changed, 278 insertions(+), 379 deletions(-) diff --git a/src/renderer/utils/notifications/handlers/checkSuite.test.ts b/src/renderer/utils/notifications/handlers/checkSuite.test.ts index 777af4f5e..30f781bc3 100644 --- a/src/renderer/utils/notifications/handlers/checkSuite.test.ts +++ b/src/renderer/utils/notifications/handlers/checkSuite.test.ts @@ -1,7 +1,8 @@ import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import { checkSuiteHandler, getCheckSuiteAttributes } from './checkSuite'; +import type { StateType } from '../../../typesGitHub'; +import { createCheckSuiteHandler, getCheckSuiteAttributes } from './checkSuite'; describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { describe('enrich', () => { @@ -11,10 +12,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'cancelled', @@ -28,10 +27,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'failure', @@ -45,10 +42,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'failure', @@ -62,10 +57,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'failure', @@ -79,10 +72,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'skipped', @@ -96,10 +87,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'success', @@ -113,10 +102,8 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toBeNull(); }); @@ -127,57 +114,29 @@ describe('renderer/utils/notifications/handlers/checkSuite.ts', () => { type: 'CheckSuite', }); - const result = await checkSuiteHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createCheckSuiteHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toBeNull(); }); }); - it('iconType', () => { - expect( - checkSuiteHandler.iconType( - mockNotificationWithSubject({ type: 'CheckSuite', state: null }), - ).displayName, - ).toBe('RocketIcon'); - - expect( - checkSuiteHandler.iconType( - mockNotificationWithSubject({ - type: 'CheckSuite', - state: 'cancelled', - }), - ).displayName, - ).toBe('StopIcon'); - - expect( - checkSuiteHandler.iconType( - mockNotificationWithSubject({ - type: 'CheckSuite', - state: 'failure', - }), - ).displayName, - ).toBe('XIcon'); - - expect( - checkSuiteHandler.iconType( - mockNotificationWithSubject({ - type: 'CheckSuite', - state: 'skipped', - }), - ).displayName, - ).toBe('SkipIcon'); - - expect( - checkSuiteHandler.iconType( - mockNotificationWithSubject({ - type: 'CheckSuite', - state: 'success', - }), - ).displayName, - ).toBe('CheckIcon'); + describe('iconType', () => { + const cases: Array<[StateType, string]> = [ + [null, 'RocketIcon'], + ['cancelled', 'StopIcon'], + ['failure', 'XIcon'], + ['skipped', 'SkipIcon'], + ['success', 'CheckIcon'], + ]; + + it.each(cases)('returns expected icon for %s', (state, expectedIcon) => { + const handler = createCheckSuiteHandler( + mockNotificationWithSubject({ type: 'CheckSuite', state }), + ); + + expect(handler.iconType().displayName).toBe(expectedIcon); + }); }); describe('getCheckSuiteState', () => { diff --git a/src/renderer/utils/notifications/handlers/commit.test.ts b/src/renderer/utils/notifications/handlers/commit.test.ts index 5788ccef6..ea98c8f0d 100644 --- a/src/renderer/utils/notifications/handlers/commit.test.ts +++ b/src/renderer/utils/notifications/handlers/commit.test.ts @@ -8,7 +8,7 @@ import { } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import type { Link } from '../../../types'; -import { commitHandler } from './commit'; +import { createCommitHandler } from './commit'; describe('renderer/utils/notifications/handlers/commit.ts', () => { describe('enrich', () => { @@ -40,7 +40,8 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { .get('/repos/gitify-app/notifications-test/comments/141012658') .reply(200, { user: mockCommenter }); - const result = await commitHandler.enrich(mockNotification, mockSettings); + const handler = createCommitHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: null, @@ -67,7 +68,8 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { ) .reply(200, { author: mockAuthor }); - const result = await commitHandler.enrich(mockNotification, mockSettings); + const handler = createCommitHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: null, @@ -88,7 +90,8 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { latest_comment_url: null, }); - const result = await commitHandler.enrich(mockNotification, { + const handler = createCommitHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterStates: ['closed'], }); @@ -98,9 +101,12 @@ describe('renderer/utils/notifications/handlers/commit.ts', () => { }); it('iconType', () => { - expect( - commitHandler.iconType(mockNotificationWithSubject({ type: 'Commit' })) - .displayName, - ).toBe('GitCommitIcon'); + const handler = createCommitHandler( + mockNotificationWithSubject({ + type: 'Commit', + }), + ); + + expect(handler.iconType().displayName).toBe('GitCommitIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/default.test.ts b/src/renderer/utils/notifications/handlers/default.test.ts index 74db4b001..07784c97b 100644 --- a/src/renderer/utils/notifications/handlers/default.test.ts +++ b/src/renderer/utils/notifications/handlers/default.test.ts @@ -3,7 +3,7 @@ import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import { IconColor } from '../../../types'; import type { StateType } from '../../../typesGitHub'; -import { defaultHandler } from './default'; +import { createDefaultHandler } from './default'; describe('renderer/utils/notifications/handlers/default.ts', () => { describe('enrich', () => { @@ -14,19 +14,17 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { type: 'RepositoryInvitation', }); - const result = await defaultHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDefaultHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toBeNull(); }); }); it('iconType', () => { - expect( - defaultHandler.iconType(mockNotificationWithSubject({})).displayName, - ).toBe('QuestionIcon'); + const handler = createDefaultHandler(mockNotificationWithSubject({})); + + expect(handler.iconType().displayName).toBe('QuestionIcon'); }); describe('iconColor', () => { @@ -50,8 +48,11 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { ]; it.each(cases)('returns correct color for state %s', (state, expected) => { - const subject = mockNotificationWithSubject({ state }); - expect(defaultHandler.iconColor(subject)).toBe(expected); + const handler = createDefaultHandler( + mockNotificationWithSubject({ state }), + ); + + expect(handler.iconColor()).toBe(expected); }); }); @@ -63,9 +64,9 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { state: 'open', }); - expect(defaultHandler.formattedNotificationType(notification)).toBe( - 'Open Pull Request', - ); + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationType()).toBe('Open Pull Request'); }); it('handles missing state (null) gracefully', () => { @@ -75,9 +76,9 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { state: null, }); - expect(defaultHandler.formattedNotificationType(notification)).toBe( - 'Issue', - ); + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationType()).toBe('Issue'); }); }); @@ -89,9 +90,10 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { state: 'open', }); notification.subject.number = 42; - expect(defaultHandler.formattedNotificationNumber(notification)).toBe( - '#42', - ); + + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationNumber()).toBe('#42'); }); it('returns empty string when number absent', () => { @@ -100,7 +102,10 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { type: 'Issue', state: 'open', }); - expect(defaultHandler.formattedNotificationNumber(notification)).toBe(''); + + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationNumber()).toBe(''); }); }); @@ -112,9 +117,10 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { state: 'open', }); notification.subject.number = 101; - expect(defaultHandler.formattedNotificationTitle(notification)).toBe( - 'Fix bug [#101]', - ); + + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationTitle()).toBe('Fix bug [#101]'); }); it('returns title unchanged when number missing', () => { @@ -123,9 +129,10 @@ describe('renderer/utils/notifications/handlers/default.ts', () => { type: 'Issue', state: 'open', }); - expect(defaultHandler.formattedNotificationTitle(notification)).toBe( - 'Improve docs', - ); + + const handler = createDefaultHandler(notification); + + expect(handler.formattedNotificationTitle()).toBe('Improve docs'); }); }); }); diff --git a/src/renderer/utils/notifications/handlers/discussion.test.ts b/src/renderer/utils/notifications/handlers/discussion.test.ts index fdcf1eb92..be1d1f6d3 100644 --- a/src/renderer/utils/notifications/handlers/discussion.test.ts +++ b/src/renderer/utils/notifications/handlers/discussion.test.ts @@ -10,8 +10,9 @@ import type { DiscussionAuthor, DiscussionStateType, Repository, + StateType, } from '../../../typesGitHub'; -import { discussionHandler } from './discussion'; +import { createDiscussionHandler } from './discussion'; const mockDiscussionAuthor: DiscussionAuthor = { login: 'discussion-author', @@ -52,10 +53,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -82,10 +81,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -112,10 +109,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -142,10 +137,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -172,10 +165,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -202,10 +193,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -240,10 +229,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -270,7 +257,8 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }, }); - const result = await discussionHandler.enrich(mockNotification, { + const handler = createDiscussionHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterStates: ['closed'], }); @@ -279,30 +267,21 @@ describe('renderer/utils/notifications/handlers/discussion.ts', () => { }); }); - it('iconType', () => { - expect( - discussionHandler.iconType( - mockNotificationWithSubject({ type: 'Discussion' }), - ).displayName, - ).toBe('CommentDiscussionIcon'); - - expect( - discussionHandler.iconType( - mockNotificationWithSubject({ type: 'Discussion', state: 'DUPLICATE' }), - ).displayName, - ).toBe('DiscussionDuplicateIcon'); - - expect( - discussionHandler.iconType( - mockNotificationWithSubject({ type: 'Discussion', state: 'OUTDATED' }), - ).displayName, - ).toBe('DiscussionOutdatedIcon'); - - expect( - discussionHandler.iconType( - mockNotificationWithSubject({ type: 'Discussion', state: 'RESOLVED' }), - ).displayName, - ).toBe('DiscussionClosedIcon'); + describe('iconType', () => { + const cases: Array<[StateType, string]> = [ + [null, 'CommentDiscussionIcon'], + ['DUPLICATE', 'DiscussionDuplicateIcon'], + ['OUTDATED', 'DiscussionOutdatedIcon'], + ['RESOLVED', 'DiscussionClosedIcon'], + ]; + + it.each(cases)('returns expected icon for %s', (state, expectedIcon) => { + const handler = createDiscussionHandler( + mockNotificationWithSubject({ type: 'Discussion', state }), + ); + + expect(handler.iconType().displayName).toBe(expectedIcon); + }); }); }); diff --git a/src/renderer/utils/notifications/handlers/index.test.ts b/src/renderer/utils/notifications/handlers/index.test.ts index 7964c41fd..1f3ea1b6c 100644 --- a/src/renderer/utils/notifications/handlers/index.test.ts +++ b/src/renderer/utils/notifications/handlers/index.test.ts @@ -1,42 +1,47 @@ import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import type { SubjectType } from '../../../typesGitHub'; -import { checkSuiteHandler } from './checkSuite'; -import { commitHandler } from './commit'; -import { defaultHandler } from './default'; -import { discussionHandler } from './discussion'; +import { createCheckSuiteHandler } from './checkSuite'; +import { createCommitHandler } from './commit'; +import { createDefaultHandler } from './default'; +import { createDiscussionHandler } from './discussion'; import { createNotificationHandler } from './index'; -import { issueHandler } from './issue'; -import { pullRequestHandler } from './pullRequest'; -import { releaseHandler } from './release'; -import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; -import { repositoryInvitationHandler } from './repositoryInvitation'; -import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; -import { workflowRunHandler } from './workflowRun'; +import { createIssueHandler } from './issue'; +import { createPullRequestHandler } from './pullRequest'; +import { createReleaseHandler } from './release'; +import { createRepositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; +import { createRepositoryInvitationHandler } from './repositoryInvitation'; +import { createRepositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; +import { createWorkflowRunHandler } from './workflowRun'; describe('renderer/utils/notifications/handlers/index.ts', () => { describe('createNotificationHandler', () => { const cases: Array<[SubjectType, object]> = [ - ['CheckSuite', checkSuiteHandler], - ['Commit', commitHandler], - ['Discussion', discussionHandler], - ['Issue', issueHandler], - ['PullRequest', pullRequestHandler], - ['Release', releaseHandler], + ['CheckSuite', createCheckSuiteHandler], + ['Commit', createCommitHandler], + ['Discussion', createDiscussionHandler], + ['Issue', createIssueHandler], + ['PullRequest', createPullRequestHandler], + ['Release', createReleaseHandler], [ 'RepositoryDependabotAlertsThread', - repositoryDependabotAlertsThreadHandler, + createRepositoryDependabotAlertsThreadHandler, ], - ['RepositoryInvitation', repositoryInvitationHandler], - ['RepositoryVulnerabilityAlert', repositoryVulnerabilityAlertHandler], - ['WorkflowRun', workflowRunHandler], + ['RepositoryInvitation', createRepositoryInvitationHandler], + [ + 'RepositoryVulnerabilityAlert', + createRepositoryVulnerabilityAlertHandler, + ], + ['WorkflowRun', createWorkflowRunHandler], ]; it.each(cases)( 'returns expected handler instance for %s', (type, expected) => { const notification = partialMockNotification({ type }); - const handler = createNotificationHandler(notification); - expect(handler).toBe(expected); + + createNotificationHandler(notification); + + expect(expected).toHaveBeenCalledWith(notification); }, ); @@ -44,8 +49,10 @@ describe('renderer/utils/notifications/handlers/index.ts', () => { const notification = partialMockNotification({ type: 'SomeFutureType' as SubjectType, }); - const handler = createNotificationHandler(notification); - expect(handler).toBe(defaultHandler); + + createNotificationHandler(notification); + + expect(createDefaultHandler).toHaveBeenCalledWith(notification); }); }); }); diff --git a/src/renderer/utils/notifications/handlers/issue.test.ts b/src/renderer/utils/notifications/handlers/issue.test.ts index fc3b5d47f..643dc1736 100644 --- a/src/renderer/utils/notifications/handlers/issue.test.ts +++ b/src/renderer/utils/notifications/handlers/issue.test.ts @@ -8,8 +8,8 @@ import { } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import type { Link } from '../../../types'; -import type { Notification } from '../../../typesGitHub'; -import { issueHandler } from './issue'; +import type { Notification, StateType } from '../../../typesGitHub'; +import { createIssueHandler } from './issue'; describe('renderer/utils/notifications/handlers/issue.ts', () => { describe('enrich', () => { @@ -46,7 +46,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -75,7 +76,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -105,7 +107,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -135,7 +138,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -165,7 +169,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -194,7 +199,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { labels: [], }); - const result = await issueHandler.enrich(mockNotification, mockSettings); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -224,10 +230,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -256,10 +260,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await issueHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -285,7 +287,8 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { labels: [], }); - const result = await issueHandler.enrich(mockNotification, { + const handler = createIssueHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterStates: ['closed'], }); @@ -294,52 +297,22 @@ describe('renderer/utils/notifications/handlers/issue.ts', () => { }); }); - it('iconType', () => { - expect( - issueHandler.iconType(mockNotificationWithSubject({ type: 'Issue' })) - .displayName, - ).toBe('IssueOpenedIcon'); - - expect( - issueHandler.iconType( - mockNotificationWithSubject({ type: 'Issue', state: 'draft' }), - ).displayName, - ).toBe('IssueDraftIcon'); - - expect( - issueHandler.iconType( - mockNotificationWithSubject({ - type: 'Issue', - state: 'closed', - }), - ).displayName, - ).toBe('IssueClosedIcon'); - - expect( - issueHandler.iconType( - mockNotificationWithSubject({ - type: 'Issue', - state: 'completed', - }), - ).displayName, - ).toBe('IssueClosedIcon'); - - expect( - issueHandler.iconType( - mockNotificationWithSubject({ - type: 'Issue', - state: 'not_planned', - }), - ).displayName, - ).toBe('SkipIcon'); - - expect( - issueHandler.iconType( - mockNotificationWithSubject({ - type: 'Issue', - state: 'reopened', - }), - ).displayName, - ).toBe('IssueReopenedIcon'); + describe('iconType', () => { + const cases: Array<[StateType, string]> = [ + [null, 'IssueOpenedIcon'], + ['draft', 'IssueDraftIcon'], + ['closed', 'IssueClosedIcon'], + ['completed', 'IssueClosedIcon'], + ['not_planned', 'SkipIcon'], + ['reopened', 'IssueReopenedIcon'], + ]; + + it.each(cases)('returns expected icon for %s', (state, expectedIcon) => { + const handler = createIssueHandler( + mockNotificationWithSubject({ type: 'Issue', state }), + ); + + expect(handler.iconType().displayName).toBe(expectedIcon); + }); }); }); diff --git a/src/renderer/utils/notifications/handlers/pullRequest.test.ts b/src/renderer/utils/notifications/handlers/pullRequest.test.ts index 2e2462369..10d7c1620 100644 --- a/src/renderer/utils/notifications/handlers/pullRequest.test.ts +++ b/src/renderer/utils/notifications/handlers/pullRequest.test.ts @@ -8,11 +8,15 @@ import { } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import type { Link } from '../../../types'; -import type { Notification, PullRequest } from '../../../typesGitHub'; +import type { + Notification, + PullRequest, + StateType, +} from '../../../typesGitHub'; import { + createPullRequestHandler, getLatestReviewForReviewers, parseLinkedIssuesFromPr, - pullRequestHandler, } from './pullRequest'; describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { @@ -58,10 +62,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -98,10 +100,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -138,10 +138,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -178,10 +176,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -217,10 +213,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -255,10 +249,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -296,10 +288,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -336,10 +326,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/pulls/1/reviews') .reply(200, []); - const result = await pullRequestHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ number: 123, @@ -405,7 +393,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { labels: [], }); - const result = await pullRequestHandler.enrich(mockNotification, { + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterStates: ['closed'], }); @@ -429,7 +418,8 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { .get('/repos/gitify-app/notifications-test/issues/comments/302888448') .reply(200, { user: mockCommenter }); - const result = await pullRequestHandler.enrich(mockNotification, { + const handler = createPullRequestHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterUserTypes: ['Bot'], }); @@ -438,39 +428,21 @@ describe('renderer/utils/notifications/handlers/pullRequest.ts', () => { }); }); - it('iconType', () => { - expect( - pullRequestHandler.iconType( - mockNotificationWithSubject({ type: 'PullRequest' }), - ).displayName, - ).toBe('GitPullRequestIcon'); - - expect( - pullRequestHandler.iconType( - mockNotificationWithSubject({ - type: 'PullRequest', - state: 'draft', - }), - ).displayName, - ).toBe('GitPullRequestDraftIcon'); - - expect( - pullRequestHandler.iconType( - mockNotificationWithSubject({ - type: 'PullRequest', - state: 'closed', - }), - ).displayName, - ).toBe('GitPullRequestClosedIcon'); - - expect( - pullRequestHandler.iconType( - mockNotificationWithSubject({ - type: 'PullRequest', - state: 'merged', - }), - ).displayName, - ).toBe('GitMergeIcon'); + describe('iconType', () => { + const cases: Array<[StateType, string]> = [ + [null, 'GitPullRequestIcon'], + ['draft', 'GitPullRequestDraftIcon'], + ['closed', 'GitPullRequestClosedIcon'], + ['merged', 'GitMergeIcon'], + ]; + + it.each(cases)('returns expected icon for %s', (state, expectedIcon) => { + const handler = createPullRequestHandler( + mockNotificationWithSubject({ type: 'PullRequest', state }), + ); + + expect(handler.iconType().displayName).toBe(expectedIcon); + }); }); describe('Pull Request Reviews - Latest Reviews By Reviewer', () => { diff --git a/src/renderer/utils/notifications/handlers/release.test.ts b/src/renderer/utils/notifications/handlers/release.test.ts index d9fc1c288..d0da4d82a 100644 --- a/src/renderer/utils/notifications/handlers/release.test.ts +++ b/src/renderer/utils/notifications/handlers/release.test.ts @@ -8,7 +8,7 @@ import { } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; import type { Link } from '../../../types'; -import { releaseHandler } from './release'; +import { createReleaseHandler } from './release'; describe('renderer/utils/notifications/handlers/release.ts', () => { describe('enrich', () => { @@ -33,10 +33,8 @@ describe('renderer/utils/notifications/handlers/release.ts', () => { .get('/repos/gitify-app/notifications-test/releases/1') .reply(200, { author: mockAuthor }); - const result = await releaseHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createReleaseHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: null, @@ -58,7 +56,8 @@ describe('renderer/utils/notifications/handlers/release.ts', () => { 'https://api.github.com/repos/gitify-app/notifications-test/releases/1' as Link, }); - const result = await releaseHandler.enrich(mockNotification, { + const handler = createReleaseHandler(mockNotification); + const result = await handler.enrich({ ...mockSettings, filterStates: ['closed'], }); @@ -68,12 +67,12 @@ describe('renderer/utils/notifications/handlers/release.ts', () => { }); it('iconType', () => { - expect( - releaseHandler.iconType( - mockNotificationWithSubject({ - type: 'Release', - }), - ).displayName, - ).toBe('TagIcon'); + const handler = createReleaseHandler( + mockNotificationWithSubject({ + type: 'Release', + }), + ); + + expect(handler.iconType().displayName).toBe('TagIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts index 3b0e497e5..e880a4408 100644 --- a/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.test.ts @@ -1,14 +1,14 @@ import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; -import { repositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; +import { createRepositoryDependabotAlertsThreadHandler } from './repositoryDependabotAlertsThread'; describe('renderer/utils/notifications/handlers/repositoryDependabotAlertsThread.ts', () => { it('iconType', () => { - expect( - repositoryDependabotAlertsThreadHandler.iconType( - mockNotificationWithSubject({ - type: 'RepositoryDependabotAlertsThread', - }), - ).displayName, - ).toBe('AlertIcon'); + const handler = createRepositoryDependabotAlertsThreadHandler( + mockNotificationWithSubject({ + type: 'RepositoryDependabotAlertsThread', + }), + ); + + expect(handler.iconType().displayName).toBe('AlertIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts index e5e249a6a..c4e2db6d7 100644 --- a/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryInvitation.test.ts @@ -1,14 +1,14 @@ import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; -import { repositoryInvitationHandler } from './repositoryInvitation'; +import { createRepositoryInvitationHandler } from './repositoryInvitation'; describe('renderer/utils/notifications/handlers/repositoryInvitation.ts', () => { it('iconType', () => { - expect( - repositoryInvitationHandler.iconType( - mockNotificationWithSubject({ - type: 'RepositoryInvitation', - }), - ).displayName, - ).toBe('MailIcon'); + const handler = createRepositoryInvitationHandler( + mockNotificationWithSubject({ + type: 'RepositoryInvitation', + }), + ); + + expect(handler.iconType().displayName).toBe('MailIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts index 153fbb712..616df80da 100644 --- a/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts +++ b/src/renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.test.ts @@ -1,14 +1,14 @@ import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; -import { repositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; +import { createRepositoryVulnerabilityAlertHandler } from './repositoryVulnerabilityAlert'; describe('renderer/utils/notifications/handlers/repositoryVulnerabilityAlert.ts', () => { it('iconType', () => { - expect( - repositoryVulnerabilityAlertHandler.iconType( - mockNotificationWithSubject({ - type: 'RepositoryVulnerabilityAlert', - }), - ).displayName, - ).toBe('AlertIcon'); + const handler = createRepositoryVulnerabilityAlertHandler( + mockNotificationWithSubject({ + type: 'RepositoryVulnerabilityAlert', + }), + ); + + expect(handler.iconType().displayName).toBe('AlertIcon'); }); }); diff --git a/src/renderer/utils/notifications/handlers/workflowRun.test.ts b/src/renderer/utils/notifications/handlers/workflowRun.test.ts index e8a2ebd1b..7ae7da0ad 100644 --- a/src/renderer/utils/notifications/handlers/workflowRun.test.ts +++ b/src/renderer/utils/notifications/handlers/workflowRun.test.ts @@ -1,7 +1,10 @@ import { mockNotificationWithSubject } from '../../../__mocks__/notifications-mocks'; import { partialMockNotification } from '../../../__mocks__/partial-mocks'; import { mockSettings } from '../../../__mocks__/state-mocks'; -import { getWorkflowRunAttributes, workflowRunHandler } from './workflowRun'; +import { + createWorkflowRunHandler, + getWorkflowRunAttributes, +} from './workflowRun'; describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { describe('enrich', () => { @@ -11,10 +14,8 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { type: 'WorkflowRun', }); - const result = await workflowRunHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createWorkflowRunHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toEqual({ state: 'waiting', @@ -29,10 +30,8 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { type: 'WorkflowRun', }); - const result = await workflowRunHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createWorkflowRunHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toBeNull(); }); @@ -43,23 +42,21 @@ describe('renderer/utils/notifications/handlers/workflowRun.ts', () => { type: 'WorkflowRun', }); - const result = await workflowRunHandler.enrich( - mockNotification, - mockSettings, - ); + const handler = createWorkflowRunHandler(mockNotification); + const result = await handler.enrich(mockSettings); expect(result).toBeNull(); }); }); it('iconType', () => { - expect( - workflowRunHandler.iconType( - mockNotificationWithSubject({ - type: 'WorkflowRun', - }), - ).displayName, - ).toBe('RocketIcon'); + const handler = createWorkflowRunHandler( + mockNotificationWithSubject({ + type: 'WorkflowRun', + }), + ); + + expect(handler.iconType().displayName).toBe('RocketIcon'); }); describe('getWorkflowRunAttributes', () => { diff --git a/src/renderer/utils/notifications/notifications.ts b/src/renderer/utils/notifications/notifications.ts index 86d7f256c..e8a498d04 100644 --- a/src/renderer/utils/notifications/notifications.ts +++ b/src/renderer/utils/notifications/notifications.ts @@ -150,7 +150,7 @@ export async function enrichNotification( try { const handler = createNotificationHandler(notification); - additionalSubjectDetails = await handler.enrich(notification, settings); + additionalSubjectDetails = await handler.enrich(settings); } catch (err) { rendererLogError( 'enrichNotification',