import { afterEach, describe, expect, it, vi } from 'vitest'; import { render } from 'svelte/server'; import Freshness from '../../src/lib/ui/components/Freshness.svelte'; import { stripGameColorCodes } from '../../src/lib/ui/log-text.js'; afterEach(() => vi.useRealTimers()); describe('quiet refresh feedback', () => { it('shows relative receipt time with an exact tooltip without announcing every tick', () => { vi.useFakeTimers(); vi.setSystemTime('2026-10-07T12:03:00Z'); const body = render(Freshness, { props: { updatedAt: Date.parse('2026-10-07T12:00:00Z'), intervalLabel: 'Updates every 5 seconds' } }).body; expect(body).toContain('Live'); expect(body).toContain('3 min ago'); expect(body).toContain('datetime="2026-10-07T12:00:00.000Z"'); expect(body).toMatch(/]+title="[^"]+"/); expect(body).toContain('Updates every 5 seconds'); expect(body).not.toMatch(/aria-live|role="status"|>Retry { const body = render(Freshness, { props: { paused: true, updatedAt: Date.now(), intervalLabel: 'Updates every 5 seconds' } }).body; expect(body).toContain('Paused'); expect(body).not.toMatch(/Live|Updates every/); }); it('replaces live claims with a retry on failures and treats invalid timestamps as unknown', () => { const body = render(Freshness, { props: { updatedAt: Date.now(), error: 'Log updates interrupted.', intervalLabel: 'Updates every 5 seconds', onRetry: () => {}, retryDisabled: true } }).body; expect(body).toContain('Log updates interrupted.'); expect(body).toMatch(/]+disabled[^>]*>Retry<\/button>/); expect(body).not.toMatch(/Live|Updates every/); const waiting = render(Freshness, { props: { updatedAt: Number.NaN } }).body; expect(waiting).toContain('Waiting for update'); expect(waiting).not.toContain(' { it('strips digit colour directives while preserving log whitespace and other carets', () => { const raw = ' [12:30] ^1Red^7 player\t^2joined^0\r\npath ^name, exponent x^y, trailing ^'; expect(stripGameColorCodes(raw)).toBe(' [12:30] Red player\tjoined\r\npath ^name, exponent x^y, trailing ^'); expect(raw).toContain('^1Red^7'); }); it('allows searches across embedded colour changes without mutating downloadable source lines', () => { const raw = ['^1Con^2nection^7 accepted', ' Player ^3Jamie^7 joined ']; const displayed = raw.map(stripGameColorCodes); expect(displayed.filter(line => line.toLowerCase().includes('connection'))).toEqual(['Connection accepted']); expect(displayed.join('\n')).toBe('Connection accepted\n Player Jamie joined '); expect(raw.join('\n')).toBe('^1Con^2nection^7 accepted\n Player ^3Jamie^7 joined '); }); });