Commit
test formatting/visual helpers and fix truncate length
commit
ce5bb93
3 changed files with +81 and −2
Jump to a changed file
- src/lib/model-visuals.test.ts +34 −0
- src/lib/utils.test.ts +44 −0
- src/lib/utils.ts +3 −2
added src/lib/model-visuals.test.ts +34 −0
| @@ -0,0 +1,34 @@ | ||
| 1 | +import { describe, expect, it } from 'vitest'; | |
| 2 | +import { participantColor, participantTag, scoreColor, stageColorVar } from './model-visuals'; | |
| 3 | + | |
| 4 | +describe('participantTag', () => { | |
| 5 | + it('maps index to letters', () => { | |
| 6 | + expect(participantTag(0)).toBe('A'); | |
| 7 | + expect(participantTag(2)).toBe('C'); | |
| 8 | + }); | |
| 9 | +}); | |
| 10 | + | |
| 11 | +describe('participantColor', () => { | |
| 12 | + it('is a stable hsl string and cycles the palette', () => { | |
| 13 | + expect(participantColor(0)).toMatch(/^hsl\(/); | |
| 14 | + expect(participantColor(0)).toBe(participantColor(6)); // 6-colour palette wraps | |
| 15 | + expect(participantColor(0)).not.toBe(participantColor(1)); | |
| 16 | + }); | |
| 17 | +}); | |
| 18 | + | |
| 19 | +describe('scoreColor', () => { | |
| 20 | + it('shifts hue upward with the score', () => { | |
| 21 | + const low = scoreColor(1); | |
| 22 | + const high = scoreColor(10); | |
| 23 | + const hueOf = (s: string) => Number(s.match(/hsl\(([\d.]+)/)![1]); | |
| 24 | + expect(hueOf(high)).toBeGreaterThan(hueOf(low)); | |
| 25 | + }); | |
| 26 | +}); | |
| 27 | + | |
| 28 | +describe('stageColorVar', () => { | |
| 29 | + it('maps each stage to its css variable', () => { | |
| 30 | + expect(stageColorVar('answer')).toBe('var(--stage-answer)'); | |
| 31 | + expect(stageColorVar('synthesis')).toBe('var(--stage-synthesis)'); | |
| 32 | + expect(stageColorVar('unknown')).toBe('var(--primary)'); | |
| 33 | + }); | |
| 34 | +}); |
added src/lib/utils.test.ts +44 −0
| @@ -0,0 +1,44 @@ | ||
| 1 | +import { describe, expect, it } from 'vitest'; | |
| 2 | +import { cn, formatDuration, formatLatency, formatTokens, formatUsd, truncate } from './utils'; | |
| 3 | + | |
| 4 | +describe('formatUsd', () => { | |
| 5 | + it('adapts precision to magnitude', () => { | |
| 6 | + expect(formatUsd(0)).toBe('$0.00'); | |
| 7 | + expect(formatUsd(0.0005)).toBe('$0.0005'); | |
| 8 | + expect(formatUsd(0.25)).toBe('$0.250'); | |
| 9 | + expect(formatUsd(12.5)).toBe('$12.50'); | |
| 10 | + }); | |
| 11 | +}); | |
| 12 | + | |
| 13 | +describe('formatTokens', () => { | |
| 14 | + it('abbreviates thousands and millions', () => { | |
| 15 | + expect(formatTokens(500)).toBe('500'); | |
| 16 | + expect(formatTokens(1500)).toBe('1.5k'); | |
| 17 | + expect(formatTokens(20000)).toBe('20k'); | |
| 18 | + expect(formatTokens(2_000_000)).toBe('2.0M'); | |
| 19 | + }); | |
| 20 | +}); | |
| 21 | + | |
| 22 | +describe('formatLatency / formatDuration', () => { | |
| 23 | + it('formats ms and seconds', () => { | |
| 24 | + expect(formatLatency(450)).toBe('450ms'); | |
| 25 | + expect(formatLatency(1500)).toBe('1.5s'); | |
| 26 | + expect(formatDuration(45_000)).toBe('45s'); | |
| 27 | + expect(formatDuration(125_000)).toBe('2m 5s'); | |
| 28 | + }); | |
| 29 | +}); | |
| 30 | + | |
| 31 | +describe('truncate', () => { | |
| 32 | + it('leaves short strings and ellipsizes long ones within max length', () => { | |
| 33 | + expect(truncate('hello', 10)).toBe('hello'); | |
| 34 | + expect(truncate('hello world', 5)).toBe('he...'); | |
| 35 | + expect(truncate('hello world', 5).length).toBe(5); | |
| 36 | + }); | |
| 37 | +}); | |
| 38 | + | |
| 39 | +describe('cn', () => { | |
| 40 | + it('merges and de-conflicts tailwind classes', () => { | |
| 41 | + expect(cn('p-2', 'p-4')).toBe('p-4'); | |
| 42 | + expect(cn('text-sm', false && 'hidden', 'font-bold')).toBe('text-sm font-bold'); | |
| 43 | + }); | |
| 44 | +}); |
modified src/lib/utils.ts +3 −2
| @@ -45,7 +45,8 @@export function formatRelativeTime(date: Date | string): string { | ||
| 45 | 45 | return d.toLocaleDateString(); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | -/** Truncate to a max length with an ellipsis. */ | |
| 48 | +/** Truncate to a max length (including the trailing "..."). */ | |
| 49 | 49 | export function truncate(s: string, max: number): string { |
| 50 | - return s.length <= max ? s : `${s.slice(0, max - 1)}...`; | |
| 50 | + if (s.length <= max) return s; | |
| 51 | + return `${s.slice(0, Math.max(0, max - 3))}...`; | |
| 51 | 52 | } |