mock mock mock !
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 57m50s

This commit is contained in:
2025-12-19 20:31:04 -08:00
parent e62739810e
commit a3d3ddd772
48 changed files with 1062 additions and 507 deletions

View File

@@ -6,8 +6,9 @@ import { useApi } from './useApi';
import { useAuth } from '../hooks/useAuth';
import { useUserData } from '../hooks/useUserData';
import * as apiClient from '../services/apiClient';
import type { ShoppingList, ShoppingListItem, User } from '../types';
import React from 'react'; // Required for Dispatch/SetStateAction types
import { createMockShoppingList, createMockShoppingListItem, createMockUserProfile } from '../tests/utils/mockFactories';
import React from 'react';
import type { ShoppingList, User } from '../types'; // Import ShoppingList and User types
// Define a type for the mock return value of useApi to ensure type safety in tests
type MockApiResult = {
@@ -30,7 +31,9 @@ const mockedUseApi = vi.mocked(useApi);
const mockedUseAuth = vi.mocked(useAuth);
const mockedUseUserData = vi.mocked(useUserData);
const mockUser: User = { user_id: 'user-123', email: 'test@example.com' };
// Create a mock User object by extracting it from a mock UserProfile
const mockUserProfile = createMockUserProfile({ user_id: 'user-123', user: { user_id: 'user-123', email: 'test@example.com' } });
const mockUser: User = mockUserProfile.user;
describe('useShoppingLists Hook', () => {
// Create a mock setter function that we can spy on
@@ -96,9 +99,9 @@ describe('useShoppingLists Hook', () => {
});
it('should set the first list as active on initial load if lists exist', async () => {
const mockLists: ShoppingList[] = [
{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] },
{ shopping_list_id: 2, name: 'Hardware Store', user_id: 'user-123', created_at: '', items: [] },
const mockLists = [
createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' }),
createMockShoppingList({ shopping_list_id: 2, name: 'Hardware Store', user_id: 'user-123' }),
];
mockedUseUserData.mockReturnValue({
@@ -125,9 +128,7 @@ describe('useShoppingLists Hook', () => {
logout: vi.fn(),
updateProfile: vi.fn(),
});
const mockLists: ShoppingList[] = [
{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] },
];
const mockLists = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' })];
mockedUseUserData.mockReturnValue({
shoppingLists: mockLists,
setShoppingLists: mockSetShoppingLists,
@@ -143,7 +144,7 @@ describe('useShoppingLists Hook', () => {
});
it('should set activeListId to null when lists become empty', async () => {
const mockLists: ShoppingList[] = [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] }];
const mockLists = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' })];
// Initial render with a list
mockedUseUserData.mockReturnValue({
@@ -221,7 +222,7 @@ describe('useShoppingLists Hook', () => {
describe('createList', () => {
it('should call the API and update state on successful creation', async () => {
const newList: ShoppingList = { shopping_list_id: 99, name: 'New List', user_id: 'user-123', created_at: '', items: [] };
const newList = createMockShoppingList({ shopping_list_id: 99, name: 'New List', user_id: 'user-123' });
let currentLists: ShoppingList[] = [];
// Mock the implementation of the setter to simulate a real state update.
@@ -257,10 +258,10 @@ describe('useShoppingLists Hook', () => {
describe('deleteList', () => {
// Use a function to get a fresh copy for each test run
const getMockLists = (): ShoppingList[] => ([
{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] },
{ shopping_list_id: 2, name: 'Hardware Store', user_id: 'user-123', created_at: '', items: [] },
]);
const getMockLists = () => [
createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' }),
createMockShoppingList({ shopping_list_id: 2, name: 'Hardware Store', user_id: 'user-123' }),
];
let currentLists: ShoppingList[] = [];
@@ -347,7 +348,7 @@ describe('useShoppingLists Hook', () => {
it('should set activeListId to null when the last list is deleted', async () => {
console.log('TEST: should set activeListId to null when the last list is deleted');
const singleList: ShoppingList[] = [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] }];
const singleList = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' })];
// Override the state for this specific test
currentLists = singleList;
mockDeleteListApi.mockResolvedValue(null);
@@ -371,7 +372,7 @@ describe('useShoppingLists Hook', () => {
describe('addItemToList', () => {
let currentLists: ShoppingList[] = [];
const getMockLists = () => [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [] }];
const getMockLists = () => [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123' })];
beforeEach(() => {
currentLists = getMockLists();
@@ -389,7 +390,7 @@ describe('useShoppingLists Hook', () => {
});
it('should call API and add item to the correct list', async () => {
const newItem: ShoppingListItem = { shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk', is_purchased: false, quantity: 1, added_at: new Date().toISOString() };
const newItem = createMockShoppingListItem({ shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk' });
mockAddItemApi.mockResolvedValue(newItem);
const { result, rerender } = renderHook(() => useShoppingLists());
@@ -406,11 +407,11 @@ describe('useShoppingLists Hook', () => {
it('should not add a duplicate item (by master_item_id) to a list', async () => {
console.log('TEST: should not add a duplicate item (by master_item_id) to a list');
const existingItem: ShoppingListItem = { shopping_list_item_id: 100, shopping_list_id: 1, master_item_id: 5, custom_item_name: 'Milk', is_purchased: false, quantity: 1, added_at: '' };
const existingItem = createMockShoppingListItem({ shopping_list_item_id: 100, shopping_list_id: 1, master_item_id: 5, custom_item_name: 'Milk' });
// Override state for this specific test
currentLists = [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [existingItem] }];
currentLists = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', items: [existingItem] })];
// This is what the API would return for adding master_item_id 5 again. It has a new shopping_list_item_id.
const newItemFromApi: ShoppingListItem = { shopping_list_item_id: 101, shopping_list_id: 1, master_item_id: 5, custom_item_name: 'Milk', is_purchased: false, quantity: 1, added_at: '' };
const newItemFromApi = createMockShoppingListItem({ shopping_list_item_id: 101, shopping_list_id: 1, master_item_id: 5, custom_item_name: 'Milk' });
mockAddItemApi.mockResolvedValue(newItemFromApi);
@@ -433,17 +434,15 @@ describe('useShoppingLists Hook', () => {
});
describe('updateItemInList', () => {
const initialItem: ShoppingListItem = { shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk', is_purchased: false, quantity: 1, added_at: new Date().toISOString() };
const mockLists: ShoppingList[] = [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [initialItem] }];
const otherList: ShoppingList = { shopping_list_id: 2, name: 'Other', user_id: 'user-123', created_at: '', items: [] };
const multiLists = [mockLists[0], otherList];
const initialItem = createMockShoppingListItem({ shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk', is_purchased: false, quantity: 1 });
const multiLists = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', items: [initialItem] }), createMockShoppingList({ shopping_list_id: 2, name: 'Other' })];
beforeEach(() => {
mockedUseUserData.mockReturnValue({ shoppingLists: multiLists, setShoppingLists: mockSetShoppingLists, watchedItems: [], setWatchedItems: vi.fn(), isLoading: false, error: null });
});
it('should call API and update the correct item, leaving other lists unchanged', async () => {
const updatedItem: ShoppingListItem = { ...initialItem, is_purchased: true };
const updatedItem = { ...initialItem, is_purchased: true };
mockUpdateItemApi.mockResolvedValue(updatedItem);
const { result } = renderHook(() => useShoppingLists());
@@ -457,7 +456,7 @@ describe('useShoppingLists Hook', () => {
const updater = (mockSetShoppingLists as Mock).mock.calls[0][0];
const newState = updater(multiLists);
expect(newState[0].items[0].is_purchased).toBe(true);
expect(newState[1]).toBe(otherList); // Verify other list is unchanged
expect(newState[1]).toBe(multiLists[1]); // Verify other list is unchanged
});
it('should not call update API if no list is active', async () => {
@@ -482,10 +481,8 @@ describe('useShoppingLists Hook', () => {
});
describe('removeItemFromList', () => {
const initialItem: ShoppingListItem = { shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk', is_purchased: false, quantity: 1, added_at: new Date().toISOString() };
const mockLists: ShoppingList[] = [{ shopping_list_id: 1, name: 'Groceries', user_id: 'user-123', created_at: '', items: [initialItem] }];
const otherList: ShoppingList = { shopping_list_id: 2, name: 'Other', user_id: 'user-123', created_at: '', items: [] };
const multiLists = [mockLists[0], otherList];
const initialItem = createMockShoppingListItem({ shopping_list_item_id: 101, shopping_list_id: 1, custom_item_name: 'Milk' });
const multiLists = [createMockShoppingList({ shopping_list_id: 1, name: 'Groceries', items: [initialItem] }), createMockShoppingList({ shopping_list_id: 2, name: 'Other' })];
beforeEach(() => {
mockedUseUserData.mockReturnValue({
@@ -511,7 +508,7 @@ describe('useShoppingLists Hook', () => {
const updater = (mockSetShoppingLists as Mock).mock.calls[0][0];
const newState = updater(multiLists);
expect(newState[0].items).toHaveLength(0);
expect(newState[1]).toBe(otherList); // Verify other list is unchanged
expect(newState[1]).toBe(multiLists[1]); // Verify other list is unchanged
});
it('should not call remove API if no list is active', async () => {