/** * @vitest-environment node */ import { describe, expect, it } from 'vitest' import { parseVtt } from '@/connectors/zoom/zoom' const HEADER = 'WEBVTT\n\n' describe('parseVtt', () => { it.concurrent('returns empty string for input with no cues', () => { expect(parseVtt(HEADER)).toBe('') }) it.concurrent('extracts plain spoken text from a single cue', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nHello world\n` expect(parseVtt(vtt)).toBe('Hello world') }) it.concurrent('preserves WebVTT voice tags as "Speaker: text"', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nhello there\n` expect(parseVtt(vtt)).toBe('Alice: hello there') }) it.concurrent('preserves voice tags with class suffix', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nwelcome\n` expect(parseVtt(vtt)).toBe('Bob: welcome') }) it.concurrent('strips inline formatting tags but keeps text', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nbold and italic\n` expect(parseVtt(vtt)).toBe('bold and italic') }) it.concurrent('strips karaoke timestamp tags', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nhello <00:00:01.000>world\n` expect(parseVtt(vtt)).toBe('hello world') }) it.concurrent('strips class spans', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nSHOUT\n` expect(parseVtt(vtt)).toBe('SHOUT') }) it.concurrent('skips cue identifier lines before timing', () => { const vtt = `${HEADER}cue-1\n00:00:00.000 --> 00:00:02.000\nhello\n` expect(parseVtt(vtt)).toBe('hello') }) it.concurrent('joins multiple cues with newlines', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nfirst\n\n00:00:02.000 --> 00:00:04.000\nsecond\n` expect(parseVtt(vtt)).toBe('first\nsecond') }) it.concurrent('collapses repeated whitespace within a cue', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nhello world\n` expect(parseVtt(vtt)).toBe('hello world') }) it.concurrent('iteratively strips overlapping tags that reconstruct after one pass', () => { const crafted = '<b>injectedb>' const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\n${crafted}\n` const result = parseVtt(vtt) expect(result).not.toMatch(/<\/?[^>]+>/) expect(result).toContain('injected') }) it.concurrent('iteratively strips nested script-like tag fragments', () => { const crafted = 'ipt>alert(1)ipt>' const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\n${crafted}\n` const result = parseVtt(vtt) expect(result).not.toMatch(/<\/?[^>]+>/) expect(result.toLowerCase()).not.toContain('script') }) it.concurrent('sanitizes crafted speaker names that embed tag fragments', () => { const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\nEvil>payload\n` const result = parseVtt(vtt) expect(result).not.toMatch(/<\/?[^>]+>/) }) it.concurrent('terminates on adversarial deeply-nested input', () => { const crafted = `${'<'.repeat(50)}b${'>'.repeat(50)}text${'<'.repeat(50)}/b${'>'.repeat(50)}` const vtt = `${HEADER}00:00:00.000 --> 00:00:02.000\n${crafted}\n` const result = parseVtt(vtt) expect(result).not.toMatch(/<\/?[^>]+>/) }) })