Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions lib/supabase/PasswordUpdate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,23 @@ describe("PasswordUpdate", () => {
expect(container).toBeEmptyDOMElement();
});

it("renders password form when user is logged in", () => {
it("hides the password form until Set/Change is clicked", async () => {
const user = userEvent.setup();
render(
<MockSupabaseProvider user={{ email: "test@example.com" }}>
<PasswordUpdate />
</MockSupabaseProvider>,
);

expect(screen.queryByPlaceholderText("New password")).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText("Confirm new password")).not.toBeInTheDocument();

await user.click(screen.getByRole("button", { name: /set\/change password/i }));

expect(screen.getByPlaceholderText("New password")).toBeInTheDocument();
expect(screen.getByPlaceholderText("Confirm new password")).toBeInTheDocument();
expect(screen.getByRole("button", { name: /set password/i })).toBeInTheDocument();
expect(screen.getByRole("button", { name: /^set password$/i })).toBeInTheDocument();
expect(screen.getByRole("button", { name: /cancel/i })).toBeInTheDocument();
});

it("shows error when passwords do not match", async () => {
Expand All @@ -35,10 +42,32 @@ describe("PasswordUpdate", () => {
</MockSupabaseProvider>,
);

await user.click(screen.getByRole("button", { name: /set\/change password/i }));
await user.type(screen.getByPlaceholderText("New password"), "newpass123");
await user.type(screen.getByPlaceholderText("Confirm new password"), "different");
await user.click(screen.getByRole("button", { name: /set password/i }));
await user.click(screen.getByRole("button", { name: /^set password$/i }));

expect(screen.getByText(/passwords do not match/i)).toBeInTheDocument();
});

it("Cancel collapses the form and clears the fields", async () => {
const user = userEvent.setup();
render(
<MockSupabaseProvider user={{ email: "test@example.com" }}>
<PasswordUpdate />
</MockSupabaseProvider>,
);

await user.click(screen.getByRole("button", { name: /set\/change password/i }));
await user.type(screen.getByPlaceholderText("New password"), "newpass123");
await user.type(screen.getByPlaceholderText("Confirm new password"), "newpass123");
await user.click(screen.getByRole("button", { name: /cancel/i }));

expect(screen.queryByPlaceholderText("New password")).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText("Confirm new password")).not.toBeInTheDocument();

await user.click(screen.getByRole("button", { name: /set\/change password/i }));
expect(screen.getByPlaceholderText("New password")).toHaveValue("");
expect(screen.getByPlaceholderText("Confirm new password")).toHaveValue("");
});
});
60 changes: 40 additions & 20 deletions lib/supabase/PasswordUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ export function PasswordUpdate({ className }: PasswordUpdateProps) {
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isEditing, setIsEditing] = useState(false);

if (!user) return null;

function handleCancel() {
setIsEditing(false);
setPassword("");
setConfirmPassword("");
setError(null);
}

async function handleSubmit(e: FormEvent) {
e.preventDefault();
setError(null);
Expand All @@ -37,6 +45,7 @@ export function PasswordUpdate({ className }: PasswordUpdateProps) {
setPassword("");
setConfirmPassword("");
setSuccess(true);
setIsEditing(false);
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
} finally {
Expand All @@ -63,27 +72,38 @@ export function PasswordUpdate({ className }: PasswordUpdateProps) {
</Alert>
)}

<form onSubmit={handleSubmit} className="flex flex-col gap-3">
<Input
type="password"
placeholder="New password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={6}
/>
<Input
type="password"
placeholder="Confirm new password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
minLength={6}
/>
<Button type="submit" disabled={isSubmitting} variant="secondary">
{isSubmitting ? "Updating..." : "Set password"}
{!isEditing ? (
<Button type="button" variant="secondary" onClick={() => setIsEditing(true)}>
Set/Change password
</Button>
</form>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-3">
<Input
type="password"
placeholder="New password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={6}
/>
<Input
type="password"
placeholder="Confirm new password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
minLength={6}
/>
<div className="flex gap-2">
<Button type="submit" disabled={isSubmitting} variant="secondary">
{isSubmitting ? "Updating..." : "Set password"}
</Button>
<Button type="button" variant="ghost" onClick={handleCancel} disabled={isSubmitting}>
Cancel
</Button>
</div>
</form>
)}
</div>
);
}