diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-16 19:37:50 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-16 19:40:10 -0800 |
| commit | cba29e6aae637b04ff6eaf28f74bc15b6242b9ea (patch) | |
| tree | 226753a3fcd18a6d45089dafcb1ee72557140aa8 /frontend/src/components/FeedItems.test.tsx | |
| parent | cb6d0c9e330c27ff85ff065c2ea6dd1a756cbf6d (diff) | |
| download | neko-cba29e6aae637b04ff6eaf28f74bc15b6242b9ea.tar.gz neko-cba29e6aae637b04ff6eaf28f74bc15b6242b9ea.tar.bz2 neko-cba29e6aae637b04ff6eaf28f74bc15b6242b9ea.zip | |
Remove legacy V2 React frontend and update build/test scripts to focus on Vanilla JS (V3)
Diffstat (limited to 'frontend/src/components/FeedItems.test.tsx')
| -rw-r--r-- | frontend/src/components/FeedItems.test.tsx | 250 |
1 files changed, 0 insertions, 250 deletions
diff --git a/frontend/src/components/FeedItems.test.tsx b/frontend/src/components/FeedItems.test.tsx deleted file mode 100644 index 1a002d8..0000000 --- a/frontend/src/components/FeedItems.test.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import React from 'react'; -import '@testing-library/jest-dom'; -import { render, screen, waitFor, fireEvent, act } from '@testing-library/react'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import FeedItems from './FeedItems'; - -describe('FeedItems Component', () => { - beforeEach(() => { - vi.resetAllMocks(); - global.fetch = vi.fn(); - window.HTMLElement.prototype.scrollIntoView = vi.fn(); - - // Mock IntersectionObserver - class MockIntersectionObserver { - observe = vi.fn(); - unobserve = vi.fn(); - disconnect = vi.fn(); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - window.IntersectionObserver = MockIntersectionObserver as any; - }); - - it('renders loading state', () => { - vi.mocked(global.fetch).mockImplementation(() => new Promise(() => { })); - render( - <MemoryRouter initialEntries={['/feed/1']}> - <Routes> - <Route path="/feed/:feedId" element={<FeedItems />} /> - </Routes> - </MemoryRouter> - ); - expect(screen.getByText(/loading items/i)).toBeInTheDocument(); - }); - - it('renders items for a feed', async () => { - const mockItems = [ - { - _id: 101, - title: 'Item One', - url: 'http://example.com/1', - publish_date: '2023-01-01', - read: false, - }, - { - _id: 102, - title: 'Item Two', - url: 'http://example.com/2', - publish_date: '2023-01-02', - read: true, - }, - ]; - - vi.mocked(global.fetch).mockResolvedValueOnce({ - ok: true, - json: async () => mockItems, - } as Response); - - render( - <MemoryRouter initialEntries={['/feed/1']}> - <Routes> - <Route path="/feed/:feedId" element={<FeedItems />} /> - </Routes> - </MemoryRouter> - ); - - await waitFor(() => { - expect(screen.getByText('Item One')).toBeInTheDocument(); - }); - - const params = new URLSearchParams(); - params.append('feed_id', '1'); - params.append('read_filter', 'unread'); - expect(global.fetch).toHaveBeenCalledWith(`/api/stream?${params.toString()}`, expect.anything()); - }); - - - - afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); - }); - - it('marks items as read when scrolled past', async () => { - const mockItems = [{ _id: 101, title: 'Item 1', url: 'u1', read: false, starred: false }]; - vi.mocked(global.fetch).mockResolvedValue({ - ok: true, - json: async () => mockItems, - } as Response); - - // Mock getBoundingClientRect - const getBoundingClientRectMock = vi.spyOn(Element.prototype, 'getBoundingClientRect'); - getBoundingClientRectMock.mockImplementation(function (this: Element) { - if (this.classList && this.classList.contains('dashboard-main')) { - return { - top: 0, bottom: 500, height: 500, left: 0, right: 1000, width: 1000, x: 0, y: 0, - toJSON: () => { } - } as DOMRect; - } - if (this.id && this.id.startsWith('item-')) { - // Item top is -50 (above container top 0) - return { - top: -150, bottom: -50, height: 100, left: 0, right: 1000, width: 1000, x: 0, y: 0, - toJSON: () => { } - } as DOMRect; - } - return { - top: 0, bottom: 0, height: 0, left: 0, right: 0, width: 0, x: 0, y: 0, - toJSON: () => { } - } as DOMRect; - }); - - render( - <MemoryRouter> - <div className="dashboard-main"> - <FeedItems /> - </div> - </MemoryRouter> - ); - - // Initial load fetch - await waitFor(() => { - expect(screen.getByText('Item 1')).toBeVisible(); - }); - - // Trigger scroll - const container = document.querySelector('.dashboard-main'); - expect(container).not.toBeNull(); - - act(() => { - // Dispatch scroll event - fireEvent.scroll(container!); - }); - - // Wait for throttle (500ms) + buffer - await new Promise(r => setTimeout(r, 600)); - - await waitFor(() => { - expect(global.fetch).toHaveBeenCalledWith( - '/api/item/101', - expect.objectContaining({ - method: 'PUT', - body: JSON.stringify({ read: true, starred: false }), - }) - ); - }); - }); - - it('loads more items when sentinel becomes visible', async () => { - const initialItems = [{ _id: 101, title: 'Item 1', url: 'u1', read: true, starred: false }]; - const moreItems = [{ _id: 100, title: 'Item 0', url: 'u0', read: true, starred: false }]; - - vi.mocked(global.fetch) - .mockResolvedValueOnce({ ok: true, json: async () => initialItems } as Response) - .mockResolvedValueOnce({ ok: true, json: async () => moreItems } as Response); - - const observerCallbacks: IntersectionObserverCallback[] = []; - class MockIntersectionObserver { - constructor(callback: IntersectionObserverCallback) { - observerCallbacks.push(callback); - } - observe = vi.fn(); - unobserve = vi.fn(); - disconnect = vi.fn(); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - window.IntersectionObserver = MockIntersectionObserver as any; - - render( - <MemoryRouter> - <FeedItems /> - </MemoryRouter> - ); - - await waitFor(() => { - expect(screen.getByText('Item 1')).toBeInTheDocument(); - }); - - const entry = { - isIntersecting: true, - target: { id: 'load-more-sentinel' } as unknown as Element, - boundingClientRect: {} as DOMRectReadOnly, - intersectionRatio: 1, - time: 0, - rootBounds: null, - intersectionRect: {} as DOMRectReadOnly, - } as IntersectionObserverEntry; - - act(() => { - // Trigger all observers - observerCallbacks.forEach(cb => cb([entry], {} as IntersectionObserver)); - }); - - await waitFor(() => { - expect(screen.getByText('Item 0')).toBeInTheDocument(); - const params = new URLSearchParams(); - params.append('max_id', '101'); - params.append('read_filter', 'unread'); - // Verify the second fetch call content - expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('max_id=101'), - expect.anything() - ); - }); - }); - - it('loads more items when pressing j on last item', async () => { - const initialItems = [ - { _id: 103, title: 'Item 3', url: 'u3', read: true, starred: false }, - { _id: 102, title: 'Item 2', url: 'u2', read: true, starred: false }, - { _id: 101, title: 'Item 1', url: 'u1', read: true, starred: false }, - ]; - const moreItems = [ - { _id: 100, title: 'Item 0', url: 'u0', read: true, starred: false }, - ]; - - vi.mocked(global.fetch) - .mockResolvedValueOnce({ ok: true, json: async () => initialItems } as Response) - .mockResolvedValueOnce({ ok: true, json: async () => moreItems } as Response); - - render( - <MemoryRouter> - <FeedItems /> - </MemoryRouter> - ); - - await waitFor(() => { - expect(screen.getByText('Item 1')).toBeInTheDocument(); - }); - - fireEvent.keyDown(window, { key: 'j' }); // index 0 - await waitFor(() => expect(document.getElementById('item-0')).toHaveAttribute('data-selected', 'true')); - - fireEvent.keyDown(window, { key: 'j' }); // index 1 - await waitFor(() => expect(document.getElementById('item-1')).toHaveAttribute('data-selected', 'true')); - - fireEvent.keyDown(window, { key: 'j' }); // index 2 (last item) - await waitFor(() => expect(document.getElementById('item-2')).toHaveAttribute('data-selected', 'true')); - - await waitFor(() => { - expect(screen.getByText('Item 0')).toBeInTheDocument(); - }); - - // Check fetch call - expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('max_id=101'), - expect.anything() - ); - }); -}); |
