Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
508243d
wip
simo6529 Jan 8, 2026
8392624
wip
simo6529 Jan 8, 2026
3ff5988
wip
simo6529 Jan 8, 2026
1e5426d
wip
simo6529 Jan 8, 2026
027fffe
Merge branch 'main' into index-redesign
simo6529 Jan 8, 2026
33b796d
wip
simo6529 Jan 9, 2026
283105f
wip
simo6529 Jan 9, 2026
165ad92
wip
simo6529 Jan 9, 2026
4fc3aa7
wip
simo6529 Jan 9, 2026
122da89
wip
simo6529 Jan 9, 2026
72197e1
wip
simo6529 Jan 9, 2026
a4c9fb5
Merge branch 'main' into index-redesign
simo6529 Jan 9, 2026
f227c4f
wip
simo6529 Jan 9, 2026
b6860f8
wip
simo6529 Jan 9, 2026
6dc7e3c
wip
simo6529 Jan 9, 2026
e06c61f
wip
simo6529 Jan 9, 2026
829c9dc
wip
simo6529 Jan 10, 2026
09e5e49
Merge branch 'main' into index-redesign
simo6529 Jan 10, 2026
5958626
wip
simo6529 Jan 12, 2026
5e3526e
wip
simo6529 Jan 12, 2026
25073bb
wip
simo6529 Jan 12, 2026
7d63b9d
wip
simo6529 Jan 12, 2026
0ba8171
wip
ragnep Jan 12, 2026
cbeadc6
Merge branch 'main' into index-redesign
ragnep Jan 12, 2026
d8461db
wip
ragnep Jan 13, 2026
ede1f58
Merge branch 'main' into index-redesign
ragnep Jan 13, 2026
8016a0d
wip
ragnep Jan 13, 2026
3dfc5a2
wip
ragnep Jan 14, 2026
38ba440
wip
ragnep Jan 14, 2026
f4202fc
wip
ragnep Jan 14, 2026
88fa05c
wip
ragnep Jan 15, 2026
0854974
wip
ragnep Jan 15, 2026
b2c7ae6
Merge branch 'main' into index-redesign
ragnep Jan 15, 2026
ff8d417
wip
ragnep Jan 15, 2026
d8bd5da
wip
ragnep Jan 16, 2026
54aa2c4
wip
simo6529 Jan 16, 2026
bdaf3d4
wip
ragnep Jan 16, 2026
1499116
wip
simo6529 Jan 16, 2026
09e60c5
wip
simo6529 Jan 16, 2026
60a07ed
wip
simo6529 Jan 16, 2026
90ccc1f
wip
ragnep Jan 16, 2026
15de448
Merge branch 'index-redesign-2' into index-redesign
simo6529 Jan 16, 2026
55f136e
wip
ragnep Jan 16, 2026
f6fad7e
Merge branch 'main' into index-redesign
simo6529 Jan 16, 2026
42cb7a5
wip
simo6529 Jan 16, 2026
0f4bb07
wip
simo6529 Jan 16, 2026
009671a
wip
simo6529 Jan 16, 2026
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
8 changes: 8 additions & 0 deletions __tests__/components/DropListItemContentMediaImage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ describe('DropListItemContentMediaImage', () => {
fireEvent.click(screen.getByLabelText('View drop details'));
expect(onContainerClick).toHaveBeenCalled();
});

it('does not open modal when disableModal is true', () => {
render(<DropListItemContentMediaImage src="img" disableModal />);
const img = screen.getByAltText('Drop media');
fireEvent.load(img);
fireEvent.click(img);
expect(screen.queryByLabelText('View drop details')).not.toBeInTheDocument();
});
});

describe('DropListItemContentMediaImage retry', () => {
Expand Down
111 changes: 83 additions & 28 deletions __tests__/components/brain/right-sidebar/WaveContent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,106 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { ApiWaveType } from '@/generated/models/ApiWaveType';
import { Mode, SidebarTab } from '@/components/brain/right-sidebar/BrainRightSidebar';
import { WaveContent } from '@/components/brain/right-sidebar/WaveContent';
import { render, screen } from "@testing-library/react";
import React from "react";
import { ApiWaveType } from "@/generated/models/ApiWaveType";
import {
Mode,
SidebarTab,
} from "@/components/brain/right-sidebar/BrainRightSidebar";
import { WaveContent } from "@/components/brain/right-sidebar/WaveContent";

const useWaveTimers = jest.fn();

jest.mock('@/hooks/useWaveTimers', () => ({ useWaveTimers: (...args: any[]) => useWaveTimers(...args) }));
jest.mock("@/hooks/useWaveTimers", () => ({
useWaveTimers: (...args: any[]) => useWaveTimers(...args),
}));

jest.mock('@/components/waves/header/WaveHeader', () => ({
jest.mock("@/components/waves/header/WaveHeader", () => ({
__esModule: true,
default: () => <div data-testid="header">header</div>,
WaveHeaderPinnedSide: { LEFT: 'LEFT' }
WaveHeaderPinnedSide: { LEFT: "LEFT" },
}));

jest.mock('@/components/common/TabToggleWithOverflow', () => ({
jest.mock("@/components/common/TabToggleWithOverflow", () => ({
__esModule: true,
TabToggleWithOverflow: ({ options, activeKey }: any) => (
<div data-testid="tabs">{activeKey}-{options.length}</div>
)
<div data-testid="tabs">
{activeKey}-{options.length}
</div>
),
}));

jest.mock('@/components/waves/winners/WaveWinnersSmall', () => ({ __esModule: true, WaveWinnersSmall: () => <div>winners</div> }));
jest.mock('@/components/waves/small-leaderboard/WaveSmallLeaderboard', () => ({ __esModule: true, WaveSmallLeaderboard: () => <div>leaderboard</div> }));
jest.mock('@/components/waves/leaderboard/sidebar/WaveLeaderboardRightSidebarVoters', () => ({ __esModule: true, WaveLeaderboardRightSidebarVoters: () => <div>voters</div> }));
jest.mock('@/components/waves/leaderboard/sidebar/WaveLeaderboardRightSidebarActivityLogs', () => ({ __esModule: true, WaveLeaderboardRightSidebarActivityLogs: () => <div>logs</div> }));
jest.mock('@/components/brain/right-sidebar/BrainRightSidebarContent', () => ({ __esModule: true, default: () => <div>content</div> }));
jest.mock('@/components/brain/right-sidebar/BrainRightSidebarFollowers', () => ({ __esModule: true, default: () => <div>followers</div> }));
jest.mock("@/components/waves/winners/WaveWinnersSmall", () => ({
__esModule: true,
WaveWinnersSmall: () => <div>winners</div>,
}));
jest.mock("@/components/waves/small-leaderboard/WaveSmallLeaderboard", () => ({
__esModule: true,
WaveSmallLeaderboard: () => <div>leaderboard</div>,
}));
jest.mock(
"@/components/waves/leaderboard/sidebar/WaveLeaderboardRightSidebarVoters",
() => ({
__esModule: true,
WaveLeaderboardRightSidebarVoters: () => <div>voters</div>,
})
);
jest.mock(
"@/components/waves/leaderboard/sidebar/WaveLeaderboardRightSidebarActivityLogs",
() => ({
__esModule: true,
WaveLeaderboardRightSidebarActivityLogs: () => <div>logs</div>,
})
);
jest.mock("@/components/brain/right-sidebar/BrainRightSidebarContent", () => ({
__esModule: true,
default: () => <div>content</div>,
}));
jest.mock(
"@/components/brain/right-sidebar/BrainRightSidebarFollowers",
() => ({ __esModule: true, default: () => <div>followers</div> })
);

jest.mock("next/navigation", () => ({
useRouter: () => ({ push: jest.fn() }),
usePathname: () => "/test",
useSearchParams: () => new URLSearchParams(),
}));

describe('WaveContent', () => {
const wave = { wave: { type: ApiWaveType.Chat }, name: 'Wave' } as any;
describe("WaveContent", () => {
const wave = { wave: { type: ApiWaveType.Chat }, name: "Wave" } as any;

it('renders non-rank wave without tabs', () => {
useWaveTimers.mockReturnValue({ voting: { isCompleted: false }, decisions: { firstDecisionDone: false } });
it("renders non-rank wave without tabs", () => {
useWaveTimers.mockReturnValue({
voting: { isCompleted: false },
decisions: { firstDecisionDone: false },
});
render(
<WaveContent wave={wave} mode={Mode.CONTENT} setMode={jest.fn()} activeTab={SidebarTab.ABOUT} setActiveTab={jest.fn()} onDropClick={jest.fn()} />
<WaveContent
wave={wave}
mode={Mode.CONTENT}
setMode={jest.fn()}
activeTab={SidebarTab.ABOUT}
setActiveTab={jest.fn()}
/>
);
expect(screen.getByTestId('header')).toBeInTheDocument();
expect(screen.queryByTestId('tabs')).toBeNull();
expect(screen.getByText('content')).toBeInTheDocument();
expect(screen.getByTestId("header")).toBeInTheDocument();
expect(screen.queryByTestId("tabs")).toBeNull();
expect(screen.getByText("content")).toBeInTheDocument();
});

it('switches tab to about when voting completed', () => {
it("switches tab to about when voting completed", () => {
const setActiveTab = jest.fn();
useWaveTimers.mockReturnValue({ voting: { isCompleted: true }, decisions: { firstDecisionDone: true } });
useWaveTimers.mockReturnValue({
voting: { isCompleted: true },
decisions: { firstDecisionDone: true },
});
render(
<WaveContent wave={{ wave: { type: ApiWaveType.Rank }, name: 'Wave' } as any} mode={Mode.CONTENT} setMode={jest.fn()} activeTab={SidebarTab.LEADERBOARD} setActiveTab={setActiveTab} onDropClick={jest.fn()} />
<WaveContent
wave={{ wave: { type: ApiWaveType.Rank }, name: "Wave" } as any}
mode={Mode.CONTENT}
setMode={jest.fn()}
activeTab={SidebarTab.LEADERBOARD}
setActiveTab={setActiveTab}
/>
);
expect(setActiveTab).toHaveBeenCalledWith(SidebarTab.ABOUT);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,19 @@ describe('DropListItemContentMediaVideo', () => {
render(<DropListItemContentMediaVideo src="foo.mp4" />);
expect(document.querySelector('video')).toBeTruthy(); // Video is still rendered, just paused
});

it('does not autoplay when disableAutoPlay is true', () => {
const ref = { current: document.createElement('div') } as React.RefObject<HTMLDivElement>;
mockUseInView.mockReturnValue([ref, true]);
mockUseOptimizedVideo.mockReturnValue({ playableUrl: 'foo.mp4', isHls: false });

const playSpy = jest.fn().mockResolvedValue(undefined);
Object.defineProperty(HTMLVideoElement.prototype, 'play', {
writable: true,
value: playSpy,
});

render(<DropListItemContentMediaVideo src="foo.mp4" disableAutoPlay />);
expect(playSpy).not.toHaveBeenCalled();
});
});
54 changes: 35 additions & 19 deletions __tests__/hooks/useWaveDropsLeaderboard.extra.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { renderHook, waitFor } from '@testing-library/react';
import { useWaveDropsLeaderboard, WaveDropsLeaderboardSort } from '@/hooks/useWaveDropsLeaderboard';
import { useInfiniteQuery, useQueryClient, useQuery } from '@tanstack/react-query';
import { renderHook, waitFor } from "@testing-library/react";
import {
useWaveDropsLeaderboard,
WaveDropsLeaderboardSort,
} from "@/hooks/useWaveDropsLeaderboard";
import {
useInfiniteQuery,
useQueryClient,
useQuery,
} from "@tanstack/react-query";

jest.mock('@tanstack/react-query', () => ({
jest.mock("@tanstack/react-query", () => ({
useInfiniteQuery: jest.fn(),
useQuery: jest.fn(),
useQueryClient: jest.fn(),
keepPreviousData: {},
}));
jest.mock('react-use', () => ({ useDebounce: jest.fn() }));
jest.mock('@/services/api/common-api', () => ({ commonApiFetch: jest.fn() }));
jest.mock('@/hooks/useCapacitor', () => () => ({ isCapacitor: false }));
jest.mock('@/helpers/waves/wave-drops.helpers', () => ({
generateUniqueKeys: jest.fn((a:any)=>a),
mapToExtendedDrops: jest.fn((pages:any)=> pages.flatMap((p:any)=>p.drops)),
jest.mock("react-use", () => ({ useDebounce: jest.fn() }));
jest.mock("@/services/api/common-api", () => ({ commonApiFetch: jest.fn() }));
jest.mock("@/hooks/useCapacitor", () => () => ({ isCapacitor: false }));
jest.mock("@/helpers/waves/wave-drops.helpers", () => ({
generateUniqueKeys: jest.fn((a: any) => a),
mapToExtendedDrops: jest.fn((pages: any) =>
pages.flatMap((p: any) => p.drops)
),
}));

const fetchNextPage = jest.fn();
const queryClientMock = { prefetchInfiniteQuery: jest.fn(), removeQueries: jest.fn() };
const queryClientMock = {
prefetchInfiniteQuery: jest.fn(),
removeQueries: jest.fn(),
};
(useQueryClient as jest.Mock).mockReturnValue(queryClientMock);
(useQuery as jest.Mock).mockReturnValue({});

Expand All @@ -33,29 +45,33 @@ beforeEach(() => {
});
});

describe('useWaveDropsLeaderboard extra', () => {
it('maps pages to drops', async () => {
describe("useWaveDropsLeaderboard extra", () => {
it("maps pages to drops", async () => {
(useInfiniteQuery as jest.Mock).mockReturnValue({
data: { pages: [{ wave: {}, drops: [{ id: 'a' }, { id: 'b' }] }] },
data: { pages: [{ wave: {}, drops: [{ id: "a" }, { id: "b" }] }] },
fetchNextPage,
hasNextPage: true,
isFetching: false,
isFetchingNextPage: false,
refetch: jest.fn(),
});
const { result } = renderHook(() =>
useWaveDropsLeaderboard({ waveId: '1', connectedProfileHandle: 'h' })
useWaveDropsLeaderboard({ waveId: "1" })
);
await waitFor(() => result.current.drops.length === 2);
expect(result.current.drops).toEqual([{ id: 'a' }, { id: 'b' }]);
expect(result.current.drops).toEqual([{ id: "a" }, { id: "b" }]);
expect(result.current.isFetching).toBe(false);
});

it('prefetches with correct sort', () => {
it("prefetches with correct sort", () => {
renderHook(() =>
useWaveDropsLeaderboard({ waveId: '2', connectedProfileHandle: 'h', sort: WaveDropsLeaderboardSort.CREATED_AT })
useWaveDropsLeaderboard({
waveId: "2",
sort: WaveDropsLeaderboardSort.CREATED_AT,
})
);
const call = (queryClientMock.prefetchInfiniteQuery as jest.Mock).mock.calls[0][0];
const call = (queryClientMock.prefetchInfiniteQuery as jest.Mock).mock
.calls[0][0];
expect(call.queryKey[1].sort).toBe(WaveDropsLeaderboardSort.CREATED_AT);
});
});
49 changes: 32 additions & 17 deletions __tests__/hooks/useWaveDropsLeaderboard.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { renderHook, act } from '@testing-library/react';
import { useWaveDropsLeaderboard } from '@/hooks/useWaveDropsLeaderboard';
import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query';
import { renderHook, act } from "@testing-library/react";
import { useWaveDropsLeaderboard } from "@/hooks/useWaveDropsLeaderboard";
import {
useInfiniteQuery,
useQuery,
useQueryClient,
} from "@tanstack/react-query";

jest.mock('@tanstack/react-query', () => ({
jest.mock("@tanstack/react-query", () => ({
useInfiniteQuery: jest.fn(),
useQuery: jest.fn(),
useQueryClient: jest.fn(),
keepPreviousData: {},
}));
jest.mock('react-use', () => ({ useDebounce: jest.fn() }));
jest.mock('@/services/api/common-api', () => ({ commonApiFetch: jest.fn() }));
jest.mock('@/hooks/useCapacitor', () => () => ({ isCapacitor: false }));
jest.mock('@/helpers/waves/wave-drops.helpers', () => ({
jest.mock("react-use", () => ({ useDebounce: jest.fn() }));
jest.mock("@/services/api/common-api", () => ({ commonApiFetch: jest.fn() }));
jest.mock("@/hooks/useCapacitor", () => () => ({ isCapacitor: false }));
jest.mock("@/helpers/waves/wave-drops.helpers", () => ({
generateUniqueKeys: jest.fn((a) => a),
mapToExtendedDrops: jest.fn((pages) => pages.flatMap((p: any) => p.drops)),
}));

const queryClientMock = { prefetchInfiniteQuery: jest.fn(), removeQueries: jest.fn() };
const queryClientMock = {
prefetchInfiniteQuery: jest.fn(),
removeQueries: jest.fn(),
};
(useQueryClient as jest.Mock).mockReturnValue(queryClientMock);
(useInfiniteQuery as jest.Mock).mockReturnValue({
data: { pages: [] },
Expand All @@ -28,8 +35,8 @@ const queryClientMock = { prefetchInfiniteQuery: jest.fn(), removeQueries: jest.
});
(useQuery as jest.Mock).mockReturnValue({});

describe('useWaveDropsLeaderboard', () => {
it('calls fetchNextPage via manualFetch when more pages', async () => {
describe("useWaveDropsLeaderboard", () => {
it("calls fetchNextPage via manualFetch when more pages", async () => {
const fetchNext = jest.fn();
(useInfiniteQuery as jest.Mock).mockReturnValue({
data: { pages: [] },
Expand All @@ -39,20 +46,26 @@ describe('useWaveDropsLeaderboard', () => {
isFetchingNextPage: false,
refetch: jest.fn(),
});
const { result } = renderHook(() => useWaveDropsLeaderboard({ waveId: '1', connectedProfileHandle: 'h' }));
const { result } = renderHook(() =>
useWaveDropsLeaderboard({ waveId: "1" })
);
await act(async () => {
await result.current.manualFetch();
});
expect(fetchNext).toHaveBeenCalled();
});

it('removes queries on unmount', () => {
const { unmount } = renderHook(() => useWaveDropsLeaderboard({ waveId: '2', connectedProfileHandle: 'h' }));
it("removes queries on unmount", () => {
const { unmount } = renderHook(() =>
useWaveDropsLeaderboard({ waveId: "2" })
);
unmount();
expect(queryClientMock.removeQueries).toHaveBeenCalledWith({ queryKey: ['DROPS', { waveId: '2' }] });
expect(queryClientMock.removeQueries).toHaveBeenCalledWith({
queryKey: ["DROPS", { waveId: "2" }],
});
});

it('does not fetch next page when none left', async () => {
it("does not fetch next page when none left", async () => {
const fetchNext = jest.fn();
(useInfiniteQuery as jest.Mock).mockReturnValue({
data: { pages: [] },
Expand All @@ -62,7 +75,9 @@ describe('useWaveDropsLeaderboard', () => {
isFetchingNextPage: false,
refetch: jest.fn(),
});
const { result } = renderHook(() => useWaveDropsLeaderboard({ waveId: '3', connectedProfileHandle: 'h' }));
const { result } = renderHook(() =>
useWaveDropsLeaderboard({ waveId: "3" })
);
await act(async () => {
await result.current.manualFetch();
});
Expand Down
Loading