From 6c51a9afe10bdc58909385b0e63ed7d9e7f386ea Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:21:38 -0400 Subject: [PATCH 01/12] updated data store for new leagues and entries data structures --- .../[leagueId]/entry/Entries.interface.ts | 4 +- app/(main)/league/all/page.tsx | 30 ++++++++-- store/dataStore.test.ts | 47 ++++++++++----- store/dataStore.ts | 58 ++++++++----------- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/app/(main)/league/[leagueId]/entry/Entries.interface.ts b/app/(main)/league/[leagueId]/entry/Entries.interface.ts index 210cf3f9..2521e43d 100644 --- a/app/(main)/league/[leagueId]/entry/Entries.interface.ts +++ b/app/(main)/league/[leagueId]/entry/Entries.interface.ts @@ -6,8 +6,8 @@ import { ILeague, INFLTeam, IUser } from '@/api/apiFunctions.interface'; export interface IEntry { $id: string; name: string; - user: IUser; - league: ILeague; + user: IUser['id']; + league: ILeague['leagueId']; selectedTeams: INFLTeam[]; eliminated: boolean; } diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index 4b8c70a8..4e237ac0 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -5,20 +5,19 @@ import { ENTRY_URL, LEAGUE_URL } from '@/const/global'; import { getUserLeagues } from '@/utils/utils'; -import { ILeague } from '@/api/apiFunctions.interface'; import { LeagueCard } from '@/components/LeagueCard/LeagueCard'; import { useDataStore } from '@/store/dataStore'; import GlobalSpinner from '@/components/GlobalSpinner/GlobalSpinner'; import React, { JSX, useEffect, useState } from 'react'; +import { getCurrentUserEntries, getGameWeek } from '@/api/apiFunctions'; /** * Renders the leagues component. * @returns {JSX.Element} The rendered leagues component. */ const Leagues = (): JSX.Element => { - const [leagues, setLeagues] = useState([]); const [loadingData, setLoadingData] = useState(true); - const { user } = useDataStore((state) => state); + const { user, leagues, updateLeagues, updateGameWeek, updateEntries } = useDataStore((state) => state); /** * Fetches the user's leagues. @@ -27,7 +26,7 @@ const Leagues = (): JSX.Element => { const getLeagues = async (): Promise => { try { const userLeagues = await getUserLeagues(user.leagues); - setLeagues(userLeagues); + updateLeagues(userLeagues); } catch (error) { throw new Error('Error fetching user leagues'); } finally { @@ -35,6 +34,23 @@ const Leagues = (): JSX.Element => { } }; + /** + * Fetches entries per league and the current week. + * @returns {Promise} + */ + const fetchAdditionalUserData = async (): Promise => { + try { + const promises = leagues.map((league) => getCurrentUserEntries(user.id, league.leagueId)); + const entries = await Promise.all(promises); + const currentWeek = await getGameWeek(); + updateEntries(entries.flat()); + updateGameWeek(currentWeek); + } catch (error) { + console.error('Error fetching entries and game week:', error); + throw new Error('Error fetching entries and game week'); + } + }; + useEffect(() => { if (!user.id || user.id === '') { return; @@ -43,6 +59,12 @@ const Leagues = (): JSX.Element => { getLeagues(); }, [user]); + useEffect(() => { + if (leagues.length > 0) { + fetchAdditionalUserData(); + } + }, [leagues]) + return (
{loadingData ? ( diff --git a/store/dataStore.test.ts b/store/dataStore.test.ts index 364f336e..c524da07 100644 --- a/store/dataStore.test.ts +++ b/store/dataStore.test.ts @@ -21,13 +21,22 @@ const NFLTeam = [ }, ]; -const league = { +const league = [{ leagueId: '123', leagueName: 'Test League', logo: 'https://findmylogo.com/logo.png', participants: ['123456', '78'], survivors: ['123456', '78', '9'], -}; +}]; + +const entries = [{ + $id: '123', + name: 'Test Entry', + user: '123', + league: '123', + selectedTeams: [], + eliminated: false, +}]; const gameCurrentWeek = { id: '1234567890', @@ -130,24 +139,36 @@ describe('Data Store', () => { describe('League Test', () => { it('Check the default league state', () => { const { result } = renderHook(() => useDataStore()); - expect(result.current.league.leagueId).toBe(''); - expect(result.current.league.leagueName).toBe(''); - expect(result.current.league.logo).toBe(''); - expect(result.current.league.participants).toStrictEqual([]); - expect(result.current.league.survivors).toStrictEqual([]); + expect(result.current.leagues).toStrictEqual([]); }); it('Check the updated league state', () => { const { result } = renderHook(() => useDataStore()); act(() => { - result.current.updateLeague(league); + result.current.updateLeagues(league); + }); + + expect(result.current.leagues[0].leagueId).toBe(league[0].leagueId); + expect(result.current.leagues[0].leagueName).toBe(league[0].leagueName); + expect(result.current.leagues[0].logo).toBe(league[0].logo); + expect(result.current.leagues[0].participants).toBe(league[0].participants); + expect(result.current.leagues[0].survivors).toBe(league[0].survivors); + }); + }); + + describe('Entries Test', () => { + it('Check the default entries state', () => { + const { result } = renderHook(() => useDataStore()); + expect(result.current.entries).toStrictEqual([]); + }); + it('Check the updated entries state', () => { + const { result } = renderHook(() => useDataStore()); + + act(() => { + result.current.updateEntries(entries); }); - expect(result.current.league.leagueId).toBe(league.leagueId); - expect(result.current.league.leagueName).toBe(league.leagueName); - expect(result.current.league.logo).toBe(league.logo); - expect(result.current.league.participants).toBe(league.participants); - expect(result.current.league.survivors).toBe(league.survivors); + expect(result.current.entries).toStrictEqual(entries); }); }); diff --git a/store/dataStore.ts b/store/dataStore.ts index 9832e89b..3960d0de 100644 --- a/store/dataStore.ts +++ b/store/dataStore.ts @@ -10,14 +10,16 @@ import { ILeague, IGameWeek, } from '@/api/apiFunctions.interface'; +import { IEntry } from '@/app/(main)/league/[leagueId]/entry/Entries.interface'; //Define the shape of the state interface IDataStoreState { user: IUser; NFLTeam: INFLTeam[]; weeklyPicks: IWeeklyPicks; - league: ILeague; + leagues: ILeague[]; gameWeek: IGameWeek; + entries: IEntry[]; } /* eslint-disable */ @@ -36,14 +38,9 @@ interface IDataStoreAction { gameWeekId, userResults, }: IWeeklyPicks) => void; - updateLeague: ({ - leagueId, - logo, - leagueName, - participants, - survivors, - }: ILeague) => void; + updateLeagues: (leagues: ILeague[]) => void; updateGameWeek: (gameWeek: IGameWeek) => void; + updateEntries: (entries: IEntry[]) => void; } /* eslint-disable */ @@ -62,17 +59,12 @@ const initialState: IDataStoreState = { gameWeekId: '', userResults: {}, }, - league: { - leagueId: '', - leagueName: '', - logo: '', - participants: [], - survivors: [], - }, + leagues: [], gameWeek: { id: '', week: 0, }, + entries: [], }; //create the store @@ -132,29 +124,15 @@ export const useDataStore = create((set) => ({ /** * Update the game group * @param props - props - * @param props.leagueId - The league id - * @param props.leagueName - The league name - * @param props.logo - The logo - * @param props.participants - The participants - * @param props.survivors - The survivors + * @param props.leagues - The leagues the user is a part of * @returns {void} */ - updateLeague: ({ - leagueId, - leagueName, - logo, - participants, - survivors, - }: ILeague): void => + updateLeagues: (leagues: ILeague[]): void => set( produce((state: IDataStoreState) => { - state.league.leagueId = leagueId; - state.league.leagueName = leagueName; - state.league.logo = logo; - state.league.participants = participants; - state.league.survivors = survivors; - }), - ), + state.leagues = [...leagues]; + }) + ), /** * Update the current week * @param props - props @@ -169,4 +147,16 @@ export const useDataStore = create((set) => ({ state.gameWeek.week = week; }), ), + /** + * Update the entries + * @param props - props + * @param props.entries - all user entries from all leagues + * @returns {void} + */ + updateEntries: (entries: IEntry[]): void => + set( + produce((state: IDataStoreState) => { + state.entries = [...entries]; + }) + ), })); From 040b9ce491e6600a18966c98b73db980e825ae6c Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:48:11 -0400 Subject: [PATCH 02/12] update testing for leage/all page --- app/(main)/league/all/page.test.tsx | 169 +++++++++++++++++++++++----- app/(main)/league/all/page.tsx | 9 +- components/TableData/TableData.tsx | 6 +- store/dataStore.test.ts | 40 ++++--- store/dataStore.ts | 8 +- utils/utils.ts | 1 + 6 files changed, 179 insertions(+), 54 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index dcf7bd73..87b69026 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -1,68 +1,183 @@ import { render, screen, waitFor } from '@testing-library/react'; import Leagues from './page'; import { useDataStore } from '@/store/dataStore'; -import { getUserLeagues } from '@/utils/utils'; -import { getGameWeek } from '@/api/apiFunctions'; +import { getUserLeagues, cn } from '@/utils/utils'; +import { getGameWeek, getCurrentUserEntries } from '@/api/apiFunctions'; jest.mock('@/store/dataStore', () => ({ - useDataStore: jest.fn(() => ({ user: { id: '123', leagues: [] } })), + useDataStore: jest.fn(), })); jest.mock('@/utils/utils', () => ({ - getUserLeagues: jest.fn(() => Promise.resolve([])), + getUserLeagues: jest.fn(), + cn: jest.fn(), })); jest.mock('@/api/apiFunctions', () => ({ - getGameWeek: jest.fn(() => - Promise.resolve({ - week: 1, - }), - ), + getGameWeek: jest.fn(), + getCurrentUserEntries: jest.fn(), })); +const mockLeagues = [ + { + leagueId: '123', + leagueName: 'Test League', + logo: 'https://findmylogo.com/logo.png', + participants: ['123456', '78'], + survivors: ['123456', '78', '9'], + }, +]; + +const mockEntries = [ + { + $id: '123', + name: 'Test Entry', + user: '123', + league: '123', + selectedTeams: [], + eliminated: false, + }, +]; + +const mockGameWeek = { + id: '123', + week: 1, +}; + +const mockUseDataStore = useDataStore as jest.MockedFunction< + typeof useDataStore +>; +const mockGetUserLeagues = getUserLeagues as jest.MockedFunction< + typeof getUserLeagues +>; +const mockGetGameWeek = getGameWeek as jest.MockedFunction; +const mockGetCurrentUserEntries = getCurrentUserEntries as jest.MockedFunction< + typeof getCurrentUserEntries +>; + describe('Leagues Component', () => { - const mockUseDataStore = useDataStore as unknown as jest.Mock; - const mockGetUserLeagues = getUserLeagues as jest.Mock; - const mockGetGameWeek = getGameWeek as jest.Mock; + const mockUpdateLeagues = jest.fn(); + const mockUpdateGameWeek = jest.fn(); + const mockUpdateEntries = jest.fn(); + const mockCn = cn as jest.MockedFunction; beforeEach(() => { jest.clearAllMocks(); + mockUseDataStore.mockReturnValue({ + user: { id: '123', leagues: [] }, + leagues: [], + updateLeagues: mockUpdateLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, + }); + }); + + test('should display GlobalSpinner while loading data', async () => { + render(); + + expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + + await waitFor(() => { + expect(mockGetUserLeagues).toHaveBeenCalled(); + }); }); test('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { - mockUseDataStore.mockReturnValueOnce({ user: { id: '123', leagues: [] } }); - mockGetUserLeagues.mockResolvedValueOnce([]); - mockGetGameWeek.mockResolvedValueOnce({ week: 1 }); + mockGetUserLeagues.mockResolvedValue([]); + render(); await waitFor(() => { - expect( - screen.getByText('You are not enrolled in any leagues'), - ).toBeInTheDocument(); + expect(mockGetUserLeagues).toHaveBeenCalled(); }); + + expect( + screen.getByText('You are not enrolled in any leagues'), + ).toBeInTheDocument(); }); - test('should display GlobalSpinner while loading data', async () => { - mockUseDataStore.mockReturnValueOnce({ user: { id: '123', leagues: [] } }); - mockGetUserLeagues.mockResolvedValueOnce([]); - mockGetGameWeek.mockResolvedValueOnce({ week: 1 }); + test('should not call getGameWeek or getCurrentUserEntries if no leagues are found', async () => { + mockGetUserLeagues.mockResolvedValue([]); + render(); await waitFor(() => { - expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + expect(mockGetUserLeagues).toHaveBeenCalled(); + expect(mockGetGameWeek).not.toHaveBeenCalled(); + expect(mockGetCurrentUserEntries).not.toHaveBeenCalled(); }); }); + test('should not display GlobalSpinner after loading data', async () => { - mockUseDataStore.mockReturnValueOnce({ user: { id: '123', leagues: [] } }); - mockGetUserLeagues.mockResolvedValueOnce([]); - mockGetGameWeek.mockResolvedValueOnce({ week: 1 }); + mockUseDataStore.mockReturnValue({ + user: { id: '123', leagues: ['123'] }, + leagues: mockLeagues, + updateLeagues: mockUpdateLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, + }); + mockGetUserLeagues.mockResolvedValue(mockLeagues); + mockGetGameWeek.mockResolvedValue(mockGameWeek); + mockGetCurrentUserEntries.mockResolvedValue(mockEntries); render(); expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); await waitFor(() => { - expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + expect(mockGetUserLeagues).toHaveBeenCalled(); + expect(mockGetGameWeek).toHaveBeenCalled(); + expect(mockGetCurrentUserEntries).toHaveBeenCalled(); + }); + + expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + }); + test('should display "Test League" when leagues are found', async () => { + mockUseDataStore.mockReturnValue({ + user: { id: '123', leagues: ['123'] }, + leagues: mockLeagues, + updateLeagues: mockUpdateLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, + }); + mockGetUserLeagues.mockResolvedValue(mockLeagues); + mockGetGameWeek.mockResolvedValue(mockGameWeek); + mockGetCurrentUserEntries.mockResolvedValue(mockEntries); + + render(); + + await waitFor(() => { + expect(mockGetUserLeagues).toHaveBeenCalled(); + expect(mockGetGameWeek).toHaveBeenCalled(); + expect(mockGetCurrentUserEntries).toHaveBeenCalled(); }); + + expect(mockUpdateGameWeek).toHaveBeenCalledWith(mockGameWeek); + expect(mockUpdateEntries).toHaveBeenCalledWith(mockEntries); + + expect(screen.getByText('Test League')).toBeInTheDocument(); + }); + test('should call getGameWeek and getCurrentUserEntries if leagues are found', async () => { + mockUseDataStore.mockReturnValue({ + user: { id: '123', leagues: ['123'] }, + leagues: mockLeagues, + updateLeagues: mockUpdateLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, + }); + mockGetUserLeagues.mockResolvedValue(mockLeagues); + mockGetGameWeek.mockResolvedValue(mockGameWeek); + mockGetCurrentUserEntries.mockResolvedValue(mockEntries); + + render(); + + await waitFor(() => { + expect(mockGetUserLeagues).toHaveBeenCalled(); + expect(mockGetGameWeek).toHaveBeenCalled(); + expect(mockGetCurrentUserEntries).toHaveBeenCalled(); + }); + + expect(mockUpdateGameWeek).toHaveBeenCalledWith(mockGameWeek); + expect(mockUpdateEntries).toHaveBeenCalledWith(mockEntries); }); }); diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index 4e237ac0..a64b2bbc 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -17,7 +17,8 @@ import { getCurrentUserEntries, getGameWeek } from '@/api/apiFunctions'; */ const Leagues = (): JSX.Element => { const [loadingData, setLoadingData] = useState(true); - const { user, leagues, updateLeagues, updateGameWeek, updateEntries } = useDataStore((state) => state); + const { user, leagues, updateLeagues, updateGameWeek, updateEntries } = + useDataStore((state) => state); /** * Fetches the user's leagues. @@ -40,7 +41,9 @@ const Leagues = (): JSX.Element => { */ const fetchAdditionalUserData = async (): Promise => { try { - const promises = leagues.map((league) => getCurrentUserEntries(user.id, league.leagueId)); + const promises = leagues.map((league) => + getCurrentUserEntries(user.id, league.leagueId), + ); const entries = await Promise.all(promises); const currentWeek = await getGameWeek(); updateEntries(entries.flat()); @@ -63,7 +66,7 @@ const Leagues = (): JSX.Element => { if (leagues.length > 0) { fetchAdditionalUserData(); } - }, [leagues]) + }, [leagues]); return (
diff --git a/components/TableData/TableData.tsx b/components/TableData/TableData.tsx index 2c2b550f..49493b65 100644 --- a/components/TableData/TableData.tsx +++ b/components/TableData/TableData.tsx @@ -57,9 +57,9 @@ const TableData = ({ {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext(), - )} + header.column.columnDef.header, + header.getContext(), + )} ); })} diff --git a/store/dataStore.test.ts b/store/dataStore.test.ts index c524da07..139f2bd5 100644 --- a/store/dataStore.test.ts +++ b/store/dataStore.test.ts @@ -21,22 +21,26 @@ const NFLTeam = [ }, ]; -const league = [{ - leagueId: '123', - leagueName: 'Test League', - logo: 'https://findmylogo.com/logo.png', - participants: ['123456', '78'], - survivors: ['123456', '78', '9'], -}]; - -const entries = [{ - $id: '123', - name: 'Test Entry', - user: '123', - league: '123', - selectedTeams: [], - eliminated: false, -}]; +const league = [ + { + leagueId: '123', + leagueName: 'Test League', + logo: 'https://findmylogo.com/logo.png', + participants: ['123456', '78'], + survivors: ['123456', '78', '9'], + }, +]; + +const entries = [ + { + $id: '123', + name: 'Test Entry', + user: '123', + league: '123', + selectedTeams: [], + eliminated: false, + }, +]; const gameCurrentWeek = { id: '1234567890', @@ -151,7 +155,9 @@ describe('Data Store', () => { expect(result.current.leagues[0].leagueId).toBe(league[0].leagueId); expect(result.current.leagues[0].leagueName).toBe(league[0].leagueName); expect(result.current.leagues[0].logo).toBe(league[0].logo); - expect(result.current.leagues[0].participants).toBe(league[0].participants); + expect(result.current.leagues[0].participants).toBe( + league[0].participants, + ); expect(result.current.leagues[0].survivors).toBe(league[0].survivors); }); }); diff --git a/store/dataStore.ts b/store/dataStore.ts index 3960d0de..8ac70cb2 100644 --- a/store/dataStore.ts +++ b/store/dataStore.ts @@ -131,8 +131,8 @@ export const useDataStore = create((set) => ({ set( produce((state: IDataStoreState) => { state.leagues = [...leagues]; - }) - ), + }), + ), /** * Update the current week * @param props - props @@ -157,6 +157,6 @@ export const useDataStore = create((set) => ({ set( produce((state: IDataStoreState) => { state.entries = [...entries]; - }) - ), + }), + ), })); diff --git a/utils/utils.ts b/utils/utils.ts index 3110957b..c0a81305 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -125,6 +125,7 @@ export const parseUserPick = ( export const getUserLeagues = async ( leagues: IUser['leagues'], ): Promise => { + // TODO fix leagues.length === 0. If no leagues are found it returns an array with a length of 1 containing an empty string. if (!leagues || leagues.length === 0) { return []; } From 0eca60271cc90be1e794968709e306500d968d8e Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:55:14 -0400 Subject: [PATCH 03/12] added in tanstack dependencies --- components/TableData/TableData.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/TableData/TableData.tsx b/components/TableData/TableData.tsx index 49493b65..2c2b550f 100644 --- a/components/TableData/TableData.tsx +++ b/components/TableData/TableData.tsx @@ -57,9 +57,9 @@ const TableData = ({ {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext(), - )} + header.column.columnDef.header, + header.getContext(), + )} ); })} From 66915672343cb424e698a8986cf730508f707f30 Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:02:46 -0400 Subject: [PATCH 04/12] refactor fetchAdditionalUserData for promise.all (parallel fetches) --- app/(main)/league/all/page.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index a64b2bbc..61efbcb3 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -41,15 +41,19 @@ const Leagues = (): JSX.Element => { */ const fetchAdditionalUserData = async (): Promise => { try { - const promises = leagues.map((league) => + const entryPromises = leagues.map((league) => getCurrentUserEntries(user.id, league.leagueId), ); - const entries = await Promise.all(promises); - const currentWeek = await getGameWeek(); + const gameWeekPromise = getGameWeek(); + + const [entries, currentWeek] = await Promise.all([ + Promise.all(entryPromises), + gameWeekPromise, + ]); + updateEntries(entries.flat()); updateGameWeek(currentWeek); } catch (error) { - console.error('Error fetching entries and game week:', error); throw new Error('Error fetching entries and game week'); } }; From e7853fe80ed24b3089a4d68122fdeb0d18284dc6 Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Fri, 11 Oct 2024 23:32:02 -0400 Subject: [PATCH 05/12] rebased /league/all page and test --- app/(main)/league/all/page.test.tsx | 332 ++++++++++++++++++---------- app/(main)/league/all/page.tsx | 186 ++++++++++++---- 2 files changed, 358 insertions(+), 160 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index 87b69026..b1a783c8 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -1,183 +1,277 @@ -import { render, screen, waitFor } from '@testing-library/react'; +import { + render, + screen, + waitFor, + waitForElementToBeRemoved, + fireEvent, +} from '@testing-library/react'; import Leagues from './page'; import { useDataStore } from '@/store/dataStore'; -import { getUserLeagues, cn } from '@/utils/utils'; -import { getGameWeek, getCurrentUserEntries } from '@/api/apiFunctions'; +import { getUserLeagues } from '@/utils/utils'; +import { getAllLeagues, addUserToLeague } from '@/api/apiFunctions'; +import { toast } from 'react-hot-toast'; +import Alert from '@/components/AlertNotification/AlertNotification'; +import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; + +const mockUseAuthContext = { + isSignedIn: false, +}; + +jest.mock('@/context/AuthContextProvider', () => ({ + useAuthContext() { + return { + ...mockUseAuthContext, + }; + }, +})); jest.mock('@/store/dataStore', () => ({ - useDataStore: jest.fn(), + useDataStore: jest.fn(() => ({ + user: { + documentId: '123', + id: '1234', + email: 'test@test.com', + leagues: ['league1'], + }, + allLeagues: [ + { + leagueId: '123', + leagueName: 'Test League', + logo: 'logo.png', + participants: ['123456', '78'], + survivors: ['123456', '78'], + }, + ], + updateUser: jest.fn(), + })), })); jest.mock('@/utils/utils', () => ({ - getUserLeagues: jest.fn(), + getUserLeagues: jest.fn(() => Promise.resolve([])), cn: jest.fn(), })); jest.mock('@/api/apiFunctions', () => ({ - getGameWeek: jest.fn(), - getCurrentUserEntries: jest.fn(), + getAllLeagues: jest.fn(), + addUserToLeague: jest.fn(), })); -const mockLeagues = [ - { - leagueId: '123', - leagueName: 'Test League', - logo: 'https://findmylogo.com/logo.png', - participants: ['123456', '78'], - survivors: ['123456', '78', '9'], - }, -]; - -const mockEntries = [ - { - $id: '123', - name: 'Test Entry', - user: '123', - league: '123', - selectedTeams: [], - eliminated: false, +jest.mock('react-hot-toast', () => ({ + toast: { + custom: jest.fn(), }, -]; - -const mockGameWeek = { - id: '123', - week: 1, -}; - -const mockUseDataStore = useDataStore as jest.MockedFunction< - typeof useDataStore ->; -const mockGetUserLeagues = getUserLeagues as jest.MockedFunction< - typeof getUserLeagues ->; -const mockGetGameWeek = getGameWeek as jest.MockedFunction; -const mockGetCurrentUserEntries = getCurrentUserEntries as jest.MockedFunction< - typeof getCurrentUserEntries ->; +})); describe('Leagues Component', () => { - const mockUpdateLeagues = jest.fn(); - const mockUpdateGameWeek = jest.fn(); - const mockUpdateEntries = jest.fn(); - const mockCn = cn as jest.MockedFunction; + const mockUseDataStore = useDataStore as unknown as jest.Mock; + const mockGetUserLeagues = getUserLeagues as jest.Mock; + const mockGetAllLeagues = getAllLeagues as jest.Mock; + const mockAddUserToLeague = addUserToLeague as jest.Mock; beforeEach(() => { jest.clearAllMocks(); + }); + + it('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { + mockUseAuthContext.isSignedIn = true; mockUseDataStore.mockReturnValue({ - user: { id: '123', leagues: [] }, - leagues: [], - updateLeagues: mockUpdateLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, + user: { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], + }, + allLeagues: [], }); - }); - test('should display GlobalSpinner while loading data', async () => { render(); - expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); + const messageElement = screen.getByTestId('no-leagues-message'); + expect(messageElement).toBeInTheDocument(); }); }); - test('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { - mockGetUserLeagues.mockResolvedValue([]); - - render(); + it('should display GlobalSpinner while loading data', async () => { + mockUseAuthContext.isSignedIn = true; - await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); + mockUseDataStore.mockReturnValueOnce({ + user: { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], + }, + allLeagues: [], }); - expect( - screen.getByText('You are not enrolled in any leagues'), - ).toBeInTheDocument(); - }); - - test('should not call getGameWeek or getCurrentUserEntries if no leagues are found', async () => { - mockGetUserLeagues.mockResolvedValue([]); - render(); - await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); - expect(mockGetGameWeek).not.toHaveBeenCalled(); - expect(mockGetCurrentUserEntries).not.toHaveBeenCalled(); - }); + expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); }); - test('should not display GlobalSpinner after loading data', async () => { + it('should not display GlobalSpinner after loading data', async () => { + mockUseAuthContext.isSignedIn = true; + mockUseDataStore.mockReturnValue({ - user: { id: '123', leagues: ['123'] }, - leagues: mockLeagues, - updateLeagues: mockUpdateLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, + user: { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], + }, + allLeagues: [ + { + leagueId: '123', + leagueName: 'Test League', + logo: 'logo.png', + participants: ['123456', '78'], + survivors: ['123456', '78'], + }, + ], }); - mockGetUserLeagues.mockResolvedValue(mockLeagues); - mockGetGameWeek.mockResolvedValue(mockGameWeek); - mockGetCurrentUserEntries.mockResolvedValue(mockEntries); - render(); + mockGetUserLeagues.mockResolvedValueOnce([]); - expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + render(); - await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); - expect(mockGetGameWeek).toHaveBeenCalled(); - expect(mockGetCurrentUserEntries).toHaveBeenCalled(); - }); + await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); }); - test('should display "Test League" when leagues are found', async () => { + + it('should handle form submission to join a league', async () => { + mockUseAuthContext.isSignedIn = true; + + const user = { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], + }; + + const league = { + leagueId: '123', + leagueName: 'Test League', + logo: 'logo.png', + participants: [], + survivors: [], + }; + + const updateUser = jest.fn(); + mockUseDataStore.mockReturnValue({ - user: { id: '123', leagues: ['123'] }, - leagues: mockLeagues, - updateLeagues: mockUpdateLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, + user, + allLeagues: [league], + updateUser, }); - mockGetUserLeagues.mockResolvedValue(mockLeagues); - mockGetGameWeek.mockResolvedValue(mockGameWeek); - mockGetCurrentUserEntries.mockResolvedValue(mockEntries); + + mockGetAllLeagues.mockResolvedValueOnce([league]); + mockAddUserToLeague.mockResolvedValue( + Promise.resolve({ + userDocumentId: user.documentId, + selectedLeague: league.leagueId, + selectedLeagues: [league.leagueId], + participants: [user.id], + survivors: [user.id], + }), + ); render(); await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); - expect(mockGetGameWeek).toHaveBeenCalled(); - expect(mockGetCurrentUserEntries).toHaveBeenCalled(); + expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); }); - expect(mockUpdateGameWeek).toHaveBeenCalledWith(mockGameWeek); - expect(mockUpdateEntries).toHaveBeenCalledWith(mockEntries); + const selectElement = screen.getByTestId('select-available-leagues'); + fireEvent.change(selectElement, { target: { value: '123' } }); + fireEvent.click(screen.getByTestId('join-league-button')); - expect(screen.getByText('Test League')).toBeInTheDocument(); + await waitFor(() => { + expect(mockAddUserToLeague).toHaveBeenCalledWith({ + userDocumentId: user.documentId, + selectedLeague: league.leagueId, + selectedLeagues: [league.leagueId], + participants: [user.id], + survivors: [user.id], + }); + expect(updateUser).toHaveBeenCalledWith( + user.documentId, + user.id, + user.email, + [...user.leagues, league.leagueId], + ); + expect(toast.custom).toHaveBeenCalledWith( + , + ); + }); }); - test('should call getGameWeek and getCurrentUserEntries if leagues are found', async () => { + + it('should show error if adding to league fails', async () => { + mockUseAuthContext.isSignedIn = true; + + const user = { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], + }; + + const league = { + leagueId: '123', + leagueName: 'Test League', + logo: 'logo.png', + participants: [], + survivors: [], + }; + mockUseDataStore.mockReturnValue({ - user: { id: '123', leagues: ['123'] }, - leagues: mockLeagues, - updateLeagues: mockUpdateLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, + user, + allLeagues: [league], }); - mockGetUserLeagues.mockResolvedValue(mockLeagues); - mockGetGameWeek.mockResolvedValue(mockGameWeek); - mockGetCurrentUserEntries.mockResolvedValue(mockEntries); + + mockGetUserLeagues.mockResolvedValueOnce([]); + mockGetAllLeagues.mockResolvedValueOnce([league]); + mockAddUserToLeague.mockResolvedValue( + Promise.resolve({ + userDocumentId: user.documentId, + selectedLeague: league.leagueId, + selectedLeagues: [league.leagueId], + participants: [user.id], + survivors: [user.id], + }), + ); render(); await waitFor(() => { - expect(mockGetUserLeagues).toHaveBeenCalled(); - expect(mockGetGameWeek).toHaveBeenCalled(); - expect(mockGetCurrentUserEntries).toHaveBeenCalled(); + expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); }); - expect(mockUpdateGameWeek).toHaveBeenCalledWith(mockGameWeek); - expect(mockUpdateEntries).toHaveBeenCalledWith(mockEntries); + const selectElement = screen.getByTestId('select-available-leagues'); + fireEvent.change(selectElement, { target: { value: '123' } }); + fireEvent.click(screen.getByTestId('join-league-button')); + + await waitFor(() => { + expect(mockAddUserToLeague).toHaveBeenCalledWith({ + userDocumentId: user.documentId, + selectedLeague: league.leagueId, + selectedLeagues: [league.leagueId], + participants: [user.id], + survivors: [user.id], + }); + + expect(toast.custom).toHaveBeenCalledWith( + , + ); + }); }); }); diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index 61efbcb3..56c04323 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -3,85 +3,134 @@ 'use client'; +import Alert from '@/components/AlertNotification/AlertNotification'; +import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; import { ENTRY_URL, LEAGUE_URL } from '@/const/global'; +import { Button } from '@/components/Button/Button'; import { getUserLeagues } from '@/utils/utils'; +import { ILeague } from '@/api/apiFunctions.interface'; +import { addUserToLeague, getAllLeagues } from '@/api/apiFunctions'; import { LeagueCard } from '@/components/LeagueCard/LeagueCard'; import { useDataStore } from '@/store/dataStore'; import GlobalSpinner from '@/components/GlobalSpinner/GlobalSpinner'; import React, { JSX, useEffect, useState } from 'react'; -import { getCurrentUserEntries, getGameWeek } from '@/api/apiFunctions'; +import { toast } from 'react-hot-toast'; +import { useAuthContext } from '@/context/AuthContextProvider'; +import { useForm, Controller, SubmitHandler } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; + +const leagueSchema = z.object({ + selectedLeague: z.string().nonempty('Please select a league'), +}); + +type LeagueFormInputs = z.infer; /** * Renders the leagues component. * @returns {JSX.Element} The rendered leagues component. */ const Leagues = (): JSX.Element => { + const [leagues, setLeagues] = useState([]); const [loadingData, setLoadingData] = useState(true); - const { user, leagues, updateLeagues, updateGameWeek, updateEntries } = - useDataStore((state) => state); + const { user, updateUser, allLeagues, updateAllLeagues } = useDataStore( + (state) => state, + ); + const { isSignedIn } = useAuthContext(); + const { handleSubmit, control } = useForm({ + resolver: zodResolver(leagueSchema), + }); /** - * Fetches the user's leagues. - * @returns {Promise} + * Fetches all leagues and leagues user is a part of from the database. */ - const getLeagues = async (): Promise => { + const fetchData = async (): Promise => { try { - const userLeagues = await getUserLeagues(user.leagues); - updateLeagues(userLeagues); + // Only fetch all leagues if they're not already in the store + if (allLeagues.length === 0) { + const fetchedLeagues = await getAllLeagues(); + updateAllLeagues(fetchedLeagues); + } + + // Fetch user leagues + const fetchedUserLeagues = await getUserLeagues(user.leagues); + setLeagues(fetchedUserLeagues); } catch (error) { - throw new Error('Error fetching user leagues'); + console.error('Error fetching leagues:', error); + toast.custom( + , + ); } finally { setLoadingData(false); } }; + useEffect(() => { + if (isSignedIn) { + fetchData(); + } + }, [isSignedIn]); + /** - * Fetches entries per league and the current week. - * @returns {Promise} + * Handles the form submission. + * @param {LeagueFormInputs} data - The data from the form. + * @throws {Error} Throws an error if the selected league is not provided. */ - const fetchAdditionalUserData = async (): Promise => { - try { - const entryPromises = leagues.map((league) => - getCurrentUserEntries(user.id, league.leagueId), - ); - const gameWeekPromise = getGameWeek(); + const onSubmit: SubmitHandler = async (data) => { + const { selectedLeague } = data; + const league = allLeagues.find( + (league) => league.leagueId === selectedLeague, + ); - const [entries, currentWeek] = await Promise.all([ - Promise.all(entryPromises), - gameWeekPromise, - ]); - - updateEntries(entries.flat()); - updateGameWeek(currentWeek); - } catch (error) { - throw new Error('Error fetching entries and game week'); - } - }; - - useEffect(() => { - if (!user.id || user.id === '') { + if (!league) { + alert('Please select a valid league.'); return; } - getLeagues(); - }, [user]); + try { + await addUserToLeague({ + userDocumentId: user.documentId, + selectedLeague: league.leagueId, + selectedLeagues: [...(user.leagues ?? []), league.leagueId], + participants: [...(league.participants ?? []), user.id], + survivors: [...(league.survivors ?? []), user.id], + }); - useEffect(() => { - if (leagues.length > 0) { - fetchAdditionalUserData(); + setLeagues([...leagues, league]); + updateUser(user.documentId, user.id, user.email, [ + ...user.leagues, + league.leagueId, + ]); + toast.custom( + , + ); + } catch (error) { + console.error('Error adding league:', error); + toast.custom( + , + ); } - }, [leagues]); + }; return (
{loadingData ? ( - + ) : ( <>

- Your leagues + Your Leagues

-
+
{leagues.length > 0 ? ( leagues.map((league) => ( { )) ) : (
-

+

You are not enrolled in any leagues

)}
+ +
+
+ + ( + <> + + {fieldState.error && ( + + {fieldState.error.message} + + )} + + )} + /> +
+ +
)}
From 19a416fcd3634b259cf1d823e727caea8e8d9d81 Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:03:57 -0400 Subject: [PATCH 06/12] updated logic for fetching game data and storing in data store --- api/apiFunctions.ts | 1 - app/(main)/league/all/page.tsx | 72 +++++++++++++++++++++++++--------- store/dataStore.ts | 40 ++++++++++++------- 3 files changed, 79 insertions(+), 34 deletions(-) diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 2fcc88d1..79b15cc3 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -382,7 +382,6 @@ export async function getAllLeagues(): Promise { participants: league.participants, survivors: league.survivors, })); - return leagues; } catch (error) { throw new Error('Error getting all leagues', { cause: error }); diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index 56c04323..1652b402 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -8,8 +8,12 @@ import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; import { ENTRY_URL, LEAGUE_URL } from '@/const/global'; import { Button } from '@/components/Button/Button'; import { getUserLeagues } from '@/utils/utils'; -import { ILeague } from '@/api/apiFunctions.interface'; -import { addUserToLeague, getAllLeagues } from '@/api/apiFunctions'; +import { + addUserToLeague, + getAllLeagues, + getCurrentUserEntries, + getGameWeek, +} from '@/api/apiFunctions'; import { LeagueCard } from '@/components/LeagueCard/LeagueCard'; import { useDataStore } from '@/store/dataStore'; import GlobalSpinner from '@/components/GlobalSpinner/GlobalSpinner'; @@ -19,6 +23,7 @@ import { useAuthContext } from '@/context/AuthContextProvider'; import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; +import { ILeague } from '@/api/apiFunctions.interface'; const leagueSchema = z.object({ selectedLeague: z.string().nonempty('Please select a league'), @@ -31,30 +36,37 @@ type LeagueFormInputs = z.infer; * @returns {JSX.Element} The rendered leagues component. */ const Leagues = (): JSX.Element => { - const [leagues, setLeagues] = useState([]); const [loadingData, setLoadingData] = useState(true); - const { user, updateUser, allLeagues, updateAllLeagues } = useDataStore( - (state) => state, - ); + const { + user, + updateUser, + allLeagues, + updateAllLeagues, + userLeagues, + updateUserLeagues, + updateEntries, + updateGameWeek, + } = useDataStore((state) => state); const { isSignedIn } = useAuthContext(); const { handleSubmit, control } = useForm({ resolver: zodResolver(leagueSchema), }); /** - * Fetches all leagues and leagues user is a part of from the database. + * Fetches all leagues, user leagues, and user entries */ - const fetchData = async (): Promise => { + const fetchGameData = async (): Promise => { try { - // Only fetch all leagues if they're not already in the store - if (allLeagues.length === 0) { - const fetchedLeagues = await getAllLeagues(); - updateAllLeagues(fetchedLeagues); - } + // fetch all leagues + const fetchedLeagues = await getAllLeagues(); + updateAllLeagues(fetchedLeagues); // Fetch user leagues const fetchedUserLeagues = await getUserLeagues(user.leagues); - setLeagues(fetchedUserLeagues); + updateUserLeagues(fetchedUserLeagues); + + // Fetch addition user data + fetchAdditionalUserData(fetchedUserLeagues); } catch (error) { console.error('Error fetching leagues:', error); toast.custom( @@ -68,9 +80,33 @@ const Leagues = (): JSX.Element => { } }; + /** + * Fetches entries per league and the current week. + * @param {ILeague[]} leagues - The leagues to fetch entries for. + * @returns {Promise} + */ + const fetchAdditionalUserData = async (leagues: ILeague[]): Promise => { + try { + const entryPromises = leagues.map((league) => + getCurrentUserEntries(user.id, league.leagueId), + ); + const gameWeekPromise = getGameWeek(); + + const [entries, currentWeek] = await Promise.all([ + Promise.all(entryPromises), + gameWeekPromise, + ]); + + updateEntries(entries.flat()); + updateGameWeek(currentWeek); + } catch (error) { + throw new Error('Error fetching entries and game week'); + } + }; + useEffect(() => { if (isSignedIn) { - fetchData(); + fetchGameData(); } }, [isSignedIn]); @@ -99,7 +135,7 @@ const Leagues = (): JSX.Element => { survivors: [...(league.survivors ?? []), user.id], }); - setLeagues([...leagues, league]); + updateUserLeagues([...userLeagues, league]); updateUser(user.documentId, user.id, user.email, [ ...user.leagues, league.leagueId, @@ -131,8 +167,8 @@ const Leagues = (): JSX.Element => { Your Leagues
- {leagues.length > 0 ? ( - leagues.map((league) => ( + {userLeagues.length > 0 ? ( + userLeagues.map((league) => ( void; - updateLeagues: (leagues: ILeague[]) => void; updateGameWeek: (gameWeek: IGameWeek) => void; updateEntries: (entries: IEntry[]) => void; updateAllLeagues: (allLeagues: ILeague[]) => void; + updateUserLeagues: (userLeagues: ILeague[]) => void; } /* eslint-disable */ @@ -66,13 +66,13 @@ const initialState: IDataStoreState = { gameWeekId: '', userResults: {}, }, - leagues: [], gameWeek: { id: '', week: 0, }, entries: [], allLeagues: [], + userLeagues: [], }; //create the store @@ -132,18 +132,6 @@ export const useDataStore = create((set) => ({ state.weeklyPicks.userResults = userResults; }), ), - /** - * Update the game group - * @param props - props - * @param props.leagues - The leagues the user is a part of - * @returns {void} - */ - updateLeagues: (leagues: ILeague[]): void => - set( - produce((state: IDataStoreState) => { - state.leagues = [...leagues]; - }), - ), /** * Update the current week * @param props - props @@ -182,4 +170,26 @@ export const useDataStore = create((set) => ({ state.allLeagues = [...state.allLeagues, ...updatedLeagues]; }), ), + /** + * Updates user leagues in the data store. + * + * @param {ILeague[]} props - The league properties to update.. + * @returns {void} + */ + updateUserLeagues: (updatedUserLeagues: ILeague[]): void => + set( + produce((state: IDataStoreState) => { + // Create a Set of current league IDs to avoid duplicates + const existingLeagueIds = new Set( + state.userLeagues.map((league) => league.leagueId), + ); + + // Add only leagues that are not already in the state + const newLeagues = updatedUserLeagues.filter( + (league) => !existingLeagueIds.has(league.leagueId), + ); + + state.userLeagues = [...state.userLeagues, ...newLeagues]; + }), + ), })); From d1b7c32d3ed903ea24be8641ab5452d07410d13f Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:07:32 -0400 Subject: [PATCH 07/12] fixed test after adding in new functions --- app/(main)/league/all/page.test.tsx | 121 ++++++++++++++++++---------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index b1a783c8..8cb6ddd1 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -8,7 +8,12 @@ import { import Leagues from './page'; import { useDataStore } from '@/store/dataStore'; import { getUserLeagues } from '@/utils/utils'; -import { getAllLeagues, addUserToLeague } from '@/api/apiFunctions'; +import { + getAllLeagues, + addUserToLeague, + getGameWeek, + getCurrentUserEntries, +} from '@/api/apiFunctions'; import { toast } from 'react-hot-toast'; import Alert from '@/components/AlertNotification/AlertNotification'; import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; @@ -26,54 +31,93 @@ jest.mock('@/context/AuthContextProvider', () => ({ })); jest.mock('@/store/dataStore', () => ({ - useDataStore: jest.fn(() => ({ - user: { - documentId: '123', - id: '1234', - email: 'test@test.com', - leagues: ['league1'], - }, - allLeagues: [ - { - leagueId: '123', - leagueName: 'Test League', - logo: 'logo.png', - participants: ['123456', '78'], - survivors: ['123456', '78'], - }, - ], - updateUser: jest.fn(), - })), + useDataStore: jest.fn(), })); jest.mock('@/utils/utils', () => ({ getUserLeagues: jest.fn(() => Promise.resolve([])), - cn: jest.fn(), + cn: jest.fn((className) => className), })); jest.mock('@/api/apiFunctions', () => ({ getAllLeagues: jest.fn(), addUserToLeague: jest.fn(), + getGameWeek: jest.fn(), + getCurrentUserEntries: jest.fn(), })); +const mockAllLeagues = [ + { + leagueId: '123', + leagueName: 'Test League', + logo: 'https://findmylogo.com/logo.png', + participants: ['123456', '78'], + survivors: ['123456', '78', '9'], + }, +]; + +const mockEntries = [ + { + $id: '123', + name: 'Test Entry', + user: '123', + league: '123', + selectedTeams: [], + eliminated: false, + }, +]; + +const mockGameWeek = { + id: '123', + week: 1, +}; + jest.mock('react-hot-toast', () => ({ toast: { custom: jest.fn(), }, })); +const mockUseDataStore = useDataStore as jest.MockedFunction< + typeof useDataStore +>; +const mockGetUserLeagues = getUserLeagues as jest.MockedFunction< + typeof getUserLeagues +>; +const mockGetGameWeek = getGameWeek as jest.MockedFunction; +const mockGetCurrentUserEntries = getCurrentUserEntries as jest.MockedFunction< + typeof getCurrentUserEntries +>; + describe('Leagues Component', () => { - const mockUseDataStore = useDataStore as unknown as jest.Mock; - const mockGetUserLeagues = getUserLeagues as jest.Mock; + const mockUpdateLeagues = jest.fn(); + const mockUpdateUserLeagues = jest.fn(); + const mockUpdateGameWeek = jest.fn(); + const mockUpdateEntries = jest.fn(); const mockGetAllLeagues = getAllLeagues as jest.Mock; const mockAddUserToLeague = addUserToLeague as jest.Mock; beforeEach(() => { jest.clearAllMocks(); + mockUseDataStore.mockReturnValue({ + user: { id: '123', leagues: [] }, + leagues: [], + updateAllLeagues: mockUpdateLeagues, + updateUserLeagues: mockUpdateUserLeagues, + updateEntries: mockUpdateEntries, + updateGameWeek: mockUpdateGameWeek, + }); + }); + + it('should display GlobalSpinner while loading data', async () => { + render(); + expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); }); it('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { mockUseAuthContext.isSignedIn = true; + mockGetUserLeagues.mockResolvedValue([]); + mockUseDataStore.mockReturnValue({ user: { documentId: '123', @@ -81,7 +125,8 @@ describe('Leagues Component', () => { id: '123', leagues: [], }, - allLeagues: [], + allLeagues: mockAllLeagues, + userLeagues: [], }); render(); @@ -94,24 +139,6 @@ describe('Leagues Component', () => { }); }); - it('should display GlobalSpinner while loading data', async () => { - mockUseAuthContext.isSignedIn = true; - - mockUseDataStore.mockReturnValueOnce({ - user: { - documentId: '123', - email: 'test@test.com', - id: '123', - leagues: [], - }, - allLeagues: [], - }); - - render(); - - expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); - }); - it('should not display GlobalSpinner after loading data', async () => { mockUseAuthContext.isSignedIn = true; @@ -131,6 +158,7 @@ describe('Leagues Component', () => { survivors: ['123456', '78'], }, ], + userLeagues: [], }); mockGetUserLeagues.mockResolvedValueOnce([]); @@ -161,11 +189,20 @@ describe('Leagues Component', () => { }; const updateUser = jest.fn(); + const updateUserLeagues = jest.fn(); + const updateAllLeagues = jest.fn(); + const updateGameWeek = jest.fn(); + const updateEntries = jest.fn(); mockUseDataStore.mockReturnValue({ user, allLeagues: [league], updateUser, + updateUserLeagues, + userLeagues: [], + updateAllLeagues, + updateGameWeek, + updateEntries, }); mockGetAllLeagues.mockResolvedValueOnce([league]); @@ -197,6 +234,8 @@ describe('Leagues Component', () => { participants: [user.id], survivors: [user.id], }); + expect(updateAllLeagues).toHaveBeenCalledWith([league]); + expect(updateUserLeagues).toHaveBeenCalledWith([league]); expect(updateUser).toHaveBeenCalledWith( user.documentId, user.id, From 7b35b780612a493fcb47ba7d0e728c3d38d9393b Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:22:13 -0400 Subject: [PATCH 08/12] fix testing error on leage all page --- app/(main)/league/all/page.test.tsx | 31 +++++++++++++---------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index 8cb6ddd1..13caba28 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -17,6 +17,7 @@ import { import { toast } from 'react-hot-toast'; import Alert from '@/components/AlertNotification/AlertNotification'; import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; +import { mock } from 'node:test'; const mockUseAuthContext = { isSignedIn: false, @@ -269,22 +270,26 @@ describe('Leagues Component', () => { survivors: [], }; + const updateUser = jest.fn(); + const updateUserLeagues = jest.fn(); + const updateAllLeagues = jest.fn(); + const updateGameWeek = jest.fn(); + const updateEntries = jest.fn(); + mockUseDataStore.mockReturnValue({ user, allLeagues: [league], + updateUser, + updateUserLeagues, + userLeagues: [], + updateAllLeagues, + updateGameWeek, + updateEntries, }); mockGetUserLeagues.mockResolvedValueOnce([]); mockGetAllLeagues.mockResolvedValueOnce([league]); - mockAddUserToLeague.mockResolvedValue( - Promise.resolve({ - userDocumentId: user.documentId, - selectedLeague: league.leagueId, - selectedLeagues: [league.leagueId], - participants: [user.id], - survivors: [user.id], - }), - ); + mockAddUserToLeague.mockRejectedValue(new Error()); render(); @@ -297,14 +302,6 @@ describe('Leagues Component', () => { fireEvent.click(screen.getByTestId('join-league-button')); await waitFor(() => { - expect(mockAddUserToLeague).toHaveBeenCalledWith({ - userDocumentId: user.documentId, - selectedLeague: league.leagueId, - selectedLeagues: [league.leagueId], - participants: [user.id], - survivors: [user.id], - }); - expect(toast.custom).toHaveBeenCalledWith( Date: Wed, 16 Oct 2024 13:29:05 -0400 Subject: [PATCH 09/12] fix data store test --- store/dataStore.test.ts | 65 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/store/dataStore.test.ts b/store/dataStore.test.ts index 02daa8b8..4846d000 100644 --- a/store/dataStore.test.ts +++ b/store/dataStore.test.ts @@ -54,7 +54,8 @@ const allLeagues = [ leagueName: 'Test League', logo: 'https://findmylogo.com/logo.png', participants: ['123456', '78'], - survivors: ['123456', '78', '9'],} + survivors: ['123456', '78', '9'], + }, ]; describe('Data Store', () => { @@ -71,14 +72,14 @@ describe('Data Store', () => { act(() => { result.current.updateUser( userData.documentId, - userData.userId, - userData.userEmail, + userData.id, + userData.email, userData.leagues, ); }); - expect(result.current.user.id).toBe(userData.userId); - expect(result.current.user.email).toBe(userData.userEmail); + expect(result.current.user.id).toBe(userData.id); + expect(result.current.user.email).toBe(userData.email); }); it('Checks the reset user state matches default', () => { const { result } = renderHook(() => useDataStore()); @@ -86,8 +87,8 @@ describe('Data Store', () => { act(() => { result.current.updateUser( userData.documentId, - userData.userId, - userData.userEmail, + userData.id, + userData.email, userData.leagues, ); result.current.resetUser(); @@ -130,20 +131,20 @@ describe('Data Store', () => { userResults: { '123': { '456': { - 'entry1': { - teamName: 'New England Patriots', - correct:true + entry1: { + teamName: 'New England Patriots', + correct: true, + }, + entry2: { + teamName: 'Kansas City Chiefs', + correct: false, + }, + entry3: { + teamName: 'New England Patriots', + correct: false, + }, }, - 'entry2': { - teamName: 'Kansas City Chiefs', - correct:false - }, - 'entry3': { - teamName: 'New England Patriots', - correct:false - } - } - } + }, }, }; act(() => { @@ -156,25 +157,27 @@ describe('Data Store', () => { }); }); - describe('League Test', () => { + describe('All Leagues Test', () => { it('Check the default league state', () => { const { result } = renderHook(() => useDataStore()); - expect(result.current.leagues).toStrictEqual([]); + expect(result.current.allLeagues).toStrictEqual([]); }); it('Check the updated league state', () => { const { result } = renderHook(() => useDataStore()); act(() => { - result.current.updateLeagues(league); + result.current.updateAllLeagues(league); }); - expect(result.current.leagues[0].leagueId).toBe(league[0].leagueId); - expect(result.current.leagues[0].leagueName).toBe(league[0].leagueName); - expect(result.current.leagues[0].logo).toBe(league[0].logo); - expect(result.current.leagues[0].participants).toBe( + expect(result.current.allLeagues[0].leagueId).toBe(league[0].leagueId); + expect(result.current.allLeagues[0].leagueName).toBe( + league[0].leagueName, + ); + expect(result.current.allLeagues[0].logo).toBe(league[0].logo); + expect(result.current.allLeagues[0].participants).toBe( league[0].participants, ); - expect(result.current.leagues[0].survivors).toBe(league[0].survivors); + expect(result.current.allLeagues[0].survivors).toBe(league[0].survivors); }); }); @@ -219,10 +222,10 @@ xdescribe('getting all leagues test', () => { expect(result.current.allLeagues).toStrictEqual([]); }); it('check the updated allLeagues state', async () => { - const { result } = renderHook(() => useDataStore()); + const { result } = renderHook(() => useDataStore()); act(() => { result.current.updateAllLeagues(allLeagues); }); expect(result.current.allLeagues).toStrictEqual(allLeagues); - }) -}) + }); +}); From 98f0f76761f98b28c61bfc7e7434d0ed6158e6b4 Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:32:21 -0400 Subject: [PATCH 10/12] fix type errors in IEntry --- app/(main)/league/[leagueId]/entry/Entries.interface.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(main)/league/[leagueId]/entry/Entries.interface.ts b/app/(main)/league/[leagueId]/entry/Entries.interface.ts index af624df8..5c324ff9 100644 --- a/app/(main)/league/[leagueId]/entry/Entries.interface.ts +++ b/app/(main)/league/[leagueId]/entry/Entries.interface.ts @@ -6,8 +6,8 @@ import { ILeague, INFLTeam, IUser } from '@/api/apiFunctions.interface'; export interface IEntry { $id: string; name: string; - user: IUser; - league: ILeague; + user: IUser['id']; + league: ILeague['leagueId']; selectedTeams: INFLTeam['teamName'][]; eliminated: boolean; } From b69e3fb837a66d0429ed0e1073626fe18a4f665e Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:19:50 -0400 Subject: [PATCH 11/12] fix reusable variables --- app/(main)/league/all/page.test.tsx | 182 +++++++++------------------- 1 file changed, 58 insertions(+), 124 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index 13caba28..a9adfbd3 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -47,9 +47,24 @@ jest.mock('@/api/apiFunctions', () => ({ getCurrentUserEntries: jest.fn(), })); +const mockUser = { + documentId: '123', + email: 'test@test.com', + id: '123', + leagues: [], +}; + +const mockLeague = { + leagueId: '123', + leagueName: 'Test League', + logo: 'logo.png', + participants: [], + survivors: [], +}; + const mockAllLeagues = [ { - leagueId: '123', + leagueId: '1234', leagueName: 'Test League', logo: 'https://findmylogo.com/logo.png', participants: ['123456', '78'], @@ -91,19 +106,22 @@ const mockGetCurrentUserEntries = getCurrentUserEntries as jest.MockedFunction< >; describe('Leagues Component', () => { - const mockUpdateLeagues = jest.fn(); + const mockUpdateUser = jest.fn(); + const mockUpdateAllLeagues = jest.fn(); const mockUpdateUserLeagues = jest.fn(); - const mockUpdateGameWeek = jest.fn(); const mockUpdateEntries = jest.fn(); + const mockUpdateGameWeek = jest.fn(); const mockGetAllLeagues = getAllLeagues as jest.Mock; const mockAddUserToLeague = addUserToLeague as jest.Mock; beforeEach(() => { jest.clearAllMocks(); + mockUseAuthContext.isSignedIn = true; mockUseDataStore.mockReturnValue({ - user: { id: '123', leagues: [] }, - leagues: [], - updateAllLeagues: mockUpdateLeagues, + user: mockUser, + allLeagues: mockAllLeagues, + userLeagues: [], + updateAllLeagues: mockUpdateAllLeagues, updateUserLeagues: mockUpdateUserLeagues, updateEntries: mockUpdateEntries, updateGameWeek: mockUpdateGameWeek, @@ -116,20 +134,6 @@ describe('Leagues Component', () => { }); it('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { - mockUseAuthContext.isSignedIn = true; - mockGetUserLeagues.mockResolvedValue([]); - - mockUseDataStore.mockReturnValue({ - user: { - documentId: '123', - email: 'test@test.com', - id: '123', - leagues: [], - }, - allLeagues: mockAllLeagues, - userLeagues: [], - }); - render(); await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); @@ -141,29 +145,6 @@ describe('Leagues Component', () => { }); it('should not display GlobalSpinner after loading data', async () => { - mockUseAuthContext.isSignedIn = true; - - mockUseDataStore.mockReturnValue({ - user: { - documentId: '123', - email: 'test@test.com', - id: '123', - leagues: [], - }, - allLeagues: [ - { - leagueId: '123', - leagueName: 'Test League', - logo: 'logo.png', - participants: ['123456', '78'], - survivors: ['123456', '78'], - }, - ], - userLeagues: [], - }); - - mockGetUserLeagues.mockResolvedValueOnce([]); - render(); await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); @@ -172,48 +153,25 @@ describe('Leagues Component', () => { }); it('should handle form submission to join a league', async () => { - mockUseAuthContext.isSignedIn = true; - - const user = { - documentId: '123', - email: 'test@test.com', - id: '123', - leagues: [], - }; - - const league = { - leagueId: '123', - leagueName: 'Test League', - logo: 'logo.png', - participants: [], - survivors: [], - }; - - const updateUser = jest.fn(); - const updateUserLeagues = jest.fn(); - const updateAllLeagues = jest.fn(); - const updateGameWeek = jest.fn(); - const updateEntries = jest.fn(); - mockUseDataStore.mockReturnValue({ - user, - allLeagues: [league], - updateUser, - updateUserLeagues, + user: mockUser, + allLeagues: [mockLeague], userLeagues: [], - updateAllLeagues, - updateGameWeek, - updateEntries, + updateUser: mockUpdateUser, + updateUserLeagues: mockUpdateUserLeagues, + updateAllLeagues: mockUpdateAllLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, }); - mockGetAllLeagues.mockResolvedValueOnce([league]); + mockGetAllLeagues.mockResolvedValueOnce([mockLeague]); mockAddUserToLeague.mockResolvedValue( Promise.resolve({ - userDocumentId: user.documentId, - selectedLeague: league.leagueId, - selectedLeagues: [league.leagueId], - participants: [user.id], - survivors: [user.id], + userDocumentId: mockUser.documentId, + selectedLeague: mockLeague.leagueId, + selectedLeagues: [mockLeague.leagueId], + participants: [mockUser.id], + survivors: [mockUser.id], }), ); @@ -229,66 +187,42 @@ describe('Leagues Component', () => { await waitFor(() => { expect(mockAddUserToLeague).toHaveBeenCalledWith({ - userDocumentId: user.documentId, - selectedLeague: league.leagueId, - selectedLeagues: [league.leagueId], - participants: [user.id], - survivors: [user.id], + userDocumentId: mockUser.documentId, + selectedLeague: mockLeague.leagueId, + selectedLeagues: [mockLeague.leagueId], + participants: [mockUser.id], + survivors: [mockUser.id], }); - expect(updateAllLeagues).toHaveBeenCalledWith([league]); - expect(updateUserLeagues).toHaveBeenCalledWith([league]); - expect(updateUser).toHaveBeenCalledWith( - user.documentId, - user.id, - user.email, - [...user.leagues, league.leagueId], + expect(mockUpdateAllLeagues).toHaveBeenCalledWith([mockLeague]); + expect(mockUpdateUserLeagues).toHaveBeenCalledWith([mockLeague]); + expect(mockUpdateUser).toHaveBeenCalledWith( + mockUser.documentId, + mockUser.id, + mockUser.email, + [...mockUser.leagues, mockLeague.leagueId], ); expect(toast.custom).toHaveBeenCalledWith( , ); }); }); it('should show error if adding to league fails', async () => { - mockUseAuthContext.isSignedIn = true; - - const user = { - documentId: '123', - email: 'test@test.com', - id: '123', - leagues: [], - }; - - const league = { - leagueId: '123', - leagueName: 'Test League', - logo: 'logo.png', - participants: [], - survivors: [], - }; - - const updateUser = jest.fn(); - const updateUserLeagues = jest.fn(); - const updateAllLeagues = jest.fn(); - const updateGameWeek = jest.fn(); - const updateEntries = jest.fn(); - mockUseDataStore.mockReturnValue({ - user, - allLeagues: [league], - updateUser, - updateUserLeagues, + user: mockUser, + allLeagues: [mockLeague], userLeagues: [], - updateAllLeagues, - updateGameWeek, - updateEntries, + updateUser: mockUpdateUser, + updateUserLeagues: mockUpdateUserLeagues, + updateAllLeagues: mockUpdateAllLeagues, + updateGameWeek: mockUpdateGameWeek, + updateEntries: mockUpdateEntries, }); - mockGetUserLeagues.mockResolvedValueOnce([]); - mockGetAllLeagues.mockResolvedValueOnce([league]); + mockGetAllLeagues.mockResolvedValueOnce([mockLeague]); mockAddUserToLeague.mockRejectedValue(new Error()); render(); From 80d4681cb43adc007139ab9fc980447d787cdb36 Mon Sep 17 00:00:00 2001 From: Chris Nowicki <102450568+chris-nowicki@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:12:23 -0400 Subject: [PATCH 12/12] update /league/all page test to include getting entries and gameweek test --- app/(main)/league/all/page.test.tsx | 220 ++++++++++------------------ app/(main)/league/all/page.tsx | 1 - 2 files changed, 75 insertions(+), 146 deletions(-) diff --git a/app/(main)/league/all/page.test.tsx b/app/(main)/league/all/page.test.tsx index a9adfbd3..535e135c 100644 --- a/app/(main)/league/all/page.test.tsx +++ b/app/(main)/league/all/page.test.tsx @@ -17,35 +17,15 @@ import { import { toast } from 'react-hot-toast'; import Alert from '@/components/AlertNotification/AlertNotification'; import { AlertVariants } from '@/components/AlertNotification/Alerts.enum'; -import { mock } from 'node:test'; - -const mockUseAuthContext = { - isSignedIn: false, -}; jest.mock('@/context/AuthContextProvider', () => ({ - useAuthContext() { - return { - ...mockUseAuthContext, - }; - }, -})); - -jest.mock('@/store/dataStore', () => ({ - useDataStore: jest.fn(), + useAuthContext: () => ({ isSignedIn: true }), })); -jest.mock('@/utils/utils', () => ({ - getUserLeagues: jest.fn(() => Promise.resolve([])), - cn: jest.fn((className) => className), -})); - -jest.mock('@/api/apiFunctions', () => ({ - getAllLeagues: jest.fn(), - addUserToLeague: jest.fn(), - getGameWeek: jest.fn(), - getCurrentUserEntries: jest.fn(), -})); +jest.mock('@/store/dataStore'); +jest.mock('@/utils/utils'); +jest.mock('@/api/apiFunctions'); +jest.mock('react-hot-toast'); const mockUser = { documentId: '123', @@ -62,144 +42,107 @@ const mockLeague = { survivors: [], }; -const mockAllLeagues = [ - { - leagueId: '1234', - leagueName: 'Test League', - logo: 'https://findmylogo.com/logo.png', - participants: ['123456', '78'], - survivors: ['123456', '78', '9'], - }, -]; - -const mockEntries = [ - { - $id: '123', - name: 'Test Entry', - user: '123', - league: '123', - selectedTeams: [], - eliminated: false, - }, -]; - -const mockGameWeek = { - id: '123', - week: 1, +const setup = (initialStoreState = {}) => { + const mockStore = { + user: mockUser, + allLeagues: [mockLeague], + userLeagues: [], + updateUser: jest.fn(), + updateUserLeagues: jest.fn(), + updateAllLeagues: jest.fn(), + updateGameWeek: jest.fn(), + updateEntries: jest.fn(), + ...initialStoreState, + }; + + (useDataStore as unknown as jest.Mock).mockReturnValue(mockStore); + (getAllLeagues as jest.Mock).mockResolvedValue([mockLeague]); + (getUserLeagues as jest.Mock).mockResolvedValue([]); + (getCurrentUserEntries as jest.Mock).mockResolvedValue([]); + (getGameWeek as jest.Mock).mockResolvedValue({ id: '123', week: 1 }); + + return { mockStore }; }; -jest.mock('react-hot-toast', () => ({ - toast: { - custom: jest.fn(), - }, -})); - -const mockUseDataStore = useDataStore as jest.MockedFunction< - typeof useDataStore ->; -const mockGetUserLeagues = getUserLeagues as jest.MockedFunction< - typeof getUserLeagues ->; -const mockGetGameWeek = getGameWeek as jest.MockedFunction; -const mockGetCurrentUserEntries = getCurrentUserEntries as jest.MockedFunction< - typeof getCurrentUserEntries ->; - describe('Leagues Component', () => { - const mockUpdateUser = jest.fn(); - const mockUpdateAllLeagues = jest.fn(); - const mockUpdateUserLeagues = jest.fn(); - const mockUpdateEntries = jest.fn(); - const mockUpdateGameWeek = jest.fn(); - const mockGetAllLeagues = getAllLeagues as jest.Mock; - const mockAddUserToLeague = addUserToLeague as jest.Mock; - beforeEach(() => { jest.clearAllMocks(); - mockUseAuthContext.isSignedIn = true; - mockUseDataStore.mockReturnValue({ - user: mockUser, - allLeagues: mockAllLeagues, - userLeagues: [], - updateAllLeagues: mockUpdateAllLeagues, - updateUserLeagues: mockUpdateUserLeagues, - updateEntries: mockUpdateEntries, - updateGameWeek: mockUpdateGameWeek, - }); }); it('should display GlobalSpinner while loading data', async () => { + setup(); render(); expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); }); it('should render "You are not enrolled in any leagues" message when no leagues are found', async () => { + setup(); render(); - await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); + expect(screen.getByTestId('no-leagues-message')).toBeInTheDocument(); + }); - await waitFor(() => { - const messageElement = screen.getByTestId('no-leagues-message'); - expect(messageElement).toBeInTheDocument(); + it('should call getCurrentUserEntries and getGameWeek when component loads and user has leagues', async () => { + const userWithLeague = { ...mockUser, leagues: [mockLeague.leagueId] }; + const { mockStore } = setup({ + user: userWithLeague, + userLeagues: [mockLeague], }); - }); - it('should not display GlobalSpinner after loading data', async () => { + (getUserLeagues as jest.Mock).mockResolvedValue([mockLeague]); + render(); await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); - expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + await waitFor(() => { + expect(getAllLeagues).toHaveBeenCalled(); + expect(getUserLeagues).toHaveBeenCalledWith(userWithLeague.leagues); + expect(getCurrentUserEntries).toHaveBeenCalledWith( + userWithLeague.id, + mockLeague.leagueId, + ); + expect(getGameWeek).toHaveBeenCalled(); + expect(mockStore.updateAllLeagues).toHaveBeenCalled(); + expect(mockStore.updateUserLeagues).toHaveBeenCalled(); + expect(mockStore.updateEntries).toHaveBeenCalled(); + expect(mockStore.updateGameWeek).toHaveBeenCalled(); + }); }); it('should handle form submission to join a league', async () => { - mockUseDataStore.mockReturnValue({ - user: mockUser, - allLeagues: [mockLeague], - userLeagues: [], - updateUser: mockUpdateUser, - updateUserLeagues: mockUpdateUserLeagues, - updateAllLeagues: mockUpdateAllLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, + const { mockStore } = setup(); + (addUserToLeague as jest.Mock).mockResolvedValue({ + userDocumentId: mockUser.documentId, + selectedLeague: mockLeague.leagueId, + selectedLeagues: [mockLeague.leagueId], + participants: [mockUser.id], + survivors: [mockUser.id], }); - mockGetAllLeagues.mockResolvedValueOnce([mockLeague]); - mockAddUserToLeague.mockResolvedValue( - Promise.resolve({ - userDocumentId: mockUser.documentId, - selectedLeague: mockLeague.leagueId, - selectedLeagues: [mockLeague.leagueId], - participants: [mockUser.id], - survivors: [mockUser.id], - }), - ); - render(); + await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); - await waitFor(() => { - expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + fireEvent.change(screen.getByTestId('select-available-leagues'), { + target: { value: '123' }, }); - - const selectElement = screen.getByTestId('select-available-leagues'); - fireEvent.change(selectElement, { target: { value: '123' } }); fireEvent.click(screen.getByTestId('join-league-button')); await waitFor(() => { - expect(mockAddUserToLeague).toHaveBeenCalledWith({ - userDocumentId: mockUser.documentId, - selectedLeague: mockLeague.leagueId, - selectedLeagues: [mockLeague.leagueId], - participants: [mockUser.id], - survivors: [mockUser.id], - }); - expect(mockUpdateAllLeagues).toHaveBeenCalledWith([mockLeague]); - expect(mockUpdateUserLeagues).toHaveBeenCalledWith([mockLeague]); - expect(mockUpdateUser).toHaveBeenCalledWith( + expect(addUserToLeague).toHaveBeenCalledWith( + expect.objectContaining({ + userDocumentId: mockUser.documentId, + selectedLeague: mockLeague.leagueId, + }), + ); + expect(mockStore.updateAllLeagues).toHaveBeenCalledWith([mockLeague]); + expect(mockStore.updateUserLeagues).toHaveBeenCalledWith([mockLeague]); + expect(mockStore.updateUser).toHaveBeenCalledWith( mockUser.documentId, mockUser.id, mockUser.email, - [...mockUser.leagues, mockLeague.leagueId], + [mockLeague.leagueId], ); expect(toast.custom).toHaveBeenCalledWith( { }); it('should show error if adding to league fails', async () => { - mockUseDataStore.mockReturnValue({ - user: mockUser, - allLeagues: [mockLeague], - userLeagues: [], - updateUser: mockUpdateUser, - updateUserLeagues: mockUpdateUserLeagues, - updateAllLeagues: mockUpdateAllLeagues, - updateGameWeek: mockUpdateGameWeek, - updateEntries: mockUpdateEntries, - }); - mockGetUserLeagues.mockResolvedValueOnce([]); - mockGetAllLeagues.mockResolvedValueOnce([mockLeague]); - mockAddUserToLeague.mockRejectedValue(new Error()); + setup(); + (addUserToLeague as jest.Mock).mockRejectedValue(new Error()); render(); + await waitForElementToBeRemoved(() => screen.getByTestId('global-spinner')); - await waitFor(() => { - expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + fireEvent.change(screen.getByTestId('select-available-leagues'), { + target: { value: '123' }, }); - - const selectElement = screen.getByTestId('select-available-leagues'); - fireEvent.change(selectElement, { target: { value: '123' } }); fireEvent.click(screen.getByTestId('join-league-button')); await waitFor(() => { diff --git a/app/(main)/league/all/page.tsx b/app/(main)/league/all/page.tsx index 1652b402..aee4e65a 100644 --- a/app/(main)/league/all/page.tsx +++ b/app/(main)/league/all/page.tsx @@ -68,7 +68,6 @@ const Leagues = (): JSX.Element => { // Fetch addition user data fetchAdditionalUserData(fetchedUserLeagues); } catch (error) { - console.error('Error fetching leagues:', error); toast.custom(