Stop treating login/save-progress as a 48-character email limit.
The overlay was failing Zod on preset method-card support text attached with the draft; raise that cap, allow RFC-length emails, and drop Back to home from the overlay. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -43,6 +43,7 @@ vi.mock("../../app/(app)/create/utils/anonymousDraftStorage", async (importOrigi
|
||||
};
|
||||
});
|
||||
|
||||
import { EMAIL_MAX_LEN } from "../../lib/create/isValidCreateFlowSaveEmail";
|
||||
import { requestMagicLink } from "../../lib/create/api";
|
||||
import { setTransferPendingFlag } from "../../app/(app)/create/utils/anonymousDraftStorage";
|
||||
|
||||
@@ -70,7 +71,7 @@ describe("LoginForm", () => {
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("textbox", { name: /email address/i }),
|
||||
).toBeInTheDocument();
|
||||
).toHaveAttribute("maxLength", String(EMAIL_MAX_LEN));
|
||||
expect(
|
||||
screen.getByRole("button", { name: /send me a magic link/i }),
|
||||
).toBeInTheDocument();
|
||||
@@ -126,6 +127,24 @@ describe("LoginForm", () => {
|
||||
expect(screen.getByText(/we sent a sign-in link/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("submits a long email without treating length as invalid", async () => {
|
||||
const user = userEvent.setup();
|
||||
const email =
|
||||
"very.long.local-part.for.testing@subdomain.example.communityrule.org";
|
||||
vi.mocked(requestMagicLink).mockResolvedValue({ ok: true });
|
||||
renderLoginForm();
|
||||
await user.type(
|
||||
screen.getByRole("textbox", { name: /email address/i }),
|
||||
email,
|
||||
);
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: /send me a magic link/i }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(requestMagicLink).toHaveBeenCalledWith(email, "/", undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it("saveProgress variant uses magicLinkNextPath and sets transfer pending on success", async () => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(requestMagicLink).mockResolvedValue({ ok: true });
|
||||
|
||||
@@ -96,8 +96,8 @@ describe("AuthModalProvider (header overlay)", () => {
|
||||
screen.getByRole("heading", { name: /log in to communityrule/i }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("link", { name: /back to home/i }),
|
||||
).toBeInTheDocument();
|
||||
screen.queryByRole("link", { name: /back to home/i }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("closes overlay when closeLogin is called", async () => {
|
||||
|
||||
@@ -113,6 +113,63 @@ describe("POST /api/auth/magic-link/request", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts an email longer than 48 characters", async () => {
|
||||
const email =
|
||||
"very.long.local-part.for.testing@subdomain.example.communityrule.org";
|
||||
expect(email.length).toBeGreaterThan(48);
|
||||
const res = await POST(
|
||||
new NextRequest("https://x.test/api/auth/magic-link/request", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email }),
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
expect(sendMagicLinkEmailMock).toHaveBeenCalledWith(
|
||||
email,
|
||||
expect.stringContaining("/api/auth/magic-link/verify?token="),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects an email longer than 254 characters", async () => {
|
||||
const email = `${"a".repeat(243)}@example.com`;
|
||||
expect(email.length).toBeGreaterThan(254);
|
||||
const res = await POST(
|
||||
new NextRequest("https://x.test/api/auth/magic-link/request", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email }),
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(res.status).toBe(400);
|
||||
expect(createMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("accepts a draft whose method-card support text is longer than 48 characters", async () => {
|
||||
const res = await POST(
|
||||
new NextRequest("https://x.test/api/auth/magic-link/request", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
email: "a@b.c",
|
||||
draft: {
|
||||
customMethodCardMetaById: {
|
||||
"00000000-0000-4000-8000-000000000001": {
|
||||
label: "Signal",
|
||||
supportText:
|
||||
"A decision is assumed approved unless objections are raised within a specified timeframe.",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
|
||||
it("returns 502 and rolls back the token when mail fails", async () => {
|
||||
sendMagicLinkEmailMock.mockRejectedValueOnce(new Error("smtp down"));
|
||||
const res = await POST(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS } from "../../lib/create/customMethodCardWizardConstants";
|
||||
import {
|
||||
assertPlainJsonValue,
|
||||
DEFAULT_PLAIN_JSON_LIMITS,
|
||||
@@ -87,6 +88,33 @@ describe("createFlowStateSchema", () => {
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts custom method card support text longer than 48 characters", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
customMethodCardMetaById: {
|
||||
"00000000-0000-4000-8000-000000000001": {
|
||||
label: "Signal",
|
||||
supportText:
|
||||
"A decision is assumed approved unless objections are raised within a specified timeframe.",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects custom method card support text over the description max", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
customMethodCardMetaById: {
|
||||
"00000000-0000-4000-8000-000000000001": {
|
||||
label: "Signal",
|
||||
supportText: "x".repeat(
|
||||
CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS + 1,
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts communityStructureChipSnapshots with custom chip rows", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
communityStructureChipSnapshots: {
|
||||
|
||||
Reference in New Issue
Block a user