更新换行算法和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,107 @@
import { describe, expect, it, vi } from 'vitest';
import type { MeasureWidthImpl } from '../../measure/types';
import { applyOverflowFallback } from '../fallback';
function t(text: string, start: number) {
return { text, start, end: start + text.length };
}
describe('textWrap overflow-fallback', () => {
it('SYSTEM_DEFAULT仍返回单行但 meta 标记 SYSTEM_DEFAULT', async () => {
const res = await applyOverflowFallback({
tokens: [t('I', 0), t('am', 2), t('tired', 5)],
lang: 'EN',
context: 'APP',
availableWidth: 10,
maxLines: 2,
overflowMode: 'SYSTEM_DEFAULT',
ellipsisToken: '…',
reason: 'NO_CANDIDATE',
partialLayout: null,
});
expect(res.lines.length).toBe(1);
expect(res.meta.fallback_type).toBe('SYSTEM_DEFAULT');
expect(res.meta.overflow_type).toBe('NONE');
});
it('CLIP截断到 maxLines', async () => {
const res = await applyOverflowFallback({
tokens: [t('a', 0), t('b', 2), t('c', 4)],
lang: 'EN',
context: 'APP',
availableWidth: 10,
maxLines: 2,
overflowMode: 'CLIP',
ellipsisToken: '…',
reason: 'TOO_LONG',
partialLayout: {
breaks: [1, 2],
lines: [
{ start: 0, end: 1, text: 'a' },
{ start: 1, end: 2, text: 'b' },
// 造一个“未覆盖到 N”的 partial触发 overflow
{ start: 2, end: 2, text: 'c' },
],
},
});
expect(res.lines).toEqual(['a', 'b']);
expect(res.meta.overflow_type).toBe('CLIP');
});
it('ELLIPSIS清理末尾空白与 TC 末尾标点(不输出 \" …\" / \",…\"', async () => {
const res = await applyOverflowFallback({
tokens: [t('我', 0), t('好', 1), t('累', 2), t('', 3)],
lang: 'TC',
context: 'WIDGET',
availableWidth: 10,
maxLines: 1,
overflowMode: 'ELLIPSIS',
ellipsisToken: '…',
reason: 'NO_CANDIDATE',
partialLayout: {
breaks: [],
// 造一个“未覆盖到 N”的 partial触发 overflow
lines: [{ start: 0, end: 3, text: '我好累, ' }],
},
tcPunctuations: [''],
});
expect(res.lines[0]).toBe('我好累…');
expect(res.meta.overflow_type).toBe('ELLIPSIS');
});
it('ELLIPSIS可测量时超宽则按 token 回退直到不超宽', async () => {
// mock含省略号时宽度=100否则宽度=文本长度
const impl: MeasureWidthImpl = vi.fn(async ({ text }) => (text.includes('…') ? 100 : text.length));
const res = await applyOverflowFallback({
tokens: [t('I', 0), t('am', 2), t('so', 5), t('tired', 8)],
lang: 'EN',
context: 'APP',
availableWidth: 10,
maxLines: 1,
overflowMode: 'ELLIPSIS',
ellipsisToken: '…',
reason: 'NO_CANDIDATE',
partialLayout: {
breaks: [],
// 造一个“未覆盖到 N”的 partial触发 overflow
lines: [{ start: 0, end: 3, text: 'I am so' }],
},
measure: {
context: 'APP',
contextProfile: 'APP|ios|ellipsis',
fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
measureWidthImpl: impl,
},
});
// 因为任何带 ellipsis 的测量都超宽,这里会一路回退到空串 + ellipsis
expect(res.lines[0]).toBe('…');
expect(res.meta.overflow_type).toBe('ELLIPSIS');
});
});