Files
2026-09-29 15:52:07 +08:00

111 lines
3.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { Command, Device } from './api';
import { describeCommand, describeDevice, durationText, initial, relativeTime } from './format';
const now = new Date(2026, 8, 28, 15, 30, 0);
function device(overrides: Partial<Device> = {}): Device {
return {
device_id: 'dev-1',
device_name: 'PC',
app_version: '1.5.0',
online: true,
session_active: true,
last_seen_at: '',
last_seen_seconds: 3,
remote_capable: true,
blacklist_sync: true,
status: {
listening: true,
state: 'running',
state_label: '监听中',
transport: 'visual',
transport_label: '视觉通道',
wecom_connected: true,
pending_count: 0,
reported_at: '',
reported_seconds: 4,
fresh: true,
},
last_command: null,
...overrides,
};
}
describe('relativeTime', () => {
it('uses friendly buckets', () => {
expect(relativeTime(new Date(2026, 8, 28, 15, 29, 40).toISOString(), now)).toBe('刚刚');
expect(relativeTime(new Date(2026, 8, 28, 15, 10, 0).toISOString(), now)).toBe('20 分钟前');
expect(relativeTime(new Date(2026, 8, 28, 9, 5, 0).toISOString(), now)).toBe('今天 09:05');
expect(relativeTime(new Date(2026, 8, 27, 21, 0, 0).toISOString(), now)).toBe('昨天 21:00');
expect(relativeTime(new Date(2026, 1, 3, 8, 0, 0).toISOString(), now)).toBe('02-03 08:00');
expect(relativeTime(new Date(2025, 11, 31, 8, 0, 0).toISOString(), now)).toBe('2025-12-31');
});
it('accepts backend offsets and tolerates junk', () => {
expect(relativeTime('2026-09-28T15:29:30+08:00', new Date('2026-09-28T15:30:00+08:00'))).toBe('刚刚');
expect(relativeTime('', now)).toBe('—');
expect(relativeTime('not a date', now)).toBe('—');
});
});
describe('durationText', () => {
it('rounds to the largest sensible unit', () => {
expect(durationText(12)).toBe('12 秒');
expect(durationText(150)).toBe('3 分钟');
expect(durationText(7200)).toBe('2 小时');
expect(durationText(3 * 86400)).toBe('3 天');
expect(durationText(null)).toBe('—');
});
});
describe('describeDevice', () => {
it('prioritises offline, then outdated, then status', () => {
expect(describeDevice(device({ online: false, last_seen_seconds: 600 })).label).toBe('离线');
expect(describeDevice(device({ remote_capable: false })).label).toBe('版本过低');
expect(describeDevice(device()).tone).toBe('success');
const stopped = device();
stopped.status = { ...stopped.status!, listening: false, state: 'stopped', state_label: '已停止' };
expect(describeDevice(stopped)).toMatchObject({ tone: 'muted', label: '已停止' });
const failed = device();
failed.status = { ...failed.status!, state: 'error', state_label: '连接失败' };
expect(describeDevice(failed).tone).toBe('danger');
});
});
describe('describeCommand', () => {
const base: Command = {
id: 'c1',
type: 'stop_listening',
type_label: '停止监听',
device_id: 'dev-1',
status: 'pending',
status_label: '',
final: false,
created_at: '',
created_by: '',
delivered_at: '',
acked_at: '',
expires_at: 0,
result: {},
error: '',
};
it('describes progress and outcomes', () => {
expect(describeCommand(base)).toMatchObject({ busy: true, tone: 'primary' });
expect(describeCommand({ ...base, status: 'succeeded', result: { message: '监听已停止' } }).text).toBe('监听已停止');
expect(describeCommand({ ...base, status: 'failed', error: '企业微信未登录' }).text).toContain('企业微信未登录');
expect(describeCommand({ ...base, status: 'expired' }).tone).toBe('warning');
});
});
describe('initial', () => {
it('takes the first visible character', () => {
expect(initial('张三')).toBe('张');
expect(initial('alice')).toBe('A');
expect(initial(' ')).toBe('?');
expect(initial('😀客户')).toBe('😀');
});
});