mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
f61bab3ffd
The year list comes back sorted, so picking its last element meant Vacay always landed on the highest year — add 2030 to plan ahead and every visit from then on opened 2030. It now opens the leave-year period today falls into, falls back to the closest year the plan has when that period has none, and keeps the year the viewer is on when a live update reloads the list.
157 lines
6.5 KiB
TypeScript
157 lines
6.5 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
||
import {
|
||
DEFAULT_YEAR_SETTINGS,
|
||
currentPeriodYear,
|
||
defaultPeriodYear,
|
||
gridWindow,
|
||
inGridWindow,
|
||
resolveYearWindow,
|
||
windowCalendarYears,
|
||
windowMonths,
|
||
} from './yearWindow'
|
||
import type { VacayYearSettings } from '../types'
|
||
|
||
const fiscal = (month: number, day = 1): VacayYearSettings => ({
|
||
year_type: 'fiscal', year_start_month: month, year_start_day: day, hire_date: null,
|
||
})
|
||
|
||
const anniversary = (hire: string | null): VacayYearSettings => ({
|
||
year_type: 'anniversary', year_start_month: 1, year_start_day: 1, hire_date: hire,
|
||
})
|
||
|
||
describe('resolveYearWindow', () => {
|
||
// FE-VACAY-YEARWINDOW-001
|
||
it('defaults to the plain calendar year', () => {
|
||
expect(resolveYearWindow(2026)).toEqual({ start: '2026-01-01', end: '2027-01-01' })
|
||
expect(resolveYearWindow(2026, DEFAULT_YEAR_SETTINGS)).toEqual({ start: '2026-01-01', end: '2027-01-01' })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-002
|
||
it('starts a fiscal year on the configured month and day', () => {
|
||
expect(resolveYearWindow(2026, fiscal(7))).toEqual({ start: '2026-07-01', end: '2027-07-01' })
|
||
expect(resolveYearWindow(2026, fiscal(4, 6))).toEqual({ start: '2026-04-06', end: '2027-04-06' })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-003
|
||
it('follows the hire date for an anniversary year, falling back to January 1 without one', () => {
|
||
expect(resolveYearWindow(2026, anniversary('2019-09-16'))).toEqual({ start: '2026-09-16', end: '2027-09-16' })
|
||
expect(resolveYearWindow(2026, anniversary(null))).toEqual({ start: '2026-01-01', end: '2027-01-01' })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-004
|
||
it('matches the server: consecutive periods meet exactly', () => {
|
||
expect(resolveYearWindow(2025, fiscal(7)).end).toBe(resolveYearWindow(2026, fiscal(7)).start)
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-004a
|
||
it('ignores a month left behind by a previous fiscal setting when there is no hire date', () => {
|
||
// The settings UI sends the whole object, so switching to anniversary carries
|
||
// the old fiscal month along until a hire date is entered.
|
||
const stale = { year_type: 'anniversary' as const, year_start_month: 4, year_start_day: 1, hire_date: null }
|
||
expect(resolveYearWindow(2026, stale)).toEqual({ start: '2026-01-01', end: '2027-01-01' })
|
||
expect(windowMonths(2026, stale)[0]).toEqual({ year: 2026, month: 0 })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-004b
|
||
it('clamps a start day the month cannot have, matching the server', () => {
|
||
expect(resolveYearWindow(2026, anniversary('2024-02-29'))).toEqual({ start: '2026-02-28', end: '2027-02-28' })
|
||
expect(resolveYearWindow(2026, fiscal(2, 31))).toEqual({ start: '2026-02-28', end: '2027-02-28' })
|
||
})
|
||
})
|
||
|
||
describe('windowMonths', () => {
|
||
// FE-VACAY-YEARWINDOW-005
|
||
it('lists January to December of the year for a calendar window', () => {
|
||
const months = windowMonths(2026)
|
||
expect(months).toHaveLength(12)
|
||
expect(months[0]).toEqual({ year: 2026, month: 0 })
|
||
expect(months[11]).toEqual({ year: 2026, month: 11 })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-006
|
||
it('rolls over the calendar year for a fiscal window', () => {
|
||
const months = windowMonths(2026, fiscal(7))
|
||
expect(months[0]).toEqual({ year: 2026, month: 6 }) // Jul 2026
|
||
expect(months[5]).toEqual({ year: 2026, month: 11 }) // Dec 2026
|
||
expect(months[6]).toEqual({ year: 2027, month: 0 }) // Jan 2027
|
||
expect(months[11]).toEqual({ year: 2027, month: 5 }) // Jun 2027
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-007
|
||
it('ignores the start day — the grid always renders whole months', () => {
|
||
expect(windowMonths(2026, fiscal(4, 6))[0]).toEqual({ year: 2026, month: 3 })
|
||
})
|
||
})
|
||
|
||
describe('gridWindow / inGridWindow', () => {
|
||
// FE-VACAY-YEARWINDOW-008
|
||
it('is the window itself whenever it starts on the first of a month', () => {
|
||
expect(gridWindow(2026)).toEqual(resolveYearWindow(2026))
|
||
expect(gridWindow(2026, fiscal(7))).toEqual(resolveYearWindow(2026, fiscal(7)))
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-009
|
||
it('widens to whole months when the window starts later in the month', () => {
|
||
expect(gridWindow(2026, fiscal(4, 6))).toEqual({ start: '2026-04-01', end: '2027-04-01' })
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-010
|
||
it('accepts the days the grid shows and rejects the neighbouring periods', () => {
|
||
expect(inGridWindow('2026-12-31', 2026, fiscal(7))).toBe(true)
|
||
expect(inGridWindow('2027-01-01', 2026, fiscal(7))).toBe(true)
|
||
expect(inGridWindow('2027-06-30', 2026, fiscal(7))).toBe(true)
|
||
expect(inGridWindow('2026-06-30', 2026, fiscal(7))).toBe(false)
|
||
expect(inGridWindow('2027-07-01', 2026, fiscal(7))).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('windowCalendarYears', () => {
|
||
// FE-VACAY-YEARWINDOW-011
|
||
it('is one year for a calendar window and two for a shifted one', () => {
|
||
expect(windowCalendarYears(2026)).toEqual([2026])
|
||
expect(windowCalendarYears(2026, fiscal(7))).toEqual([2026, 2027])
|
||
})
|
||
})
|
||
|
||
describe('currentPeriodYear', () => {
|
||
// FE-VACAY-YEARWINDOW-012
|
||
it('is today’s year for a calendar user', () => {
|
||
expect(currentPeriodYear(DEFAULT_YEAR_SETTINGS, new Date(2026, 2, 15))).toBe(2026)
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-013
|
||
it('is the previous year while a shifted period has not started yet', () => {
|
||
expect(currentPeriodYear(fiscal(7), new Date(2026, 2, 15))).toBe(2025)
|
||
expect(currentPeriodYear(fiscal(7), new Date(2026, 7, 15))).toBe(2026)
|
||
expect(currentPeriodYear(fiscal(7), new Date(2026, 6, 1))).toBe(2026)
|
||
})
|
||
})
|
||
|
||
describe('defaultPeriodYear', () => {
|
||
// FE-VACAY-YEARWINDOW-014
|
||
it('picks the current period when the plan has it', () => {
|
||
const today = new Date(2026, 2, 15)
|
||
expect(defaultPeriodYear([2024, 2025, 2026, 2030], DEFAULT_YEAR_SETTINGS, today)).toBe(2026)
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-015
|
||
it('falls back to the closest year and prefers the coming one on a tie', () => {
|
||
const today = new Date(2026, 2, 15)
|
||
expect(defaultPeriodYear([2022, 2024], DEFAULT_YEAR_SETTINGS, today)).toBe(2024)
|
||
expect(defaultPeriodYear([2028, 2030], DEFAULT_YEAR_SETTINGS, today)).toBe(2028)
|
||
expect(defaultPeriodYear([2024, 2028], DEFAULT_YEAR_SETTINGS, today)).toBe(2028)
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-016
|
||
it('is the current period for an empty plan', () => {
|
||
expect(defaultPeriodYear([], DEFAULT_YEAR_SETTINGS, new Date(2026, 2, 15))).toBe(2026)
|
||
})
|
||
|
||
// FE-VACAY-YEARWINDOW-017
|
||
it('follows a shifted leave year rather than the calendar year', () => {
|
||
const today = new Date(2026, 2, 15)
|
||
expect(defaultPeriodYear([2025, 2026], fiscal(7), today)).toBe(2025)
|
||
expect(defaultPeriodYear([2026], fiscal(7), today)).toBe(2026)
|
||
})
|
||
})
|