108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
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');
|
||
});
|
||
});
|
||
|