import { render, screen } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; import { MessageTimestamp } from '@/pages/Chat/MessageTimestamp'; import { formatMessageTimestamp } from '@/lib/message-timestamp'; import { messageTimestamp } from '../../shared/message-timestamp'; const now = new Date(2026, 8, 29, 17, 8, 9); describe('message timestamps', () => { it('uses local dates, distinguishes older years, and exposes full precision without a ticking clock', () => { expect(formatMessageTimestamp(now.getTime(), now)).toEqual({ iso: now.toISOString(), short: '17:08', full: '2026年9月29日 17:08:09' }); expect(formatMessageTimestamp(new Date(2026, 8, 28, 23, 59).toISOString(), now)?.short).toBe('9月28日 23:59'); expect(formatMessageTimestamp(new Date(2025, 8, 29, 17, 8).getTime(), now)?.short).toBe('2025年9月29日 17:08'); expect(formatMessageTimestamp(new Date(2026, 8, 30, 0, 1).getTime(), now)?.short).toBe('9月30日 00:01'); }); it.each([undefined, null, '', 'now', '1', NaN, Infinity, -1, 1e20, '2026-99-99T00:00:00Z'])('does not invent a date for %s', value => { expect(messageTimestamp(value)).toBeUndefined(); expect(formatMessageTimestamp(value, now)).toBeUndefined(); }); it('renders a semantic time with full hover text; pending and legacy replies do not get fabricated times', () => { const view = render(); expect(screen.getByText('回复中…')).toHaveAttribute('data-message-time', 'pending'); expect(view.container.querySelector('time')).toBeNull(); view.rerender(); expect(view.container.querySelector('time')).toHaveAttribute('datetime', now.toISOString()); expect(view.container.querySelector('time')).toHaveAttribute('title', '2026年9月29日 17:08:09'); view.rerender(); expect(screen.getByText('时间未记录')).toBeVisible(); expect(view.container.querySelector('time')).toBeNull(); }); });