shared-utils.test.ts
1 import assert from 'node:assert/strict' 2 import { describe, it } from 'node:test' 3 4 import { 5 errorMessage, 6 safeJsonParse, 7 truncate, 8 hmrSingleton, 9 dedup, 10 dedupBy, 11 sleep, 12 } from './shared-utils' 13 14 describe('errorMessage', () => { 15 it('extracts message from Error', () => { 16 assert.equal(errorMessage(new Error('boom')), 'boom') 17 }) 18 it('converts string to string', () => { 19 assert.equal(errorMessage('fail'), 'fail') 20 }) 21 it('converts number to string', () => { 22 assert.equal(errorMessage(42), '42') 23 }) 24 it('converts null to string', () => { 25 assert.equal(errorMessage(null), 'null') 26 }) 27 it('converts undefined to string', () => { 28 assert.equal(errorMessage(undefined), 'undefined') 29 }) 30 it('handles TypeError subclass', () => { 31 assert.equal(errorMessage(new TypeError('bad type')), 'bad type') 32 }) 33 }) 34 35 describe('safeJsonParse', () => { 36 it('parses valid JSON', () => { 37 assert.deepEqual(safeJsonParse('{"a":1}', null), { a: 1 }) 38 }) 39 it('returns fallback for invalid JSON', () => { 40 assert.equal(safeJsonParse('not json', 'default'), 'default') 41 }) 42 it('returns fallback for empty string', () => { 43 assert.deepEqual(safeJsonParse('', []), []) 44 }) 45 it('parses arrays', () => { 46 assert.deepEqual(safeJsonParse('[1,2,3]', []), [1, 2, 3]) 47 }) 48 it('parses null literal', () => { 49 assert.equal(safeJsonParse('null', 'fallback'), null) 50 }) 51 }) 52 53 describe('truncate', () => { 54 it('returns short strings unchanged', () => { 55 assert.equal(truncate('hello', 10), 'hello') 56 }) 57 it('truncates long strings', () => { 58 assert.equal(truncate('hello world', 5), 'hello') 59 }) 60 it('truncates with suffix', () => { 61 assert.equal(truncate('hello world', 8, '…'), 'hello w…') 62 }) 63 it('handles exact limit', () => { 64 assert.equal(truncate('hello', 5), 'hello') 65 }) 66 it('handles zero limit', () => { 67 assert.equal(truncate('hello', 0), '') 68 }) 69 it('handles suffix longer than limit gracefully', () => { 70 assert.equal(truncate('hello world', 2, '...'), '...') 71 }) 72 it('empty string unchanged', () => { 73 assert.equal(truncate('', 10), '') 74 }) 75 }) 76 77 describe('hmrSingleton', () => { 78 it('creates and returns a value', () => { 79 const val = hmrSingleton('__test_hmr_1__', () => ({ count: 0 })) 80 assert.deepEqual(val, { count: 0 }) 81 }) 82 it('returns same instance on second call', () => { 83 const a = hmrSingleton('__test_hmr_2__', () => ({ id: Math.random() })) 84 const b = hmrSingleton('__test_hmr_2__', () => ({ id: Math.random() })) 85 assert.equal(a, b) 86 assert.equal(a.id, b.id) 87 }) 88 it('creates different instances for different keys', () => { 89 const a = hmrSingleton('__test_hmr_3a__', () => 'a') 90 const b = hmrSingleton('__test_hmr_3b__', () => 'b') 91 assert.notEqual(a, b) 92 }) 93 }) 94 95 describe('dedup', () => { 96 it('removes duplicates', () => { 97 assert.deepEqual(dedup([1, 2, 2, 3, 1]), [1, 2, 3]) 98 }) 99 it('preserves order', () => { 100 assert.deepEqual(dedup(['b', 'a', 'b', 'c']), ['b', 'a', 'c']) 101 }) 102 it('handles empty array', () => { 103 assert.deepEqual(dedup([]), []) 104 }) 105 it('handles all unique', () => { 106 assert.deepEqual(dedup([1, 2, 3]), [1, 2, 3]) 107 }) 108 }) 109 110 describe('dedupBy', () => { 111 it('deduplicates by key function', () => { 112 const items = [ 113 { id: '1', name: 'a' }, 114 { id: '2', name: 'b' }, 115 { id: '1', name: 'c' }, 116 ] 117 const result = dedupBy(items, (i) => i.id) 118 assert.equal(result.length, 2) 119 assert.equal(result[0].name, 'a') 120 assert.equal(result[1].name, 'b') 121 }) 122 it('keeps first occurrence', () => { 123 const result = dedupBy(['hello', 'HELLO', 'world'], (s) => s.toLowerCase()) 124 assert.deepEqual(result, ['hello', 'world']) 125 }) 126 it('handles empty array', () => { 127 assert.deepEqual(dedupBy([], (x) => String(x)), []) 128 }) 129 }) 130 131 describe('sleep', () => { 132 it('resolves after delay', async () => { 133 const start = Date.now() 134 await sleep(50) 135 const elapsed = Date.now() - start 136 assert.ok(elapsed >= 40, `Expected ≥40ms, got ${elapsed}ms`) 137 }) 138 it('resolves with undefined', async () => { 139 const result = await sleep(1) 140 assert.equal(result, undefined) 141 }) 142 })