Files
malenia-frontend/src/lib/format.test.ts

120 lines
3.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
DURATION_OPTIONS,
calculatePricing,
durationByDays,
pluralize,
round2,
sumAddonPrices,
devicesLabel,
formatMoney,
} from './format';
import type { AddonData, PricingPlans } from '../types';
const pricing: PricingPlans = {
device_price: 100,
addons: [
{ id: 'a', name: 'A', price: 50, free_threshold: -1, is_enabled: true },
{ id: 'b', name: 'B', price: 30, free_threshold: -1, is_enabled: true },
],
};
describe('round2', () => {
it('rounds to two decimals', () => {
expect(round2(1.005)).toBe(1.01);
expect(round2(2.345)).toBe(2.35);
expect(round2(10)).toBe(10);
});
});
describe('sumAddonPrices', () => {
const addons: AddonData[] = pricing.addons;
it('sums selected addon prices', () => {
expect(sumAddonPrices(addons, ['a', 'b'], 6)).toBe(80);
expect(sumAddonPrices(addons, ['a'], 6)).toBe(50);
expect(sumAddonPrices(addons, ['unknown'], 6)).toBe(0);
expect(sumAddonPrices(addons, [], 6)).toBe(0);
});
it('makes addons free at their device threshold but keeps -1 paid', () => {
const thresholdAddons: AddonData[] = [
{ id: 'threshold', name: 'Threshold', price: 50, free_threshold: 6, is_enabled: true },
{ id: 'paid', name: 'Paid', price: 30, free_threshold: -1, is_enabled: true },
];
expect(sumAddonPrices(thresholdAddons, ['threshold', 'paid'], 5)).toBe(80);
expect(sumAddonPrices(thresholdAddons, ['threshold', 'paid'], 6)).toBe(30);
});
});
describe('calculatePricing', () => {
it('computes linear device cost + addons + duration total', () => {
const r = calculatePricing({
pricing,
devices: 6,
addonIds: ['a'],
duration: DURATION_OPTIONS[1]!, // 90 days
});
// monthly = 6*100 + 50 = 650
// total = 650 * 90 / 30 = 1950
expect(r.deviceCost).toBe(600);
expect(r.addonCost).toBe(50);
expect(r.monthly).toBe(650);
expect(r.periodTotal).toBe(1950);
});
it('handles 1 month / 30 days', () => {
const r = calculatePricing({
pricing,
devices: 3,
addonIds: [],
duration: DURATION_OPTIONS[0]!,
});
expect(r.monthly).toBe(300);
expect(r.periodTotal).toBe(300);
});
it('12 months / 365 days yields near 12x', () => {
const r = calculatePricing({
pricing,
devices: 1,
addonIds: [],
duration: DURATION_OPTIONS[3]!,
});
// monthly=100, total = 100 * 365/30 = 1216.67
expect(r.periodTotal).toBeCloseTo(1216.67, 1);
});
});
describe('durationByDays', () => {
it('finds duration by days', () => {
expect(durationByDays(30).months).toBe(1);
expect(durationByDays(90).months).toBe(3);
expect(durationByDays(180).months).toBe(6);
expect(durationByDays(365).months).toBe(12);
});
it('falls back to first option for unknown days', () => {
expect(durationByDays(7).months).toBe(1);
});
});
describe('pluralize / devicesLabel', () => {
it('picks correct form', () => {
expect(pluralize(1, 'устройство', 'устройства', 'устройств')).toBe('устройство');
expect(pluralize(2, 'устройство', 'устройства', 'устройств')).toBe('устройства');
expect(pluralize(5, 'устройство', 'устройства', 'устройств')).toBe('устройств');
expect(pluralize(11, 'устройство', 'устройства', 'устройств')).toBe('устройств');
});
it('devicesLabel combines number and noun', () => {
expect(devicesLabel(3)).toBe('3 устройства');
expect(devicesLabel(6)).toBe('6 устройств');
});
});
describe('formatMoney', () => {
it('formats rubles', () => {
expect(formatMoney(100)).toMatch(/100/);
expect(formatMoney(1950)).toMatch(/1\s950/);
});
});