66 lines
2.5 KiB
TypeScript
66 lines
2.5 KiB
TypeScript
|
|
import { test } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
|
||
|
|
import { getActiveSeason, SEASON_SCHEDULE, SEASON_DATE_RANGES } from './seasonSchedule';
|
||
|
|
import { SeasonTheme } from './types';
|
||
|
|
|
||
|
|
// Date(year, monthIndex0, day)
|
||
|
|
const on = (monthIndex0: number, day: number): Date => new Date(2026, monthIndex0, day);
|
||
|
|
|
||
|
|
test('each theme activates on a representative day in its window', () => {
|
||
|
|
const cases: Array<[Date, SeasonTheme]> = [
|
||
|
|
[on(11, 31), 'newyear'], // Dec 31
|
||
|
|
[on(0, 1), 'newyear'], // Jan 1
|
||
|
|
[on(0, 25), 'lunar'], // Jan 25
|
||
|
|
[on(1, 3), 'lunar'], // Feb 3
|
||
|
|
[on(1, 12), 'valentines'], // Feb 12
|
||
|
|
[on(2, 16), 'stpatricks'], // Mar 16
|
||
|
|
[on(3, 1), 'aprilfools'], // Apr 1
|
||
|
|
[on(3, 21), 'earthday'], // Apr 21
|
||
|
|
[on(8, 12), 'arcade'], // Sep 12
|
||
|
|
[on(8, 25), 'autumn'], // Sep 25
|
||
|
|
[on(9, 20), 'halloween'], // Oct 20
|
||
|
|
[on(10, 1), 'halloween'], // Nov 1
|
||
|
|
[on(11, 15), 'christmas'], // Dec 15
|
||
|
|
];
|
||
|
|
for (const [date, expected] of cases) {
|
||
|
|
assert.equal(getActiveSeason(date), expected, `${date.toDateString()} -> ${expected}`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('priority order resolves overlapping windows (Deep Space outranks Autumn)', () => {
|
||
|
|
// Oct 4-10 is inside Autumn's Oct<=14 window too; Deep Space comes first.
|
||
|
|
assert.equal(getActiveSeason(on(9, 5)), 'deepspace'); // Oct 5
|
||
|
|
// Oct 12 is past Deep Space -> falls through to Autumn.
|
||
|
|
assert.equal(getActiveSeason(on(9, 12)), 'autumn');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('New Year outranks Lunar New Year on Jan 1-2', () => {
|
||
|
|
assert.equal(getActiveSeason(on(0, 1)), 'newyear');
|
||
|
|
// Jan 22+ is past New Year -> Lunar.
|
||
|
|
assert.equal(getActiveSeason(on(0, 22)), 'lunar');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('returns null on an off-season day', () => {
|
||
|
|
assert.equal(getActiveSeason(on(5, 15)), null); // Jun 15
|
||
|
|
assert.equal(getActiveSeason(on(6, 4)), null); // Jul 4
|
||
|
|
});
|
||
|
|
|
||
|
|
test('window boundaries are inclusive at both ends', () => {
|
||
|
|
assert.equal(getActiveSeason(on(1, 10)), 'valentines'); // Feb 10 start
|
||
|
|
assert.equal(getActiveSeason(on(1, 15)), 'valentines'); // Feb 15 end
|
||
|
|
assert.equal(getActiveSeason(on(1, 16)), null); // Feb 16 just after
|
||
|
|
});
|
||
|
|
|
||
|
|
test('SEASON_DATE_RANGES has a label for every scheduled theme', () => {
|
||
|
|
assert.equal(SEASON_SCHEDULE.length, 11);
|
||
|
|
const themes = SEASON_SCHEDULE.map((e) => e.theme);
|
||
|
|
assert.equal(new Set(themes).size, 11); // unique
|
||
|
|
for (const t of themes) {
|
||
|
|
assert.ok(
|
||
|
|
typeof SEASON_DATE_RANGES[t] === 'string' && SEASON_DATE_RANGES[t].length > 0,
|
||
|
|
`missing date range for ${t}`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
});
|