gitshark

Clone repository

git clone https://git.vilip.de/git/vilip/taskflow.git
git clone git@git.vilip.de:vilip/taskflow.git
import {
  Recurrence,
  computeNextDueDate,
  isValidRecurrence,
  describeRecurrence,
} from '../recurrence';

// Helper: build a local timestamp. Month is 0-indexed.
const at = (y: number, m: number, d: number, h = 9, min = 0) =>
  new Date(y, m, d, h, min, 0, 0).getTime();

describe('computeNextDueDate', () => {
  it('weekly advances by 7 days', () => {
    // June 1 2026 is a Monday.
    expect(computeNextDueDate(at(2026, 5, 1), { kind: 'weekly' })).toBe(at(2026, 5, 8));
  });

  it('monthly advances one month keeping the day', () => {
    expect(computeNextDueDate(at(2026, 0, 15), { kind: 'monthly' })).toBe(at(2026, 1, 15));
  });

  it('everyNDays advances by the interval', () => {
    expect(
      computeNextDueDate(at(2026, 5, 1), { kind: 'everyNDays', intervalDays: 10 })
    ).toBe(at(2026, 5, 11));
  });

  it('weekdays picks the next selected weekday', () => {
    // Mon June 1 2026, selected Wed(3) + Fri(5) -> next is Wed June 3.
    expect(
      computeNextDueDate(at(2026, 5, 1), { kind: 'weekdays', weekdays: [3, 5] })
    ).toBe(at(2026, 5, 3));
  });

  it('weekdays wraps into the following week when needed', () => {
    // Fri June 5 2026, selected only Mon(1) -> next is Mon June 8.
    expect(
      computeNextDueDate(at(2026, 5, 5), { kind: 'weekdays', weekdays: [1] })
    ).toBe(at(2026, 5, 8));
  });

  it('preserves the time of day', () => {
    expect(
      computeNextDueDate(at(2026, 5, 1, 23, 59), { kind: 'weekly' })
    ).toBe(at(2026, 5, 8, 23, 59));
  });
});

describe('isValidRecurrence', () => {
  it('weekly and monthly are always valid', () => {
    expect(isValidRecurrence({ kind: 'weekly' })).toBe(true);
    expect(isValidRecurrence({ kind: 'monthly' })).toBe(true);
  });

  it('everyNDays requires a positive integer interval', () => {
    expect(isValidRecurrence({ kind: 'everyNDays', intervalDays: 3 })).toBe(true);
    expect(isValidRecurrence({ kind: 'everyNDays', intervalDays: 0 })).toBe(false);
    expect(isValidRecurrence({ kind: 'everyNDays' })).toBe(false);
  });

  it('weekdays requires at least one in-range day', () => {
    expect(isValidRecurrence({ kind: 'weekdays', weekdays: [0, 6] })).toBe(true);
    expect(isValidRecurrence({ kind: 'weekdays', weekdays: [] })).toBe(false);
    expect(isValidRecurrence({ kind: 'weekdays', weekdays: [7] })).toBe(false);
  });
});

describe('describeRecurrence', () => {
  it('labels the presets', () => {
    expect(describeRecurrence({ kind: 'weekly' })).toBe('Weekly');
    expect(describeRecurrence({ kind: 'monthly' })).toBe('Monthly');
  });

  it('labels everyNDays', () => {
    expect(describeRecurrence({ kind: 'everyNDays', intervalDays: 1 })).toBe('Every day');
    expect(describeRecurrence({ kind: 'everyNDays', intervalDays: 3 })).toBe('Every 3 days');
  });

  it('labels weekdays in week order', () => {
    const r: Recurrence = { kind: 'weekdays', weekdays: [5, 1, 3] };
    expect(describeRecurrence(r)).toBe('Mon, Wed, Fri');
  });
});

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog