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
23 changes: 18 additions & 5 deletions __tests__/app/tools/block-finder/page.client.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
const mockSetToast = jest.fn();
const mockSetTitle = jest.fn();

const DEFAULT_DATE = "2025-09-26";
const DEFAULT_TIME = "12:00";

jest.mock("@/contexts/TitleContext", () => ({
useTitle: () => ({ setTitle: mockSetTitle }),
}));
Expand Down Expand Up @@ -112,13 +115,22 @@ jest.mock("@/components/block-picker/result/BlockPickerResult", () => ({
/** Utilities */
function setDateAndTime() {
fireEvent.change(screen.getByTestId("date-input"), {
target: { value: "2025-09-26" },
target: { value: DEFAULT_DATE },
});
fireEvent.change(screen.getByTestId("time-input"), {
target: { value: "12:00" },
target: { value: DEFAULT_TIME },
});
}

// Mirrors the component's timestamp calculation so expectations stay timezone agnostic.
function getTimestamp(date: string, time: string) {
const dateObj = new Date(date);
const [hours, minutes] = time.split(":");
const startDate = new Date(dateObj);
startDate.setHours(parseInt(hours, 10), parseInt(minutes, 10), 0, 0);
return startDate.getTime();
}
Comment thread
simo6529 marked this conversation as resolved.

describe("tools/block-finder/page.client.tsx (client)", () => {
beforeEach(() => {
jest.useFakeTimers().setSystemTime(new Date("2025-09-26T12:00:00+03:00"));
Expand Down Expand Up @@ -202,9 +214,10 @@ describe("tools/block-finder/page.client.tsx (client)", () => {
expect(init.method).toBe("POST");

const body = JSON.parse(init.body as string);
const expectedTimestamp = getTimestamp(DEFAULT_DATE, DEFAULT_TIME);

expect(body).toEqual({
// timestamp equals 2025-09-26 date with time 12:00 local -> client code uses Date(date)+time
timestamp: new Date("2025-09-26T12:00:00.000+03:00").getTime(),
timestamp: expectedTimestamp,
});

// Result rendered with returned block number
Expand Down Expand Up @@ -252,7 +265,7 @@ describe("tools/block-finder/page.client.tsx (client)", () => {
expect(init.method).toBe("POST");

const parsed = JSON.parse(init.body as string);
const min = new Date("2025-09-26T12:00:00.000+03:00").getTime();
const min = getTimestamp(DEFAULT_DATE, DEFAULT_TIME);
const max = min + 60_000; // ONE_MINUTE

expect(parsed.minTimestamp).toBe(min);
Expand Down
16 changes: 13 additions & 3 deletions __tests__/components/brain/NotificationsWrapper.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,25 @@ describe('NotificationsWrapper', () => {
it('shows loading spinner and handles actions', () => {
const setActive = jest.fn();
render(
<NotificationsWrapper items={[]} loading={true} activeDrop={null} setActiveDrop={setActive} />
<NotificationsWrapper
items={[]}
loadingOlder={true}
activeDrop={null}
setActiveDrop={setActive}
/>
);
expect(screen.getByText(/Loading notifications/, { selector: 'div' })).toBeInTheDocument();
expect(screen.getByText(/Loading older notifications/i)).toBeInTheDocument();
});

it('delegates callbacks to router and state setter', () => {
const setActive = jest.fn();
render(
<NotificationsWrapper items={[]} loading={false} activeDrop={null} setActiveDrop={setActive} />
<NotificationsWrapper
items={[]}
loadingOlder={false}
activeDrop={null}
setActiveDrop={setActive}
/>
);
screen.getByTestId('items').click();
expect(setActive).toHaveBeenCalledTimes(2);
Expand Down
28 changes: 28 additions & 0 deletions __tests__/components/brain/feed/FeedScrollContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@ import { act } from 'react-dom/test-utils';
import React, { createRef } from 'react';
import { FeedScrollContainer } from '@/components/brain/feed/FeedScrollContainer';

beforeAll(() => {
class IntersectionObserverMock implements IntersectionObserver {
readonly root: Element | Document | null = null;
readonly rootMargin = '';
readonly thresholds = [] as ReadonlyArray<number>;

constructor(public callback: IntersectionObserverCallback) {}

disconnect(): void {}

observe(): void {}

takeRecords(): IntersectionObserverEntry[] {
return [];
}

unobserve(): void {}
}

(globalThis as typeof globalThis & {
IntersectionObserver: typeof IntersectionObserver;
}).IntersectionObserver = IntersectionObserverMock as unknown as typeof IntersectionObserver;
});
Comment thread
simo6529 marked this conversation as resolved.

afterAll(() => {
delete (globalThis as Record<string, unknown>).IntersectionObserver;
});

jest.useFakeTimers();

describe('FeedScrollContainer', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { render } from '@testing-library/react';
const NotificationItem = jest.fn(() => <div data-testid="item" />);
const CommonChangeAnimation = jest.fn(({ children }) => <div data-testid="anim">{children}</div>);

jest.mock('@/components/brain/notifications/NotificationItem', () => ({ __esModule: true, default: NotificationItem }));
jest.mock('@/components/utils/animation/CommonChangeAnimation', () => ({ __esModule: true, default: CommonChangeAnimation }));

import NotificationItems from '@/components/brain/notifications/NotificationItems';
import React from 'react';
Expand All @@ -26,7 +24,6 @@ describe('NotificationItems', () => {
/>
);

expect(CommonChangeAnimation).toHaveBeenCalledTimes(2);
expect(NotificationItem).toHaveBeenCalledTimes(2);
expect(NotificationItem.mock.calls[0][0]).toEqual(
expect.objectContaining({
Expand Down
31 changes: 17 additions & 14 deletions __tests__/components/brain/notifications/Notifications.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
Comment thread
simo6529 marked this conversation as resolved.
import React from 'react';

const mutateAsyncMock = jest.fn();
Expand Down Expand Up @@ -43,12 +43,6 @@ jest.mock('@/components/brain/notifications/NotificationsCauseFilter', () => ({
default: () => <div data-testid="filter" />,
}));

jest.mock('@/components/brain/feed/FeedScrollContainer', () => ({
FeedScrollContainer: React.forwardRef((props: any, ref) => (
<div data-testid="scroll" ref={ref} {...props} />
)),
}));

jest.mock('@/components/brain/content/input/BrainContentInput', () => ({
__esModule: true,
default: () => <div data-testid="input" />,
Expand Down Expand Up @@ -94,11 +88,12 @@ import Notifications from '@/components/brain/notifications/Notifications';
describe('Notifications component', () => {
beforeEach(() => {
mutateAsyncMock.mockClear();
mutateAsyncMock.mockResolvedValue(undefined);
useNotificationsQueryMock.mockReset();
setTitleMock.mockClear();
});

it('shows loader when fetching and no items', () => {
it('shows loader when fetching and no items', async () => {
useNotificationsQueryMock.mockReturnValue({
items: [],
isFetching: true,
Expand All @@ -109,14 +104,16 @@ describe('Notifications component', () => {
isInitialQueryDone: false,
});

render(<Notifications />);
render(<Notifications activeDrop={null} setActiveDrop={jest.fn()} />);

expect(screen.getByText('Loading notifications...', { selector: 'div' })).toBeInTheDocument();
expect(mutateAsyncMock).toHaveBeenCalled();
await waitFor(() => {
expect(mutateAsyncMock).toHaveBeenCalled();
});
// Title is set via TitleContext hooks
});

it('renders wrapper with items', () => {
it('renders wrapper with items', async () => {
useNotificationsQueryMock.mockReturnValue({
items: ['a'],
isFetching: false,
Expand All @@ -127,12 +124,15 @@ describe('Notifications component', () => {
isInitialQueryDone: true,
});

render(<Notifications />);
render(<Notifications activeDrop={null} setActiveDrop={jest.fn()} />);

expect(screen.getByTestId('wrapper')).toBeInTheDocument();
await waitFor(() => {
expect(mutateAsyncMock).toHaveBeenCalled();
});
});

it('shows no items component when query done but empty', () => {
it('shows no items component when query done but empty', async () => {
useNotificationsQueryMock.mockReturnValue({
items: [],
isFetching: false,
Expand All @@ -143,8 +143,11 @@ describe('Notifications component', () => {
isInitialQueryDone: true,
});

render(<Notifications />);
render(<Notifications activeDrop={null} setActiveDrop={jest.fn()} />);

expect(screen.getByTestId('no-items')).toBeInTheDocument();
await waitFor(() => {
expect(mutateAsyncMock).toHaveBeenCalled();
});
});
});
1 change: 1 addition & 0 deletions components/brain/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const NEAR_TOP_SCROLL_THRESHOLD_PX = 200;
Loading