diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-16 07:38:51 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-16 07:44:50 -0800 |
| commit | e7516c4124d033332922db91a3dc0e8dc417c2aa (patch) | |
| tree | 91842fb5ae12815563752b8eb2c541ed0227e705 /frontend-vanilla/src/main.test.ts | |
| parent | 7196992916b42fe6180c0d05331716c52fac79b0 (diff) | |
| download | neko-e7516c4124d033332922db91a3dc0e8dc417c2aa.tar.gz neko-e7516c4124d033332922db91a3dc0e8dc417c2aa.tar.bz2 neko-e7516c4124d033332922db91a3dc0e8dc417c2aa.zip | |
Fix scroll mark as read in V3 UI
- Close NK-k2fh32: scroll mark as read broken in V3 UI
- Add IntersectionObserver to item list in renderItems
- Add test case ensuring apiFetch is called when item intersects
Diffstat (limited to 'frontend-vanilla/src/main.test.ts')
| -rw-r--r-- | frontend-vanilla/src/main.test.ts | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/frontend-vanilla/src/main.test.ts b/frontend-vanilla/src/main.test.ts index 7cae34b..a8b6969 100644 --- a/frontend-vanilla/src/main.test.ts +++ b/frontend-vanilla/src/main.test.ts @@ -22,7 +22,11 @@ vi.mock('./api', () => ({ })); // Mock IntersectionObserver as a constructor +let observerCallback: IntersectionObserverCallback; class MockIntersectionObserver { + constructor(callback: IntersectionObserverCallback) { + observerCallback = callback; + } observe = vi.fn(); unobserve = vi.fn(); disconnect = vi.fn(); @@ -254,4 +258,39 @@ describe('main application logic', () => { toggleBtn.click(); expect(store.sidebarVisible).toBe(!initialVisible); }); + + it('should mark item as read when scrolled into view', () => { + const mockItem = { + _id: 123, + title: 'Scroll Test Item', + read: false, + url: 'http://example.com', + publish_date: '2023-01-01' + } as any; + + store.setItems([mockItem]); + renderLayout(); + renderItems(); + + const itemEl = document.querySelector(`.feed-item[data-id="123"]`); + expect(itemEl).not.toBeNull(); + + vi.mocked(apiFetch).mockResolvedValue({ ok: true } as Response); + + // Simulate intersection + const entry = { + target: itemEl, + isIntersecting: true + } as IntersectionObserverEntry; + + // This relies on the LAST created observer's callback being captured. + expect(observerCallback).toBeDefined(); + // @ts-ignore + observerCallback([entry], {} as IntersectionObserver); + + expect(apiFetch).toHaveBeenCalledWith(expect.stringContaining('/api/item/123'), expect.objectContaining({ + method: 'PUT', + body: expect.stringContaining('"read":true') + })); + }); }); |
