mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 23:43:13 +00:00
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { useState } from "react";
|
|
import { render, screen, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { PasswordChecklist } from "@/components/auth/password-checklist";
|
|
|
|
function ChecklistHarness() {
|
|
const [password, setPassword] = useState("");
|
|
return (
|
|
<>
|
|
<label htmlFor="pw">Password</label>
|
|
<input id="pw" value={password} onChange={(event) => setPassword(event.target.value)} />
|
|
<PasswordChecklist password={password} />
|
|
</>
|
|
);
|
|
}
|
|
|
|
function ruleStates(): Record<string, string | null> {
|
|
const list = screen.getByRole("list", { name: "Password requirements" });
|
|
return Object.fromEntries(
|
|
within(list)
|
|
.getAllByRole("listitem")
|
|
.map((item) => [item.getAttribute("data-rule") ?? "", item.getAttribute("data-met")]),
|
|
);
|
|
}
|
|
|
|
describe("PasswordChecklist", () => {
|
|
it("starts with every rule unmet", () => {
|
|
render(<ChecklistHarness />);
|
|
|
|
expect(ruleStates()).toEqual({
|
|
length: "false",
|
|
uppercase: "false",
|
|
number: "false",
|
|
special: "false",
|
|
});
|
|
expect(screen.getAllByText("requirement not met")).toHaveLength(4);
|
|
});
|
|
|
|
it("marks each rule met live as the user types", async () => {
|
|
const user = userEvent.setup();
|
|
render(<ChecklistHarness />);
|
|
const input = screen.getByLabelText("Password");
|
|
|
|
await user.type(input, "A");
|
|
expect(ruleStates()).toMatchObject({ uppercase: "true", number: "false", special: "false" });
|
|
|
|
await user.type(input, "1");
|
|
expect(ruleStates()).toMatchObject({ uppercase: "true", number: "true", special: "false" });
|
|
|
|
await user.type(input, "!");
|
|
expect(ruleStates()).toMatchObject({ special: "true", length: "false" });
|
|
|
|
await user.type(input, "xyz");
|
|
expect(ruleStates()).toEqual({
|
|
length: "true",
|
|
uppercase: "true",
|
|
number: "true",
|
|
special: "true",
|
|
});
|
|
expect(screen.getAllByText("requirement met")).toHaveLength(4);
|
|
});
|
|
|
|
it("announces only the rules whose state changed through a polite status region", async () => {
|
|
const user = userEvent.setup();
|
|
render(<ChecklistHarness />);
|
|
const input = screen.getByLabelText("Password");
|
|
const status = screen.getByRole("status");
|
|
|
|
expect(status).toHaveAttribute("aria-live", "polite");
|
|
expect(status).toHaveTextContent(/^$/);
|
|
|
|
await user.type(input, "A");
|
|
expect(status).toHaveTextContent(/^One uppercase letter: requirement met$/);
|
|
|
|
await user.type(input, "b");
|
|
expect(status).toHaveTextContent(/^One uppercase letter: requirement met$/);
|
|
|
|
await user.type(input, "1!xy");
|
|
expect(status).toHaveTextContent(/^At least 6 characters: requirement met$/);
|
|
|
|
await user.clear(input);
|
|
expect(status).toHaveTextContent(
|
|
"At least 6 characters: requirement not met. One uppercase letter: requirement not met. One number: requirement not met. One special character: requirement not met",
|
|
);
|
|
});
|
|
|
|
it("marks a rule unmet again when the user deletes the character that satisfied it", async () => {
|
|
const user = userEvent.setup();
|
|
render(<ChecklistHarness />);
|
|
const input = screen.getByLabelText("Password");
|
|
|
|
await user.type(input, "Abcde1!");
|
|
await user.type(input, "{Backspace}");
|
|
|
|
expect(ruleStates()).toMatchObject({ special: "false", length: "true", number: "true" });
|
|
});
|
|
});
|