diff --git a/.env.example b/.env.example
index 5ea9f001..d8516b00 100644
--- a/.env.example
+++ b/.env.example
@@ -1 +1 @@
-VITE_API_URL="http://localhost:8083"
\ No newline at end of file
+VITE_API_URL="http://localhost:8080/v1/"
\ No newline at end of file
diff --git a/src/components/Dashboard/MyCollections/MyCollectionsContent/index.test.tsx b/src/components/Dashboard/MyCollections/MyCollectionsContent/index.test.tsx
index cc9e0997..1a19ffc4 100644
--- a/src/components/Dashboard/MyCollections/MyCollectionsContent/index.test.tsx
+++ b/src/components/Dashboard/MyCollections/MyCollectionsContent/index.test.tsx
@@ -3,7 +3,7 @@ import { MantineProvider } from '@mantine/core';
import MyCollectionsContent from './index';
import Collection from '../../../../models/user/collection';
import { mockCollection } from '../../../../test-utils/mocks/models/collection';
-import { mockPagination } from '../../../../test-utils/mocks';
+import { mockPagination } from '../../../../test-utils/mocks/pagination';
// Mock the CollectionCard component
jest.mock('./CollectionCard', () => ({
diff --git a/src/components/Dashboard/MyCollections/index.test.tsx b/src/components/Dashboard/MyCollections/index.test.tsx
index d51fdd89..fe30c765 100644
--- a/src/components/Dashboard/MyCollections/index.test.tsx
+++ b/src/components/Dashboard/MyCollections/index.test.tsx
@@ -4,7 +4,7 @@ import MyCollections from './index';
import User from '../../../models/user/user';
import { UserCollectionsContext, UserCollectionsContextState } from '../../../contexts/UserCollectionsContext';
import CollectionManagementRequests from '../../../services/requests/CollectionManagementRequests';
-import { mockPagination } from '../../../test-utils/mocks';
+import { mockPagination } from '../../../test-utils/mocks/pagination';
import { renderWithProviders } from '../../../test-utils';
import Collection from '../../../models/user/collection';
import CollectionItem from '../../../models/user/collection-items';
diff --git a/src/components/Template/Page/index.test.tsx b/src/components/Template/Page/index.test.tsx
index c5d54536..e66ef233 100644
--- a/src/components/Template/Page/index.test.tsx
+++ b/src/components/Template/Page/index.test.tsx
@@ -17,7 +17,8 @@ describe('Page', () => {
renderWithRouter();
// Check for sidebar
- expect(screen.getByRole('link', { name: /home/i })).toBeInTheDocument();
+ const homeLink = document.querySelector('a[href="/"]');
+ expect(homeLink).toBeInTheDocument();
// Check for navigation
expect(screen.getByText('Header Title')).toBeInTheDocument();
diff --git a/src/contexts/UserCollectionsContext.tsx b/src/contexts/UserCollectionsContext.tsx
index eb88a1a0..5ef4629b 100644
--- a/src/contexts/UserCollectionsContext.tsx
+++ b/src/contexts/UserCollectionsContext.tsx
@@ -1,9 +1,9 @@
import {
BasePaginatedContextProviderProps,
BasePaginatedContextState, createCallbacks,
- defaultBaseContext,
+ defaultBaseContext, prepareContextState,
} from './BasePaginatedContext';
-import React, {PropsWithChildren, useCallback, useEffect, useMemo, useState, Dispatch, SetStateAction} from 'react';
+import React, {PropsWithChildren, useEffect, useState} from 'react';
import Collection from "../models/user/collection";
/**
@@ -13,13 +13,12 @@ export interface UserCollectionsContextState extends BasePaginatedContextState(), // Specify Model type
+ ...defaultBaseContext(),
loadAll: true,
order: {
'created_at': 'desc',
@@ -38,47 +37,15 @@ export interface UserCollectionsContextProviderProps extends BasePaginatedContex
}
export const UserCollectionsContextProvider: React.FC> = (props => {
- const [userCollectionsState, setUserCollectionsState] = useState(initialPersistentContextState);
-
- const wrappedSetUserCollectionsState: Dispatch>> =
- useCallback((action) => {
- setUserCollectionsState(currentUserCollectionsState => {
- const baseStateChanges = typeof action === 'function'
- ? (action as (prevState: BasePaginatedContextState) => BasePaginatedContextState)(currentUserCollectionsState)
- : action;
- return { ...currentUserCollectionsState, ...baseStateChanges } as UserCollectionsContextState;
- });
- }, [setUserCollectionsState]);
-
+ const [userCollectionsState, setUserCollectionsState] = useState(persistentContext);
useEffect(() => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- setUserCollectionsState(_prevState => {
- let newState = createDefaultState();
-
- newState = createCallbacks(
- wrappedSetUserCollectionsState,
- newState,
- `/users/${props.userId}/collections`
- );
-
- newState.refreshData(false).catch(console.error);
+ prepareContextState(setUserCollectionsState, userCollectionsState, '/users/' + props.userId + '/collections')
+ }, [props.userId, userCollectionsState]);
- return {
- ...newState,
- loadedData: [],
- lastLoadedPage: undefined,
- noResults: false,
- refreshing: true,
- initialLoadComplete: false,
- initiated: true,
- };
- });
- }, [props.userId, setUserCollectionsState, wrappedSetUserCollectionsState]);
-
- const fullContext = useMemo(() => ({
- ...userCollectionsState,
- ...createCallbacks(wrappedSetUserCollectionsState, userCollectionsState, `/users/${props.userId}/collections`)
- }), [userCollectionsState, wrappedSetUserCollectionsState, props.userId]);
+ const fullContext = {
+ ...persistentContext,
+ ...createCallbacks(setUserCollectionsState, persistentContext, '/users/' + props.userId + '/collections')
+ }
return (
diff --git a/src/test-utils/mocks/api.ts b/src/test-utils/mocks/api.ts
index 4561c98f..a3e2d12d 100644
--- a/src/test-utils/mocks/api.ts
+++ b/src/test-utils/mocks/api.ts
@@ -1,19 +1,5 @@
-/* eslint-disable */
-// @ts-nocheck
-import { mockUser } from './models/user';
-import { mockCategory } from './models/category';
-import { mockCollection } from './models/collection';
-import { mockCollectionItem } from './models/collection-items';
-import { mockOrganization } from './models/organization';
-import { mockBusiness } from './models/business';
-import { mockPostResponse } from './models/post-response';
-import { mockLocation } from './models/location';
-import { mockRole } from './models/role';
-import { mockOrganizationManager } from '../../../models/organization/organization-manager';
-import { mockPage } from '../../../models/page';
-
export interface ApiConfig {
- params?: Record;
+ params?: Record;
signal?: AbortSignal;
}
@@ -28,81 +14,19 @@ export interface Page {
data: T[];
}
-// Export mock data for use in tests
-export { mockPlatform, mockPlatformGroup };
-
const api = {
- get: jest.fn().mockImplementation((url: string, config?: ApiConfig): Promise> => {
+ get: jest.fn().mockImplementation((url: string, config?: ApiConfig): Promise>> => {
// Parse URL parameters from both URL and config
const urlParams = new URLSearchParams(url.split('?')[1] || '');
const configParams = config?.params || {};
// Merge URL params with config params
Object.entries(configParams).forEach(([key, value]) => {
- if (value !== undefined) {
+ if (value !== undefined && value !== null) {
urlParams.set(key, value.toString());
}
});
- const expand = urlParams.get('expand[platforms]') === '*';
- const page = parseInt(urlParams.get('page') || '1');
- const limit = parseInt(urlParams.get('limit') || '10');
-
- // Handle paginated endpoints
- if (url === '/platforms') {
- const response: Page = {
- current_page: page,
- last_page: 1,
- total: 1,
- data: [mockPlatform]
- };
- return Promise.resolve({ data: response });
- }
-
- if (url === '/platform-groups') {
- const response: Page = {
- current_page: page,
- last_page: 1,
- total: 1,
- data: [mockPlatformGroup]
- };
- return Promise.resolve({ data: response });
- }
-
- // Handle single platform group endpoint
- const platformGroupMatch = url.match(/\/platform-groups\/(\d+)/);
- if (platformGroupMatch) {
- const groupId = parseInt(platformGroupMatch[1]);
- if (groupId === mockPlatformGroup.id) {
- const response = {
- ...mockPlatformGroup,
- platforms: expand ? [mockPlatform] : undefined
- };
- return Promise.resolve({ data: response });
- }
- return Promise.reject({
- response: {
- data: { error: 'Platform group not found' },
- status: 404
- }
- });
- }
-
- // Handle single platform endpoint
- const platformMatch = url.match(/\/platforms\/(\d+)/);
- if (platformMatch) {
- const platformId = parseInt(platformMatch[1]);
- if (platformId === mockPlatform.id) {
- return Promise.resolve({ data: mockPlatform });
- }
- return Promise.reject({
- response: {
- data: { error: 'Platform not found' },
- status: 404
- }
- });
- }
-
// Default response for unhandled endpoints
return Promise.resolve({
data: {
@@ -114,29 +38,15 @@ const api = {
});
}),
- post: jest.fn().mockImplementation((url: string, data: any): Promise> => {
- if (url === '/platform-groups') {
- return Promise.resolve({ data: { ...mockPlatformGroup, ...data } });
- }
- if (url === '/platforms') {
- return Promise.resolve({ data: { ...mockPlatform, ...data } });
- }
+ post: jest.fn().mockImplementation((): Promise> => {
return Promise.resolve({ data: null });
}),
- put: jest.fn().mockImplementation((url: string, data: any): Promise> => {
- const platformGroupMatch = url.match(/\/platform-groups\/(\d+)/);
- if (platformGroupMatch) {
- return Promise.resolve({ data: { ...mockPlatformGroup, ...data } });
- }
- const platformMatch = url.match(/\/platforms\/(\d+)/);
- if (platformMatch) {
- return Promise.resolve({ data: { ...mockPlatform, ...data } });
- }
+ put: jest.fn().mockImplementation((): Promise> => {
return Promise.resolve({ data: null });
}),
- delete: jest.fn().mockImplementation((url: string): Promise> => {
+ delete: jest.fn().mockImplementation((): Promise> => {
return Promise.resolve({ data: { success: true } });
})
};
diff --git a/src/test-utils/mocks/contexts.tsx b/src/test-utils/mocks/contexts.tsx
deleted file mode 100644
index c9ad7347..00000000
--- a/src/test-utils/mocks/contexts.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import React, { PropsWithChildren } from 'react';
-import { MeContextStateConsumer, MeContext } from '../../contexts/MeContext';
-import { placeholderUser } from '../../models/user/user';
-import User from '../../models/user/user';
-import { CategoriesContextState } from '../../contexts/CategoriesContext';
-import Category from '../../models/category';
-import { Provider } from 'react-redux';
-import configureStore from 'redux-mock-store';
-import { mockCategory } from './models/category';
-import { mockPagination } from './pagination';
-
-// Mock appState
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-(global as any).appState = {
- state: {
- persistent: {
- tokenData: null
- },
- session: {
- loadingCount: 0
- }
- },
- dispatch: jest.fn()
-};
-
-// Mock functions
-export const mockSetFilter = jest.fn();
-
-// Base mock context state creator
-export const createBaseMockContextState = (data: T[]) => mockPagination({
- loadedData: data,
- initialLoadComplete: true,
- refreshing: false,
- hasAnotherPage: false,
- initiated: true,
- noResults: false,
- expands: [],
- order: {},
- filter: {},
- search: {},
- limit: 20,
- loadAll: false,
- setFilter: jest.fn(),
- setSearch: jest.fn(),
- setOrder: jest.fn(),
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- addModel: jest.fn((_model: T) => {}),
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- removeModel: jest.fn((_model: T) => {}),
- getModel: jest.fn((id: number) => data.find(item => item.id === id) || null),
- params: {},
- loadNext: jest.fn(),
- refreshData: jest.fn()
-});
-
-// Mock CategoriesContext
-export const mockCategoriesContextValue = {
- ...createBaseMockContextState([
- mockCategory({ id: 1, name: 'Test Category', can_be_primary: true })
- ])
-};
-
-export const mockCategoriesContextValueLoading = {
- ...createBaseMockContextState([]),
- initialLoadComplete: false,
- isLoading: true
-};
-
-export const mockCategoriesContextValueEmpty = {
- ...createBaseMockContextState([]),
- noResults: true
-};
-
-// Mock CategoriesContext
-jest.mock('../../contexts/CategoriesContext', () => ({
- __esModule: true,
- CategoriesContext: React.createContext(mockCategoriesContextValue)
-}));
-
-// Create mock store
-const mockStore = configureStore([]);
-
-type MeContextProviderProps = {
- initialState?: {
- me: {
- user: User;
- networkError: boolean;
- isLoggedIn: boolean;
- isLoading: boolean;
- };
- };
- hideLoadingSpace?: boolean;
-}
-
-// MeContext Provider
-export const MeContextProvider: React.FC> = ({ children, initialState, hideLoadingSpace }) => {
- const store = mockStore(initialState || {
- me: {
- user: placeholderUser(),
- networkError: false,
- isLoggedIn: false,
- isLoading: false
- }
- });
-
- const [meContext, setMeContext] = React.useState({
- me: initialState?.me?.user || placeholderUser(),
- networkError: initialState?.me?.networkError || false,
- isLoggedIn: initialState?.me?.isLoggedIn || false,
- isLoading: initialState?.me?.isLoading || false,
- });
-
- const fullContext = {
- ...meContext,
- setMe: (user: User) => {
- setMeContext(prev => ({
- ...prev,
- me: user,
- isLoggedIn: !!user.id,
- isLoading: false
- }));
- store.dispatch({ type: 'SET_USER', payload: user });
- },
- } as MeContextStateConsumer;
-
- return (
-
-
- {(!meContext.isLoading || hideLoadingSpace) ? children :
- (meContext.networkError ?
- Network Error
:
- Loading...
- )
- }
-
-
- );
-};
\ No newline at end of file
diff --git a/src/test-utils/mocks/contexts/base.ts b/src/test-utils/mocks/contexts/base.ts
new file mode 100644
index 00000000..8afec445
--- /dev/null
+++ b/src/test-utils/mocks/contexts/base.ts
@@ -0,0 +1,49 @@
+import { mockPagination } from '../pagination';
+import BaseModel from '../../../models/base-model';
+
+// Mock appState
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+(global as any).appState = {
+ state: {
+ persistent: {
+ tokenData: null
+ },
+ session: {
+ loadingCount: 0
+ }
+ },
+ dispatch: jest.fn()
+};
+
+// Mock functions
+export const mockSetFilter = jest.fn();
+
+// Base mock context state creator
+export const createBaseMockContextState = (data: T[]) => mockPagination({
+ loadedData: data,
+ initialLoadComplete: true,
+ refreshing: false,
+ hasAnotherPage: false,
+ initiated: true,
+ noResults: false,
+ expands: [],
+ order: {},
+ filter: {},
+ search: {},
+ limit: 20,
+ loadAll: false,
+ setFilter: jest.fn(),
+ setSearch: jest.fn(),
+ setOrder: jest.fn(),
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ addModel: jest.fn((_model: T) => {}),
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ removeModel: jest.fn((_model: T) => {}),
+ getModel: jest.fn((id: number) => {
+ const found = data.find(item => typeof item.id === 'number' && item.id === id);
+ return found || null;
+ }),
+ params: {},
+ loadNext: jest.fn(),
+ refreshData: jest.fn()
+});
\ No newline at end of file
diff --git a/src/test-utils/mocks/contexts/categories.ts b/src/test-utils/mocks/contexts/categories.ts
new file mode 100644
index 00000000..461d3ab0
--- /dev/null
+++ b/src/test-utils/mocks/contexts/categories.ts
@@ -0,0 +1,29 @@
+import React from 'react';
+import { CategoriesContextState } from '../../../contexts/CategoriesContext';
+import Category from '../../../models/category';
+import { mockCategory } from '../models/category';
+import { createBaseMockContextState } from './base';
+
+// Mock CategoriesContext
+export const mockCategoriesContextValue = {
+ ...createBaseMockContextState([
+ mockCategory({ id: 1, name: 'Test Category', can_be_primary: true })
+ ])
+};
+
+export const mockCategoriesContextValueLoading = {
+ ...createBaseMockContextState([]),
+ initialLoadComplete: false,
+ isLoading: true
+};
+
+export const mockCategoriesContextValueEmpty = {
+ ...createBaseMockContextState([]),
+ noResults: true
+};
+
+// Mock CategoriesContext
+jest.mock('../../../contexts/CategoriesContext', () => ({
+ __esModule: true,
+ CategoriesContext: React.createContext(mockCategoriesContextValue)
+}));
\ No newline at end of file
diff --git a/src/test-utils/mocks/contexts/index.ts b/src/test-utils/mocks/contexts/index.ts
new file mode 100644
index 00000000..5f741350
--- /dev/null
+++ b/src/test-utils/mocks/contexts/index.ts
@@ -0,0 +1,12 @@
+// Base context utilities
+export { createBaseMockContextState, mockSetFilter } from './base';
+
+// Categories context mocks
+export {
+ mockCategoriesContextValue,
+ mockCategoriesContextValueLoading,
+ mockCategoriesContextValueEmpty
+} from './categories';
+
+// MeContext provider and types
+export { MeContextProvider, type MeContextProviderProps } from './me';
\ No newline at end of file
diff --git a/src/test-utils/mocks/contexts/me.tsx b/src/test-utils/mocks/contexts/me.tsx
new file mode 100644
index 00000000..53e44e2d
--- /dev/null
+++ b/src/test-utils/mocks/contexts/me.tsx
@@ -0,0 +1,75 @@
+import React, { PropsWithChildren } from 'react';
+import { MeContextStateConsumer, MeContext } from '../../../contexts/MeContext';
+import { placeholderUser } from '../../../models/user/user';
+import User from '../../../models/user/user';
+import { Provider } from 'react-redux';
+import configureStore from 'redux-mock-store';
+
+// Create mock store
+const mockStore = configureStore([]);
+
+// Mock MeContext value
+export const mockMeContextValue = {
+ me: placeholderUser(),
+ networkError: false,
+ isLoggedIn: false,
+ isLoading: false,
+ setMe: jest.fn()
+};
+
+export type MeContextProviderProps = {
+ initialState?: {
+ me: {
+ user: User;
+ networkError: boolean;
+ isLoggedIn: boolean;
+ isLoading: boolean;
+ };
+ };
+ hideLoadingSpace?: boolean;
+}
+
+// MeContext Provider
+export const MeContextProvider: React.FC> = ({ children, initialState, hideLoadingSpace }) => {
+ const store = mockStore(initialState || {
+ me: {
+ user: placeholderUser(),
+ networkError: false,
+ isLoggedIn: false,
+ isLoading: false
+ }
+ });
+
+ const [meContext, setMeContext] = React.useState({
+ me: initialState?.me?.user || placeholderUser(),
+ networkError: initialState?.me?.networkError || false,
+ isLoggedIn: initialState?.me?.isLoggedIn || false,
+ isLoading: initialState?.me?.isLoading || false,
+ });
+
+ const fullContext = {
+ ...meContext,
+ setMe: (user: User) => {
+ setMeContext(prev => ({
+ ...prev,
+ me: user,
+ isLoggedIn: !!user.id,
+ isLoading: false
+ }));
+ store.dispatch({ type: 'SET_USER', payload: user });
+ },
+ } as MeContextStateConsumer;
+
+ return (
+
+
+ {(!meContext.isLoading || hideLoadingSpace) ? children :
+ (meContext.networkError ?
+ Network Error
:
+ Loading...
+ )
+ }
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/test-utils/mocks/index.ts b/src/test-utils/mocks/index.ts
deleted file mode 100644
index 77f4f57e..00000000
--- a/src/test-utils/mocks/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './pagination';
\ No newline at end of file
diff --git a/src/testing/wrappers.tsx b/src/testing/wrappers.tsx
deleted file mode 100644
index e41519fa..00000000
--- a/src/testing/wrappers.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { configure } from 'enzyme';
-import Adapter from '@cfaester/enzyme-adapter-react-18';
-
-configure({adapter: new Adapter()});
-