Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ba5cb3c
test: add partial challenge tests
igorntwari Jul 10, 2024
f867447
test: add full challenge tests
igorntwari Jul 17, 2024
6db1bea
Merge branch 'dev' into test/challenge-card
igorntwari Jul 17, 2024
53af166
fix: add missing data-tesid
igorntwari Jul 17, 2024
025a803
fix: rename state variable
igorntwari Jul 17, 2024
098d751
fix: node version
igorntwari Jul 17, 2024
aca13f7
fix: renamve node version
igorntwari Jul 17, 2024
1290fea
fix: add clearclearImmediate
igorntwari Jul 17, 2024
4d68ac9
fix: downgrade node version
igorntwari Jul 17, 2024
1a58301
feat: add button test
igorntwari Jul 17, 2024
2584ba8
fix: clearImediate function
igorntwari Jul 18, 2024
f982b0d
refactor: remove duplicate mocks
igorntwari Jul 18, 2024
0fca9c8
refactor: remove extra duplicates
igorntwari Jul 19, 2024
25f4082
refactor: remove unnecessary codes
igorntwari Jul 24, 2024
c92c11d
test: challenge card
igorntwari Jul 24, 2024
c408ff4
Merge branch 'dev' into test/challenge-card
igorntwari Jul 24, 2024
0c24ca0
feat: install need packages
igorntwari Jul 24, 2024
dff4c97
feat: add more reliable test
igorntwari Jul 24, 2024
eee7016
refactor: remove deleted file
igorntwari Jul 24, 2024
c525fba
fix: remove deleted test files
igorntwari Jul 24, 2024
f76dfd0
Merge branch 'dev' into test/challenge-card
igorntwari Aug 23, 2024
8a9bdb3
refactor: add proper props naming
igorntwari Aug 23, 2024
27a6acf
refactor: add proper testid and fix linting issues
igorntwari Aug 29, 2024
f98e70d
Merge branch 'dev' into test/challenge-card
igorntwari Aug 29, 2024
4adffb1
refactor: add proper type coerciaon
igorntwari Sep 3, 2024
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
68 changes: 68 additions & 0 deletions __tests__/components/cards/challenge/_partials/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Button, { ButtonProps } from "@/components/cards/challenge/_partials/Button";
import { renderWithRedux } from "@__mocks__/renderWithRedux";
import "@testing-library/jest-dom";
import { screen, fireEvent } from '@testing-library/react';
import { useRouter } from 'next/router';

jest.mock("next/router", () => ({
useRouter: jest.fn(),
}));

const mockPush = jest.fn();

(useRouter as jest.Mock).mockReturnValue({
push: mockPush,
events: {
on: jest.fn(),
off: jest.fn(),
emit: jest.fn(),
},
isFallback: false,
});

const buttonProps: ButtonProps = {
text: "Test Button",
onClick: jest.fn(),
loading: true,
};

function RenderButton(props: ButtonProps = buttonProps) {
renderWithRedux(
<Button
text={props.text}
onClick={props.onClick}
loading={props.loading}
/>
);
return screen.getByTestId("button");
}

describe("Button", () => {
it("should render the button", () => {
const button = RenderButton();
expect(button).toBeInTheDocument();
});

it("should show loader when loading is true", () => {
RenderButton({ ...buttonProps, loading: true });
expect(screen.getByTestId("loader")).toBeInTheDocument();
});

it("should not show loader when loading is false", () => {
RenderButton({ ...buttonProps, loading: false });
expect(screen.queryByTestId("loader")).not.toBeInTheDocument();
});

it("should call onClick when clicked", () => {
const onClickMock = jest.fn();
RenderButton({ ...buttonProps, onClick: onClickMock, loading: false });
fireEvent.click(screen.getByTestId("button"));
expect(onClickMock).toHaveBeenCalled();
});

it("should display text when loading is false and isTextVisible is true", () => {
renderWithRedux(<Button {...buttonProps} loading={false} />);
fireEvent.mouseEnter(screen.getByTestId("button"));
expect(screen.getByTestId("button-text")).toHaveTextContent(buttonProps.text);
});
});
36 changes: 36 additions & 0 deletions __tests__/components/cards/challenge/_partials/FormTeam.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import FormTeamCard, { FormTeamCardProps } from "@/components/cards/challenge/_partials/FormTeam";
import { renderWithRedux } from "@__mocks__/renderWithRedux";
import "@testing-library/jest-dom";
import { screen } from '@testing-library/react';

const formTeamCardProps: FormTeamCardProps = {
title: "FormTeamCardProps",
description: "this is FormTeamCardProps description",
index: 1,
};

function RenderFormTeamCard(props: FormTeamCardProps = formTeamCardProps) {
renderWithRedux(
<FormTeamCard
title={props.title}
description={props.description}
index={props.index}
/>
);
}

describe("FormTeamCard", () => {
it('should render the FormTeamCard', () => {
RenderFormTeamCard();
expect(screen.getByText(formTeamCardProps.title)).toBeInTheDocument();
expect(screen.getByText(formTeamCardProps.description)).toBeInTheDocument();
expect(screen.getByText(`${formTeamCardProps.index}.`)).toBeInTheDocument();
});

it('should have a link to the correct URL', () => {
RenderFormTeamCard();
const linkElement = screen.getByRole('link', { name: /Start now/i });
expect(linkElement).toHaveAttribute('href', 'https://t.me/+0oJye8IwAuxkMDY0');
expect(linkElement).toHaveAttribute('target', '_blank');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import InvitationButton, { InvitationButtonProps } from "@/components/cards/challenge/_partials/InvitationButton";
import { renderWithRedux } from "@__mocks__/renderWithRedux";
import "@testing-library/jest-dom";
import { screen, fireEvent } from "@testing-library/react";

const invitationButtonPropsAccept: InvitationButtonProps = {
text: "accept",
confirmInvitation: jest.fn(),
};

const invitationButtonPropsDecline: InvitationButtonProps = {
text: "decline",
confirmInvitation: jest.fn(),
};

function RenderInvitationButton(props: InvitationButtonProps) {
renderWithRedux(
<InvitationButton
text={props.text}
confirmInvitation={props.confirmInvitation}
/>
);
}

describe('InvitationButton', () => {
it('should render the accept button', () => {
RenderInvitationButton(invitationButtonPropsAccept);
expect(screen.getByTestId('invitation-button')).toBeInTheDocument();
expect(screen.getByText('accept')).toBeInTheDocument();
});

it('should render the decline button', () => {
RenderInvitationButton(invitationButtonPropsDecline);
expect(screen.getByTestId('invitation-button')).toBeInTheDocument();
expect(screen.getByText('decline')).toBeInTheDocument();
});

it('should call confirmInvitation with "accept" when accept button is clicked', () => {
RenderInvitationButton(invitationButtonPropsAccept);
const button = screen.getByTestId('invitation-button');
fireEvent.click(button);
expect(invitationButtonPropsAccept.confirmInvitation).toHaveBeenCalledWith('accept');
});

it('should call confirmInvitation with "decline" when decline button is clicked', () => {
RenderInvitationButton(invitationButtonPropsDecline);
const button = screen.getByTestId('invitation-button');
fireEvent.click(button);
expect(invitationButtonPropsDecline.confirmInvitation).toHaveBeenCalledWith('decline');
});
});
44 changes: 44 additions & 0 deletions __tests__/components/cards/challenge/_partials/Learning.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Learning, { LearningProps } from "@/components/cards/challenge/_partials/Learning";
import { renderWithRedux } from "@__mocks__/renderWithRedux";
import "@testing-library/jest-dom";
import { screen } from "@testing-library/react";

jest.mock("next-i18next", () => ({
useTranslation: jest.fn().mockReturnValue({
t: (key: string) => key,
}),
}));

const learningProps: LearningProps = {
title: "Learning Title",
description: "This is the learning description.",
link: "/learning-link",
};

function RenderLearning(props: LearningProps = learningProps) {
renderWithRedux(<Learning {...props} />);
}

describe("Learning", () => {
it("should render the Learning component", () => {
RenderLearning();
expect(screen.getByTestId("learning-component")).toBeInTheDocument();
});

it("should display the title and description", () => {
RenderLearning();
expect(screen.getByTestId("learning-title")).toHaveTextContent(learningProps.title);
expect(screen.getByTestId("learning-description")).toHaveTextContent(learningProps.description);
});

it("should have a link with the correct URL", () => {
RenderLearning();
const linkElement = screen.getByRole("link", { name: /communities.overview.challenge.learning.start/i });
expect(linkElement).toHaveAttribute("href", learningProps.link);
});

it("should render the ArrowButton with the correct text", () => {
RenderLearning();
expect(screen.getByText("communities.overview.challenge.learning.start")).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import { useDispatch } from "../../../../../src/hooks/useTypedDispatch";
import { useMultiSelector } from "@/hooks/useTypedSelector";
import ReplyToInvitation, { InvitationProps } from "@/components/cards/challenge/_partials/ReplyToInvitation";
import { acceptInvitation, declineInvitation } from "@/store/feature/communities/challenges/invites.slice";


jest.mock("../../../../../src/hooks/useTypedDispatch");
jest.mock("../../../../../src/hooks/useTypedSelector");
jest.mock("../../../../../src/store/services/teams.service");
jest.mock("../../../../../src/store/feature/communities/challenges/invites.slice");
const mockDispatch = jest.fn();

(useDispatch as jest.Mock).mockReturnValue(mockDispatch);
(useMultiSelector as jest.Mock).mockReturnValue({
challenge: { id: "challenge-id" },
team: { invites: [{ id: "invite-id", status: "PENDING" }] },
});

const invitationProps: InvitationProps = {
invite_id: "invite-id",
team_ref: "team/1",
};

describe("ReplyToInvitation", () => {
beforeEach(() => {
jest.clearAllMocks();
});

it("should render the ReplyToInvitation component", () => {
render(<ReplyToInvitation {...invitationProps} />);
expect(screen.getByTestId("reply-to-invitation")).toBeInTheDocument();
});

it("should display loader when loading", async () => {
render(<ReplyToInvitation {...invitationProps} />);
expect(screen.getByTestId("loader")).toBeInTheDocument();
await waitFor(() => expect(screen.queryByTestId("loader")).not.toBeInTheDocument());
});

it("should call acceptInvitation when accept button is clicked", async () => {
render(<ReplyToInvitation {...invitationProps} />);
await waitFor(() => expect(screen.queryByTestId("loader")).not.toBeInTheDocument());

const acceptButton = screen.getByText("accept");
fireEvent.click(acceptButton);
await waitFor(() => expect(mockDispatch).toHaveBeenCalledWith(acceptInvitation("invite-id")));
});

it("should call declineInvitation when decline button is clicked", async () => {
render(<ReplyToInvitation {...invitationProps} />);
await waitFor(() => expect(screen.queryByTestId("loader")).not.toBeInTheDocument());

const declineButton = screen.getByText("decline");
fireEvent.click(declineButton);
await waitFor(() => expect(mockDispatch).toHaveBeenCalledWith(declineInvitation("invite-id")));
});

it("should not allow replies if the team is locked", async () => {
(useMultiSelector as jest.Mock).mockReturnValueOnce({
challenge: { id: "challenge-id" },
team: { invites: [{ id: "invite-id", status: "PENDING" }], locked: true },
});

render(<ReplyToInvitation {...invitationProps} />);

await waitFor(() => expect(screen.queryByTestId("loader")).not.toBeInTheDocument());
const invitationButtons = screen.queryAllByTestId("invitation-button");
expect(invitationButtons).toHaveLength(2);
});
});
52 changes: 52 additions & 0 deletions __tests__/components/cards/challenge/_partials/Reward.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import Rewards, { OverviewRewardsProps } from "@/components/cards/challenge/_partials/Reward";


jest.mock("next-i18next", () => ({
useTranslation: jest.fn().mockReturnValue({
t: (key: string) => key,
}),
}));

const rewardProps: OverviewRewardsProps = {
reward: {
id: "1",
ref: "ref-1",
created_at: new Date(),
updated_at: new Date(),
challenge: "challenge-1",
type: "submission",
community: "community-1",
token: "Token",
stable: true,
amount: 100,
timestamp: Date.now(),
},
size: "medium",
};

describe("Rewards", () => {
it("should render the Rewards component with medium size", () => {
render(<Rewards {...rewardProps} />);
expect(screen.getByText("100")).toBeInTheDocument();
expect(screen.getByText("Token")).toBeInTheDocument();
expect(screen.getByText("reward.type.prefix")).toBeInTheDocument();
expect(screen.getByText("communities.challenge.submission")).toBeInTheDocument();
});

it("should not render the Rewards component if size is not medium", () => {
const smallSizeProps = { ...rewardProps, size: "small" };
render(<Rewards {...smallSizeProps} />);
expect(screen.queryByText("100")).not.toBeInTheDocument();
expect(screen.queryByText("Token")).not.toBeInTheDocument();
});

it("should not render the Rewards component if reward is not provided", () => {
const noRewardProps = { ...rewardProps, size: "" };
render(<Rewards {...noRewardProps} />);
expect(screen.queryByText("100")).not.toBeInTheDocument();
expect(screen.queryByText("Token")).not.toBeInTheDocument();
});
});
12 changes: 8 additions & 4 deletions src/components/cards/challenge/_partials/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from "react";
import CloseIcon from "@/icons/close-top-right.svg";
import Loader from "@/components/ui/Loader";

interface ButtonProps {
export interface ButtonProps {
text: string;
onClick: () => void;
loading: boolean;
Expand All @@ -11,14 +11,18 @@ interface ButtonProps {
export default function Button({ text, onClick, loading }: ButtonProps) {
const [isTextVisible, setistextVisible] = useState(false);
return (
<div className="ml-auto cursor-pointer relative" onMouseEnter={() => setistextVisible(true)} onMouseLeave={() => setistextVisible(false)} onClick={onClick}>
<div className="ml-auto cursor-pointer relative " onMouseEnter={() => setistextVisible(true)} onMouseLeave={() => setistextVisible(false)} onClick={onClick}
data-testid = "button"
>
<>
{loading ? (
<Loader isSmallSpinner />
<Loader isSmallSpinner data-testid = "loader"/>
) : (
<>
<CloseIcon />
<span className={`absolute -top-8 -right-6 text-sm bg-white p-0.5 px-1.5 rounded shadow-md ${isTextVisible ? "block" : "hidden"}`}>{text} </span>
<span className={`absolute -top-8 -right-6 text-sm bg-white p-0.5 px-1.5 rounded shadow-md ${isTextVisible ? "block" : "hidden"}`}
data-testid="button-text"
>{text} </span>
</>
)}
</>
Expand Down
2 changes: 1 addition & 1 deletion src/components/cards/challenge/_partials/FormTeam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Link from "next/link";
/**
* Props for the FormTeam component.
*/
interface FormTeamCardProps {
export interface FormTeamCardProps {
title: string;
description: string;
index: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CheckIcon from "@/icons/check.svg";
* @interface InvitationButtonProps
* @typedef {InvitationButtonProps}
*/
interface InvitationButtonProps {
export interface InvitationButtonProps {
text: "accept" | "decline";
confirmInvitation: (text: "accept" | "decline") => void;
}
Expand All @@ -36,6 +36,7 @@ export default function InvitationButton({ text, confirmInvitation }: Invitation
e.stopPropagation();
confirmInvitation(text);
}}
data-testid="invitation-button"
>
{text === "accept" ? <CheckIcon className="text-green-700" /> : <CloseIcon className="text-red-700" />}
<span className="capitalize">{text}</span>
Expand Down
Loading