Invite people after review, then finish the Rule, with login before inviting and Invite your members on Profile.
This commit is contained in:
1 parent
88bfccd1e7
commit
70ceb73104
31 files changed
+1388
-401
No files matched your search
@@ -183,7 +183,7 @@ describe("flowSteps", () => {
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("parseReviewReturnSearchParam accepts final-review, edit-rule, and review", () => {
|
||||
it("parseReviewReturnSearchParam accepts final-review, edit-rule, review, and completed", () => {
|
||||
expect(
|
||||
parseReviewReturnSearchParam(
|
||||
new URLSearchParams("reviewReturn=final-review"),
|
||||
@@ -197,6 +197,11 @@ describe("flowSteps", () => {
|
||||
expect(
|
||||
parseReviewReturnSearchParam(new URLSearchParams("reviewReturn=review")),
|
||||
).toBe("review");
|
||||
expect(
|
||||
parseReviewReturnSearchParam(
|
||||
new URLSearchParams("reviewReturn=completed"),
|
||||
),
|
||||
).toBe("completed");
|
||||
expect(
|
||||
parseReviewReturnSearchParam(new URLSearchParams("reviewReturn=nope")),
|
||||
).toBeNull();
|
||||
|
||||
@@ -306,6 +306,71 @@ describe("useCreateFlowFinalize", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("omits stakeholder emails when skipInvites is set", async () => {
|
||||
vi.mocked(publishRule).mockResolvedValue({
|
||||
ok: true,
|
||||
id: "new-rule-id",
|
||||
title: "Published title",
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useCreateFlowFinalize({
|
||||
state: {
|
||||
...emptyState,
|
||||
stakeholderEmails: ["invitee@example.com"],
|
||||
},
|
||||
router,
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath: "/create/final-review",
|
||||
sessionUser: signedIn,
|
||||
}),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.finalize({ skipInvites: true });
|
||||
});
|
||||
|
||||
expect(publishRule).toHaveBeenCalledWith({
|
||||
title: "Published title",
|
||||
summary: "Published summary",
|
||||
document: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the send-failed banner when invitations do not send", async () => {
|
||||
vi.mocked(publishRule).mockResolvedValue({
|
||||
ok: false,
|
||||
status: 500,
|
||||
error: "mail failed",
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useCreateFlowFinalize({
|
||||
state: {
|
||||
...emptyState,
|
||||
stakeholderEmails: ["invitee@example.com"],
|
||||
},
|
||||
router,
|
||||
openLogin,
|
||||
updateState,
|
||||
loginReturnPath: "/create/final-review",
|
||||
sessionUser: signedIn,
|
||||
}),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.finalize({ skipInvites: false });
|
||||
});
|
||||
|
||||
expect(result.current.publishBannerTitle).toBe(
|
||||
messages.create.reviewAndComplete.confirmStakeholders.sendFailedTitle,
|
||||
);
|
||||
expect(result.current.publishBannerMessage).toBe(
|
||||
messages.create.reviewAndComplete.confirmStakeholders.sendFailedDescription,
|
||||
);
|
||||
});
|
||||
|
||||
it("routes to /create/completed without celebrate after PATCH update", async () => {
|
||||
vi.mocked(updatePublishedRule).mockResolvedValue({ ok: true });
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { membershipInvitePreview } from "../../lib/create/membershipInvitePreview";
|
||||
import type { CreateFlowState } from "../../app/(app)/create/types";
|
||||
|
||||
describe("membershipInvitePreview", () => {
|
||||
it("returns null when no membership method is selected", () => {
|
||||
expect(membershipInvitePreview({} as CreateFlowState)).toBeNull();
|
||||
});
|
||||
|
||||
it("uses the first selected membership method and saved words", () => {
|
||||
const preview = membershipInvitePreview({
|
||||
selectedMembershipMethodIds: ["open-admission"],
|
||||
membershipMethodDetailsById: {
|
||||
"open-admission": {
|
||||
eligibility: "Anyone who shares the work belongs.",
|
||||
joiningProcess: "Work two Sunday shares, then ask to join.",
|
||||
expectations: "",
|
||||
},
|
||||
},
|
||||
} as CreateFlowState);
|
||||
expect(preview?.title).toBe("Open admission");
|
||||
expect(preview?.categoryName).toBe("Membership");
|
||||
expect(preview?.eligibility).toBe("Anyone who shares the work belongs.");
|
||||
expect(preview?.joiningProcess).toBe(
|
||||
"Work two Sunday shares, then ask to join.",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatInviteCountWord,
|
||||
parseInvitePaste,
|
||||
splitInvitePasteTokens,
|
||||
} from "../../lib/create/parseInviteEmails";
|
||||
|
||||
describe("parseInviteEmails", () => {
|
||||
it("splits comma, semicolon, and whitespace lists", () => {
|
||||
expect(
|
||||
splitInvitePasteTokens("rosa@example.org, dev@example.org; sam@example.org\nlee@example.org"),
|
||||
).toEqual([
|
||||
"rosa@example.org",
|
||||
"dev@example.org",
|
||||
"sam@example.org",
|
||||
"lee@example.org",
|
||||
]);
|
||||
});
|
||||
|
||||
it("adds valid addresses and leaves invalid tokens in the remainder", () => {
|
||||
const result = parseInvitePaste(
|
||||
"rosa@example.org, jo@example, sam@example.org",
|
||||
["maya@example.org"],
|
||||
30,
|
||||
);
|
||||
expect(result.added).toEqual(["rosa@example.org", "sam@example.org"]);
|
||||
expect(result.invalidRemainder).toBe("jo@example");
|
||||
});
|
||||
|
||||
it("skips duplicates already on the list", () => {
|
||||
const result = parseInvitePaste(
|
||||
"maya@example.org, rosa@example.org",
|
||||
["maya@example.org"],
|
||||
30,
|
||||
);
|
||||
expect(result.added).toEqual(["rosa@example.org"]);
|
||||
expect(result.skippedDuplicate).toBe(1);
|
||||
});
|
||||
|
||||
it("keeps overflow addresses in the remainder once the max is hit", () => {
|
||||
const result = parseInvitePaste(
|
||||
"a@example.org, b@example.org, c@example.org",
|
||||
[],
|
||||
2,
|
||||
);
|
||||
expect(result.added).toEqual(["a@example.org", "b@example.org"]);
|
||||
expect(result.invalidRemainder).toBe("c@example.org");
|
||||
});
|
||||
|
||||
it("uses words for counts through ten", () => {
|
||||
expect(formatInviteCountWord(5)).toBe("five");
|
||||
expect(formatInviteCountWord(11)).toBe("11");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user