Skip to content

Commit

Permalink
fix: schedule cooldown removal from useCooldownTimer hook instead of …
Browse files Browse the repository at this point in the history
…CooldownTimer (#2208)
  • Loading branch information
MartinCupela committed Dec 13, 2023
1 parent ff554ad commit 9523b45
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 16 deletions.
24 changes: 14 additions & 10 deletions src/components/MessageInput/CooldownTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ export type CooldownTimerProps = {
cooldownInterval: number;
setCooldownRemaining: React.Dispatch<React.SetStateAction<number | undefined>>;
};
export const CooldownTimer = ({ cooldownInterval, setCooldownRemaining }: CooldownTimerProps) => {
const [seconds, setSeconds] = useState(cooldownInterval);
export const CooldownTimer = ({ cooldownInterval }: CooldownTimerProps) => {
const [seconds, setSeconds] = useState<number | undefined>();

useEffect(() => {
const countdownInterval = setInterval(() => {
if (seconds > 0) {
let countdownTimeout: ReturnType<typeof setTimeout>;
if (typeof seconds === 'number' && seconds > 0) {
countdownTimeout = setTimeout(() => {
setSeconds(seconds - 1);
} else {
setCooldownRemaining(0);
}
}, 1000);
}, 1000);
}
return () => {
clearTimeout(countdownTimeout);
};
}, [seconds]);

return () => clearInterval(countdownInterval);
});
useEffect(() => {
setSeconds(cooldownInterval ?? 0);
}, [cooldownInterval]);

return (
<div className='str-chat__message-input-cooldown' data-testid='cooldown-timer'>
Expand Down
65 changes: 65 additions & 0 deletions src/components/MessageInput/__tests__/CooldownTimer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { CooldownTimer } from '../CooldownTimer';
import '@testing-library/jest-dom';

jest.useFakeTimers();

const TIMER_TEST_ID = 'cooldown-timer';
const remainingProp = 'cooldownInterval';
describe('CooldownTimer', () => {
it('renders CooldownTimer component', () => {
render(<CooldownTimer />);
expect(screen.getByTestId(TIMER_TEST_ID)).toHaveTextContent('0');
});

it('initializes with correct state based on cooldownRemaining prop', () => {
const props = { [remainingProp]: 10 };
render(<CooldownTimer {...props} />);
expect(screen.getByTestId(TIMER_TEST_ID)).toHaveTextContent('10');
});

it('updates countdown logic correctly', () => {
const cooldownRemaining = 5;
const props = { [remainingProp]: cooldownRemaining };
render(<CooldownTimer {...props} />);

for (let countDown = cooldownRemaining; countDown >= 0; countDown--) {
expect(screen.getByTestId(TIMER_TEST_ID)).toHaveTextContent(countDown.toString());
act(() => {
jest.runAllTimers();
});
}
expect(screen.getByTestId(TIMER_TEST_ID)).toHaveTextContent('0');
});

it('resets countdown when cooldownRemaining prop changes', () => {
const cooldownRemaining1 = 5;
const cooldownRemaining2 = 10;
const props1 = { [remainingProp]: cooldownRemaining1 };
const props2 = { [remainingProp]: cooldownRemaining2 };
const timeElapsedBeforeUpdate = 2;

const { rerender } = render(<CooldownTimer {...props1} />);

for (let round = timeElapsedBeforeUpdate; round > 0; round--) {
act(() => {
jest.runAllTimers();
});
}

expect(screen.getByTestId(TIMER_TEST_ID)).toHaveTextContent(
(cooldownRemaining1 - timeElapsedBeforeUpdate).toString(),
);

rerender(<CooldownTimer {...props2} />);

expect(screen.queryByTestId(TIMER_TEST_ID)).toHaveTextContent(cooldownRemaining2.toString());
act(() => {
jest.runAllTimers();
});
expect(screen.queryByTestId(TIMER_TEST_ID)).toHaveTextContent(
(cooldownRemaining2 - 1).toString(),
);
});
});
14 changes: 13 additions & 1 deletion src/components/MessageInput/__tests__/MessageInput.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const mockedChannelData = generateChannel({
thread: [threadMessage],
});

const cooldown = 30;
const filename = 'some.txt';
const fileUploadUrl = 'http://www.getstream.io'; // real url, because ImagePreview will try to load the image

Expand Down Expand Up @@ -1147,7 +1148,7 @@ function axeNoViolations(container) {

const renderWithActiveCooldown = async ({ messageInputProps = {} } = {}) => {
channel = chatClient.channel('messaging', mockedChannelData.channel.id);
channel.data.cooldown = 30;
channel.data.cooldown = cooldown;
channel.initialized = true;
const lastSentSecondsAhead = 5;
await render({
Expand Down Expand Up @@ -1263,6 +1264,17 @@ function axeNoViolations(container) {
expect(screen.queryByTestId(COOLDOWN_TIMER_TEST_ID)).not.toBeInTheDocument();
}
});

it('should be removed after cool-down period elapsed', async () => {
jest.useFakeTimers();
await renderWithActiveCooldown();
expect(screen.getByTestId(COOLDOWN_TIMER_TEST_ID)).toHaveTextContent(cooldown.toString());
act(() => {
jest.advanceTimersByTime(cooldown * 1000);
});
expect(screen.queryByTestId(COOLDOWN_TIMER_TEST_ID)).not.toBeInTheDocument();
jest.useRealTimers();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { useCooldownTimer } from '../useCooldownTimer';

import { ChannelStateProvider, ChatProvider } from '../../../../context';
import { getTestClient } from '../../../../mock-builders';
import { act } from '@testing-library/react';

jest.useFakeTimers();

async function renderUseCooldownTimerHook({ channel, chatContext }) {
const client = await getTestClient();
Expand Down Expand Up @@ -126,4 +129,23 @@ describe('useCooldownTimer', () => {
const { result } = await renderUseCooldownTimerHook({ channel, chatContext });
expect(result.current.cooldownRemaining).toBe(cooldown);
});

it('remove the cooldown after the cooldown period elapses', async () => {
const channel = { cid, data: { cooldown } };
const chatContext = {
latestMessageDatesByChannels: {
[cid]: new Date(),
},
};

const { result } = await renderUseCooldownTimerHook({ channel, chatContext });

expect(result.current.cooldownRemaining).toBe(cooldown);

await act(() => {
jest.advanceTimersByTime(cooldown * 1000);
});

expect(result.current.cooldownRemaining).toBe(0);
});
});
21 changes: 16 additions & 5 deletions src/components/MessageInput/hooks/useCooldownTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@ export const useCooldownTimer = <
Math.max(0, (new Date().getTime() - ownLatestMessageDate.getTime()) / 1000)
: undefined;

setCooldownRemaining(
const remaining =
!skipCooldown &&
typeof timeSinceOwnLastMessage !== 'undefined' &&
cooldownInterval > timeSinceOwnLastMessage
typeof timeSinceOwnLastMessage !== 'undefined' &&
cooldownInterval > timeSinceOwnLastMessage
? Math.round(cooldownInterval - timeSinceOwnLastMessage)
: 0,
);
: 0;

setCooldownRemaining(remaining);

if (!remaining) return;

const timeout = setTimeout(() => {
setCooldownRemaining(0);
}, remaining * 1000);

return () => {
clearTimeout(timeout);
};
}, [cooldownInterval, ownLatestMessageDate, skipCooldown]);

return {
Expand Down

0 comments on commit 9523b45

Please sign in to comment.