Новая модель Meal — напоминание по настенным часам (time HH:MM + дни недели), в отличие от interval-based Exercise. Отдельная вкладка «Питание» с пресетами быстрого добавления (Завтрак/Обед/Ужин/Перекус). - shared: тип Meal, meals в AppState, nextMealOccurrence (DST-safe), SAMPLE_MEALS, MEAL_PRESETS; IPC-каналы meal:* + evtFireMeal - main: валидация (строгая HH:MM-проверка диапазона), store-мутаторы с пересчётом nextFireAt, scheduler.checkDueMeals (гейт только globalEnabled, grace-окно 120с, игнор тихих часов/ВКС), notifications.fireMealReminder, IPC-хендлеры - renderer: вкладка Meals + MealEditor (время/дни/иконка), MealReminder в окне напоминания (Поел/Отложить, TTS), пункт в Sidebar, маршрут, i18n RU/EN, иконки UtensilsCrossed/Soup - persistence: meals additive (без bump схемы — старые state'ы получают []) - +24 теста (203 -> 227): nextMealOccurrence, валидаторы приёмов пищи, scheduler meal-gating (вкл/выкл, grace, игнор тихих часов) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
2.9 KiB
TypeScript
71 lines
2.9 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
||
import { nextMealOccurrence } from './types'
|
||
|
||
/**
|
||
* Тесты планирования приёмов пищи по времени суток. Используем фиксированную
|
||
* «отправную точку» в локальном времени; helper тоже работает в локальном TZ,
|
||
* поэтому тесты детерминированы независимо от таймзоны CI.
|
||
*
|
||
* 2026-01-15 — четверг (getDay() === 4).
|
||
*/
|
||
const THU_10_00 = new Date(2026, 0, 15, 10, 0, 0, 0).getTime()
|
||
const THU_14_00 = new Date(2026, 0, 15, 14, 0, 0, 0).getTime()
|
||
const DAY_MS = 24 * 60 * 60 * 1000
|
||
|
||
describe('nextMealOccurrence', () => {
|
||
it('возвращает сегодняшнее время, если оно ещё не наступило', () => {
|
||
const r = new Date(nextMealOccurrence('13:00', [], THU_10_00))
|
||
expect(r.getDate()).toBe(15)
|
||
expect(r.getHours()).toBe(13)
|
||
expect(r.getMinutes()).toBe(0)
|
||
})
|
||
|
||
it('переносит на завтра, если время сегодня уже прошло', () => {
|
||
const r = new Date(nextMealOccurrence('08:00', [], THU_10_00))
|
||
expect(r.getDate()).toBe(16)
|
||
expect(r.getHours()).toBe(8)
|
||
})
|
||
|
||
it('всегда строго в будущем относительно from', () => {
|
||
expect(nextMealOccurrence('13:00', [], THU_10_00)).toBeGreaterThan(
|
||
THU_10_00
|
||
)
|
||
expect(nextMealOccurrence('08:00', [], THU_10_00)).toBeGreaterThan(
|
||
THU_10_00
|
||
)
|
||
})
|
||
|
||
it('учитывает фильтр дней недели (только пятница)', () => {
|
||
// Четверг 10:00, напоминание 13:00, дни = [5] (пятница) → завтра 16-е.
|
||
const r = new Date(nextMealOccurrence('13:00', [5], THU_10_00))
|
||
expect(r.getDate()).toBe(16)
|
||
expect(r.getDay()).toBe(5)
|
||
expect(r.getHours()).toBe(13)
|
||
})
|
||
|
||
it('сегодня входит в фильтр и время не прошло → сегодня', () => {
|
||
const r = new Date(nextMealOccurrence('13:00', [4], THU_10_00))
|
||
expect(r.getDate()).toBe(15)
|
||
expect(r.getDay()).toBe(4)
|
||
})
|
||
|
||
it('единственный день недели, время прошло → следующая неделя', () => {
|
||
// Четверг 14:00, 13:00 уже прошло, дни = [4] → следующий четверг 22-е.
|
||
const r = new Date(nextMealOccurrence('13:00', [4], THU_14_00))
|
||
expect(r.getDate()).toBe(22)
|
||
expect(r.getDay()).toBe(4)
|
||
})
|
||
|
||
it('пустой массив дней = каждый день', () => {
|
||
const r = new Date(nextMealOccurrence('23:59', [], THU_10_00))
|
||
expect(r.getDate()).toBe(15)
|
||
})
|
||
|
||
it('малформированное время → +24ч (safety)', () => {
|
||
expect(nextMealOccurrence('99:99', [], THU_10_00)).toBe(THU_10_00 + DAY_MS)
|
||
expect(nextMealOccurrence('not-a-time', [], THU_10_00)).toBe(
|
||
THU_10_00 + DAY_MS
|
||
)
|
||
})
|
||
})
|