import { describe, expect, it, vi } from 'vitest' import type { Editor } from 'tldraw' import { installTldrawTextMeasurementGuard } from './tldrawTextMeasurementGuard' type TextMeasure = Pick['textMeasure'] type MeasureElementTextNodeSpans = TextMeasure['measureElementTextNodeSpans'] function textMeasureHost(measureElementTextNodeSpans: MeasureElementTextNodeSpans) { return { textMeasure: { measureElementTextNodeSpans, }, } } function elementWithText(text: string): HTMLElement { const element = document.createElement('div') element.textContent = text vi.spyOn(element, 'getBoundingClientRect').mockReturnValue(DOMRect.fromRect({ height: 24, width: 88, })) return element } describe('tldraw text measurement guard', () => { it('falls back to a whole-text span when Edge returns no range rect', () => { const originalMeasure = vi.fn(() => { throw new TypeError("Cannot read properties of undefined (reading 'top')") }) const host = textMeasureHost(originalMeasure) const cleanup = installTldrawTextMeasurementGuard(host) expect(host.textMeasure.measureElementTextNodeSpans(elementWithText('Label'))).toEqual({ didTruncate: false, spans: [{ box: { h: 24, w: 88, x: 0, y: 0 }, text: 'Label', }], }) cleanup() expect(() => host.textMeasure.measureElementTextNodeSpans(elementWithText('Label'))).toThrow('top') }) it('preserves first-line truncation for fallback measurements', () => { const originalMeasure = vi.fn(() => { throw new TypeError("Cannot read properties of undefined (reading 'top')") }) const host = textMeasureHost(originalMeasure) installTldrawTextMeasurementGuard(host) expect(host.textMeasure.measureElementTextNodeSpans( elementWithText('Title\nOverflow'), { shouldTruncateToFirstLine: true }, )).toEqual({ didTruncate: true, spans: [{ box: { h: 24, w: 88, x: 0, y: 0 }, text: 'Title', }], }) }) it('rethrows unrelated measurement failures', () => { const originalMeasure = vi.fn(() => { throw new RangeError('bad measurement state') }) const host = textMeasureHost(originalMeasure) installTldrawTextMeasurementGuard(host) expect(() => host.textMeasure.measureElementTextNodeSpans(elementWithText('Label'))) .toThrow('bad measurement state') }) })