Skip to content

Commit

Permalink
feat(Two Factor Authentication):Seller authentication and OTP verific…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
emmanueltct committed Jun 6, 2024
1 parent c3ce05a commit 66fa32e
Show file tree
Hide file tree
Showing 19 changed files with 566 additions and 61 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_URL=https://champs-ec-be.onrender.com/api
33 changes: 33 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"react-dom": "^18",
"react-hook-form": "^7.51.5",
"react-redux": "^9.1.2",
"redux-mock-store": "^1.5.4",
"zod": "^3.23.8"
},
"devDependencies": {
Expand All @@ -48,6 +49,7 @@
"@types/node": "^20.12.13",
"@types/react": "^18.3.3",
"@types/react-dom": "^18",
"@types/redux-mock-store": "^1.0.6",
"babel-jest": "^29.7.0",
"eslint": "^8",
"eslint-config-next": "14.2.3",
Expand Down
Binary file added public/otp_removebg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
150 changes: 150 additions & 0 deletions src/__tests__/2FA_Authentication.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

import React,{ act } from 'react';
import { render, screen} from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { Provider } from 'react-redux';
//import configureStore from 'redux-mock-store'; // Assuming you are using Redux
import OtpVerify from '../components/2faVerification'
import userEvent from '@testing-library/user-event'
import { handleOTPVerification, resendOTPCode, ISELLEROTP } from '../redux/slices/2faAuthenticationSlice'; // replace with your actual slice file
import { configureStore } from '@reduxjs/toolkit';
import twoFactorAuthSlice from "../redux/slices/2faAuthenticationSlice";
import axios from 'axios';


jest.mock('axios');
const URL = process.env.NEXT_PUBLIC_URL
const store:any = configureStore({
reducer: {
sellerOTP:twoFactorAuthSlice
}
});


type RootState = ReturnType<typeof store.getState>;
type AppDispatch = typeof store.dispatch;


jest.mock("next/navigation", () => ({
useRouter() {
return {
prefetch: () => null
};
}
}));


let mockedAxios:any;

beforeEach(() => {

mockedAxios = new MockAdapter(axios);
localStorage.clear();
localStorage.setItem('email', '[email protected]');
localStorage.setItem('password', 'Password123!');
});

describe("Login Tests", () => {
it('renders OtpVerify component and shows dialog when isOpen is true', () => {
//const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: document.createElement('dialog') });
const{getByText}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);
const dialog = screen.getByTestId('divSection');
expect(dialog).toHaveAttribute;
});


// it('should handle OTP verification fulfilled action', async () => {
// const {getByTestId}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);

// const otpToken = 'token-login';
// const result= mockedAxios.onPost(`${URL}/users/otp/${otpToken}`).reply(201, {token:otpToken })
// await act(async () => {
// await store.dispatch(handleOTPVerification('12345'));
// await store.dispatch({
// type: handleOTPVerification.fulfilled.type,
// payload: otpToken,
// })
// });
// const state:RootState= store.getState();
// console.log('state', state);
// expect(state.sellerOTP.loading).toBe(false);
// expect(state.sellerOTP.newOtp).toBe(false);
// expect(localStorage.getItem('token')).toBe(otpToken);
// });


it('should handle OTP verification pending action', async () => {
const {getByTestId}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);
const otpToken = 'token-login';
const result= mockedAxios.onPost(`${URL}/users/otp/${otpToken}`).reply(201, {token:otpToken })
await act(async () => {
await store.dispatch(handleOTPVerification('12345'));
await store.dispatch({
type: handleOTPVerification.pending.type,
payload: otpToken,
})
});
const state:RootState= store.getState();
expect(state.sellerOTP.loading).toBe(true);
expect(localStorage.getItem('token')).toBe(null);
});

it('should handle OTP verification rejected action', async () => {
const {getByTestId}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);

const otpToken = 'token-login';
const result= mockedAxios.onPost(`${URL}/users/otp/${otpToken}`).reply(201, {token:otpToken })
await act(async () => {
await store.dispatch(handleOTPVerification('12345'));
await store.dispatch({
type: handleOTPVerification.rejected.type,
payload: otpToken,
})
});
const state:RootState= store.getState();
expect(state.sellerOTP.loading).toBe(false);
expect(state.sellerOTP.error).toContain(otpToken);
expect(localStorage.getItem('token')).toBe(null);
});



it('should test resend OTP verification pending action', async () => {
const {getByTestId}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);

const otpToken = 'token-login';
const result= mockedAxios.onPost(`${URL}/users/login`).reply(200, {token:otpToken })
await act(async () => {
await store.dispatch(resendOTPCode('12345'));
await store.dispatch({
type: resendOTPCode.pending.type,
payload: otpToken,
})
});
const state= store.getState();
expect(state.sellerOTP.loading).toBe(true);
expect(state.sellerOTP.newOtp).toBe(true);

});

it('should test resend OTP verification fulfilled action', async () => {
const {getByTestId}=render(<Provider store={store}><OtpVerify isOpen={true} /></Provider>);

const otpToken = 'token-login';
const result= mockedAxios.onPost(`${URL}/users/login`).reply(200, {token:otpToken })
await act(async () => {
await store.dispatch(resendOTPCode('12345'));
await store.dispatch({
type: resendOTPCode.fulfilled.type,
payload: otpToken,
})
});
const state= store.getState();
expect(state.sellerOTP.loading).toBe(false);
expect(state.sellerOTP.newOtp).toBe(false);

});



})
4 changes: 2 additions & 2 deletions src/__tests__/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ describe('Signup Components', () => {
topText="success"
bodyText="its work"
data-testid="result"
iconImagelink="/Verified.png"
/>,
iconImagelink="/Verified.png"
testid={''} />,
);
expect(() => getByTestId('result')).toThrow();
});
Expand Down
115 changes: 64 additions & 51 deletions src/__tests__/login.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import '@testing-library/jest-dom';
import Login from '@/app/auth/login/page';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import userEvent from '@testing-library/user-event';
import OtpVerify from '../components/2faVerification'
import userEvent from '@testing-library/user-event'
import { Provider } from 'react-redux'
import { store } from '@/redux/store';


jest.mock('next/navigation', () => ({
useRouter() {
Expand All @@ -14,62 +18,71 @@ jest.mock('next/navigation', () => ({
},
}));

const mockedAxios = new MockAdapter(axios);

describe('Login Tests', () => {
beforeEach(() => {
mockedAxios.reset();
});
const URL = process.env.URL;

it('Test should view the input button', async () => {
const { getByText, getByPlaceholderText } = render(<Login />);
const button = getByText('Log in');
await userEvent.click(button);
expect(screen.queryByText('Email is required')).toBeInTheDocument();
expect(screen.queryByText('Password is required')).toBeInTheDocument();
expect(getByPlaceholderText('Enter Email')).toBeInTheDocument();
expect(getByPlaceholderText('Enter Password')).toBeInTheDocument();
});
const mockedAxios = new MockAdapter(axios)
const renderLogin=<Provider store={store}><Login /></Provider>
describe("Login Tests", () => {
beforeEach(() => {
mockedAxios.reset()
})
const URL = process.env.NEXT_PUBLIC_URL

it("Test should view the input button", async () => {
const { getByText, getByPlaceholderText } = render(renderLogin)
const button = getByText("Log in")
await userEvent.click(button)
expect(screen.queryByText('Email is required')).toBeInTheDocument()
expect(screen.queryByText('Password is required')).toBeInTheDocument()
expect(getByPlaceholderText("Enter Email")).toBeInTheDocument()
expect(getByPlaceholderText("Enter Password")).toBeInTheDocument()
})

it("Test Pass login", async () => {
const { getByPlaceholderText, getByText } = render(renderLogin)
const token = 'token-login';
mockedAxios.onPost(`${URL}/users/login`).reply(200, { token: token })
await act(() => {
fireEvent.change(getByPlaceholderText('Enter Email'), { target: { value: '[email protected]' } })
fireEvent.change(getByPlaceholderText('Enter Password'), { target: { value: 'User@12345' } })
})
const button = getByText("Log in")
await userEvent.click(button)
expect(localStorage.getItem('token')).toBe(token);
})

it("Test Pass login seller ", async () => {
const { getByPlaceholderText, findByText, getByText } = render(renderLogin)
mockedAxios.onPost(`${URL}/users/login`).reply(201, { message: "THIS IS A SELLER" })
await act(() => {
fireEvent.change(getByPlaceholderText('Enter Email'), { target: { value: '[email protected]' } })
fireEvent.change(getByPlaceholderText('Enter Password'), { target: { value: 'User@12345' } })
})
const button = getByText("Log in")
await userEvent.click(button)
expect(localStorage.getItem('email')).toBe('[email protected]');



})

// it("Test Failed login", async () => {
// const { findByText, getByPlaceholderText, getByText } = render(renderLogin)
// mockedAxios.onPost(`${URL}/users/login`).reply(400, { message: 'Invalid username' })
// await act(() => {
// fireEvent.change(getByPlaceholderText('Enter Email'), { target: { value: '[email protected]' } })
// fireEvent.change(getByPlaceholderText('Enter Password'), { target: { value: 'User@12345' } })
// })
// const button = getByText("Log in")
// await userEvent.click(button)
// const errorMessage = await findByText('Invalid username');
// expect(errorMessage).toBeInTheDocument();
// })

it('Test Pass login', async () => {
const { getByPlaceholderText, getByText } = render(<Login />);
const token = 'token-login';
mockedAxios.onPost(`${URL}/users/login`).reply(200, { token: token });
await act(() => {
fireEvent.change(getByPlaceholderText('Enter Email'), {
target: { value: '[email protected]' },
});
fireEvent.change(getByPlaceholderText('Enter Password'), {
target: { value: 'User@12345' },
});
});
const button = getByText('Log in');
await userEvent.click(button);
expect(localStorage.getItem('token')).toBe(token);
});

it('Test Pass login seller ', async () => {
const { getByPlaceholderText, findByText, getByText } = render(<Login />);
mockedAxios
.onPost(`${URL}/users/login`)
.reply(201, { message: 'THIS IS A SELLER' });
await act(() => {
fireEvent.change(getByPlaceholderText('Enter Email'), {
target: { value: '[email protected]' },
});
fireEvent.change(getByPlaceholderText('Enter Password'), {
target: { value: 'User@12345' },
});
});
const button = getByText('Log in');
await userEvent.click(button);
const errorMessage = await findByText('THIS IS A SELLER');
expect(errorMessage).toBeInTheDocument();
});

it('Test Failed login', async () => {
const { findByText, getByPlaceholderText, getByText } = render(<Login />);
const { findByText, getByPlaceholderText, getByText } = render(renderLogin);
mockedAxios
.onPost(`${URL}/users/login`)
.reply(400, { message: 'Invalid username' });
Expand Down
Loading

0 comments on commit 66fa32e

Please sign in to comment.