Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Task } from '@/components/utils/types';
import { handleLogout, deleteAllTasks } from '../navbar-utils';
import { toast } from 'react-toastify';

// Mock external dependencies
// Toast mock
jest.mock('react-toastify', () => ({
toast: {
success: jest.fn(),
Expand All @@ -11,18 +12,29 @@ jest.mock('react-toastify', () => ({
},
}));

// Dexie mock
jest.mock('dexie', () => {
return jest.fn().mockImplementation(() => ({
const mockCount = jest.fn();
const mockDelete = jest.fn();

const DexieMock = jest.fn().mockImplementation(() => ({
version: jest.fn().mockReturnThis(),
stores: jest.fn().mockReturnThis(),
table: jest.fn().mockReturnValue({
where: jest.fn().mockReturnThis(),
equals: jest.fn().mockReturnThis(),
delete: jest.fn().mockResolvedValue(undefined), // simulates delete success
count: mockCount,
delete: mockDelete,
}),
}));

return Object.assign(DexieMock, {
__mockCount: mockCount,
__mockDelete: mockDelete,
});
});

// URL mock
jest.mock('@/components/utils/URLs.ts', () => ({
url: {
backendURL: 'http://localhost:3000/',
Expand All @@ -32,12 +44,26 @@ jest.mock('@/components/utils/URLs.ts', () => ({
global.fetch = jest.fn();

describe('navbar-utils', () => {
const mockToast = toast as jest.Mocked<typeof toast>;
const Dexie = require('dexie');
const mockCount = Dexie.__mockCount as jest.Mock;
const mockDelete = Dexie.__mockDelete as jest.Mock;

beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});
});

beforeEach(() => {
mockToast.info.mockReturnValue('toast-id' as any);
});

afterEach(() => {
jest.clearAllMocks();
});

describe('handleLogout', () => {
it('should call fetch with correct URL and redirect on success', async () => {
it('calls fetch with correct URL and redirects on success', async () => {
(fetch as jest.Mock).mockResolvedValue({ ok: true });

await handleLogout();
Expand All @@ -46,47 +72,78 @@ describe('navbar-utils', () => {
method: 'POST',
credentials: 'include',
});

expect(window.location.href).toBe('http://localhost/');
});

it('should log an error if fetch fails', async () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
it('logs error when response is not ok', async () => {
const spy = jest.spyOn(console, 'error').mockImplementation();

(fetch as jest.Mock).mockResolvedValue({ ok: false });

await handleLogout();

expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to logout');
consoleErrorSpy.mockRestore();
expect(spy).toHaveBeenCalledWith('Failed to logout');
spy.mockRestore();
});

it('should log an error if fetch throws an exception', async () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
it('logs error when fetch throws exception', async () => {
const spy = jest.spyOn(console, 'error').mockImplementation();

(fetch as jest.Mock).mockRejectedValue(new Error('Network error'));

await handleLogout();

expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error logging out:',
expect.any(Error)
);
consoleErrorSpy.mockRestore();
expect(spy).toHaveBeenCalledWith('Error logging out:', expect.any(Error));
spy.mockRestore();
});
});

describe('deleteAllTasks', () => {
it('should delete tasks without error', async () => {
const props = {
imgurl: '',
email: 'test@example.com',
encryptionSecret: '',
origin: '',
UUID: '',
tasks: [] as Task[] | null,
};

await expect(deleteAllTasks(props)).resolves.toBeUndefined();
const props = {
imgurl: '',
email: 'test@example.com',
encryptionSecret: '',
origin: '',
UUID: '',
tasks: [] as Task[] | null,
};

it('shows error toast when no tasks exist', async () => {
mockCount.mockResolvedValueOnce(0);

await deleteAllTasks(props);

expect(mockToast.info).toHaveBeenCalled();
expect(mockToast.update).toHaveBeenCalledWith(
'toast-id',
expect.objectContaining({ type: 'error' })
);
});

it('deletes tasks and shows success toast when tasks exist', async () => {
mockCount.mockResolvedValueOnce(3);
mockDelete.mockResolvedValueOnce(undefined);

await deleteAllTasks(props);

expect(mockDelete).toHaveBeenCalled();
expect(mockToast.update).toHaveBeenCalledWith(
'toast-id',
expect.objectContaining({ type: 'success' })
);
});

it('shows error toast when deletion fails', async () => {
mockCount.mockResolvedValueOnce(2);
mockDelete.mockRejectedValueOnce(new Error('DB error'));

await deleteAllTasks(props);

expect(mockToast.update).toHaveBeenCalledWith(
'toast-id',
expect.objectContaining({ type: 'error' })
);
});
});
});
Loading