Ctrl K
ComunidadGuías…

tests/periodic.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Periodic notes (shared/periodic.ts): a format names a day or a week and
// reads it back.
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
  MONTHLY_FORMAT_DEFAULT,
  YEARLY_FORMAT_DEFAULT,
  formatPeriod,
  freePath,
  isWeeklyFormat,
  isoWeek,
  parsePeriod,
  periodEnd,
  periodKindOf,
  periodStart,
  periodicDateOf,
  periodicPath,
  shiftPeriod,
} from "../shared/periodic.ts";

const sun = new Date(2026, 8, 13, 12); // Sunday 13 September 2026

describe("a period format", () => {
  it("names a day and a week, literals kept", () => {
    assert.equal(formatPeriod("YYYY-MM-DD", sun), "2026-09-13");
    assert.equal(formatPeriod("YYYY/MM/[day] D", sun), "2026/09/day 13");
    assert.deepEqual(isoWeek(sun), { year: 2026, week: 37 });
    assert.equal(formatPeriod("YYYY-[W]ww", sun), "2026-W37");
    assert.equal(isWeeklyFormat("YYYY-[W]ww"), true);
    assert.equal(isWeeklyFormat("YYYY-MM-DD"), false);
    assert.equal(isWeeklyFormat("[week]-YYYY-MM-DD"), false);
  });
  it("names a weekly note by the ISO week-year, and keeps a [YYYY] literal", () => {
    const monday = new Date(2025, 11, 29, 12); // Monday 29 December 2025 = week 1 of 2026
    assert.equal(formatPeriod("YYYY-[W]ww", monday), "2026-W01");
    assert.equal(parsePeriod("YYYY-[W]ww", "2026-W01")?.getDate(), 29);
    assert.equal(formatPeriod("[YYYY]-YYYY", monday), "YYYY-2025");
  });
  it("names a minute for the unique note, and reads the day back out of it", () => {
    const at = new Date(2026, 8, 13, 7, 5, 9);
    assert.equal(formatPeriod("YYYYMMDDHHmm", at), "202609130705");
    assert.equal(formatPeriod("YYYY-MM-DD HH[h]mm[m]ss", at), "2026-09-13 07h05m09");
    // `mm` is the minute and `MM` the month: the two never trade places.
    assert.equal(formatPeriod("MM/mm", at), "09/05");
    // A name that carries the minute still names its day.
    assert.equal(parsePeriod("YYYYMMDDHHmm", "202609130705")?.getDate(), 13);
    assert.equal(parsePeriod("YYYYMMDDHHmm", "2026091307"), null);
  });
  it("finds the next free spelling of a stamped name", () => {
    const taken = new Set(["202609130705.md", "202609130705 2.md", "zettel/202609130705.md"]);
    assert.equal(freePath("202609130706.md", taken), "202609130706.md");
    assert.equal(freePath("202609130705.md", taken), "202609130705 3.md");
    assert.equal(freePath("zettel/202609130705.md", taken), "zettel/202609130705 2.md");
    // A dot in a FOLDER is not an extension.
    assert.equal(freePath("v1.0/note", new Set(["v1.0/note"])), "v1.0/note 2");
  });
  it("reads a name back, rejecting what does not fit", () => {
    assert.equal(parsePeriod("YYYY-MM-DD", "2026-09-13")?.getDate(), 13);
    assert.equal(parsePeriod("YYYY-MM-DD", "2026-02-31"), null);
    assert.equal(parsePeriod("YYYY-MM-DD", "notes"), null);
    const monday = parsePeriod("YYYY-[W]ww", "2026-W37")!;
    assert.equal(monday.getDay(), 1);
    assert.equal(monday.getDate(), 7);
  });
  it("builds and recognises a path under a folder, format carrying folders", () => {
    assert.equal(periodicPath("daily", "YYYY-MM-DD", sun), "daily/2026-09-13.md");
    assert.equal(periodicPath("Journal", "YYYY/YYYY-MM-DD", sun), "Journal/2026/2026-09-13.md");
    assert.equal(periodicPath("", "YYYY-MM-DD", sun), "2026-09-13.md");
    assert.equal(periodicDateOf("Journal", "YYYY/YYYY-MM-DD", "Journal/2026/2026-09-13.md")?.getMonth(), 8);
    assert.equal(periodicDateOf("daily", "YYYY-MM-DD", "Other/2026-09-13.md"), null);
  });
});

// Four kinds, told apart by what the format names (periodKindOf), each with
// a first day to print from and read back to, a last day, and a step.
describe("monthly and yearly periods", () => {
  it("reads the kind off the format's tokens", () => {
    assert.equal(periodKindOf("YYYY-MM-DD"), "day");
    assert.equal(periodKindOf("YYYY-[W]ww"), "week");
    assert.equal(periodKindOf(MONTHLY_FORMAT_DEFAULT), "month");
    assert.equal(periodKindOf("YYYY/[M]MM"), "month");
    assert.equal(periodKindOf(YEARLY_FORMAT_DEFAULT), "year");
    assert.equal(periodKindOf("[Year] YYYY"), "year");
    assert.equal(periodKindOf("[MM-DD] YYYY"), "year", "literals name nothing");
  });
  it("names a month and a year, and reads them back to their first day", () => {
    assert.equal(formatPeriod("YYYY-MM", sun), "2026-09");
    assert.equal(formatPeriod("YYYY", sun), "2026");
    const month = parsePeriod("YYYY-MM", "2026-09")!;
    assert.deepEqual([month.getFullYear(), month.getMonth(), month.getDate(), month.getHours()], [2026, 8, 1, 12]);
    const year = parsePeriod("YYYY", "2026")!;
    assert.deepEqual([year.getFullYear(), year.getMonth(), year.getDate()], [2026, 0, 1]);
    assert.equal(parsePeriod("YYYY-MM", "2026-13"), null);
    assert.equal(parsePeriod("YYYY-MM", "2026-09-13"), null, "a month pattern does not swallow a day");
    assert.equal(parsePeriod("YYYY", "2026-09"), null);
    assert.equal(periodicDateOf("daily", "YYYY-MM", "daily/2026-09.md")?.getMonth(), 8);
  });
  it("starts, ends and steps each kind on the calendar", () => {
    const d = new Date(2026, 0, 31, 12); // 31 January 2026
    assert.equal(periodStart("month", d).getDate(), 1);
    assert.equal(periodEnd("month", d).getDate(), 31);
    assert.equal(periodEnd("month", new Date(2026, 1, 10, 12)).getDate(), 28);
    assert.deepEqual([periodEnd("year", d).getMonth(), periodEnd("year", d).getDate()], [11, 31]);
    // A month step from the 31st lands on 1 February, not 3 March.
    const next = shiftPeriod("month", d, 1);
    assert.deepEqual([next.getMonth(), next.getDate()], [1, 1]);
    assert.equal(shiftPeriod("year", d, -1).getFullYear(), 2025);
    assert.equal(shiftPeriod("week", sun, 1).getDate(), 14, "a week steps from its Monday");
    assert.equal(shiftPeriod("day", sun, -1).getDate(), 12);
    assert.equal(periodEnd("week", sun).getDate(), 13, "Sunday ends the week that began on Monday the 7th");
  });
});