From 78302a1b1e33bdd9c200af89daeb85c13817878c Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Mon, 10 Nov 2025 07:33:57 -0500 Subject: [PATCH] tests: api client test cases Signed-off-by: Adam Setch --- .../components/metrics/MetricGroup.tsx | 3 + .../utils/api/__mocks__/request-mocks.ts | 19 ++ src/renderer/utils/api/client.test.ts | 285 ++++-------------- src/renderer/utils/api/request.test.ts | 69 +++-- src/renderer/utils/api/request.ts | 4 +- 5 files changed, 131 insertions(+), 249 deletions(-) create mode 100644 src/renderer/utils/api/__mocks__/request-mocks.ts diff --git a/src/renderer/components/metrics/MetricGroup.tsx b/src/renderer/components/metrics/MetricGroup.tsx index 27cd012df..ae32660e9 100644 --- a/src/renderer/components/metrics/MetricGroup.tsx +++ b/src/renderer/components/metrics/MetricGroup.tsx @@ -62,6 +62,7 @@ export const MetricGroup: FC = ({ /> ); })} + {notification.subject?.comments > 0 && ( = ({ title={commentsPillDescription} /> )} + {notification.subject?.labels?.length > 0 && ( = ({ title={labelsPillDescription} /> )} + {notification.subject.milestone && ( { @@ -29,79 +28,32 @@ describe('renderer/utils/api/client.ts', () => { jest.clearAllMocks(); }); - describe('getAuthenticatedUser', () => { - it('should fetch authenticated user - github', async () => { - await getAuthenticatedUser(mockGitHubHostname, mockToken); - - expect(axios).toHaveBeenCalledWith({ - url: 'https://api.github.com/user', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'GET', - data: {}, - }); - }); - - it('should fetch authenticated user - enterprise', async () => { - await getAuthenticatedUser(mockEnterpriseHostname, mockToken); + it('getAuthenticatedUser - should fetch authenticated user', async () => { + await getAuthenticatedUser(mockGitHubHostname, mockToken); - expect(axios).toHaveBeenCalledWith({ - url: 'https://example.com/api/v3/user', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'GET', - data: {}, - }); + expect(axios).toHaveBeenCalledWith({ + url: 'https://api.github.com/user', + headers: mockAuthHeaders, + method: 'GET', + data: {}, }); }); - describe('headNotifications', () => { - it('should fetch notifications head - github', async () => { - await headNotifications(mockGitHubHostname, mockToken); - - expect(axios).toHaveBeenCalledWith({ - url: 'https://api.github.com/notifications', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - }, - method: 'HEAD', - data: {}, - }); - }); - - it('should fetch notifications head - enterprise', async () => { - await headNotifications(mockEnterpriseHostname, mockToken); + it('headNotifications - should fetch notifications head', async () => { + await headNotifications(mockGitHubHostname, mockToken); - expect(axios).toHaveBeenCalledWith({ - url: 'https://example.com/api/v3/notifications', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - }, - method: 'HEAD', - data: {}, - }); + expect(axios).toHaveBeenCalledWith({ + url: 'https://api.github.com/notifications', + headers: mockNonCachedAuthHeaders, + method: 'HEAD', + data: {}, }); }); describe('listNotificationsForAuthenticatedUser', () => { - it('should list notifications for user - github cloud - fetchAllNotifications true', async () => { + it('should list only participating notifications for user', async () => { const mockSettings: Partial = { participating: true, - fetchAllNotifications: true, }; await listNotificationsForAuthenticatedUser( @@ -111,21 +63,15 @@ describe('renderer/utils/api/client.ts', () => { expect(axios).toHaveBeenCalledWith({ url: 'https://api.github.com/notifications?participating=true', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - }, + headers: mockNonCachedAuthHeaders, method: 'GET', data: {}, }); }); - it('should list notifications for user - github cloud - fetchAllNotifications false', async () => { + it('should list participating and watching notifications for user', async () => { const mockSettings: Partial = { - participating: true, - fetchAllNotifications: false, + participating: false, }; await listNotificationsForAuthenticatedUser( @@ -134,165 +80,56 @@ describe('renderer/utils/api/client.ts', () => { ); expect(axios).toHaveBeenCalledWith({ - url: 'https://api.github.com/notifications?participating=true', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - }, - method: 'GET', - data: {}, - }); - }); - - it('should list notifications for user - github enterprise server', async () => { - const mockSettings: Partial = { - participating: true, - }; - - await listNotificationsForAuthenticatedUser( - mockGitHubEnterpriseServerAccount, - mockSettings as SettingsState, - ); - - expect(axios).toHaveBeenCalledWith({ - url: 'https://github.gitify.io/api/v3/notifications?participating=true', - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/json', - }, + url: 'https://api.github.com/notifications?participating=false', + headers: mockNonCachedAuthHeaders, method: 'GET', data: {}, }); }); }); - describe('markNotificationThreadAsRead', () => { - it('should mark notification thread as read - github', async () => { - await markNotificationThreadAsRead( - mockThreadId, - mockGitHubHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://api.github.com/notifications/threads/${mockThreadId}`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'PATCH', - data: {}, - }); - }); - - it('should mark notification thread as read - enterprise', async () => { - await markNotificationThreadAsRead( - mockThreadId, - mockEnterpriseHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://example.com/api/v3/notifications/threads/${mockThreadId}`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'PATCH', - data: {}, - }); + it('markNotificationThreadAsRead - should mark notification thread as read', async () => { + await markNotificationThreadAsRead( + mockThreadId, + mockGitHubHostname, + mockToken, + ); + + expect(axios).toHaveBeenCalledWith({ + url: `https://api.github.com/notifications/threads/${mockThreadId}`, + headers: mockAuthHeaders, + method: 'PATCH', + data: {}, }); }); - describe('markNotificationThreadAsDone', () => { - it('should mark notification thread as done - github', async () => { - await markNotificationThreadAsDone( - mockThreadId, - mockGitHubHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://api.github.com/notifications/threads/${mockThreadId}`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'DELETE', - data: {}, - }); - }); - - it('should mark notification thread as done - enterprise', async () => { - await markNotificationThreadAsDone( - mockThreadId, - mockEnterpriseHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://example.com/api/v3/notifications/threads/${mockThreadId}`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'DELETE', - data: {}, - }); + it('markNotificationThreadAsDone - should mark notification thread as done', async () => { + await markNotificationThreadAsDone( + mockThreadId, + mockGitHubHostname, + mockToken, + ); + + expect(axios).toHaveBeenCalledWith({ + url: `https://api.github.com/notifications/threads/${mockThreadId}`, + headers: mockAuthHeaders, + method: 'DELETE', + data: {}, }); }); - describe('ignoreNotificationThreadSubscription', () => { - it('should ignore notification thread subscription - github', async () => { - await ignoreNotificationThreadSubscription( - mockThreadId, - mockGitHubHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://api.github.com/notifications/threads/${mockThreadId}/subscription`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'PUT', - data: { ignored: true }, - }); - }); - - it('should ignore notification thread subscription - enterprise', async () => { - await ignoreNotificationThreadSubscription( - mockThreadId, - mockEnterpriseHostname, - mockToken, - ); - - expect(axios).toHaveBeenCalledWith({ - url: `https://example.com/api/v3/notifications/threads/${mockThreadId}/subscription`, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, - method: 'PUT', - data: { ignored: true }, - }); + it('ignoreNotificationThreadSubscription - should ignore notification thread subscription', async () => { + await ignoreNotificationThreadSubscription( + mockThreadId, + mockGitHubHostname, + mockToken, + ); + + expect(axios).toHaveBeenCalledWith({ + url: `https://api.github.com/notifications/threads/${mockThreadId}/subscription`, + headers: mockAuthHeaders, + method: 'PUT', + data: { ignored: true }, }); }); diff --git a/src/renderer/utils/api/request.test.ts b/src/renderer/utils/api/request.test.ts index c759c6b1e..cba638f7c 100644 --- a/src/renderer/utils/api/request.test.ts +++ b/src/renderer/utils/api/request.test.ts @@ -1,7 +1,17 @@ import axios from 'axios'; import type { Link, Token } from '../../types'; -import { apiRequest, apiRequestAuth } from './request'; +import { + mockAuthHeaders, + mockNoAuthHeaders, + mockNonCachedAuthHeaders, +} from './__mocks__/request-mocks'; +import { + apiRequest, + apiRequestAuth, + getHeaders, + shouldRequestWithNoCache, +} from './request'; jest.mock('axios'); @@ -22,11 +32,7 @@ describe('renderer/utils/api/request.ts', () => { method, url, data, - headers: { - Accept: 'application/json', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, + headers: mockNoAuthHeaders, }); }); @@ -38,11 +44,7 @@ describe('renderer/utils/api/request.ts', () => { method, url, data, - headers: { - Accept: 'application/json', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, + headers: mockNoAuthHeaders, }); }); }); @@ -63,12 +65,7 @@ describe('apiRequestAuth', () => { method, url, data, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, + headers: mockAuthHeaders, }); }); @@ -81,12 +78,38 @@ describe('apiRequestAuth', () => { method, url, data, - headers: { - Accept: 'application/json', - Authorization: 'token decrypted', - 'Cache-Control': '', - 'Content-Type': 'application/json', - }, + headers: mockAuthHeaders, }); }); + + it('shouldRequestWithNoCache', () => { + expect( + shouldRequestWithNoCache('https://example.com/api/v3/notifications'), + ).toBe(true); + + expect( + shouldRequestWithNoCache('https://example.com/login/oauth/access_token'), + ).toBe(true); + + expect(shouldRequestWithNoCache('https://example.com/notifications')).toBe( + true, + ); + + expect( + shouldRequestWithNoCache('https://example.com/some/other/endpoint'), + ).toBe(false); + }); + + it('should get headers correctly', async () => { + expect(await getHeaders(url)).toEqual(mockNoAuthHeaders); + + expect(await getHeaders(url, token)).toEqual(mockAuthHeaders); + + expect( + await getHeaders( + 'https://example.com/api/v3/notifications' as Link, + token, + ), + ).toEqual(mockNonCachedAuthHeaders); + }); }); diff --git a/src/renderer/utils/api/request.ts b/src/renderer/utils/api/request.ts index a3d445015..bd92d74d4 100644 --- a/src/renderer/utils/api/request.ts +++ b/src/renderer/utils/api/request.ts @@ -86,7 +86,7 @@ export async function apiRequestAuth( * @param url * @returns boolean */ -function shouldRequestWithNoCache(url: string) { +export function shouldRequestWithNoCache(url: string) { const parsedUrl = new URL(url); switch (parsedUrl.pathname) { @@ -106,7 +106,7 @@ function shouldRequestWithNoCache(url: string) { * @param token * @returns */ -async function getHeaders(url: Link, token?: Token) { +export async function getHeaders(url: Link, token?: Token) { const headers: Record = { Accept: 'application/json', 'Cache-Control': shouldRequestWithNoCache(url) ? 'no-cache' : '',