Аудит тестов выявил критические пробелы в покрытии. Расширили существующие файлы и добавили два новых: Новые файлы: - 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 реэкспортирует для обратной совместимости.
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
||
import {
|
||
DEFAULT_SETTINGS,
|
||
GAME_STATS,
|
||
SAMPLE_EXERCISES,
|
||
STAT_LABELS,
|
||
type GameStat
|
||
} from './types'
|
||
|
||
describe('DEFAULT_SETTINGS', () => {
|
||
it('uses safe defaults that do not surprise the user', () => {
|
||
expect(DEFAULT_SETTINGS.globalEnabled).toBe(true)
|
||
expect(DEFAULT_SETTINGS.notificationMode).toBe('modal')
|
||
expect(DEFAULT_SETTINGS.minimizeToTray).toBe(true)
|
||
expect(DEFAULT_SETTINGS.startWithWindows).toBe(false) // never auto-enroll
|
||
expect(DEFAULT_SETTINGS.snoozeMinutes).toBeGreaterThan(0)
|
||
})
|
||
})
|
||
|
||
describe('SAMPLE_EXERCISES', () => {
|
||
it('ships at least one enabled sample so the app is not empty on first launch', () => {
|
||
expect(SAMPLE_EXERCISES.length).toBeGreaterThan(0)
|
||
expect(SAMPLE_EXERCISES.some((e) => e.enabled)).toBe(true)
|
||
})
|
||
|
||
it('all samples have positive reps and intervals', () => {
|
||
for (const ex of SAMPLE_EXERCISES) {
|
||
expect(ex.reps, `reps for ${ex.name}`).toBeGreaterThan(0)
|
||
expect(ex.intervalMinutes, `interval for ${ex.name}`).toBeGreaterThan(0)
|
||
expect(ex.icon.length, `icon set for ${ex.name}`).toBeGreaterThan(0)
|
||
}
|
||
})
|
||
|
||
})
|
||
// NB: тест «sample icons ⊆ ICON_CHOICES» лежит в
|
||
// src/renderer/src/lib/icon-choices.test.ts — он тянет renderer-сторону
|
||
// (ICON_CHOICES), а node-tsconfig сюда не пускает renderer-импорты.
|
||
|
||
describe('STAT_LABELS', () => {
|
||
it('has a Russian label for every GameStat in every GAME_STATS bundle', () => {
|
||
for (const stats of Object.values(GAME_STATS)) {
|
||
for (const stat of stats as readonly GameStat[]) {
|
||
expect(STAT_LABELS[stat], `label for ${stat}`).toBeTruthy()
|
||
}
|
||
}
|
||
})
|
||
})
|