import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; import { MemoryHeatmap } from './MemoryHeatmap'; function todaySeconds(): number { return Math.floor(Date.now() / 1000); } /** * MemoryHeatmap's display window ends at midnight-start-of-today. Timestamps * later than midnight today are placed on a future cell and NOT counted in * the total/peak summary. Use yesterday's epoch-seconds for "should be * counted" assertions. * * Anchored to noon (local time) so callers can subtract small offsets (seconds * to a few hours) without crossing the day boundary when the test happens to * run near midnight in CI. */ function yesterdaySeconds(): number { const noonYesterday = new Date(); noonYesterday.setHours(12, 0, 0, 0); noonYesterday.setDate(noonYesterday.getDate() - 1); return Math.floor(noonYesterday.getTime() / 1000); } describe('', () => { it('renders a skeleton when loading', () => { const { container } = render(); expect(container.querySelector('.animate-pulse')).not.toBeNull(); // SVG only renders in the non-loading branch. expect(container.querySelector('svg')).toBeNull(); }); it('renders the SVG grid when not loading', () => { const { container } = render(); const svg = container.querySelector('svg'); expect(svg).not.toBeNull(); // Many rect cells, at least one per displayed day. expect(svg!.querySelectorAll('rect').length).toBeGreaterThan(30); }); it('reports total counted events from timestamps inside the display window', () => { const y = yesterdaySeconds(); // 4 events yesterday — all inside the 8-month window. const timestamps = [y, y - 30, y - 60, y - 90]; render(); // Total renders as separate text nodes: " events over the last …". const summary = document.body.textContent ?? ''; expect(summary).toMatch(/4 events/); // Peak (max daily count) is 4 because all four fall on the same day. expect(summary).toMatch(/Peak.*4/); }); it('shows a hover tooltip when a cell is mouse-entered', () => { const now = todaySeconds(); const { container } = render(); const rect = container.querySelector('rect'); expect(rect).not.toBeNull(); fireEvent.mouseEnter(rect!); // Tooltip is rendered as a fixed-positioned div with z-50. expect(container.querySelector('.fixed.z-50')).not.toBeNull(); fireEvent.mouseLeave(rect!); expect(container.querySelector('.fixed.z-50')).toBeNull(); }); it('accepts millisecond-precision timestamps (>9999999999)', () => { // Yesterday in ms so it lands inside the counted window. const yMs = Date.now() - 24 * 60 * 60 * 1000; render(); const summary = document.body.textContent ?? ''; expect(summary).toMatch(/1 event/); }); });