ed5680253e
Every serious defect in this project was found by opening the app, most recently a feature that rendered blank cards to every user while its tests passed, and a progress bar labelled in the wrong unit. Five checks now run without a person: that the age-four figures on screen match what the API computed, that a multi-week period draws as one bar rather than fragments, that Sundays and holidays are red where Saturdays are not, that no identifier leaks into visible text, and that no page logs an error. Three of them were confirmed by reintroducing the original bug and watching the right test fail. Dates are derived rather than written down: Easter is computed independently instead of asked of the system under test, and the fixture period is placed relative to today, so the suite does not expire. It runs as its own job and does not block a merge until it has proved itself stable.
81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
/**
|
||
* Date-math helpers shared by the E2E specs. The whole point of this module
|
||
* is that no spec ever hardcodes a year: every date used in an assertion is
|
||
* derived, at run time, from "today" or from the seeded child's birth date.
|
||
*
|
||
* All dates here are UTC-normalized `Date`s at midnight and ISO date
|
||
* strings (`YYYY-MM-DD`) — the same representation the app's calendar
|
||
* grid (`CalendarDay.tsx`) uses (`new Date(date + 'T00:00:00Z')`).
|
||
*/
|
||
|
||
export function toIsoDate(date: Date): string {
|
||
return date.toISOString().slice(0, 10)
|
||
}
|
||
|
||
export function addDays(date: Date, days: number): Date {
|
||
const result = new Date(date.getTime())
|
||
result.setUTCDate(result.getUTCDate() + days)
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* Easter Sunday for `year`, via the Anonymous Gregorian (Meeus/Jones/
|
||
* Butcher) algorithm. Pure date math, no external calendar data — this is
|
||
* intentionally independent of the backend's `swedish_calendar.json` cache,
|
||
* so the test computes its own expectation rather than asking the system
|
||
* under test what the system under test should show.
|
||
*/
|
||
export function easterSunday(year: number): Date {
|
||
const a = year % 19
|
||
const b = Math.floor(year / 100)
|
||
const c = year % 100
|
||
const d = Math.floor(b / 4)
|
||
const e = b % 4
|
||
const f = Math.floor((b + 8) / 25)
|
||
const g = Math.floor((b - f + 1) / 3)
|
||
const h = (19 * a + b - d - g + 15) % 30
|
||
const i = Math.floor(c / 4)
|
||
const k = c % 4
|
||
const l = (32 + 2 * e + 2 * i - h - k) % 7
|
||
const m = Math.floor((a + 11 * h + 22 * l) / 451)
|
||
const monthPlusDayEncoded = h + l - 7 * m + 114
|
||
const month = Math.floor(monthPlusDayEncoded / 31) // 3 = March, 4 = April
|
||
const day = (monthPlusDayEncoded % 31) + 1
|
||
return new Date(Date.UTC(year, month - 1, day))
|
||
}
|
||
|
||
/** The first Easter Sunday on or after `from` (whole-day comparison). */
|
||
export function nextEasterOnOrAfter(from: Date): Date {
|
||
let year = from.getUTCFullYear()
|
||
let candidate = easterSunday(year)
|
||
while (candidate.getTime() < from.getTime()) {
|
||
year += 1
|
||
candidate = easterSunday(year)
|
||
}
|
||
return candidate
|
||
}
|
||
|
||
/** Whit Sunday (Pingstdagen) is always exactly 49 days after Easter Sunday. */
|
||
export function whitSunday(easter: Date): Date {
|
||
return addDays(easter, 49)
|
||
}
|
||
|
||
/** Monday of the ISO week containing `date` (Mon=0 .. Sun=6 offset). */
|
||
export function mondayOfWeek(date: Date): Date {
|
||
const isoWeekday = (date.getUTCDay() + 6) % 7 // 0 = Monday
|
||
return addDays(date, -isoWeekday)
|
||
}
|
||
|
||
/**
|
||
* Number of Mon–Sun calendar-week rows that the inclusive range
|
||
* [start, end] touches — i.e. how many `CalendarWeek` rows a band spanning
|
||
* that range should render across in the app's Monday-start grid
|
||
* (see `Calendar.tsx`'s `startOffset = (firstDay.getDay() + 6) % 7`).
|
||
*/
|
||
export function countWeekRows(start: Date, end: Date): number {
|
||
const startWeek = mondayOfWeek(start)
|
||
const endWeek = mondayOfWeek(end)
|
||
const diffDays = Math.round((endWeek.getTime() - startWeek.getTime()) / 86_400_000)
|
||
return diffDays / 7 + 1
|
||
}
|