Files
openchamber/packages/ui/src/lib/messageFreshness.test.ts
T

86 lines
3.5 KiB
TypeScript

import { beforeEach, describe, expect, test } from 'bun:test';
import { MessageFreshnessDetector } from './messageFreshness';
import type { Message } from '@opencode-ai/sdk/v2';
const makeAssistantMessage = (id: string, created: number): Message =>
({
id,
role: 'assistant',
sessionID: 'session-a',
time: { created },
}) as unknown as Message;
describe('MessageFreshnessDetector.shouldAnimateMessage', () => {
let detector: MessageFreshnessDetector;
beforeEach(() => {
detector = MessageFreshnessDetector.getInstance();
detector.clearAll();
});
test('fresh message animates once and is recorded as seen', () => {
detector.recordSessionStart('session-a');
const message = makeAssistantMessage('msg-fresh', Date.now());
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(true);
expect(detector.hasBeenAnimated('msg-fresh')).toBe(true);
});
test('regression #2124: fresh message does not re-animate when returning to the session', () => {
detector.recordSessionStart('session-a');
const message = makeAssistantMessage('msg-fresh', Date.now());
// First visit: the message is fresh and animates.
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(true);
// User switches away and back; ChatViewport remounts and re-evaluates
// before recordSessionStart runs again, so the old session start time
// is still in effect. The message must not animate a second time.
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
});
test('stale history message never animates and is recorded as seen', () => {
detector.recordSessionStart('session-a');
const message = makeAssistantMessage('msg-old', Date.now() - 60_000);
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
expect(detector.hasBeenAnimated('msg-old')).toBe(true);
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
});
test('message evaluated without a recorded session start does not animate and is recorded', () => {
const message = makeAssistantMessage('msg-no-session', Date.now());
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
expect(detector.hasBeenAnimated('msg-no-session')).toBe(true);
// Recording the session start afterwards must not resurrect the animation.
detector.recordSessionStart('session-a');
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
});
test('non-assistant messages never animate', () => {
detector.recordSessionStart('session-a');
const message = {
id: 'msg-user',
role: 'user',
sessionID: 'session-a',
time: { created: Date.now() },
} as unknown as Message;
expect(detector.shouldAnimateMessage(message, 'session-a')).toBe(false);
});
test('a new fresh message still animates after older fresh messages were seen', () => {
detector.recordSessionStart('session-a');
const first = makeAssistantMessage('msg-first', Date.now());
const second = makeAssistantMessage('msg-second', Date.now());
expect(detector.shouldAnimateMessage(first, 'session-a')).toBe(true);
expect(detector.shouldAnimateMessage(second, 'session-a')).toBe(true);
expect(detector.shouldAnimateMessage(second, 'session-a')).toBe(false);
});
});