Files
flyer-crawler.projectium.com/src/hooks/mutations/useRemoveShoppingListItemMutation.test.tsx
Torben Sorensen a42ee5a461
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 15m11s
unit tests - wheeee! Claude is the mvp
2026-01-09 21:59:09 -08:00

132 lines
4.5 KiB
TypeScript

// src/hooks/mutations/useRemoveShoppingListItemMutation.test.tsx
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { ReactNode } from 'react';
import { useRemoveShoppingListItemMutation } from './useRemoveShoppingListItemMutation';
import * as apiClient from '../../services/apiClient';
import * as notificationService from '../../services/notificationService';
vi.mock('../../services/apiClient');
vi.mock('../../services/notificationService');
const mockedApiClient = vi.mocked(apiClient);
const mockedNotifications = vi.mocked(notificationService);
describe('useRemoveShoppingListItemMutation', () => {
let queryClient: QueryClient;
const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
beforeEach(() => {
vi.resetAllMocks();
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
});
it('should remove an item from shopping list successfully', async () => {
const mockResponse = { success: true };
mockedApiClient.removeShoppingListItem.mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockResponse),
} as Response);
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 42 });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(mockedApiClient.removeShoppingListItem).toHaveBeenCalledWith(42);
expect(mockedNotifications.notifySuccess).toHaveBeenCalledWith(
'Item removed from shopping list',
);
});
it('should invalidate shopping-lists query on success', async () => {
mockedApiClient.removeShoppingListItem.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ success: true }),
} as Response);
const invalidateQueriesSpy = vi.spyOn(queryClient, 'invalidateQueries');
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 100 });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: ['shopping-lists'] });
});
it('should handle API error with error message', async () => {
mockedApiClient.removeShoppingListItem.mockResolvedValue({
ok: false,
status: 404,
json: () => Promise.resolve({ message: 'Item not found' }),
} as Response);
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 999 });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Item not found');
expect(mockedNotifications.notifyError).toHaveBeenCalledWith('Item not found');
});
it('should handle API error when json parse fails', async () => {
mockedApiClient.removeShoppingListItem.mockResolvedValue({
ok: false,
status: 500,
json: () => Promise.reject(new Error('Parse error')),
} as Response);
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 42 });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Request failed with status 500');
});
it('should handle API error with empty message in response', async () => {
mockedApiClient.removeShoppingListItem.mockResolvedValue({
ok: false,
status: 400,
json: () => Promise.resolve({ message: '' }),
} as Response);
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 88 });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.error?.message).toBe('Failed to remove shopping list item');
});
it('should use fallback error message when error has no message', async () => {
mockedApiClient.removeShoppingListItem.mockRejectedValue(new Error(''));
const { result } = renderHook(() => useRemoveShoppingListItemMutation(), { wrapper });
result.current.mutate({ itemId: 555 });
await waitFor(() => expect(result.current.isError).toBe(true));
expect(mockedNotifications.notifyError).toHaveBeenCalledWith(
'Failed to remove shopping list item',
);
});
});