Files
openhuman/app/src/components/intelligence/MemoryHeatmap.test.tsx
T

75 lines
3.0 KiB
TypeScript

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('<MemoryHeatmap />', () => {
it('renders a skeleton when loading', () => {
const { container } = render(<MemoryHeatmap timestamps={[]} loading />);
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(<MemoryHeatmap timestamps={[]} />);
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(<MemoryHeatmap timestamps={timestamps} />);
// Total renders as separate text nodes: "<n> 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(<MemoryHeatmap timestamps={[now]} />);
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(<MemoryHeatmap timestamps={[yMs]} />);
const summary = document.body.textContent ?? '';
expect(summary).toMatch(/1 event/);
});
});