maestro/ui/src/lib/cronForm.test.ts
oss-sync 3b1645cc91
Some checks failed
CI / build-and-test (push) Has been cancelled
sync: update from private repo (d31b280)
2026-06-11 11:28:40 +00:00

72 lines
2.9 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { cronToFormState } from './cronForm';
/**
* The backend's convertToCron (src/scheduler.ts) — mirrored here so the test
* asserts a true round-trip: editing a saved schedule and saving it back must
* not change the cron expression.
*/
function convertToCron(f: ReturnType<typeof cronToFormState>): string {
const m = f.minute ?? 0;
const h = f.hour ?? 0;
switch (f.scheduleType) {
case 'daily': return `${m} ${h} * * *`;
case 'weekly': return `${m} ${h} * * ${f.dayOfWeek ?? 0}`;
case 'monthly': return `${m} ${h} ${f.dayOfMonth ?? 1} * *`;
case 'cron': return f.cronExpression;
case 'once': return 'once';
}
}
describe('cronToFormState', () => {
it('classifies simple presets (daily/weekly/monthly)', () => {
expect(cronToFormState('0 9 * * *')).toMatchObject({ scheduleType: 'daily', hour: 9, minute: 0 });
expect(cronToFormState('30 14 * * *')).toMatchObject({ scheduleType: 'daily', hour: 14, minute: 30 });
expect(cronToFormState('0 9 * * 3')).toMatchObject({ scheduleType: 'weekly', hour: 9, minute: 0, dayOfWeek: 3 });
expect(cronToFormState('0 9 15 * *')).toMatchObject({ scheduleType: 'monthly', hour: 9, minute: 0, dayOfMonth: 15 });
});
it("keeps custom expressions as 'cron' and preserves the raw expression", () => {
for (const expr of [
'0 9 * * 1-5', '*/30 * * * *', '0 */2 * * *', '0 9,17 * * *', '15 9 1,15 * *', '0 9 1 6 *',
'0 9 * * 7', // Sunday-as-7 is out of the editor's 0-6 select range
'0 9 * * *', // double space → 6 tokens, not a clean preset
]) {
const f = cronToFormState(expr);
expect(f.scheduleType).toBe('cron');
expect(f.cronExpression).toBe(expr);
}
});
it('classifies Sunday-as-0 weekly', () => {
expect(cronToFormState('0 9 * * 0')).toMatchObject({ scheduleType: 'weekly', dayOfWeek: 0 });
});
it('never produces NaN fields for custom expressions', () => {
const f = cronToFormState('0 9 * * 1-5');
expect(Number.isNaN(f.hour)).toBe(false);
expect(Number.isNaN(f.minute)).toBe(false);
expect(Number.isNaN(f.dayOfWeek)).toBe(false);
expect(Number.isNaN(f.dayOfMonth)).toBe(false);
});
it("maps 'once' to the once type", () => {
expect(cronToFormState('once')).toMatchObject({ scheduleType: 'once' });
});
it('treats a malformed (non 5-field) string as a raw cron expression', () => {
expect(cronToFormState('garbage')).toMatchObject({ scheduleType: 'cron', cronExpression: 'garbage' });
});
it('round-trips losslessly through the backend convertToCron', () => {
const cases = [
'0 9 * * *', '30 14 * * *', '0 9 * * 3', '0 9 15 * *',
'0 9 * * 1-5', '*/30 * * * *', '0 */2 * * *', '0 9,17 * * *',
'15 9 1,15 * *', '0 9 1 6 *', 'once',
];
for (const expr of cases) {
expect(convertToCron(cronToFormState(expr))).toBe(expr);
}
});
});