Files
pn-new-crm/tests/unit/analytics/range.test.ts
Matt 681b94a8ef feat(reports): prior-period comparison toggle on the Sales report
Adds a "Compare to prior period" toggle to the Sales report header.
When on, the API recomputes the KPI window for the equal-length window
immediately preceding the selected range (previousPeriodBounds) behind
`?compare=1`, and the five window-derived KPI tiles (Won, Lost, Win
rate, Avg time-to-close, New leads) render colour-correct "vs prior"
deltas. Point-in-time tiles (Active interests, Pipeline value) have no
prior-window analogue and intentionally show no delta. The prior-window
query runs in parallel with the main batch and resolves to null when the
toggle is off (zero cost). Toggle state persists in the saved-template
config.

Closes the spec's "period comparison on every report" gap for Sales;
Operational already rendered period-start deltas.

Pure helpers TDD'd: previousPeriodBounds (range.ts) +
computeSalesKpiComparison (sales-comparison.ts), 7 unit tests. tsc +
lint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:49:35 +02:00

36 lines
1.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { previousPeriodBounds } from '@/lib/analytics/range';
describe('previousPeriodBounds', () => {
it('returns the equal-length window immediately preceding the given bounds', () => {
const from = new Date('2026-05-01T00:00:00.000Z');
const to = new Date('2026-05-31T00:00:00.000Z'); // 30-day span
const prior = previousPeriodBounds({ from, to });
// Prior window is contiguous (prior.to === current.from) and equal length.
expect(prior.to.toISOString()).toBe('2026-05-01T00:00:00.000Z');
expect(prior.from.toISOString()).toBe('2026-04-01T00:00:00.000Z');
});
it('preserves sub-day spans (e.g. a single-day window)', () => {
const from = new Date('2026-05-10T00:00:00.000Z');
const to = new Date('2026-05-10T23:59:59.999Z');
const prior = previousPeriodBounds({ from, to });
const lenMs = to.getTime() - from.getTime();
expect(prior.to.getTime()).toBe(from.getTime());
expect(prior.from.getTime()).toBe(from.getTime() - lenMs);
});
it('does not mutate the input bounds', () => {
const from = new Date('2026-05-01T00:00:00.000Z');
const to = new Date('2026-05-31T00:00:00.000Z');
const fromCopy = from.getTime();
const toCopy = to.getTime();
previousPeriodBounds({ from, to });
expect(from.getTime()).toBe(fromCopy);
expect(to.getTime()).toBe(toCopy);
});
});