更新换行算法和APP-PUSH

This commit is contained in:
吕新雨
2026-02-10 11:39:33 +08:00
parent f03d36b5e9
commit ee2d9f44ea
105 changed files with 9967 additions and 233 deletions

View File

@@ -0,0 +1,101 @@
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);
});
});