Files
laude/src/renderer/src/lib/format.test.ts
AnRil 36085f225f test: expand coverage 53 → 135 (+82 tests)
Аудит тестов выявил критические пробелы в покрытии. Расширили
существующие файлы и добавили два новых:

Новые файлы:
- src/main/validate.test.ts (59) — security-boundary IPC layer вообще
  не имел тестов. Покрывает NaN/Infinity, range edge cases, тип-
  сабверсии, partial-patch semantics, quietHours regex+dedup.
  Фиксирует контракт «strict для required, lenient для optional
  defaults» (input принимает enabled:'yes' → true, patch строгий).
- src/renderer/src/lib/icon-choices.test.ts (3) — SAMPLE_EXERCISES.icon
  ⊆ ICON_CHOICES (иначе fallback-Activity на первом запуске).

Расширения:
- format.test.ts: NaN/Infinity guard, EN-локаль.
- history.test.ts: DST-safe инвариант (unique keys, monotonic),
  plannedRepsToday, future-dated entries, mixed actions.
- i18n.test.ts: dict parity RU↔EN (с правильным skip для RU-only
  *_few CLDR-категории), regex-injection в var-значениях,
  weekday.short.* parity.

Рефакторинг:
- ICON_CHOICES вынесен в src/renderer/src/lib/icon-choices.ts
  (без JSX) — теперь whitelist импортируется из любого слоя без
  React-зависимости. icon.tsx реэкспортирует для обратной
  совместимости.
2026-05-19 18:15:37 +07:00

86 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest'
import { formatCountdown, formatInterval } from './format'
describe('formatCountdown', () => {
it('returns "сейчас" for zero or negative ms', () => {
expect(formatCountdown(0)).toBe('сейчас')
expect(formatCountdown(-1)).toBe('сейчас')
expect(formatCountdown(-100_000)).toBe('сейчас')
})
it('renders sub-minute as seconds only', () => {
expect(formatCountdown(1_000)).toBe('1с')
expect(formatCountdown(45_000)).toBe('45с')
expect(formatCountdown(59_999)).toBe('59с')
})
it('renders minutes with zero-padded seconds', () => {
expect(formatCountdown(60_000)).toBe('1м 00с')
expect(formatCountdown(65_000)).toBe('1м 05с')
expect(formatCountdown(125_000)).toBe('2м 05с')
expect(formatCountdown(599_000)).toBe('9м 59с')
})
it('renders hours with zero-padded minutes and drops seconds', () => {
expect(formatCountdown(3_600_000)).toBe('1ч 00м')
expect(formatCountdown(3_660_000)).toBe('1ч 01м')
expect(formatCountdown(7_245_000)).toBe('2ч 00м')
expect(formatCountdown(7_320_000)).toBe('2ч 02м')
})
it('floors fractional seconds (no rounding up)', () => {
// 999ms > 0 so not "сейчас"; Math.floor(999/1000) = 0 → "0с"
expect(formatCountdown(999)).toBe('0с')
expect(formatCountdown(500)).toBe('0с')
})
// Guard added in v0.5.2 — electron-updater и scheduler могут передать
// NaN/Infinity на ранних событиях. Должны вернуть «сейчас», не «NaNс».
it('returns "сейчас" for NaN and Infinity (defensive guard)', () => {
expect(formatCountdown(NaN)).toBe('сейчас')
expect(formatCountdown(Infinity)).toBe('сейчас')
expect(formatCountdown(-Infinity)).toBe('сейчас')
})
describe('english locale', () => {
it('renders sub-minute with "s"', () => {
expect(formatCountdown(45_000, 'en')).toBe('45s')
})
it('renders minutes+seconds with "m"/"s"', () => {
expect(formatCountdown(65_000, 'en')).toBe('1m 05s')
})
it('renders hours+minutes with "h"/"m"', () => {
expect(formatCountdown(3_660_000, 'en')).toBe('1h 01m')
})
it('returns "now" for zero', () => {
expect(formatCountdown(0, 'en')).toBe('now')
})
})
})
describe('formatInterval', () => {
it('renders minutes under an hour', () => {
expect(formatInterval(1)).toBe('1 мин')
expect(formatInterval(30)).toBe('30 мин')
expect(formatInterval(59)).toBe('59 мин')
})
it('renders whole hours without minute remainder', () => {
expect(formatInterval(60)).toBe('1 ч')
expect(formatInterval(120)).toBe('2 ч')
expect(formatInterval(180)).toBe('3 ч')
})
it('renders mixed hours+minutes', () => {
expect(formatInterval(61)).toBe('1 ч 1 мин')
expect(formatInterval(90)).toBe('1 ч 30 мин')
expect(formatInterval(125)).toBe('2 ч 5 мин')
})
it('english locale', () => {
expect(formatInterval(30, 'en')).toBe('30 min')
expect(formatInterval(60, 'en')).toBe('1 h')
expect(formatInterval(90, 'en')).toBe('1 h 30 min')
})
})