-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes #539 **NOTE:** This is a branch off my password reset branch which is why it shows so many files changed. - users can now navigate to settings using the navdrawer and update their email and/or password. - if the user forgot their OLD PASSWORD they can click on the recover link in the form. When they click the link it will log them out and route them to /recover-password. ## SCREENSHOT ![CleanShot_2024-09-18_at_17 08 32](https://github.com/user-attachments/assets/4ab11b7d-acad-4d1e-8fbe-e456ba165315)
- Loading branch information
1 parent
6bc5b39
commit 05661c5
Showing
10 changed files
with
927 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,17 @@ | ||
import { mock } from 'node:test'; | ||
import { | ||
recoverPassword, | ||
registerAccount, | ||
resetPassword, | ||
resetRecoveredPassword, | ||
updateUserEmail, | ||
} from './apiFunctions'; | ||
import { IUser } from './apiFunctions.interface'; | ||
import { account, ID } from './config'; | ||
import { account, databases, ID } from './config'; | ||
const apiFunctions = require('./apiFunctions'); | ||
import { getBaseURL } from '@/utils/getBaseUrl'; | ||
import { Collection } from './apiFunctions.enum'; | ||
import { Query } from 'appwrite'; | ||
|
||
jest.mock('./apiFunctions', () => { | ||
const actualModule = jest.requireActual('./apiFunctions'); | ||
|
@@ -30,8 +35,17 @@ jest.mock('./config', () => ({ | |
account: { | ||
create: jest.fn(), | ||
createRecovery: jest.fn(), | ||
updateEmail: jest.fn(), | ||
updatePassword: jest.fn(), | ||
updateRecovery: jest.fn(), | ||
}, | ||
appwriteConfig: { | ||
databaseId: 'mock-database-id', | ||
}, | ||
databases: { | ||
listDocuments: jest.fn(), | ||
updateDocument: jest.fn(), | ||
}, | ||
ID: { | ||
unique: jest.fn(), | ||
}, | ||
|
@@ -164,6 +178,110 @@ describe('apiFunctions', () => { | |
); | ||
}); | ||
}); | ||
describe('updateUserEmail', () => { | ||
const mockNewEmail = '[email protected]'; | ||
const mockPassword = 'password123'; | ||
const mockUserId = '123'; | ||
const mockDocumentId = '456'; | ||
it("should successfully update the user's email", async () => { | ||
(account.updateEmail as jest.Mock).mockResolvedValue({ | ||
$id: mockUserId, | ||
}); | ||
|
||
(databases.listDocuments as jest.Mock).mockResolvedValue({ | ||
documents: [ | ||
{ | ||
$id: mockDocumentId, | ||
name: 'Test User', | ||
email: '[email protected]', | ||
labels: '', | ||
userId: mockUserId, | ||
leagues: [], | ||
}, | ||
], | ||
}); | ||
|
||
(databases.updateDocument as jest.Mock).mockResolvedValue({}); | ||
|
||
await updateUserEmail({ | ||
email: mockNewEmail, | ||
password: mockPassword, | ||
}); | ||
|
||
expect(account.updateEmail).toHaveBeenCalledWith( | ||
mockNewEmail, | ||
mockPassword, | ||
); | ||
|
||
expect(databases.listDocuments).toHaveBeenCalledWith( | ||
'mock-database-id', | ||
Collection.USERS, | ||
[Query.equal('userId', mockUserId)], | ||
); | ||
|
||
expect(databases.updateDocument).toHaveBeenCalledWith( | ||
'mock-database-id', | ||
Collection.USERS, | ||
mockDocumentId, | ||
{ | ||
email: mockNewEmail, | ||
name: 'Test User', | ||
labels: '', | ||
userId: mockUserId, | ||
leagues: [], | ||
}, | ||
); | ||
}); | ||
it('should throw an error if updating email fails', async () => { | ||
(account.updateEmail as jest.Mock).mockRejectedValue(new Error()); | ||
|
||
await expect( | ||
updateUserEmail({ | ||
email: mockNewEmail, | ||
password: mockPassword, | ||
}), | ||
).rejects.toThrow(); | ||
|
||
expect(account.updateEmail).toHaveBeenCalledWith( | ||
mockNewEmail, | ||
mockPassword, | ||
); | ||
|
||
expect(databases.listDocuments).not.toHaveBeenCalled(); | ||
expect(databases.updateDocument).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe('resetPassword', () => { | ||
it('should successfully reset the password', async () => { | ||
(account.updatePassword as jest.Mock).mockResolvedValue({}); | ||
|
||
await resetPassword({ | ||
newPassword: 'newPassword123', | ||
oldPassword: 'oldPassword123', | ||
}); | ||
|
||
expect(account.updatePassword).toHaveBeenCalledWith( | ||
'newPassword123', | ||
'oldPassword123', | ||
); | ||
}); | ||
}); | ||
it('should throw an error if resetting password fails', async () => { | ||
(account.updatePassword as jest.Mock).mockRejectedValue(new Error()); | ||
|
||
await expect( | ||
resetPassword({ | ||
newPassword: 'newPassword123', | ||
oldPassword: 'oldPassword123', | ||
}), | ||
).rejects.toThrow(); | ||
|
||
expect(account.updatePassword).toHaveBeenCalledWith( | ||
'newPassword123', | ||
'oldPassword123', | ||
); | ||
}); | ||
}); | ||
|
||
describe('Get Weekly Picks Mock function', () => { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import { useAuthContext } from '@/context/AuthContextProvider'; | ||
import AccountSettings from './page'; | ||
|
||
jest.mock('@/context/AuthContextProvider'); | ||
|
||
describe('Account Settings Page', () => { | ||
let mockUseAuthContext: jest.Mock; | ||
|
||
beforeEach(() => { | ||
mockUseAuthContext = jest.fn(); | ||
(useAuthContext as jest.Mock).mockImplementation(mockUseAuthContext); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should display GlobalSpinner while loading data', async () => { | ||
mockUseAuthContext.mockReturnValue({ | ||
isSignedIn: false, | ||
}); | ||
render(<AccountSettings />); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('should not show GlobalSpinner and render the settings page', async () => { | ||
mockUseAuthContext.mockReturnValue({ | ||
isSignedIn: true, | ||
}); | ||
|
||
render(<AccountSettings />); | ||
|
||
expect(screen.getByTestId('settings-page-header')).toBeInTheDocument(); | ||
expect(screen.getByTestId('email')).toBeInTheDocument(); | ||
expect(screen.getByTestId('current-password')).toBeInTheDocument(); | ||
expect(screen.getByTestId('old-password')).toBeInTheDocument(); | ||
expect(screen.getByTestId('new-password')).toBeInTheDocument(); | ||
|
||
expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright (c) Gridiron Survivor. | ||
// Licensed under the MIT License. | ||
|
||
'use client'; | ||
import GlobalSpinner from '@/components/GlobalSpinner/GlobalSpinner'; | ||
import Heading from '@/components/Heading/Heading'; | ||
import LinkCustom from '@/components/LinkCustom/LinkCustom'; | ||
import ResetPasswordForm from '@/components/RestPasswordForm/ResetPasswordForm'; | ||
import UpdateEmailForm from '@/components/UpdateEmailForm/UpdateEmailForm'; | ||
import { useAuthContext } from '@/context/AuthContextProvider'; | ||
import { ChevronLeft } from 'lucide-react'; | ||
import { JSX, useEffect, useState } from 'react'; | ||
|
||
/** | ||
* Display user preferences | ||
* @returns {JSX.Element} The rendered user preferences component. | ||
*/ | ||
const AccountSettings = (): JSX.Element => { | ||
const [loadingData, setLoadingData] = useState<boolean>(true); | ||
const { isSignedIn } = useAuthContext(); | ||
|
||
useEffect(() => { | ||
if (isSignedIn) { | ||
setLoadingData(false); | ||
} | ||
}, [isSignedIn]); | ||
|
||
return ( | ||
<> | ||
{loadingData ? ( | ||
<GlobalSpinner /> | ||
) : ( | ||
<section className="mx-auto max-w-5xl pt-10"> | ||
<header | ||
className="flex flex-col gap-4" | ||
data-testid="settings-page-header" | ||
> | ||
<div data-testid="link-to-all-leagues-page"> | ||
<LinkCustom | ||
className="no-underline hover:underline text-primary flex gap-3 items-center font-semibold text-xl" | ||
href={`/league/all`} | ||
> | ||
<ChevronLeft /> | ||
Your Leagues | ||
</LinkCustom> | ||
</div> | ||
<Heading | ||
as="h1" | ||
className="text-4xl font-bold" | ||
data-testid="entry-page-header-page-name" | ||
> | ||
Settings | ||
</Heading> | ||
</header> | ||
|
||
<div className="flex flex-col w-full pt-10 gap-8"> | ||
<UpdateEmailForm /> | ||
<ResetPasswordForm /> | ||
</div> | ||
</section> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default AccountSettings; |
Oops, something went wrong.