Skip to content

Commit

Permalink
Merge branch 'dev' into ft-product-display-##187419124
Browse files Browse the repository at this point in the history
  • Loading branch information
soleil00 committed Jun 28, 2024
2 parents 2d7dd24 + 4a4d5f0 commit b01e7f7
Show file tree
Hide file tree
Showing 12 changed files with 452 additions and 2 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ module.exports = {
"no-unsafe-optional-chaining": "off",
"react/no-unescaped-entities": "off",
'@typescript-eslint/no-explicit-any': 0,
"react/prop-types": "off",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
Expand Down
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/i,
});
const showPasswordButton = AllshowPasswordButton[0];
await act(() => {
fireEvent.click(showPasswordButton);
});
await waitFor(() => {
expect(passwordInput).toHaveAttribute("type", "text");
});
});
});
56 changes: 56 additions & 0 deletions src/__test__/updatePasswordApiSlice.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { configureStore } from "@reduxjs/toolkit";
import axios from "axios";

import updatePasswordApiSlice, {
updatePassword,
} from "../redux/api/updatePasswordApiSlice";

jest.mock("axios");

describe("updatePasswordApiSlice", () => {
let store;

beforeEach(() => {
store = configureStore({
reducer: {
updatePassword: updatePasswordApiSlice,
},
});
});

it("handles successful password update", async () => {
const mockResponse = { data: { message: "Password updated successfully" } };
// @ts-ignore
axios.put.mockResolvedValueOnce(mockResponse);

await store.dispatch(
updatePassword({
oldPassword: "Test@123",
newPassword: "NewTest@123",
confirmPassword: "NewTest@123",
}),
);

const state = store.getState();
expect(state.updatePassword.loading).toBe(false);
expect(state.updatePassword.error).toBe(null);
});

it("handles failed password update", async () => {
const mockError = { response: { data: { message: "Update failed" } } };
// @ts-ignore
axios.put.mockRejectedValueOnce(mockError);

await store.dispatch(
updatePassword({
oldPassword: "Test@123",
newPassword: "NewTest@123",
confirmPassword: "NewTest@123",
}),
);

const state = store.getState();
expect(state.updatePassword.loading).toBe(false);
expect(state.updatePassword.error).toBe(null);
});
});
7 changes: 6 additions & 1 deletion src/components/common/auth/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ interface ButtonProps {
disabled?: boolean;
dataTestId?: string;
backgroundColor?: string;
className?: string;
onClick?: () => void;
}

const Button: React.FC<ButtonProps> = ({
text,
disabled,
dataTestId,
backgroundColor,
className,
onClick,
}) => (
<button
type="submit"
className={`${backgroundColor} text-white py-3 px-12 my-4 text-normal md:text-lg rounded-sm cursor-pointer`}
className={`${backgroundColor} text-white py-3 px-12 my-4 text-normal md:text-lg rounded-sm cursor-pointer ${className}`}
disabled={disabled}
data-testid={dataTestId}
onClick={onClick}
>
{text}
</button>
Expand Down
4 changes: 3 additions & 1 deletion src/components/common/auth/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface InputFieldProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
register: UseFormRegister<any>;
error: string | undefined;
className?: string;
}

const InputField: React.FC<InputFieldProps> = ({
Expand All @@ -16,10 +17,11 @@ const InputField: React.FC<InputFieldProps> = ({
placeholder,
register,
error,
className,
}) => (
<div className="py-4">
<input
className="w-full border-b bg-transparent font-normal text-normal py-1 border-transparent border-b-gray-500 outline-none"
className={`w-full border-b bg-transparent font-normal text-normal py-1 border-transparent border-b-gray-500 outline-none ${className}`}
type={type}
placeholder={placeholder}
{...register(name)}
Expand Down
40 changes: 40 additions & 0 deletions src/components/common/auth/password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useState } from "react";
import { FieldError, UseFormRegisterReturn } from "react-hook-form";

interface PasswordInputProps {
id: string;
placeholder: string;
register: UseFormRegisterReturn;
error: FieldError | undefined;
}

const PasswordInput: React.FC<PasswordInputProps> = ({
id,
placeholder,
register,
error,
}) => {
const [showPassword, setShowPassword] = useState(false);

return (
<div className="mb-4 relative">
<input
type={showPassword ? "text" : "password"}
id={id}
className={`w-full p-2 border-b ${error ? "border-red-500" : "border-gray-300"} pr-20`}
placeholder={placeholder}
{...register}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-2 top-1/2 transform -translate-y-1/2"
>
{showPassword ? "Hide" : "Show"}
</button>
{error && <p className="text-red-500">{error.message}</p>}
</div>
);
};

export default PasswordInput;
102 changes: 102 additions & 0 deletions src/components/password/updateModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { toast, ToastContainer } from "react-toastify";
import { AxiosError } from "axios";
import { useDispatch } from "react-redux";

import updatePasswordSchema from "../../schemas/updatePasswordSchema";
import { updatePassword } from "../../redux/api/updatePasswordApiSlice";
import PasswordInput from "../common/auth/password";

interface UpdatePasswordProps {
setPasswordModal: (isOpen: boolean) => void;
}

const UpdatePasswordmod: React.FC<UpdatePasswordProps> = ({
setPasswordModal,
}) => {
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(updatePasswordSchema),
});

const onSubmit = async (data: {
oldPassword: string;
newPassword: string;
confirmPassword: string;
}) => {
try {
setLoading(true);
// @ts-ignore
const response = await dispatch(updatePassword(data)).unwrap();
setLoading(false);
toast.success(response.message);
setTimeout(() => {
setPasswordModal(false);
}, 3000);
} catch (err) {
setLoading(false);
const error = err as AxiosError;
toast.error(error.message);
}
};

return (
<div className="fixed inset-0 z-40 flex items-center justify-center bg-[#D0D0D0] bg-opacity-50">
<div className="relative bg-white rounded-lg p-10 w-[90%] md:w-[65%] lg:w-[55%] xl:w-[50%] duration-75 animate-fadeIn">
<ToastContainer />
<h2 className="text-2xl mb-4 font-bold text-[#DB4444]">
Update Password
</h2>
<form onSubmit={handleSubmit(onSubmit)}>
<PasswordInput
id="oldPassword"
placeholder="Old Password"
register={register("oldPassword")}
error={errors.oldPassword}
/>

<PasswordInput
id="newPassword"
placeholder="New Password"
register={register("newPassword")}
error={errors.newPassword}
/>

<div className="mb-4 relative">
<PasswordInput
id="confirmPassword"
placeholder="Confirm Password"
register={register("confirmPassword")}
error={errors.confirmPassword}
/>
</div>
<div className="flex justify-center gap-4">
<button
type="button"
onClick={() => setPasswordModal(false)}
className="bg-transparent text-primary border border-[#DB4444] px-4 py-2 rounded"
>
Cancel
</button>
<button
type="submit"
disabled={loading}
className="bg-[#DB4444] text-white py-3 px-4 rounded "
>
{loading ? "Loading..." : "Save Changes"}
</button>
</div>
</form>
</div>
</div>
);
};

export default UpdatePasswordmod;
Loading

0 comments on commit b01e7f7

Please sign in to comment.