claude 1 - fixes : -/
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 24m33s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 24m33s
This commit is contained in:
@@ -37,7 +37,7 @@ export const useAddWatchedItemMutation = () => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ itemName, category }: AddWatchedItemParams) => {
|
||||
const response = await apiClient.addWatchedItem(itemName, category);
|
||||
const response = await apiClient.addWatchedItem(itemName, category ?? '');
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
// src/hooks/queries/useActivityLogQuery.ts
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import * as apiClient from '../../services/apiClient';
|
||||
|
||||
interface ActivityLogEntry {
|
||||
activity_log_id: number;
|
||||
user_id: string;
|
||||
action: string;
|
||||
entity_type: string | null;
|
||||
entity_id: number | null;
|
||||
details: any;
|
||||
ip_address: string | null;
|
||||
user_agent: string | null;
|
||||
created_at: string;
|
||||
}
|
||||
import { fetchActivityLog } from '../../services/apiClient';
|
||||
import type { ActivityLogItem } from '../../types';
|
||||
|
||||
/**
|
||||
* Query hook for fetching the admin activity log.
|
||||
@@ -33,8 +22,8 @@ interface ActivityLogEntry {
|
||||
export const useActivityLogQuery = (limit: number = 20, offset: number = 0) => {
|
||||
return useQuery({
|
||||
queryKey: ['activity-log', { limit, offset }],
|
||||
queryFn: async (): Promise<ActivityLogEntry[]> => {
|
||||
const response = await apiClient.fetchActivityLog(limit, offset);
|
||||
queryFn: async (): Promise<ActivityLogItem[]> => {
|
||||
const response = await fetchActivityLog(limit, offset);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/queries/useApplicationStatsQuery.ts
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { apiClient, AppStats } from '../../services/apiClient';
|
||||
import { getApplicationStats, AppStats } from '../../services/apiClient';
|
||||
|
||||
/**
|
||||
* Query hook for fetching application-wide statistics (admin feature).
|
||||
@@ -21,7 +21,7 @@ export const useApplicationStatsQuery = () => {
|
||||
return useQuery({
|
||||
queryKey: ['application-stats'],
|
||||
queryFn: async (): Promise<AppStats> => {
|
||||
const response = await apiClient.getApplicationStats();
|
||||
const response = await getApplicationStats();
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/queries/useCategoriesQuery.ts
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { apiClient } from '../../services/apiClient';
|
||||
import { fetchCategories } from '../../services/apiClient';
|
||||
import type { Category } from '../../types';
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export const useCategoriesQuery = () => {
|
||||
return useQuery({
|
||||
queryKey: ['categories'],
|
||||
queryFn: async (): Promise<Category[]> => {
|
||||
const response = await apiClient.fetchCategories();
|
||||
const response = await fetchCategories();
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/hooks/queries/useSuggestedCorrectionsQuery.ts
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { apiClient } from '../../services/apiClient';
|
||||
import { getSuggestedCorrections } from '../../services/apiClient';
|
||||
import type { SuggestedCorrection } from '../../types';
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export const useSuggestedCorrectionsQuery = () => {
|
||||
return useQuery({
|
||||
queryKey: ['suggested-corrections'],
|
||||
queryFn: async (): Promise<SuggestedCorrection[]> => {
|
||||
const response = await apiClient.getSuggestedCorrections();
|
||||
const response = await getSuggestedCorrections();
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
|
||||
@@ -4,15 +4,15 @@ import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useFlyers } from './useFlyers';
|
||||
import { FlyersProvider } from '../providers/FlyersProvider';
|
||||
import { useInfiniteQuery } from './useInfiniteQuery';
|
||||
import { useFlyersQuery } from './queries/useFlyersQuery';
|
||||
import type { Flyer } from '../types';
|
||||
import { createMockFlyer } from '../tests/utils/mockFactories';
|
||||
|
||||
// 1. Mock the useInfiniteQuery hook, which is the dependency of our FlyersProvider.
|
||||
vi.mock('./useInfiniteQuery');
|
||||
// 1. Mock the useFlyersQuery hook, which is the dependency of our FlyersProvider.
|
||||
vi.mock('./queries/useFlyersQuery');
|
||||
|
||||
// 2. Create a typed mock of the hook for type safety and autocompletion.
|
||||
const mockedUseInfiniteQuery = vi.mocked(useInfiniteQuery);
|
||||
const mockedUseFlyersQuery = vi.mocked(useFlyersQuery);
|
||||
|
||||
// 3. A simple wrapper component that renders our provider.
|
||||
// This is necessary because the useFlyers hook needs to be a child of FlyersProvider.
|
||||
@@ -22,7 +22,6 @@ const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
|
||||
describe('useFlyers Hook and FlyersProvider', () => {
|
||||
// Create mock functions that we can spy on to see if they are called.
|
||||
const mockFetchNextPage = vi.fn();
|
||||
const mockRefetch = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -46,16 +45,32 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
|
||||
it('should return the initial loading state correctly', () => {
|
||||
// Arrange: Configure the mocked hook to return a loading state.
|
||||
mockedUseInfiniteQuery.mockReturnValue({
|
||||
data: [],
|
||||
mockedUseFlyersQuery.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
error: null,
|
||||
fetchNextPage: mockFetchNextPage,
|
||||
hasNextPage: false,
|
||||
refetch: mockRefetch,
|
||||
isRefetching: false,
|
||||
isFetchingNextPage: false,
|
||||
});
|
||||
// TanStack Query properties (partial mock)
|
||||
status: 'pending',
|
||||
fetchStatus: 'fetching',
|
||||
isPending: true,
|
||||
isSuccess: false,
|
||||
isError: false,
|
||||
isFetched: false,
|
||||
isFetchedAfterMount: false,
|
||||
isStale: false,
|
||||
isPlaceholderData: false,
|
||||
dataUpdatedAt: 0,
|
||||
errorUpdatedAt: 0,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
errorUpdateCount: 0,
|
||||
isInitialLoading: true,
|
||||
isLoadingError: false,
|
||||
isRefetchError: false,
|
||||
promise: Promise.resolve([]),
|
||||
} as any);
|
||||
|
||||
// Act: Render the hook within the provider wrapper.
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
@@ -66,7 +81,7 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
expect(result.current.flyersError).toBeNull();
|
||||
});
|
||||
|
||||
it('should return flyers data and hasNextPage on successful fetch', () => {
|
||||
it('should return flyers data on successful fetch', () => {
|
||||
// Arrange: Mock a successful data fetch.
|
||||
const mockFlyers: Flyer[] = [
|
||||
createMockFlyer({
|
||||
@@ -77,16 +92,31 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
created_at: '2024-01-01',
|
||||
}),
|
||||
];
|
||||
mockedUseInfiniteQuery.mockReturnValue({
|
||||
mockedUseFlyersQuery.mockReturnValue({
|
||||
data: mockFlyers,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
fetchNextPage: mockFetchNextPage,
|
||||
hasNextPage: true,
|
||||
refetch: mockRefetch,
|
||||
isRefetching: false,
|
||||
isFetchingNextPage: false,
|
||||
});
|
||||
status: 'success',
|
||||
fetchStatus: 'idle',
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
isFetched: true,
|
||||
isFetchedAfterMount: true,
|
||||
isStale: false,
|
||||
isPlaceholderData: false,
|
||||
dataUpdatedAt: Date.now(),
|
||||
errorUpdatedAt: 0,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
errorUpdateCount: 0,
|
||||
isInitialLoading: false,
|
||||
isLoadingError: false,
|
||||
isRefetchError: false,
|
||||
promise: Promise.resolve(mockFlyers),
|
||||
} as any);
|
||||
|
||||
// Act
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
@@ -94,22 +124,38 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
// Assert
|
||||
expect(result.current.isLoadingFlyers).toBe(false);
|
||||
expect(result.current.flyers).toEqual(mockFlyers);
|
||||
expect(result.current.hasNextFlyersPage).toBe(true);
|
||||
// Note: hasNextFlyersPage is always false now since we're not using infinite query
|
||||
expect(result.current.hasNextFlyersPage).toBe(false);
|
||||
});
|
||||
|
||||
it('should return an error state if the fetch fails', () => {
|
||||
// Arrange: Mock a failed data fetch.
|
||||
const mockError = new Error('Failed to fetch');
|
||||
mockedUseInfiniteQuery.mockReturnValue({
|
||||
data: [],
|
||||
mockedUseFlyersQuery.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
error: mockError,
|
||||
fetchNextPage: mockFetchNextPage,
|
||||
hasNextPage: false,
|
||||
refetch: mockRefetch,
|
||||
isRefetching: false,
|
||||
isFetchingNextPage: false,
|
||||
});
|
||||
status: 'error',
|
||||
fetchStatus: 'idle',
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
isError: true,
|
||||
isFetched: true,
|
||||
isFetchedAfterMount: true,
|
||||
isStale: false,
|
||||
isPlaceholderData: false,
|
||||
dataUpdatedAt: 0,
|
||||
errorUpdatedAt: Date.now(),
|
||||
failureCount: 1,
|
||||
failureReason: mockError,
|
||||
errorUpdateCount: 1,
|
||||
isInitialLoading: false,
|
||||
isLoadingError: true,
|
||||
isRefetchError: false,
|
||||
promise: Promise.resolve(undefined),
|
||||
} as any);
|
||||
|
||||
// Act
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
@@ -120,41 +166,33 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
expect(result.current.flyersError).toBe(mockError);
|
||||
});
|
||||
|
||||
it('should call fetchNextFlyersPage when the context function is invoked', () => {
|
||||
// Arrange
|
||||
mockedUseInfiniteQuery.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
hasNextPage: true,
|
||||
isRefetching: false,
|
||||
isFetchingNextPage: false,
|
||||
fetchNextPage: mockFetchNextPage, // Pass the mock function
|
||||
refetch: mockRefetch,
|
||||
});
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
|
||||
// Act: Use `act` to wrap state updates.
|
||||
act(() => {
|
||||
result.current.fetchNextFlyersPage();
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(mockFetchNextPage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should call refetchFlyers when the context function is invoked', () => {
|
||||
// Arrange
|
||||
mockedUseInfiniteQuery.mockReturnValue({
|
||||
mockedUseFlyersQuery.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
hasNextPage: false,
|
||||
isRefetching: false,
|
||||
isFetchingNextPage: false,
|
||||
fetchNextPage: mockFetchNextPage,
|
||||
refetch: mockRefetch,
|
||||
});
|
||||
isRefetching: false,
|
||||
status: 'success',
|
||||
fetchStatus: 'idle',
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
isFetched: true,
|
||||
isFetchedAfterMount: true,
|
||||
isStale: false,
|
||||
isPlaceholderData: false,
|
||||
dataUpdatedAt: Date.now(),
|
||||
errorUpdatedAt: 0,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
errorUpdateCount: 0,
|
||||
isInitialLoading: false,
|
||||
isLoadingError: false,
|
||||
isRefetchError: false,
|
||||
promise: Promise.resolve([]),
|
||||
} as any);
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
|
||||
// Act
|
||||
@@ -165,4 +203,40 @@ describe('useFlyers Hook and FlyersProvider', () => {
|
||||
// Assert
|
||||
expect(mockRefetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should have fetchNextFlyersPage as a no-op (infinite scroll not implemented)', () => {
|
||||
// Arrange
|
||||
mockedUseFlyersQuery.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refetch: mockRefetch,
|
||||
isRefetching: false,
|
||||
status: 'success',
|
||||
fetchStatus: 'idle',
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
isFetched: true,
|
||||
isFetchedAfterMount: true,
|
||||
isStale: false,
|
||||
isPlaceholderData: false,
|
||||
dataUpdatedAt: Date.now(),
|
||||
errorUpdatedAt: 0,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
errorUpdateCount: 0,
|
||||
isInitialLoading: false,
|
||||
isLoadingError: false,
|
||||
isRefetchError: false,
|
||||
promise: Promise.resolve([]),
|
||||
} as any);
|
||||
const { result } = renderHook(() => useFlyers(), { wrapper });
|
||||
|
||||
// Act & Assert: fetchNextFlyersPage should exist but be a no-op
|
||||
expect(result.current.fetchNextFlyersPage).toBeDefined();
|
||||
expect(typeof result.current.fetchNextFlyersPage).toBe('function');
|
||||
// Calling it should not throw
|
||||
expect(() => result.current.fetchNextFlyersPage()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user