mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 15:03:57 +00:00
75 lines
3.0 KiB
TypeScript
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/);
|
|
});
|
|
});
|