feat: Admin Area General Settings and Categories 1.06
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import { GeneralSettingsSchema } from '../../backend/src/modules/admin/dto/general.dto';
|
||||
|
||||
const baseValid = {
|
||||
pageTitle: 'OpenVelo',
|
||||
logo: '',
|
||||
welcomeMarkdown: '',
|
||||
themeKey: 'classic',
|
||||
defaultChallengeIp: '127.0.0.1',
|
||||
registrationsEnabled: false,
|
||||
};
|
||||
|
||||
function withEvent(start: string, end: string) {
|
||||
return { ...baseValid, eventStartUtc: start, eventEndUtc: end };
|
||||
}
|
||||
|
||||
describe('Admin general settings - event window validation (Job 888)', () => {
|
||||
it('accepts a valid event window', () => {
|
||||
const r = GeneralSettingsSchema.safeParse(
|
||||
withEvent('2026-08-15T10:30:00.000Z', '2026-08-20T18:45:00.000Z'),
|
||||
);
|
||||
expect(r.success).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects an empty eventEndUtc with a per-field message', () => {
|
||||
const r = GeneralSettingsSchema.safeParse(withEvent('2026-08-15T10:30:00.000Z', ''));
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const endIssue = r.error.issues.find((i) => i.path.join('.') === 'eventEndUtc');
|
||||
expect(endIssue?.message).toBe('eventEndUtc must be a valid ISO-8601 datetime');
|
||||
});
|
||||
|
||||
it('rejects an empty eventStartUtc with a per-field message', () => {
|
||||
const r = GeneralSettingsSchema.safeParse(withEvent('', '2026-08-20T18:45:00.000Z'));
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const startIssue = r.error.issues.find((i) => i.path.join('.') === 'eventStartUtc');
|
||||
expect(startIssue?.message).toBe('eventStartUtc must be a valid ISO-8601 datetime');
|
||||
});
|
||||
|
||||
it('rejects a non-empty malformed eventEndUtc', () => {
|
||||
const r = GeneralSettingsSchema.safeParse(withEvent('2026-08-15T10:30:00.000Z', 'not-a-date'));
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const endIssue = r.error.issues.find((i) => i.path.join('.') === 'eventEndUtc');
|
||||
expect(endIssue?.message).toBe('eventEndUtc must be a valid ISO-8601 datetime');
|
||||
});
|
||||
|
||||
it('rejects end equal to start', () => {
|
||||
const ts = '2026-08-15T10:30:00.000Z';
|
||||
const r = GeneralSettingsSchema.safeParse(withEvent(ts, ts));
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const endIssue = r.error.issues.find((i) => i.path.join('.') === 'eventEndUtc');
|
||||
expect(endIssue?.message).toBe('eventEndUtc must be strictly after eventStartUtc');
|
||||
});
|
||||
|
||||
it('rejects end before start', () => {
|
||||
const r = GeneralSettingsSchema.safeParse(
|
||||
withEvent('2026-08-20T18:45:00.000Z', '2026-08-15T10:30:00.000Z'),
|
||||
);
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const endIssue = r.error.issues.find((i) => i.path.join('.') === 'eventEndUtc');
|
||||
expect(endIssue?.message).toBe('eventEndUtc must be strictly after eventStartUtc');
|
||||
});
|
||||
});
|
||||
@@ -141,7 +141,7 @@ describe('GeneralSettingsSchema - validation rules', () => {
|
||||
expect(r.success).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts empty strings (unconfigured event window) for both datetime fields', () => {
|
||||
it('rejects empty strings for event datetime fields with per-field messages (Job 888)', () => {
|
||||
const r = GeneralSettingsSchema.safeParse({
|
||||
pageTitle: 'T',
|
||||
logo: '',
|
||||
@@ -152,7 +152,12 @@ describe('GeneralSettingsSchema - validation rules', () => {
|
||||
defaultChallengeIp: '127.0.0.1',
|
||||
registrationsEnabled: false,
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
expect(r.success).toBe(false);
|
||||
if (r.success) return;
|
||||
const startIssue = r.error.issues.find((i) => i.path.join('.') === 'eventStartUtc');
|
||||
const endIssue = r.error.issues.find((i) => i.path.join('.') === 'eventEndUtc');
|
||||
expect(startIssue?.message).toBe('eventStartUtc must be a valid ISO-8601 datetime');
|
||||
expect(endIssue?.message).toBe('eventEndUtc must be a valid ISO-8601 datetime');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -165,10 +165,10 @@ describe('pageTitleMessage', () => {
|
||||
});
|
||||
|
||||
describe('isoDatetimeValidator', () => {
|
||||
it('returns null for an empty value (unconfigured is allowed)', () => {
|
||||
expect(isoDatetimeValidator({ value: '' })).toBeNull();
|
||||
expect(isoDatetimeValidator({ value: null })).toBeNull();
|
||||
expect(isoDatetimeValidator({ value: undefined })).toBeNull();
|
||||
it('returns { invalidDatetime: true } for an empty value (empty string no longer accepted)', () => {
|
||||
expect(isoDatetimeValidator({ value: '' })).toEqual({ invalidDatetime: true });
|
||||
expect(isoDatetimeValidator({ value: null })).toEqual({ invalidDatetime: true });
|
||||
expect(isoDatetimeValidator({ value: undefined })).toEqual({ invalidDatetime: true });
|
||||
});
|
||||
|
||||
it('returns null for a valid ISO-8601 datetime', () => {
|
||||
@@ -194,9 +194,9 @@ describe('datetimeMessage', () => {
|
||||
.toBe('Event start must be a valid ISO-8601 datetime.');
|
||||
});
|
||||
|
||||
it('returns null for an empty value', () => {
|
||||
expect(datetimeMessage('Event start', '', null)).toBeNull();
|
||||
expect(datetimeMessage('Event start', '', { invalidDatetime: false as any })).toBeNull();
|
||||
it('returns the per-field error for an empty value (empty is now invalid)', () => {
|
||||
expect(datetimeMessage('Event start', '', null)).toBe('Event start must be a valid ISO-8601 datetime.');
|
||||
expect(datetimeMessage('Event end', '', { invalidDatetime: true })).toBe('Event end must be a valid ISO-8601 datetime.');
|
||||
});
|
||||
|
||||
it('returns null for a valid ISO-8601 datetime', () => {
|
||||
|
||||
Reference in New Issue
Block a user