Files
electric-cherry/tests/unit/ui-freshness.test.ts
T

52 lines
2.9 KiB
TypeScript

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(/<time[^>]+title="[^"]+"/);
expect(body).toContain('Updates every 5 seconds');
expect(body).not.toMatch(/aria-live|role="status"|>Retry</);
});
it('does not claim live updates or an automatic schedule while paused', () => {
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(/<button[^>]+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('<time');
});
});
describe('readable game logs', () => {
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 ');
});
});