Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 24 additions & 9 deletions __tests__/app/network/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
import React from "react";
import { useSelector } from "react-redux";

// Mocks
jest.mock("react-redux", () => ({
useSelector: jest.fn(),
}));
Expand All @@ -23,10 +22,11 @@ jest.mock("next/navigation", () => ({
useRouter: jest.fn(),
}));

// Mock internal components
jest.mock("@/components/utils/sidebar/SidebarLayout", () => ({
jest.mock("@/components/network/NetworkPageLayout", () => ({
__esModule: true,
default: ({ children }: any) => <div data-testid="layout">{children}</div>,
default: ({ children }: { children: React.ReactNode }) => (
<div data-testid="layout">{children}</div>
),
}));
jest.mock(
"@/components/community/members-table/CommunityMembersTable",
Expand All @@ -36,11 +36,26 @@ jest.mock(
"@/components/utils/table/paginator/CommonTablePagination",
() => () => <div data-testid="pagination" />
);
jest.mock("@/components/utils/animation/CommonCardSkeleton", () => () => (
<div data-testid="loading-skeleton" />
jest.mock(
"@/components/community/members-table/CommunityMembersTableSkeleton",
() => () => <div data-testid="loading-skeleton" />
);
jest.mock("@/components/groups/sidebar/GroupsSidebar", () => () => (
<div data-testid="groups-sidebar" />
));
jest.mock(
"@/components/mobile-wrapper-dialog/MobileWrapperDialog",
() =>
({
children,
isOpen,
}: {
children: React.ReactNode;
isOpen: boolean;
}) =>
isOpen ? <div data-testid="mobile-dialog">{children}</div> : null
);

// Mock TitleContext
jest.mock("@/contexts/TitleContext", () => ({
useTitle: () => ({
title: "Test Title",
Expand Down Expand Up @@ -101,7 +116,7 @@ describe("CommunityPage (App Router)", () => {
});

it("renders loading state", () => {
useQueryMock.mockReturnValue({ isLoading: true });
useQueryMock.mockReturnValue({ isLoading: true, data: null });

render(<CommunityMembers />);

Expand All @@ -113,7 +128,7 @@ describe("CommunityPage (App Router)", () => {
isLoading: false,
isFetching: false,
data: {
count: 120, // ensures multiple pages
count: 120,
data: [],
page: 1,
next: true,
Expand Down
27 changes: 21 additions & 6 deletions __tests__/components/community/CommunityMembers.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { render, screen, fireEvent } from "@testing-library/react";
import CommunityMembers from "@/components/community/CommunityMembers";
import { useRouter } from "next/navigation";
import { useSearchParams, usePathname } from "next/navigation";
import { useSelector } from "react-redux";
import { useQuery } from "@tanstack/react-query";
import { TitleProvider } from "@/contexts/TitleContext";
import { useQuery } from "@tanstack/react-query";
import { fireEvent, render, screen } from "@testing-library/react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useSelector } from "react-redux";

jest.mock("next/navigation", () => ({
usePathname: jest.fn(),
Expand All @@ -31,10 +30,26 @@ jest.mock(
);

jest.mock(
"@/components/utils/animation/CommonCardSkeleton",
"@/components/community/members-table/CommunityMembersTableSkeleton",
() => () => <div data-testid="skeleton" />
);

jest.mock(
"@/components/community/members-table/CommunityMembersMobileSortContent",
() => () => <div data-testid="mobile-sort" />
);

jest.mock("@/components/groups/sidebar/GroupsSidebar", () => () => (
<div data-testid="groups-sidebar" />
));

jest.mock(
"@/components/mobile-wrapper-dialog/MobileWrapperDialog",
() =>
({ children, isOpen }: any) =>
isOpen ? <div data-testid="mobile-dialog">{children}</div> : null
);
Comment thread
prxt6529 marked this conversation as resolved.

const push = jest.fn();
const replace = jest.fn();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,149 @@
import { render, screen } from '@testing-library/react';
import React from 'react';

jest.mock('next/link', () => ({ __esModule: true, default: ({ href, children }: any) => <a href={href}>{children}</a> }));
jest.mock('@/components/utils/CommonTimeAgo', () => ({ __esModule: true, default: ({ timestamp }: any) => <span data-testid="time">{timestamp}</span> }));
jest.mock('@/components/user/utils/user-cic-type/UserCICTypeIcon', () => ({ __esModule: true, default: () => <span data-testid="cic" /> }));
jest.mock('@/components/user/utils/level/UserLevel', () => ({ __esModule: true, default: () => <div data-testid="level" /> }));
jest.mock('@/helpers/Helpers', () => ({
import CommunityMembersMobileCard from "@/components/community/members-table/CommunityMembersMobileCard";
import type { ApiCommunityMemberOverview } from "@/generated/models/ApiCommunityMemberOverview";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { ReactNode } from "react";

jest.mock("next/link", () => ({
__esModule: true,
default: ({ href, children }: { href: string; children: ReactNode }) => (
<a href={href}>{children}</a>
),
}));
Comment thread
prxt6529 marked this conversation as resolved.
jest.mock("@/components/user/utils/level/UserLevel", () => ({
__esModule: true,
default: () => <div data-testid="level" />,
}));
jest.mock("@/components/user/utils/user-cic-type/UserCICTypeIcon", () => ({
__esModule: true,
default: () => <div data-testid="cic-icon" />,
}));
jest.mock("@/helpers/Helpers", () => ({
formatNumberWithCommasOrDash: (n: number) => `f${n}`,
cicToType: (c: number) => c,
formatLargeNumber: (n: number) => `${n}M`,
getTimeAgoShort: () => "2d",
}));
jest.mock('@/helpers/AllowlistToolHelpers', () => ({
isEthereumAddress: () => false,
jest.mock("@/helpers/AllowlistToolHelpers", () => ({
isEthereumAddress: (val: string) => val.startsWith("0x"),
isAutoGeneratedHandle: () => false,
}));
jest.mock('@/helpers/image.helpers', () => ({
ImageScale: { W_AUTO_H_50: '50' },
jest.mock("@/helpers/image.helpers", () => ({
ImageScale: { W_AUTO_H_50: "50" },
getScaledImageUri: (u: string) => u,
}));

import CommunityMembersMobileCard from '@/components/community/members-table/CommunityMembersMobileCard';

const member = {
display: 'Alice',
detail_view_key: 'alice',
const baseMember: ApiCommunityMemberOverview = {
display: "Alice",
detail_view_key: "alice",
level: 1,
tdh: 100,
rep: 50,
tdh_rate: 10,
xtdh: 55,
xtdh_rate: 5,
combined_tdh: 150,
combined_tdh_rate: 15,
rep: 60,
cic: 1,
pfp: 'url',
pfp: "url",
last_activity: 10,
wallet: '0x1',
} as any;

describe('CommunityMembersMobileCard', () => {
it('renders member info and link', () => {
render(<CommunityMembersMobileCard member={member} rank={2} />);
expect(screen.getByText('#2')).toBeInTheDocument();
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('f100')).toBeInTheDocument();
expect(screen.getByText('f50')).toBeInTheDocument();
expect(screen.getByTestId('cic')).toBeInTheDocument();
expect(screen.getByRole('link')).toHaveAttribute('href', '/alice');
wallet: "0x1",
xtdh_outgoing: 0,
xtdh_incoming: 0,
};

describe("CommunityMembersMobileCard", () => {
describe("rendering", () => {
it("renders rank badge and profile link", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={2} />);

expect(screen.getByText("#2")).toBeInTheDocument();
expect(screen.getByRole("link")).toHaveAttribute("href", "/alice");
});

it("renders member display name", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

expect(screen.getByText("Alice")).toBeInTheDocument();
});

it("renders level and CIC icon", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

expect(screen.getByTestId("level")).toBeInTheDocument();
expect(screen.getByTestId("cic-icon")).toBeInTheDocument();
});

it("renders all stat labels", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

expect(screen.getByText("TDH")).toBeInTheDocument();
expect(screen.getByText("xTDH")).toBeInTheDocument();
expect(screen.getByText("REP")).toBeInTheDocument();
expect(screen.getByText("NIC")).toBeInTheDocument();
});
});

describe("TDH/xTDH formatting", () => {
it("formats large numbers correctly", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

expect(screen.getByText("100M")).toBeInTheDocument();
expect(screen.getByText("55M")).toBeInTheDocument();
});

it("handles zero values with dash", () => {
const zeroMember: ApiCommunityMemberOverview = {
...baseMember,
tdh: 0,
xtdh: 0,
};
render(<CommunityMembersMobileCard member={zeroMember} rank={1} />);

const dashValues = screen.getAllByText("-");
expect(dashValues).toHaveLength(2);
});
});

describe("last activity", () => {
it("renders Last Seen when last_activity is present", () => {
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

expect(screen.getByText("Last Seen")).toBeInTheDocument();
});

it("does not render Last Seen when last_activity is null", () => {
const noActivityMember: ApiCommunityMemberOverview = {
...baseMember,
last_activity: null,
};
render(<CommunityMembersMobileCard member={noActivityMember} rank={1} />);

expect(screen.queryByText("Last Seen")).not.toBeInTheDocument();
});
});

describe("profile type branches", () => {
it("renders with ethereum address as detail_view_key", () => {
const ethMember: ApiCommunityMemberOverview = {
...baseMember,
detail_view_key: "0xabc123",
display: "0xabc123",
};
render(<CommunityMembersMobileCard member={ethMember} rank={1} />);

expect(screen.getByRole("link")).toHaveAttribute("href", "/0xabc123");
});
});

describe("keyboard accessibility", () => {
it("link can receive focus via keyboard", async () => {
const user = userEvent.setup();
render(<CommunityMembersMobileCard member={baseMember} rank={1} />);

await user.tab();

const link = screen.getByRole("link");
expect(link).toHaveFocus();
});
});
});
Comment thread
prxt6529 marked this conversation as resolved.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { render, screen, fireEvent } from "@testing-library/react";
import CommunityMembersMobileSortContent from "@/components/community/members-table/CommunityMembersMobileSortContent";
import { ApiCommunityMembersSortOption } from "@/generated/models/ApiCommunityMembersSortOption";
import { SortDirection } from "@/entities/ISort";

jest.mock("@/components/user/utils/icons/CommonTableSortIcon", () => ({
__esModule: true,
default: ({ direction }: { direction: string }) => (
<span data-testid="sort-icon">{direction}</span>
),
}));

describe("CommunityMembersMobileSortContent", () => {
const defaultProps = {
activeSort: ApiCommunityMembersSortOption.Level,
sortDirection: SortDirection.DESC,
onSort: jest.fn(),
};

beforeEach(() => {
jest.clearAllMocks();
});

it("renders all sort options", () => {
render(<CommunityMembersMobileSortContent {...defaultProps} />);

expect(screen.getByText("Level")).toBeInTheDocument();
expect(screen.getByText("TDH")).toBeInTheDocument();
expect(screen.getByText("xTDH")).toBeInTheDocument();
expect(screen.getByText("REP")).toBeInTheDocument();
expect(screen.getByText("NIC")).toBeInTheDocument();
});

it("calls onSort when clicking an option", () => {
const onSort = jest.fn();
render(
<CommunityMembersMobileSortContent {...defaultProps} onSort={onSort} />
);

fireEvent.click(screen.getByText("TDH"));

expect(onSort).toHaveBeenCalledWith(ApiCommunityMembersSortOption.Tdh);
});

it("shows active styling for selected option", () => {
render(<CommunityMembersMobileSortContent {...defaultProps} />);

const levelButton = screen.getByText("Level").closest("button");
expect(levelButton?.className).toContain("tw-border-primary-500");
});
});
Loading