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
90 changes: 55 additions & 35 deletions __tests__/components/brain/my-stream/MyStreamWaveChat.test.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,68 @@
import { render, screen, act } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import { editSlice } from '@/store/editSlice';
import MyStreamWaveChat from '@/components/brain/my-stream/MyStreamWaveChat';
import { render, screen, act } from "@testing-library/react";
import React from "react";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import { editSlice } from "@/store/editSlice";
import MyStreamWaveChat from "@/components/brain/my-stream/MyStreamWaveChat";

const replaceMock = jest.fn();
const searchParamsMock = { get: jest.fn() };

jest.mock('next/navigation', () => ({
jest.mock("next/navigation", () => ({
useRouter: () => ({ replace: replaceMock }),
useSearchParams: () => searchParamsMock,
usePathname: jest.fn(),
}));
Comment thread
simo6529 marked this conversation as resolved.

let mockIsMemesWave = false;
jest.mock('@/hooks/useWave', () => ({ useWave: () => ({ isMemesWave: mockIsMemesWave }) }));
jest.mock("@/hooks/useWave", () => ({
useWave: () => ({ isMemesWave: mockIsMemesWave }),
}));

jest.mock('@/components/brain/my-stream/layout/LayoutContext', () => ({
useLayout: () => ({ waveViewStyle: { height: '1px' } })
jest.mock("@/components/brain/my-stream/layout/LayoutContext", () => ({
useLayout: () => ({ waveViewStyle: { height: "1px" } }),
}));

let capturedProps: any = {};
jest.mock('@/components/waves/drops/wave-drops-all', () => ({
jest.mock("@/components/waves/drops/wave-drops-all", () => ({
__esModule: true,
default: (props: any) => {
capturedProps = props;
return <div data-testid="drops" />;
}
},
}));

jest.mock('@/components/waves/CreateDropWaveWrapper', () => ({
CreateDropWaveWrapper: ({ children }: any) => <div>{children}</div>
jest.mock("@/components/waves/CreateDropWaveWrapper", () => ({
CreateDropWaveWrapper: ({ children }: any) => <div>{children}</div>,
}));

jest.mock('@/components/waves/PrivilegedDropCreator', () => ({
jest.mock("@/components/waves/PrivilegedDropCreator", () => ({
__esModule: true,
default: () => <div data-testid="creator" />,
DropMode: { BOTH: 'BOTH' }
DropMode: { BOTH: "BOTH" },
}));

jest.mock('@/components/waves/memes/submission/MobileMemesArtSubmissionBtn', () => ({
__esModule: true,
default: () => <div data-testid="memes-btn" />
}));
jest.mock(
"@/components/waves/memes/submission/MobileMemesArtSubmissionBtn",
() => ({
__esModule: true,
default: () => <div data-testid="memes-btn" />,
})
);

jest.mock('@/hooks/useDeviceInfo', () => ({
jest.mock("@/hooks/useDeviceInfo", () => ({
__esModule: true,
default: () => ({ isApp: false }),
}));

const wave = { id: '10' } as any;
jest.mock("@/components/waves/gallery", () => ({
WaveGallery: () => <div data-testid="gallery" />,
}));

const wave = { id: "10" } as any;
const mockOnDropClick = jest.fn();

describe('MyStreamWaveChat', () => {
describe("MyStreamWaveChat", () => {
let store: any;

beforeEach(() => {
Expand All @@ -66,31 +76,41 @@ describe('MyStreamWaveChat', () => {
});

const renderWithProvider = (component: React.ReactElement) => {
return render(
<Provider store={store}>
{component}
</Provider>
);
return render(<Provider store={store}>{component}</Provider>);
};

it('handles serialNo param and shows memes button', async () => {
searchParamsMock.get.mockReturnValueOnce('5').mockReturnValue(null);
it("handles serialNo param and shows memes button", async () => {
searchParamsMock.get.mockReturnValueOnce("5").mockReturnValue(null);
mockIsMemesWave = true;
await act(async () => {
renderWithProvider(<MyStreamWaveChat wave={wave} />);
renderWithProvider(
<MyStreamWaveChat
wave={wave}
firstUnreadSerialNo={null}
viewMode="chat"
onDropClick={mockOnDropClick}
/>
);
});
expect(replaceMock).toHaveBeenCalled();
expect(capturedProps.initialDrop).toBe(5);
expect(screen.getByTestId('memes-btn')).toBeInTheDocument();
expect(screen.getByTestId("memes-btn")).toBeInTheDocument();
});

it('sets initialDrop null when no param', async () => {
it("sets initialDrop null when no param", async () => {
searchParamsMock.get.mockReturnValue(null);
await act(async () => {
renderWithProvider(<MyStreamWaveChat wave={wave} />);
renderWithProvider(
<MyStreamWaveChat
wave={wave}
firstUnreadSerialNo={null}
viewMode="chat"
onDropClick={mockOnDropClick}
/>
);
});
expect(replaceMock).not.toHaveBeenCalled();
expect(capturedProps.initialDrop).toBeNull();
expect(screen.queryByTestId('memes-btn')).toBeNull();
expect(screen.queryByTestId("memes-btn")).toBeNull();
});
});
57 changes: 36 additions & 21 deletions __tests__/components/brain/my-stream/tabs/MyStreamWaveTabs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,65 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { render, screen } from "@testing-library/react";
import React from "react";

const registerRef = jest.fn();

jest.mock('@/components/brain/my-stream/layout/LayoutContext', () => ({
jest.mock("@/components/brain/my-stream/layout/LayoutContext", () => ({
useLayout: () => ({ registerRef }),
}));

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

jest.mock('@/components/brain/my-stream/tabs/MyStreamWaveTabsMeme', () => ({
jest.mock("@/components/brain/my-stream/tabs/MyStreamWaveTabsMeme", () => ({
__esModule: true,
default: () => <div data-testid="meme" />,
}));

jest.mock('@/components/brain/my-stream/tabs/MyStreamWaveTabsDefault', () => ({
jest.mock("@/components/brain/my-stream/tabs/MyStreamWaveTabsDefault", () => ({
__esModule: true,
default: () => <div data-testid="default" />,
}));

import { MyStreamWaveTabs } from '@/components/brain/my-stream/tabs/MyStreamWaveTabs';
import type { ApiWave } from '@/generated/models/ApiWave';
const { useWave } = require('@/hooks/useWave');
import { MyStreamWaveTabs } from "@/components/brain/my-stream/tabs/MyStreamWaveTabs";
import type { ApiWave } from "@/generated/models/ApiWave";
const { useWave } = require("@/hooks/useWave");

describe('MyStreamWaveTabs', () => {
const wave: ApiWave = { id: 'w1', name: 'Wave 1' } as ApiWave;
describe("MyStreamWaveTabs", () => {
const wave: ApiWave = { id: "w1", name: "Wave 1" } as ApiWave;
const mockToggleViewMode = jest.fn();

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

it('renders meme tabs when wave is a memes wave and registers ref', () => {
it("renders meme tabs when wave is a memes wave and registers ref", () => {
(useWave as jest.Mock).mockReturnValue({ isMemesWave: true });
render(<MyStreamWaveTabs wave={wave} />);
const container = document.getElementById('tabs-container');
expect(screen.getByTestId('meme')).toBeInTheDocument();
expect(screen.queryByTestId('default')).toBeNull();
expect(registerRef).toHaveBeenCalledWith('tabs', container);
render(
<MyStreamWaveTabs
wave={wave}
viewMode="chat"
onToggleViewMode={mockToggleViewMode}
showGalleryToggle={false}
/>
);
const container = document.getElementById("tabs-container");
expect(screen.getByTestId("meme")).toBeInTheDocument();
expect(screen.queryByTestId("default")).toBeNull();
expect(registerRef).toHaveBeenCalledWith("tabs", container);
});

it('renders default tabs when wave is not a memes wave', () => {
it("renders default tabs when wave is not a memes wave", () => {
(useWave as jest.Mock).mockReturnValue({ isMemesWave: false });
render(<MyStreamWaveTabs wave={wave} />);
expect(screen.getByTestId('default')).toBeInTheDocument();
expect(screen.queryByTestId('meme')).toBeNull();
render(
<MyStreamWaveTabs
wave={wave}
viewMode="chat"
onToggleViewMode={mockToggleViewMode}
showGalleryToggle={true}
/>
);
expect(screen.getByTestId("default")).toBeInTheDocument();
expect(screen.queryByTestId("meme")).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -1,65 +1,79 @@
import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import MyStreamWaveTabsDefault from '@/components/brain/my-stream/tabs/MyStreamWaveTabsDefault';
import { SidebarProvider } from '@/hooks/useSidebarState';
import { render, screen, fireEvent } from "@testing-library/react";
import React from "react";
import MyStreamWaveTabsDefault from "@/components/brain/my-stream/tabs/MyStreamWaveTabsDefault";
import { SidebarProvider } from "@/hooks/useSidebarState";

const mockPush = jest.fn();
const mockUseBreakpoint = jest.fn(() => 'LG');
const mockUseBreakpoint = jest.fn(() => "LG");

jest.mock('next/navigation', () => ({
jest.mock("next/navigation", () => ({
useRouter: () => ({ push: mockPush, replace: jest.fn(), back: jest.fn() }),
useSearchParams: () => new URLSearchParams('wave=w1'),
usePathname: () => '/waves',
useSearchParams: () => new URLSearchParams("wave=w1"),
usePathname: () => "/waves",
}));

jest.mock('react-use', () => {
const actual = jest.requireActual('react-use');
jest.mock("react-use", () => {
const actual = jest.requireActual("react-use");
return {
__esModule: true,
...actual,
createBreakpoint: () => (...args: any[]) => mockUseBreakpoint(...args),
createBreakpoint:
() =>
(...args: any[]) =>
mockUseBreakpoint(...args),
};
});

const useContentTab = jest.fn();

jest.mock('@/components/brain/my-stream/MyStreamWaveDesktopTabs', () => ({
jest.mock("@/components/brain/my-stream/MyStreamWaveDesktopTabs", () => ({
__esModule: true,
default: ({ activeTab, setActiveTab }: any) => (
<div>
<span data-testid="active">{activeTab}</span>
<button onClick={() => setActiveTab('NEW')}>change</button>
<button onClick={() => setActiveTab("NEW")}>change</button>
</div>
)
),
}));

jest.mock('@/components/brain/ContentTabContext', () => ({
useContentTab: (...args: any[]) => useContentTab(...args)
jest.mock("@/components/brain/ContentTabContext", () => ({
useContentTab: (...args: any[]) => useContentTab(...args),
}));

describe('MyStreamWaveTabsDefault', () => {
describe("MyStreamWaveTabsDefault", () => {
const mockToggleViewMode = jest.fn();

beforeEach(() => {
mockPush.mockClear();
mockUseBreakpoint.mockReturnValue('LG');
mockUseBreakpoint.mockReturnValue("LG");
useContentTab.mockReset();
mockToggleViewMode.mockClear();
});

it('passes active tab and handles tab change', () => {
it("passes active tab and handles tab change", () => {
const setActiveContentTab = jest.fn();
useContentTab.mockReturnValue({ activeContentTab: 'CHAT', setActiveContentTab });
useContentTab.mockReturnValue({
activeContentTab: "CHAT",
setActiveContentTab,
});
const wave = {
id: 'w1',
name: 'Wave',
id: "w1",
name: "Wave",
picture: null,
contributors_overview: [],
} as any;
render(
<SidebarProvider>
<MyStreamWaveTabsDefault wave={wave} />
<MyStreamWaveTabsDefault
wave={wave}
viewMode="chat"
onToggleViewMode={mockToggleViewMode}
showGalleryToggle={true}
/>
</SidebarProvider>
);
expect(screen.getByTestId('active')).toHaveTextContent('CHAT');
fireEvent.click(screen.getByText('change'));
expect(setActiveContentTab).toHaveBeenCalledWith('NEW');
expect(screen.getByTestId("active")).toHaveTextContent("CHAT");
fireEvent.click(screen.getByText("change"));
expect(setActiveContentTab).toHaveBeenCalledWith("NEW");
});
});
19 changes: 18 additions & 1 deletion components/brain/my-stream/MyStreamWave.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import MyStreamWaveFAQ from "./MyStreamWaveFAQ";
import { useMyStream } from "@/contexts/wave/MyStreamContext";
import { createBreakpoint } from "react-use";
import { getHomeFeedRoute } from "@/helpers/navigation.helpers";
import { useWaveViewMode } from "@/hooks/useWaveViewMode";
import { useWave } from "@/hooks/useWave";

interface MyStreamWaveProps {
readonly waveId: string;
Expand Down Expand Up @@ -68,6 +70,14 @@ const MyStreamWave: React.FC<MyStreamWaveProps> = ({ waveId }) => {
// Get the active tab and utilities from global context
const { activeContentTab } = useContentTab();

// View mode for chat/gallery toggle
const { viewMode, toggleViewMode } = useWaveViewMode(waveId);

// Get wave type info to determine if gallery toggle should be shown
// Show for CHAT type waves (normal waves), hide for RANK, MEMES, and DMs
const { isRankWave, isMemesWave, isDm } = useWave(wave);
const showGalleryToggle = !isRankWave && !isMemesWave && !isDm;

useBreakpoint();

// For handling clicks on drops
Expand All @@ -88,6 +98,8 @@ const MyStreamWave: React.FC<MyStreamWaveProps> = ({ waveId }) => {
<MyStreamWaveChat
wave={wave}
firstUnreadSerialNo={enhancedData.firstUnreadSerialNo}
viewMode={showGalleryToggle ? viewMode : "chat"}
onDropClick={onDropClick}
/>
),
[MyStreamWaveTab.LEADERBOARD]: (
Expand All @@ -109,7 +121,12 @@ const MyStreamWave: React.FC<MyStreamWaveProps> = ({ waveId }) => {
key={stableWaveKey}
>
{/* Always render tab container (hidden on app inside MyStreamWaveTabs) */}
<MyStreamWaveTabs wave={wave} />
<MyStreamWaveTabs
wave={wave}
viewMode={viewMode}
onToggleViewMode={toggleViewMode}
showGalleryToggle={showGalleryToggle}
/>

<div
className="tw-relative tw-flex-grow tw-overflow-hidden"
Expand Down
Loading