Add distributed settings and workspace import/export (M5)

This commit is contained in:
2026-06-07 15:51:00 +03:00
parent 80bedd2a8d
commit 548aa199d9
38 changed files with 3150 additions and 101 deletions
+91
View File
@@ -0,0 +1,91 @@
import { describe, it, expect } from 'vitest';
import {
formatCustom,
formatDate,
formatIso,
formatSmart,
type DateFormatMode,
} from './date-format';
/** A fixed local reference "now" for relative-date tests. */
const NOW = new Date(2026, 5, 7, 12, 0, 0); // 2026-06-07 12:00 local
const at = (y: number, mo: number, d: number, h = 9, mi = 0, s = 0) => new Date(y, mo, d, h, mi, s);
describe('formatSmart', () => {
it('renders the same calendar day as Today', () => {
expect(formatSmart(at(2026, 5, 7, 0, 1), NOW)).toBe('Today');
expect(formatSmart(at(2026, 5, 7, 23, 59), NOW)).toBe('Today');
});
it('renders the previous calendar day as Yesterday', () => {
expect(formatSmart(at(2026, 5, 6), NOW)).toBe('Yesterday');
});
it('renders under a week as Nd ago', () => {
expect(formatSmart(at(2026, 5, 4), NOW)).toBe('3d ago');
expect(formatSmart(at(2026, 5, 1), NOW)).toBe('6d ago');
});
it('falls back to a full locale date at a week or older', () => {
const old = at(2026, 4, 1);
expect(formatSmart(old, NOW)).toBe(old.toLocaleDateString());
});
it('does not flip to Yesterday on the hour within the same day', () => {
// 1 minute ago but same calendar day → still Today.
expect(formatSmart(at(2026, 5, 7, 11, 59), NOW)).toBe('Today');
});
});
describe('formatIso', () => {
it('returns a full ISO 8601 timestamp', () => {
const d = new Date('2026-06-07T10:30:00.000Z');
expect(formatIso(d)).toBe('2026-06-07T10:30:00.000Z');
});
});
describe('formatCustom', () => {
it('formats the placeholder pattern yyyy-MM-dd HH:mm', () => {
expect(formatCustom(at(2026, 0, 5, 8, 4), 'yyyy-MM-dd HH:mm')).toBe('2026-01-05 08:04');
});
it('supports month names and short year', () => {
expect(formatCustom(at(2026, 11, 25), 'd MMMM yy')).toBe('25 December 26');
expect(formatCustom(at(2026, 2, 9), 'MMM d, yyyy')).toBe('Mar 9, 2026');
});
it('supports 12-hour clock with meridiem', () => {
expect(formatCustom(at(2026, 5, 7, 0, 5), 'h:mm a')).toBe('12:05 AM');
expect(formatCustom(at(2026, 5, 7, 13, 5), 'h:mm a')).toBe('1:05 PM');
expect(formatCustom(at(2026, 5, 7, 12, 0), 'hh:mm a')).toBe('12:00 PM');
});
it('preserves non-token literals verbatim', () => {
expect(formatCustom(at(2026, 0, 1), 'yyyy/MM/dd')).toBe('2026/01/01');
});
});
describe('formatDate', () => {
it('dispatches by mode', () => {
const iso = at(2026, 5, 6, 9, 0).toISOString();
expect(formatDate(iso, 'smart', '', NOW)).toBe('Yesterday');
expect(formatDate(iso, 'iso')).toBe(new Date(iso).toISOString());
expect(formatDate(iso, 'custom', 'yyyy-MM-dd', NOW)).toBe('2026-06-06');
});
it('falls back to ISO when a custom pattern is blank', () => {
const iso = '2026-06-07T10:30:00.000Z';
expect(formatDate(iso, 'custom', ' ')).toBe(new Date(iso).toISOString());
expect(formatDate(iso, 'custom', '')).toBe(new Date(iso).toISOString());
});
it('returns an unparseable timestamp verbatim rather than "Invalid Date"', () => {
expect(formatDate('not-a-date', 'iso')).toBe('not-a-date');
expect(formatDate('not-a-date', 'smart', '', NOW)).toBe('not-a-date');
});
it('defaults unknown modes to smart', () => {
const iso = at(2026, 5, 7).toISOString();
expect(formatDate(iso, 'bogus' as DateFormatMode, '', NOW)).toBe('Today');
});
});