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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ describe('BrainLeftSidebarWave', () => {
contributors: [],
newDropsCount: { count: 2, latestDropTimestamp: 123 },
isPinned: false,
unreadDropsCount: 0,
latestReadTimestamp: 0,
firstUnreadDropSerialNo: null,
isMuted: false,
} as any;

beforeEach(() => {
Expand Down Expand Up @@ -85,12 +89,38 @@ describe('BrainLeftSidebarWave', () => {
render(<BrainLeftSidebarWave wave={baseWave} onHover={onHover} />);
const link = screen.getByRole('link');
await userEvent.click(link);
expect(setActiveWave).toHaveBeenCalledWith('1', { isDirectMessage: false });
expect(setActiveWave).toHaveBeenCalledWith('1', { isDirectMessage: false, serialNo: null });
});

it('shows drop indicators for non-chat waves', () => {
const dropWave = { ...baseWave, id: '2', type: ApiWaveType.Approve };
render(<BrainLeftSidebarWave wave={dropWave} onHover={onHover} />);
expect(screen.getByTestId('drop-time')).toHaveTextContent('123');
});

it('includes firstUnreadDropSerialNo in href when present', () => {
const waveWithUnread = { ...baseWave, id: '3', firstUnreadDropSerialNo: 42 };
render(<BrainLeftSidebarWave wave={waveWithUnread} onHover={onHover} />);
expect(screen.getByRole('link')).toHaveAttribute('href', '/waves?wave=3&serialNo=42');
});

it('does not include serialNo in href when firstUnreadDropSerialNo is null', () => {
const waveWithoutUnread = { ...baseWave, id: '4', firstUnreadDropSerialNo: null };
render(<BrainLeftSidebarWave wave={waveWithoutUnread} onHover={onHover} />);
expect(screen.getByRole('link')).toHaveAttribute('href', '/waves?wave=4');
});

it('shows muted indicator when wave is muted', () => {
const mutedWave = { ...baseWave, id: '5', isMuted: true };
render(<BrainLeftSidebarWave wave={mutedWave} onHover={onHover} />);
const bellSlashIcons = document.querySelectorAll('[data-icon="bell-slash"]');
expect(bellSlashIcons.length).toBeGreaterThan(0);
});

it('does not show muted indicator when wave is not muted', () => {
const unmutedWave = { ...baseWave, id: '6', isMuted: false };
render(<BrainLeftSidebarWave wave={unmutedWave} onHover={onHover} />);
const bellSlashIcons = document.querySelectorAll('[data-icon="bell-slash"]');
expect(bellSlashIcons.length).toBe(0);
});
});
93 changes: 93 additions & 0 deletions __tests__/components/drops/view/DropsList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ jest.mock("@/components/drops/view/HighlightDropWrapper", () => ({
},
}));

jest.mock("@/components/drops/view/UnreadDivider", () => ({
__esModule: true,
default: () => <div data-testid="unread-divider" />,
}));

describe("DropsList", () => {
beforeEach(() => {
dropProps = [];
Expand Down Expand Up @@ -84,4 +89,92 @@ describe("DropsList", () => {
expect(dropProps).toHaveLength(1);
expect(lightProps).toHaveLength(1);
});

it("renders unread divider when unreadDividerSerialNo matches a drop", () => {
const drops: any = [
{ stableKey: "a", serial_no: 1, type: DropSize.FULL, wave: { id: "w" } },
{ stableKey: "b", serial_no: 2, type: DropSize.FULL, wave: { id: "w" } },
{ stableKey: "c", serial_no: 3, type: DropSize.FULL, wave: { id: "w" } },
];

render(
<DropsList
scrollContainerRef={{ current: null }}
drops={drops}
showWaveInfo={false}
activeDrop={null}
showReplyAndQuote={false}
onReply={jest.fn()}
onQuote={jest.fn()}
onReplyClick={jest.fn()}
serialNo={null}
targetDropRef={null}
parentContainerRef={undefined}
onQuoteClick={jest.fn()}
onDropContentClick={jest.fn()}
dropViewDropId={null}
unreadDividerSerialNo={2}
/>
);

expect(screen.getByTestId("unread-divider")).toBeInTheDocument();
});

it("does not render unread divider when unreadDividerSerialNo is null", () => {
const drops: any = [
{ stableKey: "a", serial_no: 1, type: DropSize.FULL, wave: { id: "w" } },
{ stableKey: "b", serial_no: 2, type: DropSize.FULL, wave: { id: "w" } },
];

render(
<DropsList
scrollContainerRef={{ current: null }}
drops={drops}
showWaveInfo={false}
activeDrop={null}
showReplyAndQuote={false}
onReply={jest.fn()}
onQuote={jest.fn()}
onReplyClick={jest.fn()}
serialNo={null}
targetDropRef={null}
parentContainerRef={undefined}
onQuoteClick={jest.fn()}
onDropContentClick={jest.fn()}
dropViewDropId={null}
unreadDividerSerialNo={null}
/>
);

expect(screen.queryByTestId("unread-divider")).not.toBeInTheDocument();
});

it("does not render unread divider when unreadDividerSerialNo does not match any drop", () => {
const drops: any = [
{ stableKey: "a", serial_no: 1, type: DropSize.FULL, wave: { id: "w" } },
{ stableKey: "b", serial_no: 2, type: DropSize.FULL, wave: { id: "w" } },
];

render(
<DropsList
scrollContainerRef={{ current: null }}
drops={drops}
showWaveInfo={false}
activeDrop={null}
showReplyAndQuote={false}
onReply={jest.fn()}
onQuote={jest.fn()}
onReplyClick={jest.fn()}
serialNo={null}
targetDropRef={null}
parentContainerRef={undefined}
onQuoteClick={jest.fn()}
onDropContentClick={jest.fn()}
dropViewDropId={null}
unreadDividerSerialNo={999}
/>
);

expect(screen.queryByTestId("unread-divider")).not.toBeInTheDocument();
});
});
21 changes: 21 additions & 0 deletions __tests__/components/drops/view/UnreadDivider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { render, screen } from '@testing-library/react';
import UnreadDivider from '@/components/drops/view/UnreadDivider';

describe('UnreadDivider', () => {
it('renders with default label', () => {
render(<UnreadDivider />);
expect(screen.getByText('New Messages')).toBeInTheDocument();
});

it('renders with custom label', () => {
render(<UnreadDivider label="Unread Items" />);
expect(screen.getByText('Unread Items')).toBeInTheDocument();
});

it('renders horizontal lines', () => {
const { container } = render(<UnreadDivider />);
const lines = container.querySelectorAll(String.raw`.tw-h-0\.5.tw-bg-rose-500`);
expect(lines.length).toBe(2);
});
});

82 changes: 55 additions & 27 deletions __tests__/components/waves/drop/SingleWaveDropChat.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { render, fireEvent, act } from '@testing-library/react';
import React from 'react';
import { SingleWaveDropChat } from '@/components/waves/drop/SingleWaveDropChat';
import { SingleWaveDropChat } from "@/components/waves/drop/SingleWaveDropChat";
import { act, fireEvent, render } from "@testing-library/react";
Comment thread
prxt6529 marked this conversation as resolved.

jest.mock('@/hooks/useDeviceInfo', () => () => ({
jest.mock("@/hooks/useDeviceInfo", () => () => ({
isMobileDevice: true,
hasTouchScreen: true,
isApp: true,
Expand All @@ -12,7 +11,7 @@ jest.mock('@/hooks/useDeviceInfo', () => () => ({
// Mock useAndroidKeyboard with configurable values
let mockKeyboardVisible = false;

jest.mock('@/hooks/useAndroidKeyboard', () => ({
jest.mock("@/hooks/useAndroidKeyboard", () => ({
useAndroidKeyboard: () => ({
isVisible: mockKeyboardVisible,
keyboardHeight: mockKeyboardVisible ? 350 : 0,
Expand All @@ -21,18 +20,41 @@ jest.mock('@/hooks/useAndroidKeyboard', () => ({
}));

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

jest.mock('@/components/waves/CreateDropWaveWrapper', () => ({ CreateDropWaveWrapper: ({ children }: any) => <div data-testid="wrapper">{children}</div>, CreateDropWaveWrapperContext: { SINGLE_DROP: 'SINGLE_DROP' } }));
jest.mock("@/components/waves/CreateDropWaveWrapper", () => ({
CreateDropWaveWrapper: ({ children }: any) => (
<div data-testid="wrapper">{children}</div>
),
CreateDropWaveWrapperContext: { SINGLE_DROP: "SINGLE_DROP" },
}));

jest.mock('@/components/waves/PrivilegedDropCreator', () => ({ __esModule: true, default: (props: any) => <button data-testid="creator" type="button" onClick={props.onCancelReplyQuote} data-part={props.activeDrop?.partId} data-action={props.activeDrop?.action} />, DropMode: { BOTH: 'BOTH' } }));
jest.mock("@/components/waves/PrivilegedDropCreator", () => ({
__esModule: true,
default: (props: any) => (
<button
data-testid="creator"
type="button"
onClick={props.onCancelReplyQuote}
data-part={props.activeDrop?.partId}
data-action={props.activeDrop?.action}
/>
),
DropMode: { BOTH: "BOTH" },
}));

// Mock window.matchMedia for useDeviceInfo hook
Object.defineProperty(window, 'matchMedia', {
// Mock globalThis.matchMedia for useDeviceInfo hook
Object.defineProperty(globalThis, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(() => ({
matches: false,
media: '',
media: "",
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
Expand All @@ -42,18 +64,18 @@ Object.defineProperty(window, 'matchMedia', {
})),
});

describe('SingleWaveDropChat', () => {
describe("SingleWaveDropChat", () => {
beforeEach(() => {
mockKeyboardVisible = false;
});

it('handles reply and reset actions', () => {
const wave: any = { id: 'w1' };
const drop: any = { id: 'd1' };
it("handles reply and reset actions", () => {
const wave: any = { id: "w1" };
const drop: any = { id: "d1" };
render(<SingleWaveDropChat wave={wave} drop={drop} />);

expect(capturedProps.waveId).toBe('w1');
expect(capturedProps.dropId).toBe('d1');
expect(capturedProps.waveId).toBe("w1");
expect(capturedProps.dropId).toBe("d1");

act(() => capturedProps.onReply({ drop, partId: 2 }));
expect(document.querySelector('[data-part="2"]')).toBeInTheDocument();
Expand All @@ -62,29 +84,35 @@ describe('SingleWaveDropChat', () => {
expect(document.querySelector('[data-part="1"]')).toBeInTheDocument();
});

it('applies safe-area-inset-bottom padding when keyboard is hidden', () => {
it("applies safe-area-inset-bottom padding when keyboard is hidden", () => {
mockKeyboardVisible = false;

const wave: any = { id: 'w1' };
const drop: any = { id: 'd1' };
const wave: any = { id: "w1" };
const drop: any = { id: "d1" };
render(<SingleWaveDropChat wave={wave} drop={drop} />);

const wrapper = document.querySelector('[data-testid="wrapper"]') as HTMLElement;
const wrapper = document.querySelector(
'[data-testid="wrapper"]'
) as HTMLElement;
const container = wrapper?.parentElement as HTMLElement;

expect(container.style.paddingBottom).toBe('calc(env(safe-area-inset-bottom))');
expect(container.style.paddingBottom).toBe(
"calc(env(safe-area-inset-bottom))"
);
});

it('applies 0px padding when keyboard is visible', () => {
it("applies 0px padding when keyboard is visible", () => {
mockKeyboardVisible = true;

const wave: any = { id: 'w1' };
const drop: any = { id: 'd1' };
const wave: any = { id: "w1" };
const drop: any = { id: "d1" };
render(<SingleWaveDropChat wave={wave} drop={drop} />);

const wrapper = document.querySelector('[data-testid="wrapper"]') as HTMLElement;
const wrapper = document.querySelector(
'[data-testid="wrapper"]'
) as HTMLElement;
const container = wrapper?.parentElement as HTMLElement;

expect(container.style.paddingBottom).toBe('0px');
expect(container.style.paddingBottom).toBe("0px");
});
});
1 change: 1 addition & 0 deletions __tests__/components/waves/drops/WaveDropActions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jest.mock('@/components/waves/drops/WaveDropActionsOptions', () => () => <div da
jest.mock('@/components/waves/drops/WaveDropActionsOpen', () => () => <div data-testid="open" />);
jest.mock('@/components/waves/drops/WaveDropFollowAuthor', () => () => <div data-testid="follow" />);
jest.mock('@/components/waves/drops/WaveDropActionsAddReaction', () => () => <div data-testid="add-reaction" />);
jest.mock('@/components/waves/drops/WaveDropActionsMarkUnread', () => () => <div data-testid="mark-unread" />);

jest.mock('@/hooks/drops/useDropInteractionRules', () => ({ useDropInteractionRules: jest.fn() }));
jest.mock('@/contexts/SeizeSettingsContext', () => ({ useSeizeSettings: jest.fn() }));
Expand Down
Loading