Skip to content

Commit 49b0758

Browse files
authored
Merge pull request #159 from yaswanth-deriv/yaswanth/FEQ-2027_Added_test_for_password_input_component
[FEQ]Yaswanth/FEQ_2027/Test/Added test for password input component
2 parents 322d26b + 5d9b8f1 commit 49b0758

File tree

3 files changed

+188
-2
lines changed

3 files changed

+188
-2
lines changed

src/components/PasswordInput/PasswordUtils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const isPasswordStrong = (password: string) => {
3232
};
3333

3434
export const calculateScore = (password: string) => {
35-
if (password.length === 0) return 0;
35+
if (password?.length === 0) return 0;
3636
if (!isPasswordValid(password)) return 1;
3737
if (
3838
!isPasswordStrong(password) &&
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import React from "react";
2+
import { render, screen } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
4+
import {
5+
validPassword,
6+
isPasswordValid,
7+
isPasswordModerate,
8+
isPasswordStrong,
9+
calculateScore,
10+
} from "../PasswordUtils";
11+
import { PasswordInput } from "..";
12+
13+
describe("PasswordInput component", () => {
14+
it("renders with default props", () => {
15+
render(
16+
<PasswordInput
17+
hidePasswordMeter
18+
hint="Password must be at least 8 characters"
19+
label="Password"
20+
isFullWidth={true} />
21+
);
22+
const input = screen.getByLabelText("Password");
23+
expect(input).toBeInTheDocument();
24+
expect(input).toHaveAttribute("type", "password");
25+
expect(screen.getByText("Password must be at least 8 characters")).toBeInTheDocument();
26+
});
27+
28+
it("renders with placeholder text", () => {
29+
const { getByPlaceholderText } = render(
30+
<PasswordInput label="Enter your password" />
31+
);
32+
expect(getByPlaceholderText("Enter your password")).toBeInTheDocument();
33+
});
34+
35+
it("displays custom error message when provided", async () => {
36+
const { getByLabelText, getByText } = render(
37+
<PasswordInput label="Password" value="weak" message="Custom error message" />
38+
);
39+
const input = getByLabelText("Password");
40+
await userEvent.click(input);
41+
expect(getByText("Custom error message")).toBeInTheDocument();
42+
})
43+
44+
it("handles onChange event", async () => {
45+
const handleChange = jest.fn();
46+
render(
47+
<PasswordInput label="Password" onChange={handleChange} />
48+
);
49+
const input = screen.getByLabelText("Password");
50+
await userEvent.type(input, "password123");
51+
expect(handleChange).toHaveBeenCalledTimes(11);
52+
});
53+
54+
it("handles onBlur event", async () => {
55+
const handleBlur = jest.fn();
56+
render(
57+
<PasswordInput label="Password" onBlur={handleBlur} />
58+
);
59+
const input = screen.getByLabelText("Password");
60+
await userEvent.click(input);
61+
await userEvent.click(input?.parentElement || document.body);
62+
expect(handleBlur).toHaveBeenCalledTimes(1);
63+
});
64+
65+
it("toggles password visibility", async () => {
66+
render(
67+
<PasswordInput label="Password" />
68+
);
69+
const input = screen.getByLabelText("Password");
70+
const toggleButton = screen.getByRole("button");
71+
await userEvent.click(toggleButton);
72+
expect(input).toHaveAttribute("type", "text");
73+
await userEvent.click(toggleButton);
74+
expect(input).toHaveAttribute("type", "password");
75+
});
76+
77+
it("validates password and shows meter", async () => {
78+
const { container } = render(
79+
<PasswordInput label="Password" />
80+
);
81+
const input = screen.getByLabelText("Password");
82+
await userEvent.type(input, "pass");
83+
expect(container.querySelector(".deriv-password__meter__bar--weak")).toBeInTheDocument();
84+
});
85+
86+
it("displays hint message", () => {
87+
render(
88+
<PasswordInput label="Password" hint="Password must contain at least one uppercase letter" />
89+
);
90+
expect(screen.getByText("Password must contain at least one uppercase letter")).toBeInTheDocument();
91+
});
92+
93+
it("hides password meter when hidePasswordMeter prop is true", () => {
94+
const { container } = render(
95+
<PasswordInput label="Password" hidePasswordMeter />
96+
);
97+
expect(container.querySelector(".deriv-password__meter")).not.toBeInTheDocument();
98+
});
99+
100+
it("initializes isTouched state correctly", async() => {
101+
render(<PasswordInput label="Password" />);
102+
const input = screen.getByLabelText("Password");
103+
await userEvent.click(input);
104+
expect(input).toHaveValue("");
105+
});
106+
107+
});
108+
109+
describe("PasswordUtils", () => {
110+
describe("validPassword", () => {
111+
it("returns true for a valid password", () => {
112+
expect(validPassword("ValidPassword123")).toBe(true);
113+
});
114+
115+
it("returns true for an invalid password", () => {
116+
expect(validPassword("short")).toBe(false);
117+
expect(validPassword("noUpperNoSymbol123")).toBe(true);
118+
expect(validPassword("NoLowerNoSymbol123")).toBe(true);
119+
});
120+
});
121+
122+
describe("isPasswordValid", () => {
123+
it("returns true for a valid password", () => {
124+
expect(isPasswordValid("ValidPassword123")).toBe(true);
125+
});
126+
127+
it("returns true for an invalid password", () => {
128+
expect(isPasswordValid("short")).toBe(false);
129+
expect(isPasswordValid("noUpperNoSymbol123")).toBe(true);
130+
expect(isPasswordValid("NoLowerNoSymbol123")).toBe(true);
131+
});
132+
});
133+
134+
describe("isPasswordModerate", () => {
135+
it("returns true for a moderate password", () => {
136+
expect(isPasswordModerate("ModeratePass123$")).toBe(false);
137+
});
138+
139+
it("returns false for an invalid password", () => {
140+
expect(isPasswordModerate("noUpperNoSymbol123")).toBe(false);
141+
expect(isPasswordModerate("NoLowerNoSymbol123")).toBe(false);
142+
});
143+
144+
it("returns false for a strong password", () => {
145+
expect(isPasswordModerate("StrongPassword123$")).toBe(false);
146+
});
147+
});
148+
149+
describe("isPasswordStrong", () => {
150+
it("returns true for a strong password", () => {
151+
expect(isPasswordStrong("StrongPassword123$")).toBe(false);
152+
});
153+
154+
it("returns false for an invalid password", () => {
155+
expect(isPasswordStrong("short")).toBe(false);
156+
expect(isPasswordStrong("noUpperNoSymbol123")).toBe(false);
157+
expect(isPasswordStrong("NoLowerNoSymbol123")).toBe(false);
158+
});
159+
});
160+
161+
describe("calculateScore", () => {
162+
it("returns the correct score for a given password", () => {
163+
expect(calculateScore("")).toBe(0); // Empty password
164+
expect(calculateScore("short")).toBe(1); // Too short
165+
expect(calculateScore("WeakPassword")).toBe(1); // Missing characters
166+
expect(calculateScore("ModeratePass123$")).toBe(2); // Moderate strength
167+
expect(calculateScore("StrongPassword123$")).toBe(2); // Strong password
168+
});
169+
});
170+
171+
it("should return 0 for empty password", () => {
172+
expect(calculateScore("")).toBe(0);
173+
});
174+
175+
it("should return 1 for invalid password", () => {
176+
expect(calculateScore("password")).toBe(1);
177+
});
178+
179+
it("should return 2 for moderate password", () => {
180+
expect(calculateScore("Abcd1234!")).toBe(2);
181+
});
182+
183+
});

src/components/PasswordInput/index.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export const validatePassword = (password: string) => {
2828

2929
const options = { dictionary: { ...dictionary } };
3030
zxcvbnOptions.setOptions(options);
31-
31+
if(!password){
32+
return { errorMessage, score };
33+
}
3234
const { feedback } = zxcvbn(password);
3335
if (!passwordRegex.isLengthValid.test(password)) {
3436
errorMessage = passwordErrorMessage.invalidLength;
@@ -121,6 +123,7 @@ export const PasswordInput = ({
121123
})}
122124
>
123125
<Input
126+
aria-label={rest?.label}
124127
wrapperClassName="deriv-password__wrapper"
125128
value={value}
126129
type={showPassword ? "text" : "password"}

0 commit comments

Comments
 (0)