import { describe, expect, it, vi } from 'vitest'; import type { MeasureWidthImpl } from '../types'; import { MissingFontSpecError } from '../errors'; import { buildFontSpecKey } from '../fontSpecKey'; import { measureSliceWidthCached } from '../measureSliceWidthCached'; import { measureWidthCached } from '../measureWidthCached'; describe('textWrap width-measurement', () => { it('buildFontSpecKey: 缺字段必须报错(简体中文)', () => { expect(() => buildFontSpecKey({ fontFamily: 'PingFangSC-Regular', fontWeight: '400' } as any)).toThrow( MissingFontSpecError ); }); it('measureWidthCached: 未提供测量能力 -> approx + WIDTH_UNKNOWN', async () => { const res = await measureWidthCached({ text: 'hello', context: 'WIDGET', contextProfile: 'WIDGET', fontSpec: null, measureWidthImpl: undefined, }); expect(res.width).toBeNull(); expect(res.meta).toEqual({ isApprox: true, reason: 'WIDTH_UNKNOWN' }); }); it('measureWidthCached: 同 key 命中缓存(不会重复调用底层测量)', async () => { const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length); const a = await measureWidthCached({ text: 'abc', context: 'APP', contextProfile: 'APP|ios', fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 }, measureWidthImpl: impl, }); const b = await measureWidthCached({ text: 'abc', context: 'APP', contextProfile: 'APP|ios', fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 }, measureWidthImpl: impl, }); expect(a.width).toBe(3); expect(b.width).toBe(3); expect(impl).toHaveBeenCalledTimes(1); }); it('measureWidthCached: 测量抛错 -> approx + MEASURE_FAILED', async () => { const impl: MeasureWidthImpl = vi.fn(async () => { throw new Error('boom'); }); const res = await measureWidthCached({ text: 'abc', context: 'APP', contextProfile: 'APP|ios|throw', fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 }, measureWidthImpl: impl, }); expect(res.width).toBeNull(); expect(res.meta).toEqual({ isApprox: true, reason: 'MEASURE_FAILED' }); }); it('measureSliceWidthCached: 同 sliceKey 命中缓存', async () => { const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length); const tokens = [ { text: 'I', start: 0, end: 1 }, { text: 'am', start: 2, end: 4 }, { text: 'tired', start: 5, end: 10 }, ]; const a = await measureSliceWidthCached({ tokens: tokens as any, start: 0, end: 2, context: 'APP', contextProfile: 'APP|ios', fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 }, measureWidthImpl: impl, }); const b = await measureSliceWidthCached({ tokens: tokens as any, start: 0, end: 2, context: 'APP', contextProfile: 'APP|ios', fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 }, measureWidthImpl: impl, }); expect(a.width).toBe('I am'.length); expect(b.width).toBe('I am'.length); expect(impl).toHaveBeenCalledTimes(1); }); });