Let Use without changes from a template collect identity instead of the full questionnaire.
Direct catalog picks walk name, description, and photo, then stakeholders; leftover drafts no longer skip that path. In-flow template picks keep community identity. Exit on a direct template preview leaves immediately because nothing has been collected yet. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -104,12 +104,10 @@ describe("CommunityReviewScreen — pendingTemplateAction redirect", () => {
|
||||
expect(testRouter.push).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("redirects to /create/confirm-stakeholders when mode === 'useWithoutChanges'", async () => {
|
||||
it("redirects Use without changes to community-name so identity is not skipped", async () => {
|
||||
render(<ReviewWithPendingAction mode="useWithoutChanges" />);
|
||||
await waitFor(() => {
|
||||
expect(testRouter.replace).toHaveBeenCalledWith(
|
||||
"/create/confirm-stakeholders",
|
||||
);
|
||||
expect(testRouter.replace).toHaveBeenCalledWith("/create/community-name");
|
||||
});
|
||||
expect(testRouter.push).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
parseReviewReturnSearchParam,
|
||||
resolveCreateFlowBackTarget,
|
||||
shouldOfferCreateFlowSaveAndExit,
|
||||
isDirectTemplateReviewEntry,
|
||||
TEMPLATES_FACET_RECOMMEND_QUERY,
|
||||
TEMPLATES_FACET_RECOMMEND_VALUE,
|
||||
TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY,
|
||||
@@ -82,6 +83,31 @@ describe("flowSteps", () => {
|
||||
expect(getPreviousStep("communication-methods", opts)).toBe("core-values");
|
||||
});
|
||||
|
||||
it("useWithoutChangesIdentityOnly walks name → description → photo → stakeholders", () => {
|
||||
const opts = { useWithoutChangesIdentityOnly: true } as const;
|
||||
expect(getNextStep("community-name", opts)).toBe("community-context");
|
||||
expect(getNextStep("community-context", opts)).toBe("community-upload");
|
||||
expect(getNextStep("community-upload", opts)).toBe("confirm-stakeholders");
|
||||
expect(getNextStep("confirm-stakeholders", opts)).toBe("final-review");
|
||||
expect(getPreviousStep("community-context", opts)).toBe("community-name");
|
||||
expect(getPreviousStep("community-upload", opts)).toBe("community-context");
|
||||
expect(getPreviousStep("confirm-stakeholders", opts)).toBe(
|
||||
"community-upload",
|
||||
);
|
||||
expect(getPreviousStep("community-name", opts)).toBeNull();
|
||||
});
|
||||
|
||||
it("useWithoutChangesIdentityOnly composes with skipCommunitySave", () => {
|
||||
const opts = {
|
||||
skipCommunitySave: true,
|
||||
useWithoutChangesIdentityOnly: true,
|
||||
} as const;
|
||||
expect(getNextStep("community-upload", opts)).toBe("confirm-stakeholders");
|
||||
expect(getPreviousStep("confirm-stakeholders", opts)).toBe(
|
||||
"community-upload",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget returns template review when use-without slug is set on confirm-stakeholders", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget(
|
||||
@@ -92,6 +118,26 @@ describe("flowSteps", () => {
|
||||
).toEqual({ kind: "templateReview", slug: "mutual-aid-mondays" });
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget sends identity-only community-name back to template review", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget(
|
||||
"community-name",
|
||||
{ useWithoutChangesIdentityOnly: true },
|
||||
"mutual-aid-mondays",
|
||||
),
|
||||
).toEqual({ kind: "templateReview", slug: "mutual-aid-mondays" });
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget uses photo step behind stakeholders on the identity-only path", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget(
|
||||
"confirm-stakeholders",
|
||||
{ useWithoutChangesIdentityOnly: true },
|
||||
"mutual-aid-mondays",
|
||||
),
|
||||
).toEqual({ kind: "step", step: "community-upload" });
|
||||
});
|
||||
|
||||
it("resolveCreateFlowBackTarget falls back to linear previous when slug is absent", () => {
|
||||
expect(
|
||||
resolveCreateFlowBackTarget("confirm-stakeholders", undefined, undefined),
|
||||
@@ -150,4 +196,29 @@ describe("flowSteps", () => {
|
||||
expect(shouldOfferCreateFlowSaveAndExit("completed", null)).toBe(true);
|
||||
expect(shouldOfferCreateFlowSaveAndExit(null)).toBe(false);
|
||||
});
|
||||
|
||||
it("isDirectTemplateReviewEntry is true only on template preview without fromFlow", () => {
|
||||
expect(
|
||||
isDirectTemplateReviewEntry("/create/review-template/consensus"),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isDirectTemplateReviewEntry(
|
||||
"/create/review-template/consensus",
|
||||
new URLSearchParams(),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isDirectTemplateReviewEntry(
|
||||
"/create/review-template/consensus",
|
||||
new URLSearchParams("fromFlow=1"),
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
isDirectTemplateReviewEntry(
|
||||
"/create/community-name",
|
||||
new URLSearchParams(),
|
||||
),
|
||||
).toBe(false);
|
||||
expect(isDirectTemplateReviewEntry(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { renderHook, act } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import type { CreateFlowState } from "../../../app/(app)/create/types";
|
||||
import { useTemplateReviewActions } from "../../../app/(app)/create/hooks/useTemplateReviewActions";
|
||||
import { loadTemplateReviewBySlug } from "../../../lib/create/loadTemplateReviewBySlug";
|
||||
|
||||
vi.mock("../../../lib/create/loadTemplateReviewBySlug", () => ({
|
||||
loadTemplateReviewBySlug: vi.fn(),
|
||||
}));
|
||||
|
||||
const templateBody = {
|
||||
sections: [
|
||||
{
|
||||
categoryName: "Communication",
|
||||
entries: [{ title: "Signal" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const loadedTemplate = {
|
||||
ok: true as const,
|
||||
template: {
|
||||
id: "t1",
|
||||
slug: "mutual-aid-mondays",
|
||||
title: "Mutual Aid Mondays",
|
||||
category: null,
|
||||
description: null,
|
||||
body: templateBody,
|
||||
sortOrder: 0,
|
||||
featured: false,
|
||||
},
|
||||
};
|
||||
|
||||
describe("useTemplateReviewActions", () => {
|
||||
const router = { push: vi.fn() };
|
||||
const updateState = vi.fn();
|
||||
const markCreateFlowInteraction = vi.fn();
|
||||
let applied: CreateFlowState | undefined;
|
||||
const replaceState = vi.fn(
|
||||
(updater: (_prev: CreateFlowState) => CreateFlowState) => {
|
||||
applied = updater({});
|
||||
},
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(loadTemplateReviewBySlug).mockReset();
|
||||
vi.mocked(loadTemplateReviewBySlug).mockResolvedValue(loadedTemplate);
|
||||
router.push.mockReset();
|
||||
updateState.mockReset();
|
||||
markCreateFlowInteraction.mockReset();
|
||||
replaceState.mockClear();
|
||||
applied = undefined;
|
||||
});
|
||||
|
||||
function renderActions(
|
||||
state: CreateFlowState,
|
||||
fromCreateWizard = false,
|
||||
) {
|
||||
return renderHook(() =>
|
||||
useTemplateReviewActions({
|
||||
pathname: "/create/review-template/mutual-aid-mondays",
|
||||
state,
|
||||
updateState,
|
||||
replaceState,
|
||||
router,
|
||||
fromCreateWizard,
|
||||
markCreateFlowInteraction,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
it("direct Use without changes walks identity from community-name", async () => {
|
||||
const { result } = renderActions({});
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleUseWithoutChanges();
|
||||
});
|
||||
|
||||
expect(markCreateFlowInteraction).toHaveBeenCalled();
|
||||
expect(router.push).toHaveBeenCalledWith("/create/community-name");
|
||||
expect(applied?.pendingTemplateAction).toEqual({
|
||||
slug: "mutual-aid-mondays",
|
||||
mode: "useWithoutChanges",
|
||||
});
|
||||
});
|
||||
|
||||
it("direct Use without changes ignores a leftover draft title", async () => {
|
||||
replaceState.mockImplementation(
|
||||
(updater: (_prev: CreateFlowState) => CreateFlowState) => {
|
||||
applied = updater({
|
||||
title: "Neighborhood",
|
||||
communityContext: "Stale description",
|
||||
currentStep: "confirm-stakeholders",
|
||||
});
|
||||
},
|
||||
);
|
||||
const { result } = renderActions({ title: "Neighborhood" });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleUseWithoutChanges();
|
||||
});
|
||||
|
||||
expect(router.push).toHaveBeenCalledWith("/create/community-name");
|
||||
expect(applied?.title).toBeUndefined();
|
||||
expect(applied?.communityContext).toBeUndefined();
|
||||
expect(applied?.currentStep).toBeUndefined();
|
||||
expect(applied?.pendingTemplateAction?.mode).toBe("useWithoutChanges");
|
||||
});
|
||||
|
||||
it("in-flow Use without changes skips identity and keeps the community name", async () => {
|
||||
replaceState.mockImplementation(
|
||||
(updater: (_prev: CreateFlowState) => CreateFlowState) => {
|
||||
applied = updater({
|
||||
title: "Neighborhood",
|
||||
communityContext: "We meet weekly",
|
||||
templateReviewEntryFromCreateFlow: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
const { result } = renderActions(
|
||||
{ title: "Neighborhood", templateReviewEntryFromCreateFlow: true },
|
||||
true,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleUseWithoutChanges();
|
||||
});
|
||||
|
||||
expect(router.push).toHaveBeenCalledWith("/create/confirm-stakeholders");
|
||||
expect(applied?.title).toBe("Neighborhood");
|
||||
expect(applied?.communityContext).toBe("We meet weekly");
|
||||
expect(applied?.pendingTemplateAction).toBeUndefined();
|
||||
expect(applied?.templateReviewBackSlug).toBe("mutual-aid-mondays");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user