transcriptLines.test.ts2.7 KBView on GitHub
import {
  hasTimestamps,
  parseTranscriptLines,
  secondsAtOffset,
} from '@/modules/meetings/detail/transcript-lines';

/**
 * A transcript reaches the client as one flat string, and the only reason the meeting view
 * can hand the recording a second is that the string is re-parseable into the lines the
 * server flattened. These cover the shapes the server actually writes plus the ones that
 * would silently lose text if parsing were stricter.
 */

const TRANSCRIPT = [
  '[00:00:12] Alice Chen: thanks for making the time',
  '[00:01:05] Bob Ray: so the deal is: we sign Friday',
  '[01:02:03] Alice Chen: perfect',
].join('\n');

describe('parseTranscriptLines', () => {
  it('recovers the stamp, speaker and text from each flattened line', () => {
    const lines = parseTranscriptLines(TRANSCRIPT);

    expect(lines).toHaveLength(3);
    expect(lines[0]).toMatchObject({
      atSeconds: 12,
      speaker: 'Alice Chen',
      text: 'thanks for making the time',
    });
    expect(lines[1]!.atSeconds).toBe(65);
    // An hour-long call stamps HH:MM:SS, which must not be read as MM:SS.
    expect(lines[2]!.atSeconds).toBe(3723);
  });

  it('keeps a colon in the SPOKEN text out of the speaker name', () => {
    const [, second] = parseTranscriptLines(TRANSCRIPT);
    expect(second!.speaker).toBe('Bob Ray');
    expect(second!.text).toBe('so the deal is: we sign Friday');
  });

  it('points textStart at the text inside the original string', () => {
    const lines = parseTranscriptLines(TRANSCRIPT);
    for (const line of lines) {
      expect(TRANSCRIPT.slice(line.textStart, line.textEnd)).toBe(line.text);
    }
  });

  it('keeps unstamped lines rather than dropping them', () => {
    const lines = parseTranscriptLines('just some prose\n\nwith a blank line');
    expect(lines.map((l) => l.text)).toEqual(['just some prose', 'with a blank line']);
    expect(hasTimestamps(lines)).toBe(false);
  });

  it('reports timestamps only when at least one line carries one', () => {
    expect(hasTimestamps(parseTranscriptLines(TRANSCRIPT))).toBe(true);
  });
});

describe('secondsAtOffset', () => {
  const lines = parseTranscriptLines(TRANSCRIPT);

  it('returns the stamp of the line the offset falls in', () => {
    const offset = TRANSCRIPT.indexOf('we sign Friday');
    expect(secondsAtOffset(lines, offset)).toBe(65);
  });

  it('uses the line a quote STARTS in when it runs past that line', () => {
    // A quote beginning in Bob's line and running into Alice's belongs to Bob's moment.
    const offset = TRANSCRIPT.indexOf('so the deal is');
    expect(secondsAtOffset(lines, offset)).toBe(65);
  });

  it('has no answer before the first stamped line', () => {
    expect(secondsAtOffset(parseTranscriptLines('no stamps here'), 3)).toBeNull();
  });
});