import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { describeCallDuration, describeCallEnd, describeCallDetails, parseCallSummaryDetails, } from './callSummary'; describe('describeCallDuration', () => { it('picks the unit', () => { assert.equal(describeCallDuration(40_000), '40 s'); assert.equal(describeCallDuration(41 * 60_000), '41 min'); assert.equal(describeCallDuration(65 * 60_000), '1 h 05 min'); }); }); describe('describeCallEnd', () => { const min = 60_000; it('without a summary shows just the duration', () => { assert.equal(describeCallEnd(41 * min), 'Call ended · 41 min'); }); it('good / unknown', () => { assert.equal( describeCallEnd(41 * min, { durationMs: 41 * min, reconnects: 1, poorMs: 0, verdict: 'good', }), 'Call ended · 41 min · connection was good', ); assert.equal( describeCallEnd(2 * min, { durationMs: 2 * min, reconnects: 0, poorMs: 0, verdict: 'unknown', }), 'Call ended · 2 min', ); }); it('reconnects and poor spells', () => { assert.equal( describeCallEnd(41 * min, { durationMs: 41 * min, reconnects: 3, poorMs: 0, verdict: 'fair', }), 'Call ended · 41 min · 3 reconnects', ); assert.equal( describeCallEnd(12 * min, { durationMs: 12 * min, reconnects: 1, poorMs: 4 * min, verdict: 'poor', }), 'Call ended · 12 min · 1 reconnect, connection was poor for 4 min', ); assert.equal( describeCallEnd(12 * min, { durationMs: 12 * min, reconnects: 0, poorMs: 2_000, verdict: 'fair', }), 'Call ended · 12 min · connection was fair', ); }); }); it('[#253] parseCallSummaryDetails keeps well-formed fields and drops the rest', () => { const d = parseCallSummaryDetails({ participants: ['@bob:x', 7, '@carol:x'], peakParticipants: 3, quality: { youPoorMs: 1000, othersPoorMs: { '@bob:x': 500, evil: 9, '@c:x': -1 }, everyonePoorMs: 0, }, network: { rttMedianMs: 38, rttP95Ms: 'fast', inboundLossPct: 0.5 }, activity: { speakingMs: 60_000, mutedMs: 'x', screenshareMs: 0 }, }); assert.deepEqual(d, { participants: ['@bob:x', '@carol:x'], peakParticipants: 3, quality: { youPoorMs: 1000, othersPoorMs: { '@bob:x': 500 }, everyonePoorMs: 0 }, network: { rttMedianMs: 38, inboundLossPct: 0.5 }, activity: { speakingMs: 60_000, mutedMs: 0, screenshareMs: 0 }, }); assert.equal(parseCallSummaryDetails(undefined), undefined); assert.equal(parseCallSummaryDetails('details'), undefined); }); it('[#253] describeCallDetails: technical sections with names, blame and numbers', () => { const names: Record = { '@bob:x': 'Bob', '@carol:x': 'Carol' }; const sections = describeCallDetails( { durationMs: 2_460_000, reconnects: 2, poorMs: 120_000, verdict: 'fair', details: { participants: ['@bob:x', '@carol:x'], peakParticipants: 3, quality: { youPoorMs: 120_000, othersPoorMs: { '@carol:x': 30_000 }, everyonePoorMs: 10_000, }, network: { rttMedianMs: 38, rttP95Ms: 310, outboundLossPct: 4.1, inboundLossPct: 0.5, jitterMs: 6, }, activity: { speakingMs: 600_000, mutedMs: 1_200_000, screenshareMs: 300_000 }, }, }, 2_460_000, 90_000, (u) => names[u] ?? u, ); assert.deepEqual(sections, [ { title: 'Call', lines: ['Duration 41 min · peak 3 in the call', 'With Bob, Carol'] }, { title: 'Connection', lines: [ 'Verdict: fair · 2 reconnects', 'Your connection poor/lost: 2 min', 'Carol poor/lost: 30 s', 'Everyone poor at once: 10 s (likely server or SFU side)', ], }, { title: 'Network', lines: [ 'RTT median 38 ms · p95 310 ms', 'Outbound packet loss 4.1%', 'Inbound packet loss 0.5%', 'Jitter 6 ms', ], }, { title: 'You', lines: ['Talking 10 min · muted 20 min · deafened 2 min', 'Screensharing 5 min'], }, ]); }); it('[#253] describeCallDetails without fork details (older fork) still says something useful', () => { const sections = describeCallDetails( { durationMs: 60_000, reconnects: 0, poorMs: 0, verdict: 'good' }, 60_000, 0, (u) => u, ); assert.deepEqual( sections.map((s) => s.title), ['Call', 'Connection'], ); assert.deepEqual(sections[0].lines, ['Duration 1 min', 'Participants unknown']); });