From b500776f035779f9b9ee23ab889afa93ca987212 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Mon, 16 Feb 2026 19:17:59 -0800 Subject: Update V2/V3 'mark as read' logic to require item bottom to be above viewport, while keeping V1 unchanged --- frontend-vanilla/src/regression.test.ts | 59 +++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 14 deletions(-) (limited to 'frontend-vanilla/src/regression.test.ts') diff --git a/frontend-vanilla/src/regression.test.ts b/frontend-vanilla/src/regression.test.ts index 94eb51e..a0b13d5 100644 --- a/frontend-vanilla/src/regression.test.ts +++ b/frontend-vanilla/src/regression.test.ts @@ -39,7 +39,7 @@ describe('Scroll-to-Read Regression Tests', () => { vi.useRealTimers(); }); - it('should mark item as read when existing in store and scrolled past', async () => { + it('should mark item as read when existing in store and fully scrolled past (bottom < container top)', async () => { vi.useRealTimers(); const mockItem = { _id: 999, @@ -51,16 +51,9 @@ describe('Scroll-to-Read Regression Tests', () => { store.setItems([mockItem]); - // Manually trigger the render logic that sets up the listener (implied by renderItems/init in real app) - // Here we can't easily trigger the exact closure in main.ts without fully initializing it. - // However, we can simulate the "check logic" if we extract it or replicate the test conditions - // that the previous main.test.ts used. - - // Since we are mocking the scroll behavior on the DOM element which main.ts attaches to: + // Manual setup const mainContent = document.getElementById('main-content'); expect(mainContent).not.toBeNull(); - - // We need to invoke renderItems to attach the listener renderItems(); // Mock getBoundingClientRect for container @@ -70,18 +63,18 @@ describe('Scroll-to-Read Regression Tests', () => { })); } - // Mock getBoundingClientRect for item (scrolled past top: -50) - // renderItems creates the DOM elements based on store + // Mock getBoundingClientRect for item (fully scrolled past: bottom < 0) + // Item top: -150, Item bottom: -50. Container Top: 0. + // -50 < 0, so should mark as read. const itemEl = document.querySelector(`.feed-item[data-id="999"]`); expect(itemEl).not.toBeNull(); if (itemEl) { itemEl.getBoundingClientRect = vi.fn(() => ({ - top: -50, bottom: 50, height: 100, left: 0, right: 0, width: 0, x: 0, y: 0, toJSON: () => { } + top: -150, bottom: -50, height: 100, left: 0, right: 0, width: 0, x: 0, y: 0, toJSON: () => { } })); } - // Prepare API mock vi.mocked(apiFetch).mockResolvedValue({ ok: true } as Response); // Trigger scroll event @@ -97,7 +90,45 @@ describe('Scroll-to-Read Regression Tests', () => { })); }); - it('should NOT mark item as read if NOT scrolled past', async () => { + it('should NOT mark item as read if only partially scrolled past (top < container top but bottom > container top)', async () => { + vi.useRealTimers(); + const mockItem = { + _id: 777, + title: 'Partial Test Item', + read: false, + url: 'http://example.com/partial', + publish_date: '2023-01-01' + } as any; + + store.setItems([mockItem]); + renderItems(); + + const mainContent = document.getElementById('main-content'); + if (mainContent) { + mainContent.getBoundingClientRect = vi.fn(() => ({ + top: 0, bottom: 800, height: 800, left: 0, right: 0, width: 0, x: 0, y: 0, toJSON: () => { } + })); + } + + // Mock getBoundingClientRect for item (partially scrolled past: top < 0, bottom > 0) + // Item top: -50, Item bottom: 50. Container Top: 0. + // 50 is NOT < 0, so should NOT mark as read. + const itemEl = document.querySelector(`.feed-item[data-id="777"]`); + if (itemEl) { + itemEl.getBoundingClientRect = vi.fn(() => ({ + top: -50, bottom: 50, height: 100, left: 0, right: 0, width: 0, x: 0, y: 0, toJSON: () => { } + })); + } + + vi.mocked(apiFetch).mockResolvedValue({ ok: true } as Response); + + mainContent?.dispatchEvent(new Event('scroll')); + await new Promise(resolve => setTimeout(resolve, 300)); + + expect(apiFetch).not.toHaveBeenCalledWith(expect.stringContaining('/api/item/777'), expect.anything()); + }); + + it('should NOT mark item as read if NOT scrolled past (item below container top)', async () => { vi.useRealTimers(); const mockItem = { _id: 888, -- cgit v1.2.3