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 @@ -2,7 +2,6 @@ import React from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SingleWaveDropVoteInput } from '@/components/waves/drop/SingleWaveDropVoteInput';
import { ApiWaveCreditType } from '@/generated/models/ObjectSerializer';

// Mock timers for testing interval behavior
jest.useFakeTimers();
Expand All @@ -12,7 +11,7 @@ describe('SingleWaveDropVoteInput', () => {
voteValue: 0,
minValue: -1000,
maxValue: 1000,
creditType: ApiWaveCreditType.Rep,
label: "Rep",
setVoteValue: jest.fn(),
onSubmit: jest.fn(),
};
Expand All @@ -37,8 +36,8 @@ describe('SingleWaveDropVoteInput', () => {
});

it('displays credit type in input field', () => {
render(<SingleWaveDropVoteInput {...defaultProps} creditType={ApiWaveCreditType.Tdh} />);
render(<SingleWaveDropVoteInput {...defaultProps} label="TDH" />);

expect(screen.getByText('TDH')).toBeInTheDocument();
});

Expand Down
33 changes: 16 additions & 17 deletions __tests__/components/waves/drop/SingleWaveDropVoteSlider.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { render, screen, fireEvent } from '@testing-library/react';
import SingleWaveDropVoteSlider from '@/components/waves/drop/SingleWaveDropVoteSlider';
import { ApiWaveCreditType } from '@/generated/models/ApiWaveCreditType';

// Mock framer-motion
jest.mock('framer-motion', () => ({
Expand Down Expand Up @@ -35,7 +34,7 @@ describe('SingleWaveDropVoteSlider', () => {
minValue: -100,
maxValue: 100,
setVoteValue: jest.fn(),
creditType: ApiWaveCreditType.Rep,
label: "Rep",
};

beforeEach(() => {
Expand All @@ -44,29 +43,29 @@ describe('SingleWaveDropVoteSlider', () => {

it('renders slider with basic elements', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} />);

const sliders = screen.getAllByRole('slider');
expect(sliders.length).toBeGreaterThan(0);
const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '50 REP';
return element?.textContent === '50 Rep';
});
expect(tooltips.length).toBeGreaterThan(0); // Vote value in tooltip
});

it('displays vote value in tooltip', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} voteValue={75} />);

const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '75 REP';
return element?.textContent === '75 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});

it('handles string vote value by defaulting to 0', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} voteValue="invalid" />);

const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '0 REP';
return element?.textContent === '0 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});
Expand Down Expand Up @@ -111,18 +110,18 @@ describe('SingleWaveDropVoteSlider', () => {

it('handles zero range case', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} minValue={0} maxValue={0} voteValue={0} />);

const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '0 REP';
return element?.textContent === '0 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});

it('handles negative vote values', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} voteValue={-25} />);

const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '-25 REP';
return element?.textContent === '-25 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});
Expand Down Expand Up @@ -161,20 +160,20 @@ describe('SingleWaveDropVoteSlider', () => {
});

it('displays correct credit type in tooltip', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} creditType={ApiWaveCreditType.Rep} />);
render(<SingleWaveDropVoteSlider {...defaultProps} label="Rep" />);

const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '50 REP';
return element?.textContent === '50 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});

it('handles edge case where all values are zero', () => {
render(<SingleWaveDropVoteSlider {...defaultProps} minValue={0} maxValue={0} voteValue={0} />);

// Should render without crashing and show 0 value
const tooltips = screen.getAllByText((content, element) => {
return element?.textContent === '0 REP';
return element?.textContent === '0 Rep';
});
expect(tooltips.length).toBeGreaterThan(0);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ describe('SingleWaveDropVoteSubmit', () => {

it('renders vote button with correct initial text', () => {
renderComponent();

expect(screen.getByRole('button')).toBeInTheDocument();
expect(screen.getByText('Vote!')).toBeInTheDocument();
expect(screen.getByText('Vote')).toBeInTheDocument();
});

it('applies correct styling based on drop rank', () => {
Expand All @@ -159,13 +159,13 @@ describe('SingleWaveDropVoteSubmit', () => {
mockCommonApiPost.mockResolvedValue(mockDrop);

renderComponent();

const button = screen.getByRole('button');
fireEvent.click(button);

// Should show loading spinner
await waitFor(() => {
expect(screen.getByText('Vote!')).toBeInTheDocument();
expect(screen.getByText('Vote')).toBeInTheDocument();
});
});

Expand Down Expand Up @@ -234,12 +234,12 @@ describe('SingleWaveDropVoteSubmit', () => {
mockCommonApiPost.mockRejectedValue(mockError);

renderComponent();

const button = screen.getByRole('button');
fireEvent.click(button);

// Button should show initial text
expect(screen.getByText('Vote!')).toBeInTheDocument();
expect(screen.getByText('Vote')).toBeInTheDocument();
});

it('accepts onVoteSuccess callback prop', () => {
Expand Down Expand Up @@ -273,10 +273,10 @@ describe('SingleWaveDropVoteSubmit', () => {

it('renders button with initial vote text', () => {
renderComponent();

const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(screen.getByText('Vote!')).toBeInTheDocument();
expect(screen.getByText('Vote')).toBeInTheDocument();
});

it('exposes handleClick method through ref', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,14 @@ describe('WaveLeaderboardRightSidebarActivityLog', () => {

it('displays voter information with profile picture', () => {
renderComponent();

const voterLink = screen.getByTitle('Voter: voter_user');
expect(voterLink).toHaveAttribute('href', '/voter_user');
expect(screen.getByText('voter_user')).toBeInTheDocument();

const voterPfp = voterLink.querySelector('img');
expect(voterPfp).toHaveAttribute('src', 'https://example.com/voter.jpg');
expect(voterPfp).toBeInTheDocument();
expect(voterPfp).toHaveAttribute('src', expect.stringContaining('voter.jpg'));
expect(voterPfp).toHaveClass('tw-size-5', 'tw-rounded-md');
});

Expand All @@ -138,13 +139,14 @@ describe('WaveLeaderboardRightSidebarActivityLog', () => {

it('displays drop author information with profile picture', () => {
renderComponent();

const authorLink = screen.getByTitle('Drop creator: author_user');
expect(authorLink).toHaveAttribute('href', '/author_user');
expect(screen.getByText('author_user')).toBeInTheDocument();

const authorPfp = authorLink.querySelector('img');
expect(authorPfp).toHaveAttribute('src', 'https://example.com/author.jpg');
expect(authorPfp).toBeInTheDocument();
expect(authorPfp).toHaveAttribute('src', expect.stringContaining('author.jpg'));
});

it('displays drop author information without profile picture', () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/helpers/Helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe("additional helper functions", () => {
test("parseNftDescriptionToHtml replaces newlines and links", () => {
const input = "line1\nhttps://example.com";
expect(parseNftDescriptionToHtml(input)).toBe(
'line1<br /><a href=\'https://example.com\' target="blank" rel="noopener noreferrer">https://example.com</a>'
'line1<br /><a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>'
);
});

Expand Down
9 changes: 3 additions & 6 deletions contexts/wave/hooks/useWaveMessagesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,9 @@ function useWaveMessagesStore() {
}, [waveMessages]);

// Stable function to get data for a key
const getData = useCallback(
(key: string): WaveMessages | undefined => {
return waveMessages[key];
},
[waveMessages]
); // Dependency on store is fine here
const getData = useCallback((key: string): WaveMessages | undefined => {
return waveMessagesRef.current[key];
}, []);

// Stable function to subscribe a listener for a specific key
const subscribe = useCallback(
Expand Down
14 changes: 13 additions & 1 deletion hooks/useDeviceInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,19 @@ export default function useDeviceInfo(): DeviceInfo {

useEffect(() => {
const mq = window.matchMedia("(pointer: coarse)");
const update = () => setInfo(getInfo());
const update = () =>
setInfo((prev) => {
const next = getInfo();
if (
prev.isMobileDevice === next.isMobileDevice &&
prev.hasTouchScreen === next.hasTouchScreen &&
prev.isApp === next.isApp &&
prev.isAppleMobile === next.isAppleMobile
) {
return prev;
}
return next;
});

mq.addEventListener("change", update);
window.addEventListener("resize", update);
Expand Down