mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-16 02:22:17 +00:00
acaf760b36
The server load fetched a full combined-data page at a 9999 item page size, carrying detail text and ownership fields the grid never renders, and the client re-fetched the same payload after hydration. Load a trimmed grid row instead and pack it as positional tuples so field names are not repeated for every one of a thousand-plus entries. Entry detail is fetched on demand from the new per-entry endpoint when a cell is opened, and the grid marks itself interactive so other page-start work can queue behind it.
104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { afterCriticalPageWork, markGridInteractive } from '$lib/utils/criticalPageWork';
|
|
|
|
describe('optional work scheduling', () => {
|
|
let events: EventTarget & Record<string, unknown>;
|
|
let idle: (() => void) | undefined;
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
events = Object.assign(new EventTarget(), {
|
|
setTimeout,
|
|
clearTimeout,
|
|
requestIdleCallback: vi.fn((callback: () => void) => {
|
|
idle = callback;
|
|
return 1;
|
|
}),
|
|
cancelIdleCallback: vi.fn()
|
|
});
|
|
idle = undefined;
|
|
vi.stubGlobal('window', events);
|
|
vi.stubGlobal('location', { pathname: '/pokedex/example' });
|
|
vi.stubGlobal('document', { querySelector: () => null });
|
|
vi.stubGlobal('requestAnimationFrame', (callback: () => void) => setTimeout(callback, 16));
|
|
vi.stubGlobal('cancelAnimationFrame', clearTimeout);
|
|
});
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it('waits for interactive cells, coalesces events, then runs at idle once', () => {
|
|
const run = vi.fn();
|
|
afterCriticalPageWork(run);
|
|
vi.advanceTimersByTime(1000);
|
|
expect(run).not.toHaveBeenCalled();
|
|
events.dispatchEvent(new Event('livingdex:grid-interactive'));
|
|
events.dispatchEvent(new Event('livingdex:grid-interactive'));
|
|
vi.advanceTimersByTime(16);
|
|
expect(events.requestIdleCallback).toHaveBeenCalledTimes(1);
|
|
expect(run).not.toHaveBeenCalled();
|
|
idle!();
|
|
vi.advanceTimersByTime(5000);
|
|
expect(run).toHaveBeenCalledTimes(1);
|
|
});
|
|
it('falls back after five seconds if no grid becomes interactive', () => {
|
|
const run = vi.fn();
|
|
afterCriticalPageWork(run);
|
|
vi.advanceTimersByTime(4999);
|
|
expect(run).not.toHaveBeenCalled();
|
|
vi.advanceTimersByTime(1);
|
|
expect(run).toHaveBeenCalledTimes(1);
|
|
});
|
|
it('cancels work after navigation or an explicit refresh takes over', () => {
|
|
const run = vi.fn();
|
|
const cancel = afterCriticalPageWork(run);
|
|
events.dispatchEvent(new Event('livingdex:grid-interactive'));
|
|
vi.advanceTimersByTime(16);
|
|
cancel();
|
|
idle!();
|
|
vi.advanceTimersByTime(5000);
|
|
expect(run).not.toHaveBeenCalled();
|
|
});
|
|
it('does not announce a grid with no populated cells', () => {
|
|
const listener = vi.fn();
|
|
events.addEventListener('livingdex:grid-interactive', listener);
|
|
markGridInteractive();
|
|
expect(listener).not.toHaveBeenCalled();
|
|
});
|
|
it('schedules other pages without waiting for a grid, even without idle callbacks', () => {
|
|
vi.stubGlobal('location', { pathname: '/backup-settings' });
|
|
delete events.requestIdleCallback;
|
|
const run = vi.fn();
|
|
afterCriticalPageWork(run);
|
|
vi.advanceTimersByTime(32);
|
|
expect(run).toHaveBeenCalledTimes(1);
|
|
});
|
|
it('marks populated cells once and allows work registered after hydration', () => {
|
|
let ready = false;
|
|
const grid = {
|
|
setAttribute: () => {
|
|
ready = true;
|
|
}
|
|
};
|
|
const cell = { closest: () => grid };
|
|
vi.stubGlobal('document', {
|
|
querySelector: (selector: string) =>
|
|
selector === '[data-entry-index]' ? cell : ready ? grid : null
|
|
});
|
|
const mark = vi.fn();
|
|
vi.stubGlobal('performance', { mark });
|
|
markGridInteractive();
|
|
markGridInteractive();
|
|
expect(mark).toHaveBeenCalledTimes(1);
|
|
const run = vi.fn();
|
|
afterCriticalPageWork(run);
|
|
vi.advanceTimersByTime(16);
|
|
idle!();
|
|
expect(run).toHaveBeenCalledTimes(1);
|
|
});
|
|
it('does not access the DOM during SSR', () => {
|
|
vi.stubGlobal('window', undefined);
|
|
expect(() => markGridInteractive()).not.toThrow();
|
|
});
|
|
});
|