-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement HSButton implement HSInput mend implement login design resolve eslint errors rebase from develop fix hovering styles button rebase form develop set up formik implement stage 1 of valifation usin formik Reducing boilerplate reduce duplicate codes rebase from develop complete form validatio remove eslint error & initials unit tests abort all written tests working on lints update eslint file fix test errors rebase from develop implement Login component resolve mismatch uri
- Loading branch information
1 parent
4c0c5b6
commit b9b7d7f
Showing
15 changed files
with
1,698 additions
and
791 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
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 |
---|---|---|
|
@@ -30,3 +30,5 @@ jobs: | |
uses: codecov/[email protected] | ||
with: | ||
token: ${{ secrets.CODECOV_TOKEN }} | ||
env: | ||
VITE_BASE_URL: ${{ secrets.VITE_BASE_URL }} |
Large diffs are not rendered by default.
Oops, something went wrong.
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,207 @@ | ||
import { | ||
render, | ||
screen, | ||
fireEvent, | ||
waitFor, | ||
cleanup, | ||
} from '@testing-library/react'; | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { configureStore } from '@reduxjs/toolkit'; | ||
import { Provider } from 'react-redux'; | ||
import { MemoryRouter, Routes, Route } from 'react-router-dom'; | ||
import signInReducer, { loginUser, logout } from '@/features/Auth/SignInSlice'; | ||
import SignIn from '@/pages/SignIn'; | ||
|
||
const createTestStore = () => | ||
configureStore({ reducer: { signIn: signInReducer } }); | ||
let store: any; | ||
|
||
const renderSignIn = () => { | ||
render( | ||
<Provider store={store}> | ||
<MemoryRouter initialEntries={['/signin']}> | ||
<Routes> | ||
<Route path="/signin" element={<SignIn />} /> | ||
</Routes> | ||
</MemoryRouter> | ||
</Provider> | ||
); | ||
}; | ||
|
||
describe('signInSlice', () => { | ||
beforeEach(() => { | ||
store = createTestStore(); | ||
}); | ||
|
||
vi.mock('jwt-decode', () => ({ | ||
jwtDecode: () => ({ | ||
user: { | ||
userType: { | ||
name: 'Admin', | ||
}, | ||
}, | ||
}), | ||
})); | ||
|
||
it('should handle initial state', () => { | ||
const { signIn } = store.getState(); | ||
expect(signIn).toEqual({ | ||
token: null, | ||
loading: false, | ||
error: null, | ||
message: null, | ||
role: null, | ||
needsVerification: false, | ||
needs2FA: false, | ||
}); | ||
}); | ||
|
||
it('should handle loginUser.pending', () => { | ||
const action = { type: loginUser.pending.type }; | ||
const state = signInReducer(undefined, action); | ||
expect(state).toEqual({ | ||
token: null, | ||
loading: true, | ||
error: null, | ||
message: null, | ||
role: null, | ||
needsVerification: false, | ||
needs2FA: false, | ||
}); | ||
}); | ||
|
||
it('should handle loginUser.fulfilled', () => { | ||
const action = { | ||
type: loginUser.fulfilled.type, | ||
payload: { token: 'testToken', message: 'Login successful' }, | ||
}; | ||
const state = signInReducer(undefined, action); | ||
expect(state).toEqual({ | ||
token: 'testToken', | ||
loading: false, | ||
error: null, | ||
message: 'Login successful', | ||
role: 'Admin', | ||
needsVerification: false, | ||
needs2FA: false, | ||
}); | ||
}); | ||
|
||
it('should handle loginUser.rejected', () => { | ||
const action = { | ||
type: loginUser.rejected.type, | ||
payload: { message: 'Login failed' }, | ||
}; | ||
const state = signInReducer(undefined, action); | ||
expect(state).toEqual({ | ||
token: null, | ||
loading: false, | ||
error: 'Login failed', | ||
message: null, | ||
role: null, | ||
needsVerification: false, | ||
needs2FA: false, | ||
}); | ||
}); | ||
|
||
it('should handle logout', () => { | ||
const initialState = { | ||
token: 'testToken', | ||
loading: false, | ||
error: null, | ||
message: 'Logout Successfully', | ||
role: 'Admin', | ||
needsVerification: false, | ||
needs2FA: false, | ||
}; | ||
const action = { type: logout.type }; | ||
const state = signInReducer(initialState, action); | ||
expect(state).toEqual({ | ||
token: null, | ||
loading: false, | ||
error: null, | ||
message: 'Logout Successfully', | ||
role: null, | ||
needsVerification: false, | ||
needs2FA: false, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('SignIn Component', () => { | ||
beforeEach(() => { | ||
store = createTestStore(); | ||
renderSignIn(); | ||
}); | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
}); | ||
|
||
it('renders SignIn Title', () => { | ||
expect(screen.getByTestId('title')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders Email Input Field', () => { | ||
expect(screen.getByPlaceholderText('Enter your email')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders Password Input Field', () => { | ||
expect( | ||
screen.getByPlaceholderText('Enter your password') | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders the form and allows user to fill out and submit', async () => { | ||
const emailInput = screen.getByPlaceholderText('Enter your email'); | ||
const passwordInput = screen.getByPlaceholderText('Enter your password'); | ||
const submitButton = screen.getByText(/Sign In/); | ||
|
||
fireEvent.change(emailInput, { target: { value: '[email protected]' } }); | ||
fireEvent.change(passwordInput, { target: { value: 'password123' } }); | ||
fireEvent.click(submitButton); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByTestId('Loading')).toBeInTheDocument(); | ||
}); | ||
}); | ||
it('displays error messages for invalid input', async () => { | ||
const emailInput = screen.getByPlaceholderText('Enter your email'); | ||
const passwordInput = screen.getByPlaceholderText('Enter your password'); | ||
const submitButton = screen.getByText(/Sign In/); | ||
|
||
fireEvent.change(emailInput, { target: { value: 'invalid-email' } }); | ||
fireEvent.change(passwordInput, { target: { value: '123' } }); | ||
fireEvent.click(submitButton); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText(/Invalid email format/i)).toBeInTheDocument(); | ||
expect( | ||
screen.getByText(/password must contain at least 6 chars/i) | ||
).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('does not submit the form with incomplete user details', async () => { | ||
const submitButton = screen.getByText(/Sign In/); | ||
fireEvent.click(submitButton); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText(/email is required/i)).toBeInTheDocument(); | ||
expect(screen.getByText(/password is required/i)).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('submits the form successfully', async () => { | ||
const emailInput = screen.getByPlaceholderText('Enter your email'); | ||
const passwordInput = screen.getByPlaceholderText('Enter your password'); | ||
|
||
fireEvent.change(emailInput, { target: { value: '[email protected]' } }); | ||
fireEvent.change(passwordInput, { target: { value: 'password123' } }); | ||
fireEvent.submit(screen.getByTestId('form')); | ||
|
||
await waitFor(() => { | ||
expect(screen.queryByText(/Loading.../i)).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
Empty file.
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,37 @@ | ||
import { Link } from 'react-router-dom'; | ||
|
||
interface MyButtonProps { | ||
path?: string; | ||
title: string | JSX.Element; | ||
styles?: string; | ||
onClick?: () => void; | ||
icon?: JSX.Element; | ||
target?: '_blank' | '_self' | '_parent' | '_top'; | ||
onChange?: React.ChangeEventHandler<HTMLAnchorElement>; | ||
} | ||
|
||
function HSButton({ | ||
path, | ||
onClick, | ||
title, | ||
icon, | ||
styles, | ||
target, | ||
onChange, | ||
}: MyButtonProps) { | ||
return ( | ||
<Link | ||
target={target} | ||
type="submit" | ||
onChange={onChange} | ||
rel="noopener noreferrer" | ||
to={path!} | ||
onClick={onClick} | ||
className={`${styles} bg-primary text-white px-6 py-3 rounded-md flex justify-center items-center gap-2 text-sm active:scale-[.98] active:duration-75 hover:scale-[1.01] ease-in transition-all`} | ||
> | ||
{title} {icon} | ||
</Link> | ||
); | ||
} | ||
|
||
export default HSButton; |
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,72 @@ | ||
interface MyInputProps { | ||
id?: string; | ||
name?: string; | ||
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void; | ||
onBlurTextArea?: (event: React.FocusEvent<HTMLTextAreaElement>) => void; | ||
values?: string | number; | ||
style?: string; | ||
label?: string; | ||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; | ||
onChangeTextArea?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void; | ||
placeholder: string; | ||
type?: string; | ||
text?: string; | ||
icon?: JSX.Element; | ||
} | ||
|
||
function HSInput({ | ||
id, | ||
name, | ||
onBlur, | ||
onBlurTextArea, | ||
values, | ||
style, | ||
label, | ||
onChange, | ||
onChangeTextArea, | ||
placeholder, | ||
type, | ||
text, | ||
icon, | ||
}: MyInputProps) { | ||
return ( | ||
<div className="flex flex-col gap-2 w-full group"> | ||
<label htmlFor={id} className="text-md font-medium"> | ||
{label} | ||
</label> | ||
{type === 'input' ? ( | ||
<div | ||
className={`${style} relative bg-grayLight text-black duration-100 outline-none justify-between flex items-center gap-2 px-3 w-full rounded-md font-light group-hover:border-grayDark`} | ||
> | ||
{icon && <p>{icon}</p>} | ||
<input | ||
type={text} | ||
value={values} | ||
onBlur={onBlur} | ||
id={id} | ||
name={name} | ||
onChange={onChange} | ||
placeholder={placeholder} | ||
className="w-full h-full bg-transparent py-3 outline-none" | ||
/> | ||
</div> | ||
) : ( | ||
<textarea | ||
id={id} | ||
name={name} | ||
onBlur={onBlurTextArea} | ||
cols={30} | ||
rows={10} | ||
placeholder={placeholder} | ||
onChange={onChangeTextArea} | ||
value={values} | ||
className="text-black text-xs md:text-sm duration-150 w-full outline-none rounded-md border-[1px] group-hover:border-grayDark px-5 py-3" | ||
> | ||
{values} | ||
</textarea> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export default HSInput; |
Oops, something went wrong.