mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
UI: Login page bugs (#11520)
## Addresses #11338 - Validate emails on login page - Fix jumping error state for no email provided ("Email field must be completed") - Fix jumping error state for password field - Fix jumping error state for Forgot password > email field https://www.loom.com/share/92a238fcd2614d6e8d2655d571aa2757 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/` - [x] Added/updated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
parent
70f18dda4a
commit
6b70d11bc6
1
changes/11338-login-page-bugs
Normal file
1
changes/11338-login-page-bugs
Normal file
@ -0,0 +1 @@
|
||||
* On the login and password reset pages, added email validation and fixed some minor styling bugs.
|
@ -5,7 +5,7 @@ import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import ForgotPasswordForm from "./ForgotPasswordForm";
|
||||
|
||||
const email = "hi@thegnar.co";
|
||||
const [validEmail, invalidEmail] = ["hi@thegnar.co", "invalid-email"];
|
||||
|
||||
describe("ForgotPasswordForm - component", () => {
|
||||
const handleSubmit = jest.fn();
|
||||
@ -33,7 +33,7 @@ describe("ForgotPasswordForm - component", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should test validation for email field", async () => {
|
||||
it("correctly validates the email field", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<ForgotPasswordForm handleSubmit={handleSubmit} />
|
||||
);
|
||||
@ -43,10 +43,10 @@ describe("ForgotPasswordForm - component", () => {
|
||||
expect(emailError).toBeInTheDocument();
|
||||
expect(handleSubmit).not.toHaveBeenCalled();
|
||||
|
||||
await user.type(screen.getByPlaceholderText("Email"), "invalid-email");
|
||||
await user.type(screen.getByPlaceholderText("Email"), invalidEmail);
|
||||
await user.click(screen.getByRole("button", { name: "Get instructions" }));
|
||||
|
||||
emailError = screen.getByText("invalid-email is not a valid email");
|
||||
emailError = screen.getByText("Email must be a valid email address");
|
||||
expect(emailError).toBeInTheDocument();
|
||||
expect(handleSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
@ -56,9 +56,9 @@ describe("ForgotPasswordForm - component", () => {
|
||||
<ForgotPasswordForm handleSubmit={handleSubmit} />
|
||||
);
|
||||
|
||||
await user.type(screen.getByPlaceholderText("Email"), email);
|
||||
await user.type(screen.getByPlaceholderText("Email"), validEmail);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Get instructions" }));
|
||||
expect(handleSubmit).toHaveBeenCalledWith({ email });
|
||||
expect(handleSubmit).toHaveBeenCalledWith({ email: validEmail });
|
||||
});
|
||||
});
|
||||
|
@ -6,12 +6,10 @@ const validate = (formData) => {
|
||||
const { email } = formData;
|
||||
const errors = {};
|
||||
|
||||
if (!validEmail(email)) {
|
||||
errors.email = `${email} is not a valid email`;
|
||||
}
|
||||
|
||||
if (!validatePresence(email)) {
|
||||
errors.email = "Email field must be completed";
|
||||
} else if (!validEmail(email)) {
|
||||
errors.email = "Email must be a valid email address";
|
||||
}
|
||||
|
||||
const valid = !size(errors);
|
||||
|
@ -5,6 +5,9 @@ import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import LoginForm from "./LoginForm";
|
||||
|
||||
const [validEmail, invalidEmail] = ["hi@thegnar.co", "invalid-email"];
|
||||
const password = "p@ssw0rd";
|
||||
|
||||
describe("LoginForm - component", () => {
|
||||
const settings = { sso_enabled: false };
|
||||
const submitSpy = jest.fn();
|
||||
@ -35,7 +38,49 @@ describe("LoginForm - component", () => {
|
||||
expect(screen.getByPlaceholderText("Password")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("it does not submit the form when the form fields have not been filled out", async () => {
|
||||
it("rejects an empty or invalid email field without submitting", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<LoginForm handleSubmit={submitSpy} ssoSettings={settings} />
|
||||
);
|
||||
|
||||
// enter a valid password
|
||||
await user.type(screen.getByPlaceholderText("Password"), password);
|
||||
|
||||
// try to log in
|
||||
await user.click(screen.getByRole("button", { name: "Login" }));
|
||||
expect(
|
||||
screen.getByText("Email field must be completed")
|
||||
).toBeInTheDocument();
|
||||
expect(submitSpy).not.toHaveBeenCalled();
|
||||
|
||||
// enter an invalid email
|
||||
await user.type(screen.getByPlaceholderText("Email"), invalidEmail);
|
||||
|
||||
// try to log in again
|
||||
await user.click(screen.getByRole("button", { name: "Login" }));
|
||||
expect(
|
||||
screen.getByText("Email must be a valid email address")
|
||||
).toBeInTheDocument();
|
||||
expect(submitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects an empty password field without submitting", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<LoginForm handleSubmit={submitSpy} ssoSettings={settings} />
|
||||
);
|
||||
|
||||
await user.type(screen.getByRole("textbox", { name: "Email" }), validEmail);
|
||||
|
||||
// try to log in without entering a password
|
||||
await user.click(screen.getByRole("button", { name: "Login" }));
|
||||
|
||||
expect(
|
||||
screen.getByText("Password field must be completed")
|
||||
).toBeInTheDocument();
|
||||
expect(submitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not submit the form when both fields are empty", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<LoginForm handleSubmit={submitSpy} ssoSettings={settings} />
|
||||
);
|
||||
@ -43,26 +88,20 @@ describe("LoginForm - component", () => {
|
||||
await user.click(screen.getByRole("button", { name: "Login" }));
|
||||
|
||||
expect(submitSpy).not.toHaveBeenCalled();
|
||||
expect(
|
||||
screen.getByText("Email field must be completed")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("submits the form data when form is submitted", async () => {
|
||||
it("submits the form data when valid form data is submitted", async () => {
|
||||
const { user } = renderWithSetup(
|
||||
<LoginForm handleSubmit={submitSpy} ssoSettings={settings} />
|
||||
);
|
||||
|
||||
await user.type(
|
||||
screen.getByRole("textbox", { name: "Email" }),
|
||||
"my@email.com"
|
||||
);
|
||||
await user.type(screen.getByPlaceholderText("Password"), "p@ssw0rd");
|
||||
await user.type(screen.getByRole("textbox", { name: "Email" }), validEmail);
|
||||
await user.type(screen.getByPlaceholderText("Password"), password);
|
||||
await user.click(screen.getByRole("button", { name: "Login" }));
|
||||
|
||||
expect(submitSpy).toHaveBeenCalledWith({
|
||||
email: "my@email.com",
|
||||
password: "p@ssw0rd",
|
||||
email: validEmail,
|
||||
password,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { size } from "lodash";
|
||||
import validatePresence from "components/forms/validators/validate_presence";
|
||||
import validateEmail from "components/forms/validators/valid_email";
|
||||
|
||||
const validate = (formData) => {
|
||||
const errors = {};
|
||||
@ -7,6 +8,8 @@ const validate = (formData) => {
|
||||
|
||||
if (!validatePresence(email)) {
|
||||
errors.email = "Email field must be completed";
|
||||
} else if (!validateEmail(email)) {
|
||||
errors.email = "Email must be a valid email address";
|
||||
}
|
||||
|
||||
if (!validatePresence(password)) {
|
||||
|
@ -28,12 +28,12 @@ class InputFieldWithIcon extends InputField {
|
||||
};
|
||||
|
||||
renderHeading = () => {
|
||||
const { error, placeholder, name, label, tooltip } = this.props;
|
||||
const labelClasses = classnames(`${baseClass}__label`);
|
||||
const { error, placeholder, name, tooltip } = this.props;
|
||||
const label = this.props.label ?? placeholder;
|
||||
|
||||
if (error) {
|
||||
return <div className={`${baseClass}__errors`}>{error}</div>;
|
||||
}
|
||||
const labelClasses = classnames(`${baseClass}__label`, {
|
||||
[`${baseClass}__errors`]: !!error,
|
||||
});
|
||||
|
||||
return (
|
||||
<label
|
||||
@ -41,10 +41,12 @@ class InputFieldWithIcon extends InputField {
|
||||
className={labelClasses}
|
||||
data-has-tooltip={!!tooltip}
|
||||
>
|
||||
{tooltip ? (
|
||||
<TooltipWrapper tipContent={tooltip}>{label}</TooltipWrapper>
|
||||
{tooltip && !error ? (
|
||||
<TooltipWrapper position="top" tipContent={tooltip}>
|
||||
{label}
|
||||
</TooltipWrapper>
|
||||
) : (
|
||||
<>{label || placeholder}</>
|
||||
<>{error || label}</>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
|
@ -73,8 +73,6 @@
|
||||
}
|
||||
|
||||
&__errors {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
color: $core-vibrant-red;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user