diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-17 10:35:09 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-17 10:35:09 -0800 |
| commit | ea9ec7f41c0447027b66f52c0c4cd0d5c9777bfa (patch) | |
| tree | 76df773b1891fbeaa4349be0787a8c7f8ba53c59 /frontend-vanilla/src/polling.test.ts | |
| parent | 6cf749948220f960d3fc0867f882588a3ca43019 (diff) | |
| download | neko-ea9ec7f41c0447027b66f52c0c4cd0d5c9777bfa.tar.gz neko-ea9ec7f41c0447027b66f52c0c4cd0d5c9777bfa.tar.bz2 neko-ea9ec7f41c0447027b66f52c0c4cd0d5c9777bfa.zip | |
Fix infinite scroll not triggering on scroll
Diffstat (limited to 'frontend-vanilla/src/polling.test.ts')
| -rw-r--r-- | frontend-vanilla/src/polling.test.ts | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/frontend-vanilla/src/polling.test.ts b/frontend-vanilla/src/polling.test.ts new file mode 100644 index 0000000..fa4b62f --- /dev/null +++ b/frontend-vanilla/src/polling.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { store } from './store'; +import { apiFetch } from './api'; +import './main'; // Import to start the polling interval + +// Mock api +vi.mock('./api', () => ({ + apiFetch: vi.fn() +})); + +// Mock router to avoid errors during loadMore +vi.mock('./router', () => ({ + router: { + getCurrentRoute: () => ({ params: {}, query: new URLSearchParams() }), + updateQuery: vi.fn(), + navigate: vi.fn(), + addEventListener: vi.fn() + } +})); + +describe('Infinite Scroll Polling', () => { + beforeEach(() => { + // Use real timers because the interval starts at module import time + vi.useRealTimers(); + document.body.innerHTML = '<div id="main-content"></div>'; + store.setItems(Array(50).fill({ _id: 1 })); + store.setHasMore(true); + store.setLoading(false); + vi.clearAllMocks(); + }); + + it('should trigger loadMore via polling when near bottom', async () => { + const scrollRoot = document.getElementById('main-content')!; + + // Mock scroll properties + Object.defineProperty(scrollRoot, 'scrollHeight', { value: 2000, configurable: true }); + Object.defineProperty(scrollRoot, 'clientHeight', { value: 200, configurable: true }); + // Use defineProperty for scrollTop to ensure it overrides native behavior in JSDOM + Object.defineProperty(scrollRoot, 'scrollTop', { value: 1750, configurable: true }); + + // Mock apiFetch response + vi.mocked(apiFetch).mockResolvedValue({ + ok: true, + json: async () => [] + } as Response); + + // Wait for interval (1000ms) + buffer + await new Promise(resolve => setTimeout(resolve, 1100)); + + // Check if apiFetch was called + expect(apiFetch).toHaveBeenCalledWith(expect.stringContaining('/api/stream')); + }); + + it('should NOT trigger loadMore via polling when far from bottom', async () => { + const scrollRoot = document.getElementById('main-content')!; + + Object.defineProperty(scrollRoot, 'scrollHeight', { value: 2000, configurable: true }); + Object.defineProperty(scrollRoot, 'clientHeight', { value: 200, configurable: true }); + Object.defineProperty(scrollRoot, 'scrollTop', { value: 100, configurable: true }); + + await new Promise(resolve => setTimeout(resolve, 1100)); + + expect(apiFetch).not.toHaveBeenCalled(); + }); +}); |
