Skip to content
23 changes: 23 additions & 0 deletions __tests__/components/waves/create-wave/CreateWave.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ jest.mock("@/components/waves/create-wave/services/multiPartUpload", () => ({
multiPartUpload: jest.fn(),
}));

jest.mock("@/hooks/groups/useGroupMutations", () => ({
useGroupMutations: jest.fn(),
}));

// Mock step components
jest.mock("@/components/waves/create-wave/overview/CreateWaveOverview", () => {
return function MockCreateWaveOverview() {
Expand Down Expand Up @@ -119,6 +123,7 @@ import { useAddWaveMutation } from "@/components/waves/create-wave/services/wave
import { getAdminGroupId } from "@/components/waves/create-wave/services/waveGroupService";
import { generateDropPart } from "@/components/waves/create-wave/services/waveMediaService";
import { getCreateNewWaveBody } from "@/helpers/waves/create-wave.helpers";
import { useGroupMutations } from "@/hooks/groups/useGroupMutations";

const mockedUseRouter = useRouter as jest.Mock;
const mockedUseWaveConfig = useWaveConfig as jest.Mock;
Expand All @@ -127,6 +132,7 @@ const mockedGetCreateNewWaveBody = getCreateNewWaveBody as jest.Mock;
const mockedGenerateDropPart = generateDropPart as jest.Mock;
const mockedGetAdminGroupId = getAdminGroupId as jest.Mock;
const mockedMultiPartUpload = multiPartUpload as jest.Mock;
const mockedUseGroupMutations = useGroupMutations as jest.Mock;

describe("CreateWave", () => {
const mockRouter = {
Expand Down Expand Up @@ -157,6 +163,7 @@ describe("CreateWave", () => {
const mockQueryContext = {
waitAndInvalidateDrops: jest.fn(),
onWaveCreated: jest.fn(),
onGroupCreate: jest.fn(),
};

const mockWaveConfig = {
Expand Down Expand Up @@ -211,6 +218,13 @@ describe("CreateWave", () => {
selectedOutcomeType: null,
errors: [],
groupsCache: {},
groupBuilders: {
CAN_VIEW: {},
CAN_DROP: {},
CAN_VOTE: {},
CAN_CHAT: {},
ADMIN: {},
},
setOverview: jest.fn(),
setDates: jest.fn(),
setDrops: jest.fn(),
Expand All @@ -219,6 +233,12 @@ describe("CreateWave", () => {
onStep: jest.fn(),
onOutcomeTypeChange: jest.fn(),
onGroupSelect: jest.fn(),
setGroupBuilderPanel: jest.fn(),
setGroupBuilderRule: jest.fn(),
setGroupBuilderDraft: jest.fn(),
addGroupBuilderIdentity: jest.fn(),
removeGroupBuilderIdentity: jest.fn(),
resetGroupBuilder: jest.fn(),
onVotingTypeChange: jest.fn(),
onCategoryChange: jest.fn(),
onProfileIdChange: jest.fn(),
Expand All @@ -243,6 +263,9 @@ describe("CreateWave", () => {
mockedMultiPartUpload.mockResolvedValue({
url: "https://example.com/image.jpg",
});
mockedUseGroupMutations.mockReturnValue({
submit: jest.fn(),
});
mockAuthContext.requestAuth.mockResolvedValue({ success: true });

// Mock URL.createObjectURL
Expand Down
195 changes: 49 additions & 146 deletions __tests__/components/waves/create-wave/groups/CreateWaveGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import { CreateWaveGroupConfigType } from "@/types/waves.types";
import { ApiWaveType } from "@/generated/models/ApiWaveType";
import type { ApiGroupFull } from "@/generated/models/ApiGroupFull";

jest.mock("@tanstack/react-query", () => {
const actual = jest.requireActual("@tanstack/react-query");
return {
...actual,
useQuery: jest.fn(),
};
});
let inlinePanelProps: any;

jest.mock("@/components/waves/create-wave/utils/CreateWaveToggle", () => {
return function CreateWaveToggle({
Expand Down Expand Up @@ -41,54 +35,24 @@ jest.mock("@/components/waves/create-wave/utils/CreateWaveToggle", () => {
};
});

jest.mock("@/helpers/waves/waves.constants", () => {
const { CreateWaveGroupConfigType } = jest.requireActual(
"../../../../../types/waves.types"
);
const { ApiWaveType } = jest.requireActual(
"../../../../../generated/models/ApiWaveType"
);

return {
CREATE_WAVE_NONE_GROUP_LABELS: {
[CreateWaveGroupConfigType.ADMIN]: "Only me",
[CreateWaveGroupConfigType.CAN_VIEW]: "Anyone",
[CreateWaveGroupConfigType.CAN_DROP]: "Anyone",
[CreateWaveGroupConfigType.CAN_VOTE]: "Anyone",
[CreateWaveGroupConfigType.CAN_CHAT]: "Anyone",
},
CREATE_WAVE_SELECT_GROUP_LABELS: {
[ApiWaveType.Approve]: {
[CreateWaveGroupConfigType.ADMIN]: "Admin",
[CreateWaveGroupConfigType.CAN_VIEW]: "Who can view",
[CreateWaveGroupConfigType.CAN_DROP]: "Who can drop",
[CreateWaveGroupConfigType.CAN_VOTE]: "Who can vote",
[CreateWaveGroupConfigType.CAN_CHAT]: "Who can chat",
},
[ApiWaveType.Rank]: {
[CreateWaveGroupConfigType.ADMIN]: "Admin",
[CreateWaveGroupConfigType.CAN_VIEW]: "Who can view",
[CreateWaveGroupConfigType.CAN_DROP]: "Who can drop",
[CreateWaveGroupConfigType.CAN_VOTE]: "Who can vote",
[CreateWaveGroupConfigType.CAN_CHAT]: "Who can chat",
},
[ApiWaveType.Chat]: {
[CreateWaveGroupConfigType.ADMIN]: "Admin",
[CreateWaveGroupConfigType.CAN_VIEW]: "Who can view",
[CreateWaveGroupConfigType.CAN_DROP]: "Who can drop",
[CreateWaveGroupConfigType.CAN_VOTE]: "Who can rate",
[CreateWaveGroupConfigType.CAN_CHAT]: "Who can chat",
},
},
};
});

const { useQuery } = jest.requireMock("@tanstack/react-query");
jest.mock(
"@/components/waves/create-wave/groups/CreateWaveGroupInlinePanel",
() =>
function MockCreateWaveGroupInlinePanel(props: any) {
inlinePanelProps = props;
return (
<div data-testid="inline-panel">
{props.selectedGroup?.name ?? "none"}
</div>
);
}
);

describe("CreateWaveGroup", () => {
const mockOnGroupSelect = jest.fn();
const mockSetChatEnabled = jest.fn();
const mockSetDropsAdminCanDelete = jest.fn();
const mockOnInlineGroupCreate = jest.fn();

const exampleGroup: ApiGroupFull = {
id: "group-1",
Expand All @@ -110,37 +74,57 @@ describe("CreateWaveGroup", () => {
};

const defaultProps = {
waveName: "Test Wave",
waveType: ApiWaveType.Approve,
groupType: CreateWaveGroupConfigType.CAN_DROP,
chatEnabled: true,
adminCanDeleteDrops: false,
setChatEnabled: mockSetChatEnabled,
onGroupSelect: mockOnGroupSelect,
onInlineGroupCreate: mockOnInlineGroupCreate,
groupsCache: {},
groups: defaultGroups,
groupBuilder: {
draft: { group: {} },
identities: [],
panel: "actions",
activeRule: null,
} as any,
setGroupBuilderPanel: jest.fn(),
setGroupBuilderRule: jest.fn(),
setGroupBuilderDraft: jest.fn(),
addGroupBuilderIdentity: jest.fn(),
removeGroupBuilderIdentity: jest.fn(),
resetGroupBuilder: jest.fn(),
setDropsAdminCanDelete: mockSetDropsAdminCanDelete,
};

beforeEach(() => {
jest.clearAllMocks();
(useQuery as jest.Mock).mockReturnValue({
data: [exampleGroup],
isFetching: false,
});
inlinePanelProps = null;
});

const renderComponent = (props = {}) => {
return render(<CreateWaveGroup {...defaultProps} {...props} />);
};
const renderComponent = (props = {}) =>
render(<CreateWaveGroup {...defaultProps} {...props} />);

it("shows the scope title", () => {
renderComponent();
expect(screen.getByText("Who can drop")).toBeInTheDocument();
});

it("renders the search field label", () => {
renderComponent();
expect(screen.getByLabelText("Search groups…")).toBeInTheDocument();
it("passes the resolved selected group to the inline panel", () => {
renderComponent({
groups: {
...defaultGroups,
canDrop: exampleGroup.id,
},
groupsCache: {
[exampleGroup.id]: exampleGroup,
},
});

expect(screen.getByTestId("inline-panel")).toHaveTextContent("Alpha Group");
expect(inlinePanelProps.selectedGroup).toEqual(exampleGroup);
});

it("shows the chat toggle for non-chat waves when editing chat scope", async () => {
Expand All @@ -149,7 +133,6 @@ describe("CreateWaveGroup", () => {
groupType: CreateWaveGroupConfigType.CAN_CHAT,
});

expect(screen.getByText("Enable chat")).toBeInTheDocument();
const chatToggle = screen.getByLabelText("Enable chat");
await user.click(chatToggle);
expect(mockSetChatEnabled).toHaveBeenCalledWith(false);
Expand All @@ -160,6 +143,7 @@ describe("CreateWaveGroup", () => {
groupType: CreateWaveGroupConfigType.CAN_CHAT,
waveType: ApiWaveType.Chat,
});

expect(screen.queryByTestId("wave-toggle")).not.toBeInTheDocument();
});

Expand All @@ -168,98 +152,17 @@ describe("CreateWaveGroup", () => {
renderComponent({
groupType: CreateWaveGroupConfigType.ADMIN,
});
const toggle = screen.getByLabelText("Allow admins to delete posts");
await user.click(toggle);
expect(mockSetDropsAdminCanDelete).toHaveBeenCalledWith(true);
});

it("renders the admin delete toggle for chat waves", () => {
renderComponent({
groupType: CreateWaveGroupConfigType.ADMIN,
waveType: ApiWaveType.Chat,
});

expect(
screen.getByLabelText("Allow admins to delete posts")
).toBeInTheDocument();
});

it("does not render helper text under the admin toggle", () => {
renderComponent({
groupType: CreateWaveGroupConfigType.ADMIN,
adminCanDeleteDrops: true,
});

expect(
screen.queryByText("Admins will be able to delete posts.")
).not.toBeInTheDocument();
});

it("displays the helper text for defaults", () => {
renderComponent();
expect(screen.getByText("Default: Anyone")).toBeInTheDocument();
await user.click(screen.getByLabelText("Allow admins to delete posts"));
expect(mockSetDropsAdminCanDelete).toHaveBeenCalledWith(true);
});

it("disables the search input when chat is disabled", () => {
it("passes disabled to the inline panel when chat is disabled", () => {
renderComponent({
groupType: CreateWaveGroupConfigType.CAN_CHAT,
chatEnabled: false,
});
expect(screen.getByLabelText("Search groups…")).toBeDisabled();
});

it("pre-populates the field when a selection exists in cache", () => {
renderComponent({
groups: {
...defaultGroups,
canDrop: exampleGroup.id,
},
groupsCache: {
[exampleGroup.id]: exampleGroup,
},
});

expect(screen.getByDisplayValue("Alpha Group")).toBeInTheDocument();
expect(screen.getByText("Selected: Alpha Group")).toBeInTheDocument();
});

it("calls onGroupSelect when a suggestion is chosen", async () => {
const user = userEvent.setup();
renderComponent();

await user.click(screen.getByLabelText("Search groups…"));
await user.click(screen.getByText("Alpha Group"));

expect(mockOnGroupSelect).toHaveBeenCalledWith(exampleGroup);
});

it("clears the selected group when using the clear button", async () => {
const user = userEvent.setup();
renderComponent({
groups: {
...defaultGroups,
canDrop: exampleGroup.id,
},
groupsCache: {
[exampleGroup.id]: exampleGroup,
},
});

const clearButton = screen.getByLabelText("Clear selected group");
await user.click(clearButton);
expect(mockOnGroupSelect).toHaveBeenCalledWith(null);
});

it("shows an empty state when no groups match", async () => {
const user = userEvent.setup();
(useQuery as jest.Mock).mockReturnValue({
data: [],
isFetching: false,
});

renderComponent();
await user.click(screen.getByLabelText("Search groups…"));

expect(await screen.findByText("No groups found")).toBeInTheDocument();
expect(inlinePanelProps.disabled).toBe(true);
});
});
Loading
Loading