更新换行算法和APP-PUSH
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { generateBreakpoints } from '../index';
|
||||
|
||||
function t(text: string, start: number): { text: string; start: number; end: number } {
|
||||
return { text, start, end: start + text.length };
|
||||
}
|
||||
|
||||
describe('textWrap breakpoint-candidates', () => {
|
||||
it('EN: tokens=[I,am,so,tired] -> pos=[1,2,3](升序)', () => {
|
||||
const tokens = [t('I', 0), t('am', 2), t('so', 5), t('tired', 8)];
|
||||
const { breakpoints, meta } = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'EN',
|
||||
maxLines: 2,
|
||||
config: { tcMaxCandidateBreaks: 80, tcPunctuations: [',', '。'], balanceRange: 3 },
|
||||
});
|
||||
|
||||
expect(breakpoints.map((b) => b.pos)).toEqual([1, 2, 3]);
|
||||
expect(meta.originalCount).toBe(3);
|
||||
expect(meta.finalCount).toBe(3);
|
||||
});
|
||||
|
||||
it('TC: 标点后断点(,)应生成 pos=i+1', () => {
|
||||
const tokens = [t('我', 0), t('好', 1), t('累', 2), t(',', 3), t('😮💨', 4)];
|
||||
const { breakpoints } = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'TC',
|
||||
maxLines: 2,
|
||||
config: { tcMaxCandidateBreaks: 80, tcPunctuations: [','], balanceRange: 0 },
|
||||
});
|
||||
|
||||
expect(breakpoints.some((b) => b.kind === 'PUNCT' && b.pos === 4)).toBe(true);
|
||||
});
|
||||
|
||||
it('TC: forbiddenBreakRanges(闭区间)命中必须剔除(含 start/end)', () => {
|
||||
const tokens = [t('我', 0), t('好', 1), t('累', 2), t(',', 3), t('😮💨', 4)];
|
||||
const { breakpoints } = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'TC',
|
||||
maxLines: 2,
|
||||
constraints: { forbiddenBreakRanges: [{ start: 4, end: 4 }] },
|
||||
config: { tcMaxCandidateBreaks: 80, tcPunctuations: [','], balanceRange: 0 },
|
||||
});
|
||||
|
||||
expect(breakpoints.some((b) => b.pos === 4)).toBe(false);
|
||||
});
|
||||
|
||||
it('TC: 去重规则:同 pos 优先保留 priority 更高者;同 priority 按 kind 序', () => {
|
||||
// 构造:同一个 pos=2 同时来自 SPACE(priority=20) 和 BALANCE(priority=5),必须保留 SPACE
|
||||
const tokens = [t('我', 0), t(' ', 1), t('好', 2)];
|
||||
const { breakpoints } = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'TC',
|
||||
maxLines: 2,
|
||||
config: { tcMaxCandidateBreaks: 80, tcPunctuations: [], balanceRange: 2 },
|
||||
});
|
||||
|
||||
const b2 = breakpoints.find((b) => b.pos === 2);
|
||||
expect(b2?.kind).toBe('SPACE');
|
||||
expect(b2?.priority).toBe(20);
|
||||
});
|
||||
|
||||
it('TC: 裁剪上限生效且输出按 pos 升序(确定性)', () => {
|
||||
const tokens = Array.from({ length: 60 }).map((_, i) => t('哈', i));
|
||||
const res = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'TC',
|
||||
maxLines: 3,
|
||||
config: { tcMaxCandidateBreaks: 8, tcPunctuations: [], balanceRange: 6 },
|
||||
});
|
||||
|
||||
expect(res.breakpoints.length).toBe(8);
|
||||
// pos 升序
|
||||
for (let i = 1; i < res.breakpoints.length; i++) {
|
||||
expect(res.breakpoints[i]!.pos).toBeGreaterThanOrEqual(res.breakpoints[i - 1]!.pos);
|
||||
}
|
||||
// 确定性
|
||||
const res2 = generateBreakpoints({
|
||||
tokens: tokens as any,
|
||||
lang: 'TC',
|
||||
maxLines: 3,
|
||||
config: { tcMaxCandidateBreaks: 8, tcPunctuations: [], balanceRange: 6 },
|
||||
});
|
||||
expect(res).toEqual(res2);
|
||||
});
|
||||
|
||||
it('边界:N<=1 时不输出任何候选断点', () => {
|
||||
const { breakpoints } = generateBreakpoints({
|
||||
tokens: [t('我', 0)] as any,
|
||||
lang: 'TC',
|
||||
maxLines: 2,
|
||||
config: { tcMaxCandidateBreaks: 80, tcPunctuations: [','], balanceRange: 3 },
|
||||
});
|
||||
expect(breakpoints).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user