Skip to content

Commit

Permalink
Merge pull request #6 from atlp-rwanda/ft-seller-auth-2f-#187419122
Browse files Browse the repository at this point in the history
#187419122 Seller Authentication and Two factor authentication for seller
  • Loading branch information
teerenzo authored and gisubizo Jovan committed Jun 25, 2024
2 parents 88b567f + bea1c3b commit 725aa7a
Show file tree
Hide file tree
Showing 25 changed files with 2,283 additions and 844 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = {
"no-undef": "off",
"@typescript-eslint/ban-ts-comment": "off",
"react/no-unescaped-entities": "off",
"react/prop-types": "off",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ jobs:
uses: codecov/[email protected]
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Coveralls GitHub Action
uses: coverallsapp/[email protected]

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ dist-ssr

# Environment variables
.env
coverage
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The front-end of Eagle E-commerce utilizes React for a modern, user-friendly int
[![Maintainability](https://api.codeclimate.com/v1/badges/81fa30232b27b1482f4f/maintainability)](https://codeclimate.com/github/atlp-rwanda/eagles-ec-fe/maintainability)
![Github Actions](https://github.com/atlp-rwanda/eagles-ec-fe/actions/workflows/deploy.yml/badge.svg)
[![codecov](https://codecov.io/gh/atlp-rwanda/eagles-ec-fe/graph/badge.svg?token=MZAXZNVDXC)](https://codecov.io/gh/atlp-rwanda/eagles-ec-fe)
[![Coverage Status](https://coveralls.io/repos/github/atlp-rwanda/eagles-ec-fe/badge.svg?branch=dev)](https://coveralls.io/github/atlp-rwanda/eagles-ec-fe?branch=dev)

## Tech Stack

Expand Down
2,140 changes: 1,307 additions & 833 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 13 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,25 @@
"prettier": "prettier . --write"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@hookform/resolvers": "^3.6.0",
"@reduxjs/toolkit": "^2.2.5",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"axios": "^1.7.2",
"axios-mock-adapter": "^1.22.0",
"eslint-config-airbnb-typescript": "^18.0.0",
"expect-puppeteer": "^10.0.0",
"jest-environment-jsdom": "^29.7.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"jest-fetch-mock": "^3.0.3",
"jest-mock-extended": "^3.0.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.5",
"react-icons": "^5.2.1",
"react-modal": "^3.16.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
Expand All @@ -41,13 +48,16 @@
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.8",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/react-modal": "^3.16.3",
"@types/react-redux": "^7.1.33",
"@types/react-router-dom": "^5.3.3",
"@types/redux": "^3.6.0",
"@types/redux-mock-store": "^1.0.6",
"@types/redux-thunk": "^2.1.0",
"@types/testing-library__react": "^10.2.0",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react-swc": "^3.5.0",
Expand Down
30 changes: 30 additions & 0 deletions src/__test__/otpApiSclice.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { verifyOtp, otpVerificationApiSlice } from "../redux/api/otpApiSclice";

const { reducer } = otpVerificationApiSlice;
describe("otpVerification slice", () => {
it("handles pending state on verifyOtp.pending", () => {
// @ts-ignore
const initialState = reducer(undefined, { type: verifyOtp.pending });
expect(initialState.loading).toBeTruthy();
});

it("handles fulfilled state and data on verifyOtp.fulfilled", () => {
const mockData = { message: "Success" };
const initialState = reducer(undefined, {
// @ts-ignore
type: verifyOtp.fulfilled,
payload: mockData,
});
expect(initialState.success).toBeTruthy();
});

it("handles rejected state and error on verifyOtp.rejected", () => {
const error = { message: "Error" };
const initialState = reducer(undefined, {
// @ts-ignore
type: verifyOtp.rejected,
payload: error,
});
expect(initialState.error).toBe("Error");
});
});
160 changes: 160 additions & 0 deletions src/__test__/otpVerfication.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import "@testing-library/jest-dom";
import {
render, screen, fireEvent, waitFor,
} from "@testing-library/react";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import configureStore from "redux-mock-store";
import { thunk } from "redux-thunk";
import { toast } from "react-toastify";

import OtpVerificationForm from "../pages/otpVerfication";

jest.mock("react-toastify", () => ({
toast: {
// @ts-ignore
success: jest.fn(),
error: jest.fn(),
},
ToastContainer: () => <div />,
}));

jest.mock("../redux/api/otpApiSclice", () => ({
verifyOtp: jest.fn(),
}));

const middlewares = [thunk];
// @ts-ignores
const mockStore = configureStore(middlewares);
// @ts-ignore
const renderComponent = (store) => render(
<Provider store={store}>
<BrowserRouter>
<OtpVerificationForm />
</BrowserRouter>
</Provider>,
);

describe("OtpVerification", () => {
// @ts-ignore
let store;
beforeEach(() => {
store = mockStore({
otpVerification: {
loading: false,
},
});
localStorage.clear();
jest.clearAllMocks();
});

it("renders correctly", () => {
renderComponent(store);
expect(screen.getByText("verify your identity")).toBeInTheDocument();
expect(
screen.getByText(
"Protecting your account is our priority. Please confirm your identity by providing the code sent to your email address",
),
).toBeInTheDocument();
});
it("navigates to login on cancel", () => {
renderComponent(store);
fireEvent.click(screen.getByText("Cancel"));
expect(window.location.pathname).toBe("/login");
});

it("focuses on the next input on entering a digit", () => {
renderComponent(store);
const inputs = screen.getAllByRole("textbox");
inputs.forEach((input, index) => {
fireEvent.change(input, { target: { value: "1" } });
if (index < inputs.length - 1) {
expect(document.activeElement).toBe(inputs[index + 1]);
}
});
});
it("handles paste event", () => {
renderComponent(store);
const inputs = screen.getAllByRole("textbox");
fireEvent.paste(inputs[0], {
clipboardData: {
getData: () => "123456",
},
});
inputs.forEach((input, index) => {
// @ts-ignore
expect(input.value).toBe(String(index + 1));
});
});

it("handles backspace correctly", () => {
renderComponent(store);
const inputs = screen.getAllByRole("textbox");
inputs.forEach((input, index) => {
fireEvent.change(input, { target: { value: String(index + 1) } });
});
fireEvent.keyDown(inputs[5], { key: "Backspace", code: "Backspace" });
expect(document.activeElement).toBe(inputs[5]);
});

it("should handle submit with success", async () => {
const { getByText } = renderComponent(store);
const form = getByText("Ver");
fireEvent.submit(form);
expect(toast.success).toHaveBeenCalledTimes(0);
});

it("should handle submit with error", async () => {
const { getByText } = renderComponent(store);
const form = getByText("Ver");
fireEvent.submit(form);
await waitFor(() => expect(toast.error).toHaveBeenCalledTimes(0));
});

it("should handle backspace key press", () => {
const { getAllByRole } = renderComponent(store);
const inputs = getAllByRole("textbox");
fireEvent.keyDown(inputs[0], { key: "Backspace" });
// @ts-ignore
expect(inputs[0].value).toBe("");
});

it("should handle paste with same length", () => {
const { getAllByRole } = renderComponent(store);
const inputs = getAllByRole("textbox");
const pasteValue = "123456";
fireEvent.paste(inputs[0], {
clipboardData: { getData: () => pasteValue },
});
// @ts-ignore
inputs.forEach((input, index) => expect(input.value).toBe(pasteValue[index]));
});
it("should handle paste with shorter length", () => {
const { getAllByRole } = renderComponent(store);
const inputs = getAllByRole("textbox");
const pasteValue = "123";
fireEvent.paste(inputs[0], {
clipboardData: { getData: () => pasteValue },
});
inputs.forEach((input, index) => {
if (index < pasteValue.length) {
// @ts-ignore
expect(input.value).toBe(pasteValue[index]);
} else {
// @ts-ignore
expect(input.value).toBe("");
}
});
});

it("should render form elements", () => {
const { getByText } = renderComponent(store);

expect(
getByText(
"Protecting your account is our priority. Please confirm your identity by providing the code sent to your email address",
),
).toBeInTheDocument();
expect(getByText("Cancel")).toBeInTheDocument();
});
});
122 changes: 122 additions & 0 deletions src/__test__/passwordUpdate.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import "@testing-library/jest-dom";
import {
render,
screen,
fireEvent,
act,
waitFor,
} from "@testing-library/react";
import { Provider } from "react-redux";
import { BrowserRouter as Router } from "react-router-dom";
import configureStore from "redux-mock-store";
import { thunk } from "redux-thunk";
import { ToastContainer } from "react-toastify";

import UpdatePasswordmod from "../components/password/UpdateModal";
// import { updatePassword } from "../redux/api/updatePasswordApiSlice";
// import updatePasswordApiSlice from "../redux/api/updatePasswordApiSlice";

jest.mock("react-toastify", () => ({
toast: {
success: jest.fn(),
error: jest.fn(),
},
ToastContainer: () => <div />,
}));

jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useDispatch: () => jest.fn(),
}));

const middlewares = [thunk];
// @ts-ignore
const mockStore = configureStore(middlewares);
const setPasswordModal = jest.fn();
// @ts-ignore
const renderComponent = (store) => render(
<Provider store={store}>
<Router>
<UpdatePasswordmod setPasswordModal={setPasswordModal} />
<ToastContainer />
</Router>
</Provider>,
);

describe("Update Password Modal", () => {
let store;
beforeEach(() => {
store = mockStore({
updatePassword: {
loading: false,
},
});
jest.clearAllMocks();
});

it("update Password Modal renders correctly", () => {
renderComponent(store);
expect(screen.getByPlaceholderText("Old Password")).toBeInTheDocument();
expect(screen.getByPlaceholderText("New Password")).toBeInTheDocument();
expect(screen.getByPlaceholderText("Confirm Password")).toBeInTheDocument();
});

it("handles input and form submission", async () => {
// const mockUpdatePassword = jest.fn();
// (useDispatch as unknown as jest.Mock).mockReturnValue(mockUpdatePassword);
const mockDispatch = jest.fn();
jest.mock("react-redux", () => ({
useDispatch: () => mockDispatch,
}));

renderComponent(store);
const currentPasswordInput = screen.getByPlaceholderText("Old Password");
const newPasswordInput = screen.getByPlaceholderText("New Password");
const confirmNewPasswordInput = screen.getByPlaceholderText("Confirm Password");
const updateButton = screen.getByRole("button", { name: /Save Changes/i });

await act(() => {
fireEvent.change(currentPasswordInput, { target: { value: "Test@123" } });
fireEvent.change(newPasswordInput, { target: { value: "NewTest@123" } });
fireEvent.change(confirmNewPasswordInput, {
target: { value: "NewTest@123" },
});
});

await act(() => {
fireEvent.click(updateButton);
console.log("updateButton", updateButton.textContent);
});

await waitFor(() => {
expect(mockDispatch).toHaveBeenCalledTimes(0);
expect(updateButton).toHaveTextContent("Save Changes");
expect(setPasswordModal).toHaveBeenCalledTimes(0);
});
});
it("Should close the Modal on cancel", async () => {
renderComponent(store);
const cancelButton = screen.getByRole("button", { name: /Cancel/i });
await act(() => {
fireEvent.click(cancelButton);
});
await waitFor(() => {
expect(setPasswordModal).toHaveBeenCalledTimes(1);
});
});

it("Should show PassWord and hide Password", async () => {
renderComponent(store);
const passwordInput = screen.getByPlaceholderText("Old Password");
const AllshowPasswordButton = screen.getAllByRole("button", {
name: /Show Password/i,
});
const showPasswordButton = AllshowPasswordButton[0];
await act(() => {
fireEvent.click(showPasswordButton);
});
await waitFor(() => {
expect(passwordInput).toHaveAttribute("type", "text");
});
});
});
Loading

0 comments on commit 725aa7a

Please sign in to comment.