Skip to content

Commit 9d4720d

Browse files
setchyafonsojramos
andauthored
feat: notification filters (#1304)
Co-authored-by: Afonso Jorge Ramos <[email protected]>
1 parent 81e027e commit 9d4720d

21 files changed

+1769
-285
lines changed

src/__mocks__/state-mocks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const mockSettings: SettingsState = {
7575
participating: false,
7676
playSound: true,
7777
showNotifications: true,
78-
showBots: true,
78+
hideBots: false,
7979
showNotificationsCountInTray: false,
8080
openAtStartup: false,
8181
theme: Theme.SYSTEM,
@@ -86,6 +86,7 @@ export const mockSettings: SettingsState = {
8686
showPills: true,
8787
keyboardShortcut: true,
8888
groupBy: GroupBy.REPOSITORY,
89+
filterReasons: [],
8990
};
9091

9192
export const mockState: GitifyState = {

src/app.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Loading } from './components/Loading';
1010
import { Sidebar } from './components/Sidebar';
1111
import { AppContext, AppProvider } from './context/App';
1212
import { AccountsRoute } from './routes/Accounts';
13+
import { FiltersRoute } from './routes/Filters';
1314
import { LoginRoute } from './routes/Login';
1415
import { LoginWithOAuthApp } from './routes/LoginWithOAuthApp';
1516
import { LoginWithPersonalAccessToken } from './routes/LoginWithPersonalAccessToken';
@@ -43,6 +44,14 @@ export const App = () => {
4344
</RequireAuth>
4445
}
4546
/>
47+
<Route
48+
path="/filters"
49+
element={
50+
<RequireAuth>
51+
<FiltersRoute />
52+
</RequireAuth>
53+
}
54+
/>
4655
<Route
4756
path="/settings"
4857
element={

src/components/Header.test.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ describe('components/Header.tsx', () => {
2626

2727
fireEvent.click(screen.getByLabelText('Go Back'));
2828

29-
expect(mockNavigate).toHaveBeenCalledTimes(1);
30-
expect(mockNavigate).toHaveBeenCalledWith(-1);
29+
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
3130
});
3231

3332
it('should navigate back and fetch notifications', () => {
@@ -43,8 +42,7 @@ describe('components/Header.tsx', () => {
4342

4443
fireEvent.click(screen.getByLabelText('Go Back'));
4544

46-
expect(mockNavigate).toHaveBeenCalledTimes(1);
47-
expect(mockNavigate).toHaveBeenCalledWith(-1);
45+
expect(mockNavigate).toHaveBeenNthCalledWith(1, -1);
4846
expect(fetchNotifications).toHaveBeenCalledTimes(1);
4947
});
5048
});

src/components/Sidebar.test.tsx

Lines changed: 209 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ describe('components/Sidebar.tsx', () => {
4242
it('should render itself & its children (logged out)', () => {
4343
const tree = render(
4444
<AppContext.Provider
45-
value={{ isLoggedIn: false, notifications: mockAccountNotifications }}
45+
value={{
46+
isLoggedIn: false,
47+
notifications: mockAccountNotifications,
48+
settings: mockSettings,
49+
}}
4650
>
4751
<MemoryRouter>
4852
<Sidebar />
@@ -55,7 +59,9 @@ describe('components/Sidebar.tsx', () => {
5559

5660
it('should open the gitify repository', () => {
5761
render(
58-
<AppContext.Provider value={{ isLoggedIn: false, notifications: [] }}>
62+
<AppContext.Provider
63+
value={{ isLoggedIn: false, notifications: [], settings: mockSettings }}
64+
>
5965
<MemoryRouter>
6066
<Sidebar />
6167
</MemoryRouter>
@@ -74,7 +80,13 @@ describe('components/Sidebar.tsx', () => {
7480
describe('notifications icon', () => {
7581
it('when there are 0 notifications', () => {
7682
render(
77-
<AppContext.Provider value={{ isLoggedIn: true, notifications: [] }}>
83+
<AppContext.Provider
84+
value={{
85+
isLoggedIn: true,
86+
notifications: [],
87+
settings: mockSettings,
88+
}}
89+
>
7890
<MemoryRouter>
7991
<Sidebar />
8092
</MemoryRouter>
@@ -101,6 +113,7 @@ describe('components/Sidebar.tsx', () => {
101113
value={{
102114
isLoggedIn: true,
103115
notifications: mockAccountNotifications,
116+
settings: mockSettings,
104117
}}
105118
>
106119
<MemoryRouter>
@@ -132,6 +145,7 @@ describe('components/Sidebar.tsx', () => {
132145
value={{
133146
isLoggedIn: true,
134147
notifications: mockAccountNotifications,
148+
settings: mockSettings,
135149
}}
136150
>
137151
<MemoryRouter>
@@ -154,6 +168,7 @@ describe('components/Sidebar.tsx', () => {
154168
value={{
155169
isLoggedIn: true,
156170
notifications: mockAccountNotifications,
171+
settings: mockSettings,
157172
}}
158173
>
159174
<MemoryRouter>
@@ -177,6 +192,7 @@ describe('components/Sidebar.tsx', () => {
177192
value={{
178193
isLoggedIn: true,
179194
notifications: [],
195+
settings: mockSettings,
180196
fetchNotifications,
181197
status: 'success',
182198
}}
@@ -198,6 +214,7 @@ describe('components/Sidebar.tsx', () => {
198214
value={{
199215
isLoggedIn: true,
200216
notifications: [],
217+
settings: mockSettings,
201218
fetchNotifications,
202219
status: 'loading',
203220
}}
@@ -214,10 +231,54 @@ describe('components/Sidebar.tsx', () => {
214231
});
215232
});
216233

234+
describe('Filters', () => {
235+
it('go to the filters route', () => {
236+
render(
237+
<AppContext.Provider
238+
value={{
239+
isLoggedIn: true,
240+
notifications: [],
241+
settings: mockSettings,
242+
}}
243+
>
244+
<MemoryRouter>
245+
<Sidebar />
246+
</MemoryRouter>
247+
</AppContext.Provider>,
248+
);
249+
fireEvent.click(screen.getByTitle('Filters'));
250+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/filters');
251+
});
252+
253+
it('go to the home if filters path already shown', () => {
254+
render(
255+
<AppContext.Provider
256+
value={{
257+
isLoggedIn: true,
258+
notifications: [],
259+
settings: mockSettings,
260+
}}
261+
>
262+
<MemoryRouter initialEntries={['/filters']}>
263+
<Sidebar />
264+
</MemoryRouter>
265+
</AppContext.Provider>,
266+
);
267+
fireEvent.click(screen.getByTitle('Filters'));
268+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/', { replace: true });
269+
});
270+
});
271+
217272
describe('Settings', () => {
218273
it('go to the settings route', () => {
219274
render(
220-
<AppContext.Provider value={{ isLoggedIn: true, notifications: [] }}>
275+
<AppContext.Provider
276+
value={{
277+
isLoggedIn: true,
278+
notifications: [],
279+
settings: mockSettings,
280+
}}
281+
>
221282
<MemoryRouter>
222283
<Sidebar />
223284
</MemoryRouter>
@@ -226,13 +287,18 @@ describe('components/Sidebar.tsx', () => {
226287

227288
fireEvent.click(screen.getByTitle('Settings'));
228289

229-
expect(mockNavigate).toHaveBeenCalledWith('/settings');
290+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/settings');
230291
});
231292

232293
it('go to the home if settings path already shown', () => {
233294
render(
234295
<AppContext.Provider
235-
value={{ isLoggedIn: true, notifications: [], fetchNotifications }}
296+
value={{
297+
isLoggedIn: true,
298+
notifications: [],
299+
settings: mockSettings,
300+
fetchNotifications,
301+
}}
236302
>
237303
<MemoryRouter initialEntries={['/settings']}>
238304
<Sidebar />
@@ -243,15 +309,86 @@ describe('components/Sidebar.tsx', () => {
243309
fireEvent.click(screen.getByTitle('Settings'));
244310

245311
expect(fetchNotifications).toHaveBeenCalledTimes(1);
246-
expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
312+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/', { replace: true });
247313
});
248314
});
249315

316+
it('opens github in the notifications page', () => {
317+
const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink');
318+
319+
render(
320+
<AppContext.Provider
321+
value={{
322+
isLoggedIn: true,
323+
notifications: mockAccountNotifications,
324+
settings: mockSettings,
325+
}}
326+
>
327+
<MemoryRouter>
328+
<Sidebar />
329+
</MemoryRouter>
330+
</AppContext.Provider>,
331+
);
332+
fireEvent.click(screen.getByLabelText('4 Unread Notifications'));
333+
expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
334+
expect(openExternalLinkMock).toHaveBeenCalledWith(
335+
'https://github.com/notifications',
336+
);
337+
});
338+
339+
it('opens my github issues page', () => {
340+
const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink');
341+
342+
render(
343+
<AppContext.Provider
344+
value={{
345+
isLoggedIn: true,
346+
notifications: mockAccountNotifications,
347+
settings: mockSettings,
348+
}}
349+
>
350+
<MemoryRouter>
351+
<Sidebar />
352+
</MemoryRouter>
353+
</AppContext.Provider>,
354+
);
355+
fireEvent.click(screen.getByLabelText('My Issues'));
356+
expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
357+
expect(openExternalLinkMock).toHaveBeenCalledWith(
358+
'https://github.com/issues',
359+
);
360+
});
361+
362+
it('opens my github pull requests page', () => {
363+
const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink');
364+
365+
render(
366+
<AppContext.Provider
367+
value={{
368+
isLoggedIn: true,
369+
notifications: mockAccountNotifications,
370+
settings: mockSettings,
371+
}}
372+
>
373+
<MemoryRouter>
374+
<Sidebar />
375+
</MemoryRouter>
376+
</AppContext.Provider>,
377+
);
378+
fireEvent.click(screen.getByLabelText('My Pull Requests'));
379+
expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
380+
expect(openExternalLinkMock).toHaveBeenCalledWith(
381+
'https://github.com/pulls',
382+
);
383+
});
384+
250385
it('should quit the app', () => {
251386
const quitAppMock = jest.spyOn(comms, 'quitApp');
252387

253388
render(
254-
<AppContext.Provider value={{ isLoggedIn: false, notifications: [] }}>
389+
<AppContext.Provider
390+
value={{ isLoggedIn: false, notifications: [], settings: mockSettings }}
391+
>
255392
<MemoryRouter>
256393
<Sidebar />
257394
</MemoryRouter>
@@ -262,4 +399,68 @@ describe('components/Sidebar.tsx', () => {
262399

263400
expect(quitAppMock).toHaveBeenCalledTimes(1);
264401
});
402+
403+
it('should open the gitify repository', () => {
404+
const openExternalLinkMock = jest.spyOn(comms, 'openExternalLink');
405+
406+
render(
407+
<AppContext.Provider
408+
value={{ isLoggedIn: false, notifications: [], settings: mockSettings }}
409+
>
410+
<MemoryRouter>
411+
<Sidebar />
412+
</MemoryRouter>
413+
</AppContext.Provider>,
414+
);
415+
fireEvent.click(screen.getByTestId('gitify-logo'));
416+
expect(openExternalLinkMock).toHaveBeenCalledTimes(1);
417+
expect(openExternalLinkMock).toHaveBeenCalledWith(
418+
'https://github.com/gitify-app/gitify',
419+
);
420+
});
421+
422+
describe('should render the notifications icon', () => {
423+
it('when there are 0 notifications', () => {
424+
render(
425+
<AppContext.Provider
426+
value={{
427+
isLoggedIn: true,
428+
notifications: [],
429+
settings: mockSettings,
430+
}}
431+
>
432+
<MemoryRouter>
433+
<Sidebar />
434+
</MemoryRouter>
435+
</AppContext.Provider>,
436+
);
437+
438+
const notificationsIcon = screen.getByTitle('0 Unread Notifications');
439+
expect(notificationsIcon.className).toContain('text-white');
440+
expect(notificationsIcon.childNodes.length).toBe(1);
441+
expect(notificationsIcon.childNodes[0].nodeName).toBe('svg');
442+
});
443+
444+
it('when there are more than 0 notifications', () => {
445+
render(
446+
<AppContext.Provider
447+
value={{
448+
isLoggedIn: true,
449+
notifications: mockAccountNotifications,
450+
settings: mockSettings,
451+
}}
452+
>
453+
<MemoryRouter>
454+
<Sidebar />
455+
</MemoryRouter>
456+
</AppContext.Provider>,
457+
);
458+
459+
const notificationsIcon = screen.getByTitle('4 Unread Notifications');
460+
expect(notificationsIcon.className).toContain(IconColor.GREEN);
461+
expect(notificationsIcon.childNodes.length).toBe(2);
462+
expect(notificationsIcon.childNodes[0].nodeName).toBe('svg');
463+
expect(notificationsIcon.childNodes[1].nodeValue).toBe('4');
464+
});
465+
});
265466
});

0 commit comments

Comments
 (0)