11import { Task } from '@/components/utils/types' ;
22import { handleLogout , deleteAllTasks } from '../navbar-utils' ;
3+ import { toast } from 'react-toastify' ;
34
4- // Mock external dependencies
5+ // Toast mock
56jest . mock ( 'react-toastify' , ( ) => ( {
67 toast : {
78 success : jest . fn ( ) ,
@@ -11,18 +12,29 @@ jest.mock('react-toastify', () => ({
1112 } ,
1213} ) ) ;
1314
15+ // Dexie mock
1416jest . mock ( 'dexie' , ( ) => {
15- return jest . fn ( ) . mockImplementation ( ( ) => ( {
17+ const mockCount = jest . fn ( ) ;
18+ const mockDelete = jest . fn ( ) ;
19+
20+ const DexieMock = jest . fn ( ) . mockImplementation ( ( ) => ( {
1621 version : jest . fn ( ) . mockReturnThis ( ) ,
1722 stores : jest . fn ( ) . mockReturnThis ( ) ,
1823 table : jest . fn ( ) . mockReturnValue ( {
1924 where : jest . fn ( ) . mockReturnThis ( ) ,
2025 equals : jest . fn ( ) . mockReturnThis ( ) ,
21- delete : jest . fn ( ) . mockResolvedValue ( undefined ) , // simulates delete success
26+ count : mockCount ,
27+ delete : mockDelete ,
2228 } ) ,
2329 } ) ) ;
30+
31+ return Object . assign ( DexieMock , {
32+ __mockCount : mockCount ,
33+ __mockDelete : mockDelete ,
34+ } ) ;
2435} ) ;
2536
37+ // URL mock
2638jest . mock ( '@/components/utils/URLs.ts' , ( ) => ( {
2739 url : {
2840 backendURL : 'http://localhost:3000/' ,
@@ -32,12 +44,26 @@ jest.mock('@/components/utils/URLs.ts', () => ({
3244global . fetch = jest . fn ( ) ;
3345
3446describe ( 'navbar-utils' , ( ) => {
47+ const mockToast = toast as jest . Mocked < typeof toast > ;
48+ const Dexie = require ( 'dexie' ) ;
49+ const mockCount = Dexie . __mockCount as jest . Mock ;
50+ const mockDelete = Dexie . __mockDelete as jest . Mock ;
51+
52+ beforeAll ( ( ) => {
53+ jest . spyOn ( console , 'log' ) . mockImplementation ( ( ) => { } ) ;
54+ jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
55+ } ) ;
56+
57+ beforeEach ( ( ) => {
58+ mockToast . info . mockReturnValue ( 'toast-id' as any ) ;
59+ } ) ;
60+
3561 afterEach ( ( ) => {
3662 jest . clearAllMocks ( ) ;
3763 } ) ;
3864
3965 describe ( 'handleLogout' , ( ) => {
40- it ( 'should call fetch with correct URL and redirect on success' , async ( ) => {
66+ it ( 'calls fetch with correct URL and redirects on success' , async ( ) => {
4167 ( fetch as jest . Mock ) . mockResolvedValue ( { ok : true } ) ;
4268
4369 await handleLogout ( ) ;
@@ -46,47 +72,78 @@ describe('navbar-utils', () => {
4672 method : 'POST' ,
4773 credentials : 'include' ,
4874 } ) ;
75+
4976 expect ( window . location . href ) . toBe ( 'http://localhost/' ) ;
5077 } ) ;
5178
52- it ( 'should log an error if fetch fails ' , async ( ) => {
53- const consoleErrorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
79+ it ( 'logs error when response is not ok ' , async ( ) => {
80+ const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
5481
5582 ( fetch as jest . Mock ) . mockResolvedValue ( { ok : false } ) ;
5683
5784 await handleLogout ( ) ;
5885
59- expect ( consoleErrorSpy ) . toHaveBeenCalledWith ( 'Failed to logout' ) ;
60- consoleErrorSpy . mockRestore ( ) ;
86+ expect ( spy ) . toHaveBeenCalledWith ( 'Failed to logout' ) ;
87+ spy . mockRestore ( ) ;
6188 } ) ;
6289
63- it ( 'should log an error if fetch throws an exception' , async ( ) => {
64- const consoleErrorSpy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
90+ it ( 'logs error when fetch throws exception' , async ( ) => {
91+ const spy = jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
6592
6693 ( fetch as jest . Mock ) . mockRejectedValue ( new Error ( 'Network error' ) ) ;
6794
6895 await handleLogout ( ) ;
6996
70- expect ( consoleErrorSpy ) . toHaveBeenCalledWith (
71- 'Error logging out:' ,
72- expect . any ( Error )
73- ) ;
74- consoleErrorSpy . mockRestore ( ) ;
97+ expect ( spy ) . toHaveBeenCalledWith ( 'Error logging out:' , expect . any ( Error ) ) ;
98+ spy . mockRestore ( ) ;
7599 } ) ;
76100 } ) ;
77101
78102 describe ( 'deleteAllTasks' , ( ) => {
79- it ( 'should delete tasks without error' , async ( ) => {
80- const props = {
81- imgurl : '' ,
82- email : 'test@example.com' ,
83- encryptionSecret : '' ,
84- origin : '' ,
85- UUID : '' ,
86- tasks : [ ] as Task [ ] | null ,
87- } ;
88-
89- await expect ( deleteAllTasks ( props ) ) . resolves . toBeUndefined ( ) ;
103+ const props = {
104+ imgurl : '' ,
105+ email : 'test@example.com' ,
106+ encryptionSecret : '' ,
107+ origin : '' ,
108+ UUID : '' ,
109+ tasks : [ ] as Task [ ] | null ,
110+ } ;
111+
112+ it ( 'shows error toast when no tasks exist' , async ( ) => {
113+ mockCount . mockResolvedValueOnce ( 0 ) ;
114+
115+ await deleteAllTasks ( props ) ;
116+
117+ expect ( mockToast . info ) . toHaveBeenCalled ( ) ;
118+ expect ( mockToast . update ) . toHaveBeenCalledWith (
119+ 'toast-id' ,
120+ expect . objectContaining ( { type : 'error' } )
121+ ) ;
122+ } ) ;
123+
124+ it ( 'deletes tasks and shows success toast when tasks exist' , async ( ) => {
125+ mockCount . mockResolvedValueOnce ( 3 ) ;
126+ mockDelete . mockResolvedValueOnce ( undefined ) ;
127+
128+ await deleteAllTasks ( props ) ;
129+
130+ expect ( mockDelete ) . toHaveBeenCalled ( ) ;
131+ expect ( mockToast . update ) . toHaveBeenCalledWith (
132+ 'toast-id' ,
133+ expect . objectContaining ( { type : 'success' } )
134+ ) ;
135+ } ) ;
136+
137+ it ( 'shows error toast when deletion fails' , async ( ) => {
138+ mockCount . mockResolvedValueOnce ( 2 ) ;
139+ mockDelete . mockRejectedValueOnce ( new Error ( 'DB error' ) ) ;
140+
141+ await deleteAllTasks ( props ) ;
142+
143+ expect ( mockToast . update ) . toHaveBeenCalledWith (
144+ 'toast-id' ,
145+ expect . objectContaining ( { type : 'error' } )
146+ ) ;
90147 } ) ;
91148 } ) ;
92149} ) ;
0 commit comments