whisper-money/resources/js/lib/chart-tooltip-position.test.ts

78 lines
2.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { computeTooltipPosition } from './chart-tooltip-position';
const viewport = { viewportW: 1000, viewportH: 800 };
describe('computeTooltipPosition', () => {
it('places the tooltip below-right of the cursor when it fits', () => {
const pos = computeTooltipPosition({
cx: 100,
cy: 100,
tipW: 120,
tipH: 60,
offset: 12,
...viewport,
});
expect(pos).toEqual({ x: 112, y: 112 });
});
it('flips to the left when it would overflow the right edge', () => {
const pos = computeTooltipPosition({
cx: 950,
cy: 100,
tipW: 120,
tipH: 60,
offset: 12,
...viewport,
});
// 950 + 12 + 120 = 1082 > 1000 - 8, so it flips: 950 - 120 - 12
expect(pos.x).toBe(818);
});
it('flips upward when it would overflow the bottom edge', () => {
const pos = computeTooltipPosition({
cx: 100,
cy: 780,
tipW: 120,
tipH: 60,
offset: 12,
...viewport,
});
// 780 + 12 + 60 = 852 > 800 - 8, so it flips: 780 - 60 - 12
expect(pos.y).toBe(708);
});
it('clamps to the 8px margin when even the flipped position is off-screen', () => {
// Tooltip larger than the space on both sides of the cursor: the flip
// pushes the coordinate negative, so it must clamp to 8 rather than
// render partially off-screen.
const pos = computeTooltipPosition({
cx: 950,
cy: 780,
tipW: 980,
tipH: 790,
offset: 12,
...viewport,
});
expect(pos).toEqual({ x: 8, y: 8 });
});
it('rounds to whole pixels so sub-pixel jitter collapses to one coordinate', () => {
const base = { tipW: 120, tipH: 60, offset: 12, ...viewport };
const a = computeTooltipPosition({ cx: 100.1, cy: 100.1, ...base });
const b = computeTooltipPosition({ cx: 100.4, cy: 100.4, ...base });
expect(Number.isInteger(a.x)).toBe(true);
expect(Number.isInteger(a.y)).toBe(true);
// 100.1 and 100.4 both land on the same pixel, so the equality guard
// sees an unchanged position instead of thrashing on the difference.
expect(a).toEqual(b);
expect(a).toEqual({ x: 112, y: 112 });
});
});