Compare commits
2 Commits
25669dfdae
...
427ba6756a
| Author | SHA1 | Date | |
|---|---|---|---|
| 427ba6756a | |||
| a68ab716b7 |
@@ -9,7 +9,7 @@
|
|||||||
// become a configurable choice later (the same day->id indirection
|
// become a configurable choice later (the same day->id indirection
|
||||||
// philosophy already used for the sanctoral calendar), not hardcoded here
|
// philosophy already used for the sanctoral calendar), not hardcoded here
|
||||||
// forever — just not built yet.
|
// forever — just not built yet.
|
||||||
import type { Commemoration, FeastClass, LiturgicalDay } from './types';
|
import type { Commemoration, FeastClass, LiturgicalDay, TemporalCategory } from './types';
|
||||||
import { easterSunday } from './easter';
|
import { easterSunday } from './easter';
|
||||||
import { adventStart, firstSundayStrictlyAfter, sundayOnOrBefore } from './temporal';
|
import { adventStart, firstSundayStrictlyAfter, sundayOnOrBefore } from './temporal';
|
||||||
import { addDays, daysBetween, toIsoDate } from './date-math';
|
import { addDays, daysBetween, toIsoDate } from './date-math';
|
||||||
@@ -50,6 +50,12 @@ interface OrdinalSeason {
|
|||||||
/** Used in "the Nth Sunday/week {preposition} {ordinalName}". */
|
/** Used in "the Nth Sunday/week {preposition} {ordinalName}". */
|
||||||
ordinalName: string;
|
ordinalName: string;
|
||||||
preposition: 'after' | 'of';
|
preposition: 'after' | 'of';
|
||||||
|
/** The season's own anchor day's rank, if it sits on the closed
|
||||||
|
* pre-1955 duplex scale at all — Ash Wednesday (Lent's anchor) doesn't;
|
||||||
|
* a privileged feria has no duplex-scale rank, so it's left undefined
|
||||||
|
* and shows no parenthetical, same "omit rather than guess" convention
|
||||||
|
* `formatRank`/`status` fields use elsewhere. */
|
||||||
|
anchorRank?: FeastClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ORDINAL_SEASONS: Partial<Record<string, OrdinalSeason>> = {
|
const ORDINAL_SEASONS: Partial<Record<string, OrdinalSeason>> = {
|
||||||
@@ -66,6 +72,7 @@ const ORDINAL_SEASONS: Partial<Record<string, OrdinalSeason>> = {
|
|||||||
anchorName: 'Epiphany',
|
anchorName: 'Epiphany',
|
||||||
ordinalName: 'Epiphany',
|
ordinalName: 'Epiphany',
|
||||||
preposition: 'after',
|
preposition: 'after',
|
||||||
|
anchorRank: 'duplex-1-classis',
|
||||||
},
|
},
|
||||||
lent: {
|
lent: {
|
||||||
anchorDate: (year) => addDays(toIsoDate(easterSunday(year)), -46),
|
anchorDate: (year) => addDays(toIsoDate(easterSunday(year)), -46),
|
||||||
@@ -80,6 +87,7 @@ const ORDINAL_SEASONS: Partial<Record<string, OrdinalSeason>> = {
|
|||||||
anchorName: 'Easter',
|
anchorName: 'Easter',
|
||||||
ordinalName: 'Easter',
|
ordinalName: 'Easter',
|
||||||
preposition: 'after',
|
preposition: 'after',
|
||||||
|
anchorRank: 'duplex-1-classis',
|
||||||
},
|
},
|
||||||
trinitytide: {
|
trinitytide: {
|
||||||
anchorDate: (year) => addDays(toIsoDate(easterSunday(year)), 56),
|
anchorDate: (year) => addDays(toIsoDate(easterSunday(year)), 56),
|
||||||
@@ -87,6 +95,7 @@ const ORDINAL_SEASONS: Partial<Record<string, OrdinalSeason>> = {
|
|||||||
anchorName: 'Trinity Sunday',
|
anchorName: 'Trinity Sunday',
|
||||||
ordinalName: 'Trinity',
|
ordinalName: 'Trinity',
|
||||||
preposition: 'after',
|
preposition: 'after',
|
||||||
|
anchorRank: 'duplex-1-classis',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +114,7 @@ function anchorDayName(day: LiturgicalDay): string | undefined {
|
|||||||
}
|
}
|
||||||
const year = Number(day.date.slice(0, 4));
|
const year = Number(day.date.slice(0, 4));
|
||||||
if (day.date === config.anchorDate(year)) {
|
if (day.date === config.anchorDate(year)) {
|
||||||
return config.anchorName;
|
return config.anchorRank ? `${config.anchorName} (${formatRank(config.anchorRank)})` : config.anchorName;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -209,7 +218,7 @@ function temporalLabel(day: LiturgicalDay): string {
|
|||||||
* special-cased for that rare edge case (see applyOctaves's own
|
* special-cased for that rare edge case (see applyOctaves's own
|
||||||
* `isOwnStartDay` comment in calendar/index.ts for when it can happen). */
|
* `isOwnStartDay` comment in calendar/index.ts for when it can happen). */
|
||||||
function octaveLabel(octave: ActiveOctave): string {
|
function octaveLabel(octave: ActiveOctave): string {
|
||||||
return `${ordinal(octave.dayNumber)} Day within the Octave of ${octave.name}`;
|
return `${ordinal(octave.dayNumber)} Day within the Octave of ${octave.name} (${formatRank(octave.wins)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Every `kind: 'octave'` commemoration on `day` other than `excludeId` —
|
/** Every `kind: 'octave'` commemoration on `day` other than `excludeId` —
|
||||||
@@ -249,6 +258,24 @@ function formatRank(rank: FeastClass): string {
|
|||||||
return RANK_LABELS[rank];
|
return RANK_LABELS[rank];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sundays/ferias aren't on the `FeastClass` duplex scale at all (that
|
||||||
|
* scale is for feasts), so this is a separate, plain-string lookup rather
|
||||||
|
* than another `formatRank` case — used for the final ordinal-temporal-
|
||||||
|
* label fallback (weekdays, Sundays with no named-feast/anchor-day/octave
|
||||||
|
* standing of their own). Pre-1955, an ordinary Sunday's own rank is
|
||||||
|
* Semiduplex regardless of whether it's additionally "privileged"
|
||||||
|
* (privilege is about resisting supersession, a property of
|
||||||
|
* `temporalCategory`, not a higher spot on the duplex scale) — so both
|
||||||
|
* Sunday categories share one label here. */
|
||||||
|
const TEMPORAL_CATEGORY_RANK_LABELS: Record<TemporalCategory, string> = {
|
||||||
|
'ordinary-sunday': 'Semiduplex',
|
||||||
|
'privileged-sunday': 'Semiduplex',
|
||||||
|
'ordinary-feria': 'Feria',
|
||||||
|
'privileged-feria-minor': 'Feria',
|
||||||
|
'privileged-feria': 'Feria',
|
||||||
|
'privileged-feria-major': 'Feria',
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The full "day being celebrated" label: a feast name when
|
* The full "day being celebrated" label: a feast name when
|
||||||
* calendar/commemorations.ts says the day has one, combined with (or
|
* calendar/commemorations.ts says the day has one, combined with (or
|
||||||
@@ -280,7 +307,7 @@ export function getDayLabel(day: LiturgicalDay): string {
|
|||||||
// temporal-feasts.ts) and fall through to the ordinal label as before.
|
// temporal-feasts.ts) and fall through to the ordinal label as before.
|
||||||
const namedFeast = getTemporalFeastRecord(day.winner.id);
|
const namedFeast = getTemporalFeastRecord(day.winner.id);
|
||||||
if (namedFeast) {
|
if (namedFeast) {
|
||||||
return namedFeast.name;
|
return namedFeast.rank ? `${namedFeast.name} (${formatRank(namedFeast.rank)})` : namedFeast.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const commemoratedSaint = day.commemorations.find((c) => c.kind === 'sanctoral');
|
const commemoratedSaint = day.commemorations.find((c) => c.kind === 'sanctoral');
|
||||||
@@ -323,6 +350,6 @@ export function getDayLabel(day: LiturgicalDay): string {
|
|||||||
return [primary, ...rest].join(' — ');
|
return [primary, ...rest].join(' — ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const temporal = temporalLabel(day);
|
const temporal = `${temporalLabel(day)} (${TEMPORAL_CATEGORY_RANK_LABELS[day.temporalCategory]})`;
|
||||||
return commemoratedSaint ? `${commemoratedSaint.name} — ${temporal}` : temporal;
|
return commemoratedSaint ? `${commemoratedSaint.name} — ${temporal}` : temporal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@
|
|||||||
// don't need a record at all (they're just collect-text lookups via
|
// don't need a record at all (they're just collect-text lookups via
|
||||||
// propers/index.ts's getTemporalProper); this only exists for the ones
|
// propers/index.ts's getTemporalProper); this only exists for the ones
|
||||||
// that do.
|
// that do.
|
||||||
import type { OctaveConfig } from './types';
|
import type { FeastClass, OctaveConfig } from './types';
|
||||||
import { easterOffsetOf } from './temporal';
|
import { easterOffsetOf } from './temporal';
|
||||||
|
|
||||||
export interface TemporalFeastRecord {
|
export interface TemporalFeastRecord {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
octave?: OctaveConfig;
|
octave?: OctaveConfig;
|
||||||
|
rank?: FeastClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
const temporalFeastModules = import.meta.glob<{ default: TemporalFeastRecord }>(
|
const temporalFeastModules = import.meta.glob<{ default: TemporalFeastRecord }>(
|
||||||
|
|||||||
@@ -6,3 +6,4 @@
|
|||||||
# See calendar/index.ts's applyChristTheKing for the occurrence rule.
|
# See calendar/index.ts's applyChristTheKing for the occurrence rule.
|
||||||
id: christ-the-king
|
id: christ-the-king
|
||||||
name: "Christ the King"
|
name: "Christ the King"
|
||||||
|
rank: duplex-1-classis
|
||||||
|
|||||||
@@ -12,5 +12,6 @@
|
|||||||
# specifically.
|
# specifically.
|
||||||
id: christmas-day
|
id: christmas-day
|
||||||
name: "Christmas"
|
name: "Christmas"
|
||||||
|
rank: duplex-1-classis
|
||||||
octave:
|
octave:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -5,3 +5,4 @@
|
|||||||
# for how a day actually becomes this.
|
# for how a day actually becomes this.
|
||||||
id: marian-saturday
|
id: marian-saturday
|
||||||
name: "Our Lady's Saturday"
|
name: "Our Lady's Saturday"
|
||||||
|
rank: simplex
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
# before it gets the chance.
|
# before it gets the chance.
|
||||||
id: pentecost-sunday
|
id: pentecost-sunday
|
||||||
name: "Pentecost"
|
name: "Pentecost"
|
||||||
|
rank: duplex-1-classis
|
||||||
octave:
|
octave:
|
||||||
enabled: true
|
enabled: true
|
||||||
wins: duplex
|
wins: duplex
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe('Christ the King (applyChristTheKing)', () => {
|
|||||||
const day = resolveDay('2026-10-25'); // last Sunday of October 2026
|
const day = resolveDay('2026-10-25'); // last Sunday of October 2026
|
||||||
expect(day.winner).toEqual({ kind: 'temporal', id: 'christ-the-king' });
|
expect(day.winner).toEqual({ kind: 'temporal', id: 'christ-the-king' });
|
||||||
expect(day.commemorations).toContainEqual({ kind: 'temporal', id: 'post-pentecost-22' });
|
expect(day.commemorations).toContainEqual({ kind: 'temporal', id: 'post-pentecost-22' });
|
||||||
expect(getDayLabel(day)).toBe('Christ the King');
|
expect(getDayLabel(day)).toBe('Christ the King (Duplex I Class)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves a nested commemoration: a Simplex saint already commemorated under the ordinary Sunday stays commemorated', () => {
|
it('preserves a nested commemoration: a Simplex saint already commemorated under the ordinary Sunday stays commemorated', () => {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
// the full-year sanctoral import), which would win outright and hide
|
// the full-year sanctoral import), which would win outright and hide
|
||||||
// this purely-temporal label case. 2027's Trinity Sunday is May 23,
|
// this purely-temporal label case. 2027's Trinity Sunday is May 23,
|
||||||
// so Trinity Monday is May 24 -- free of any sanctoral entry.
|
// so Trinity Monday is May 24 -- free of any sanctoral entry.
|
||||||
expect(getDayLabel(resolveDay('2027-05-24'))).toBe('Monday after Trinity Sunday');
|
expect(getDayLabel(resolveDay('2027-05-24'))).toBe('Monday after Trinity Sunday (Feria)');
|
||||||
expect(getDayLabel(resolveDay('2026-01-07'))).toBe('Wednesday after Epiphany'); // Epiphany 2026 is a Tuesday
|
expect(getDayLabel(resolveDay('2026-01-07'))).toBe('Wednesday after Epiphany (Feria)'); // Epiphany 2026 is a Tuesday
|
||||||
});
|
});
|
||||||
|
|
||||||
it('names the anchor day itself, not "day after itself"', () => {
|
it('names the anchor day itself, not "day after itself"', () => {
|
||||||
@@ -25,7 +25,7 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
// winner/commemorations breakdown), so the label now reflects her,
|
// winner/commemorations breakdown), so the label now reflects her,
|
||||||
// not Trinity Sunday itself.
|
// not Trinity Sunday itself.
|
||||||
expect(getDayLabel(resolveDay('2026-05-31'))).toBe('The Queenship of the Blessed Virgin Mary (Duplex II Class)');
|
expect(getDayLabel(resolveDay('2026-05-31'))).toBe('The Queenship of the Blessed Virgin Mary (Duplex II Class)');
|
||||||
expect(getDayLabel(resolveDay('2026-04-05'))).toBe('Easter');
|
expect(getDayLabel(resolveDay('2026-04-05'))).toBe('Easter (Duplex I Class)');
|
||||||
expect(getDayLabel(resolveDay('2026-02-18'))).toBe('Ash Wednesday');
|
expect(getDayLabel(resolveDay('2026-02-18'))).toBe('Ash Wednesday');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
// sanctoral import), which wins outright. 2027's Trinity Sunday is
|
// sanctoral import), which wins outright. 2027's Trinity Sunday is
|
||||||
// May 23, so 5 full weeks after the first Sunday-after-Trinity
|
// May 23, so 5 full weeks after the first Sunday-after-Trinity
|
||||||
// (May 30) is Jun 28 -- free of any sanctoral entry.
|
// (May 30) is Jun 28 -- free of any sanctoral entry.
|
||||||
expect(getDayLabel(resolveDay('2027-06-28'))).toBe('Monday in the 5th week after Trinity');
|
expect(getDayLabel(resolveDay('2027-06-28'))).toBe('Monday in the 5th week after Trinity (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Advent's own anchor Sunday is already week 1, not a separate anchor-week case", () => {
|
it("Advent's own anchor Sunday is already week 1, not a separate anchor-week case", () => {
|
||||||
@@ -45,8 +45,8 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
// reference engine, which shows the same pairing), so the label
|
// reference engine, which shows the same pairing), so the label
|
||||||
// reflects both, feast name first, same pattern as Trinity Sunday's
|
// reflects both, feast name first, same pattern as Trinity Sunday's
|
||||||
// own St. Felix I case above.
|
// own St. Felix I case above.
|
||||||
expect(getDayLabel(resolveDay('2025-11-30'))).toBe('St. Andrew, Apostle — The 1st Sunday of Advent');
|
expect(getDayLabel(resolveDay('2025-11-30'))).toBe('St. Andrew, Apostle — The 1st Sunday of Advent (Semiduplex)');
|
||||||
expect(getDayLabel(resolveDay('2025-12-01'))).toBe('Monday in the 1st week of Advent');
|
expect(getDayLabel(resolveDay('2025-12-01'))).toBe('Monday in the 1st week of Advent (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
||||||
@@ -73,7 +73,7 @@ describe('getDayLabel — resumed post-Epiphany Sunday (overflow years)', () =>
|
|||||||
// appended to the Sunday's own label per this app's usual
|
// appended to the Sunday's own label per this app's usual
|
||||||
// commemorated-Simplex display.
|
// commemorated-Simplex display.
|
||||||
expect(getDayLabel(resolveDay('2024-11-10'))).toBe(
|
expect(getDayLabel(resolveDay('2024-11-10'))).toBe(
|
||||||
'Ss. Tryphon, Respicius, and Nympha, Martyrs — The 5th Sunday after Epiphany',
|
'Ss. Tryphon, Respicius, and Nympha, Martyrs — The 5th Sunday after Epiphany (Semiduplex)',
|
||||||
);
|
);
|
||||||
// 2024-11-11 is St. Martin of Tours (Duplex, pre-existing content) --
|
// 2024-11-11 is St. Martin of Tours (Duplex, pre-existing content) --
|
||||||
// an ordinary (non-privileged) Monday fully yields to a real winning
|
// an ordinary (non-privileged) Monday fully yields to a real winning
|
||||||
@@ -83,7 +83,7 @@ describe('getDayLabel — resumed post-Epiphany Sunday (overflow years)', () =>
|
|||||||
// Nov-dated Monday near this Sunday pair is clean any more, now that
|
// Nov-dated Monday near this Sunday pair is clean any more, now that
|
||||||
// Nov 4/8/15/16/20 are all fixed real content).
|
// Nov 4/8/15/16/20 are all fixed real content).
|
||||||
expect(getDayLabel(resolveDay('2024-11-11'))).toBe('St. Martin of Tours, Bishop and Confessor (Duplex Majus)');
|
expect(getDayLabel(resolveDay('2024-11-11'))).toBe('St. Martin of Tours, Bishop and Confessor (Duplex Majus)');
|
||||||
expect(getDayLabel(resolveDay('2024-11-17'))).toBe('The 6th Sunday after Epiphany');
|
expect(getDayLabel(resolveDay('2024-11-17'))).toBe('The 6th Sunday after Epiphany (Semiduplex)');
|
||||||
// 2024-11-18 is the Dedication of the Basilicas of Ss. Peter and Paul
|
// 2024-11-18 is the Dedication of the Basilicas of Ss. Peter and Paul
|
||||||
// (pre-existing fixed content) -- again a real winning saint, not the
|
// (pre-existing fixed content) -- again a real winning saint, not the
|
||||||
// ferial fallback.
|
// ferial fallback.
|
||||||
@@ -92,7 +92,7 @@ describe('getDayLabel — resumed post-Epiphany Sunday (overflow years)', () =>
|
|||||||
// stretch, is genuinely clean (no sanctoral entry, no commemoration)
|
// stretch, is genuinely clean (no sanctoral entry, no commemoration)
|
||||||
// -- covers the ferial "Monday in the Nth week after Epiphany" format
|
// -- covers the ferial "Monday in the Nth week after Epiphany" format
|
||||||
// itself, independent of the specific 5th/6th Sunday pair above.
|
// itself, independent of the specific 5th/6th Sunday pair above.
|
||||||
expect(getDayLabel(resolveDay('2035-10-29'))).toBe('Monday in the 3rd week after Epiphany');
|
expect(getDayLabel(resolveDay('2035-10-29'))).toBe('Monday in the 3rd week after Epiphany (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('the fixed final Sunday of the year always shows the fixed 23rd-after-Trinity ordinal, not a raw elapsed-week count', () => {
|
it('the fixed final Sunday of the year always shows the fixed 23rd-after-Trinity ordinal, not a raw elapsed-week count', () => {
|
||||||
@@ -102,7 +102,7 @@ describe('getDayLabel — resumed post-Epiphany Sunday (overflow years)', () =>
|
|||||||
// Every year 1900-2100 lands on this same id for its own last Sunday
|
// Every year 1900-2100 lands on this same id for its own last Sunday
|
||||||
// before Advent, so this ordinal is fixed, not overflow-year-specific
|
// before Advent, so this ordinal is fixed, not overflow-year-specific
|
||||||
// -- confirmed against a non-overflow year (1943) too.
|
// -- confirmed against a non-overflow year (1943) too.
|
||||||
expect(getDayLabel(resolveDay('2026-11-22'))).toBe('The 23rd Sunday after Trinity');
|
expect(getDayLabel(resolveDay('2026-11-22'))).toBe('The 23rd Sunday after Trinity (Semiduplex)');
|
||||||
// 1943-11-21 is also the Presentation of the BVM (Duplex Majus, added
|
// 1943-11-21 is also the Presentation of the BVM (Duplex Majus, added
|
||||||
// 2026-08 — see presentation-of-the-bvm.yml), which meets this app's
|
// 2026-08 — see presentation-of-the-bvm.yml), which meets this app's
|
||||||
// existing privileged-Sunday rank threshold (commemorations.ts's
|
// existing privileged-Sunday rank threshold (commemorations.ts's
|
||||||
@@ -120,7 +120,9 @@ describe('getDayLabel — active octave', () => {
|
|||||||
// Clare's own day -- Simplex, below the octave's semiduplex threshold,
|
// Clare's own day -- Simplex, below the octave's semiduplex threshold,
|
||||||
// so she's commemorated rather than winning. Matches the live engine's
|
// so she's commemorated rather than winning. Matches the live engine's
|
||||||
// own title for this date directly (see saints/st-clare.yml).
|
// own title for this date directly (see saints/st-clare.yml).
|
||||||
expect(getDayLabel(resolveDay('2026-08-12'))).toBe('3rd Day within the Octave of St. Lawrence, Martyr — St. Clare, Virgin');
|
expect(getDayLabel(resolveDay('2026-08-12'))).toBe(
|
||||||
|
'3rd Day within the Octave of St. Lawrence, Martyr (Semiduplex) — St. Clare, Virgin',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows the octave day alone when nothing is separately commemorated that day', () => {
|
it('shows the octave day alone when nothing is separately commemorated that day', () => {
|
||||||
@@ -137,7 +139,7 @@ describe('getDayLabel — active octave', () => {
|
|||||||
// "real content closed the gap" shape as August's Lawrence/Assumption
|
// "real content closed the gap" shape as August's Lawrence/Assumption
|
||||||
// overlap. Day 3 of the same octave (2028-11-03, a Friday) is still
|
// overlap. Day 3 of the same octave (2028-11-03, a Friday) is still
|
||||||
// genuinely open -- no sanctoral entry, no separate commemoration.
|
// genuinely open -- no sanctoral entry, no separate commemoration.
|
||||||
expect(getDayLabel(resolveDay('2028-11-03'))).toBe('3rd Day within the Octave of All Saints');
|
expect(getDayLabel(resolveDay('2028-11-03'))).toBe('3rd Day within the Octave of All Saints (Semiduplex)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("a season's own named anchor day outranks an active octave, even though Trinity Sunday is technically also day 8 of Pentecost's own octave", () => {
|
it("a season's own named anchor day outranks an active octave, even though Trinity Sunday is technically also day 8 of Pentecost's own octave", () => {
|
||||||
@@ -147,14 +149,14 @@ describe('getDayLabel — active octave', () => {
|
|||||||
// muddy this specifically octave-vs-anchor-day check with a sanctoral
|
// muddy this specifically octave-vs-anchor-day check with a sanctoral
|
||||||
// winner. 2030's Trinity Sunday (June 16) has no sanctoral entry at
|
// winner. 2030's Trinity Sunday (June 16) has no sanctoral entry at
|
||||||
// all, isolating the octave interaction cleanly.
|
// all, isolating the octave interaction cleanly.
|
||||||
expect(getDayLabel(resolveDay('2030-06-16'))).toBe('Trinity Sunday');
|
expect(getDayLabel(resolveDay('2030-06-16'))).toBe('Trinity Sunday (Duplex I Class)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("a temporal day with real standing of its own (not ordinary-feria) never shows an octave name at all, even with one active -- live-verified counterexample: the Christmas Octave's own stack (Dec 30) titles itself off the Sunday, never any of the four octaves layered on top of it", () => {
|
it("a temporal day with real standing of its own (not ordinary-feria) never shows an octave name at all, even with one active -- live-verified counterexample: the Christmas Octave's own stack (Dec 30) titles itself off the Sunday, never any of the four octaves layered on top of it", () => {
|
||||||
const day = resolveDay('2033-12-30');
|
const day = resolveDay('2033-12-30');
|
||||||
expect(day.temporalCategory).toBe('privileged-feria-minor');
|
expect(day.temporalCategory).toBe('privileged-feria-minor');
|
||||||
expect(getDayLabel(day)).not.toContain('Octave');
|
expect(getDayLabel(day)).not.toContain('Octave');
|
||||||
expect(getDayLabel(day)).toBe('Friday in Christmastide');
|
expect(getDayLabel(day)).toBe('Friday in Christmastide (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("picks the higher-ranked of two genuinely overlapping octaves for the label -- St. Lawrence's own elevated closing day (Aug 17) over the Assumption's ordinary day 3, even though the Assumption's octave started later and the Assumption is the higher-ranked feast overall -- with St. Hyacinth (Duplex, tied with Lawrence's own elevated rank) commemorated rather than winning outright", () => {
|
it("picks the higher-ranked of two genuinely overlapping octaves for the label -- St. Lawrence's own elevated closing day (Aug 17) over the Assumption's ordinary day 3, even though the Assumption's octave started later and the Assumption is the higher-ranked feast overall -- with St. Hyacinth (Duplex, tied with Lawrence's own elevated rank) commemorated rather than winning outright", () => {
|
||||||
@@ -171,9 +173,11 @@ describe('getDayLabel — active octave', () => {
|
|||||||
// day 3 (semiduplex, genuinely active but outranked by Lawrence's
|
// day 3 (semiduplex, genuinely active but outranked by Lawrence's
|
||||||
// elevated duplex) still gets named, not dropped, alongside it.
|
// elevated duplex) still gets named, not dropped, alongside it.
|
||||||
expect(getDayLabel(resolveDay('2026-08-17'))).toBe(
|
expect(getDayLabel(resolveDay('2026-08-17'))).toBe(
|
||||||
'8th Day within the Octave of St. Lawrence, Martyr — The Assumption of the Blessed Virgin Mary — St. Hyacinth, Confessor',
|
'8th Day within the Octave of St. Lawrence, Martyr (Duplex) — The Assumption of the Blessed Virgin Mary — St. Hyacinth, Confessor',
|
||||||
|
);
|
||||||
|
expect(getDayLabel(resolveDay('2026-08-18'))).toBe(
|
||||||
|
'4th Day within the Octave of The Assumption of the Blessed Virgin Mary (Semiduplex)',
|
||||||
);
|
);
|
||||||
expect(getDayLabel(resolveDay('2026-08-18'))).toBe('4th Day within the Octave of The Assumption of the Blessed Virgin Mary');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('a real Sunday with standing of its own wins over both octaves entirely, showing the plain ordinal Sunday label -- not an octave name at all', () => {
|
it('a real Sunday with standing of its own wins over both octaves entirely, showing the plain ordinal Sunday label -- not an octave name at all', () => {
|
||||||
@@ -185,6 +189,19 @@ describe('getDayLabel — active octave', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDayLabel — named temporal feast rank', () => {
|
||||||
|
it('shows rank after a named temporal feast winner, sourced from its own TemporalFeastRecord', () => {
|
||||||
|
// Not Christmas Day itself: `resolveTemporalId` maps every day of
|
||||||
|
// christmastide (Dec 25 included) to `christmas-octave-sunday`, not
|
||||||
|
// `christmas-day` — a pre-existing, separate gap (`christmas-day`'s
|
||||||
|
// own record only ever surfaces via the octave layer, never as a
|
||||||
|
// direct winner id) out of scope here. Pentecost Sunday's own id
|
||||||
|
// *does* resolve as the direct winner (`temporal-id.ts`'s
|
||||||
|
// EASTER_OFFSET_IDS), so it's a real, clean case for this branch.
|
||||||
|
expect(getDayLabel(resolveDay('2026-05-24'))).toBe('Pentecost (Duplex I Class)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getDayLabel — feast name combination', () => {
|
describe('getDayLabel — feast name combination', () => {
|
||||||
const base: LiturgicalDay = {
|
const base: LiturgicalDay = {
|
||||||
date: '2026-06-15',
|
date: '2026-06-15',
|
||||||
@@ -208,10 +225,10 @@ describe('getDayLabel — feast name combination', () => {
|
|||||||
...base,
|
...base,
|
||||||
commemorations: [{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank: 'duplex-2-classis' }],
|
commemorations: [{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank: 'duplex-2-classis' }],
|
||||||
};
|
};
|
||||||
expect(getDayLabel(day)).toBe('St. Ereden — Monday in the 2nd week after Trinity');
|
expect(getDayLabel(day)).toBe('St. Ereden — Monday in the 2nd week after Trinity (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows just the temporal label when nothing is commemorated at all', () => {
|
it('shows just the temporal label when nothing is commemorated at all', () => {
|
||||||
expect(getDayLabel(base)).toBe('Monday in the 2nd week after Trinity');
|
expect(getDayLabel(base)).toBe('Monday in the 2nd week after Trinity (Feria)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ describe('Marian Saturday (applyMarianSaturday)', () => {
|
|||||||
const day = resolveDay('2026-07-04');
|
const day = resolveDay('2026-07-04');
|
||||||
expect(day.winner).toEqual({ kind: 'temporal', id: 'marian-saturday' });
|
expect(day.winner).toEqual({ kind: 'temporal', id: 'marian-saturday' });
|
||||||
expect(day.commemorations).toEqual([]);
|
expect(day.commemorations).toEqual([]);
|
||||||
expect(getDayLabel(day)).toBe("Our Lady's Saturday");
|
expect(getDayLabel(day)).toBe("Our Lady's Saturday (Simplex)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wins over a Simplex saint, who is commemorated instead of winning outright', () => {
|
it('wins over a Simplex saint, who is commemorated instead of winning outright', () => {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ describe('resolveOrdo("compline", ...)', () => {
|
|||||||
const eve = resolveOrdo('compline', '2025-11-29');
|
const eve = resolveOrdo('compline', '2025-11-29');
|
||||||
const last = eve.parts[eve.parts.length - 1];
|
const last = eve.parts[eve.parts.length - 1];
|
||||||
expect(last?.kind === 'preces' ? last.label : undefined).toBe('Alma Redemptoris Mater');
|
expect(last?.kind === 'preces' ? last.label : undefined).toBe('Alma Redemptoris Mater');
|
||||||
expect(eve.dayLabel).toBe('St. Andrew, Apostle — The 1st Sunday of Advent');
|
expect(eve.dayLabel).toBe('St. Andrew, Apostle — The 1st Sunday of Advent (Semiduplex)');
|
||||||
|
|
||||||
const dayBefore = resolveOrdo('compline', '2025-11-28');
|
const dayBefore = resolveOrdo('compline', '2025-11-28');
|
||||||
const lastBefore = dayBefore.parts[dayBefore.parts.length - 1];
|
const lastBefore = dayBefore.parts[dayBefore.parts.length - 1];
|
||||||
|
|||||||
Reference in New Issue
Block a user