更新换行算法和APP-PUSH
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { MeasureWidthImpl } from '../../measure/types';
|
||||
import { searchBestLayoutApp } from '../index';
|
||||
import { DEFAULT_LEXICONS, DEFAULT_WEIGHTS } from '../../scoring/index';
|
||||
|
||||
function t(text: string, start: number) {
|
||||
return { text, start, end: start + text.length };
|
||||
}
|
||||
|
||||
describe('textWrap search-engine-app', () => {
|
||||
it('确定性:同输入多次调用 breaks/lines 必须一致', async () => {
|
||||
const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length);
|
||||
const input = {
|
||||
tokens: [t('I', 0), t('am', 2), t('so', 5), t('tired', 8)],
|
||||
lang: 'EN' as const,
|
||||
breakpoints: [
|
||||
{ pos: 1, kind: 'SPACE', priority: 10 },
|
||||
{ pos: 2, kind: 'SPACE', priority: 10 },
|
||||
{ pos: 3, kind: 'SPACE', priority: 10 },
|
||||
],
|
||||
availableWidth: 6,
|
||||
maxLines: 2,
|
||||
lineMode: 'AUTO' as const,
|
||||
measure: {
|
||||
contextProfile: 'APP|ios|test',
|
||||
fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
|
||||
measureWidthImpl: impl,
|
||||
},
|
||||
scoring: {
|
||||
config: {
|
||||
weights: DEFAULT_WEIGHTS,
|
||||
idealWidthRatio: { APP: 0.9, WIDGET: 0.95 },
|
||||
ellipsisToken: '…',
|
||||
tcParticleWhitelist: [],
|
||||
},
|
||||
lexicons: DEFAULT_LEXICONS,
|
||||
debug: true,
|
||||
},
|
||||
};
|
||||
|
||||
const a = await searchBestLayoutApp(input as any);
|
||||
const b = await searchBestLayoutApp(input as any);
|
||||
const c = await searchBestLayoutApp(input as any);
|
||||
|
||||
expect(a).toEqual(b);
|
||||
expect(b).toEqual(c);
|
||||
expect(impl).toHaveBeenCalled(); // 有测量发生
|
||||
});
|
||||
|
||||
it('lineMode=FIXED:无解必须返回 NO_CANDIDATE', async () => {
|
||||
const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length);
|
||||
const res = await searchBestLayoutApp({
|
||||
tokens: [t('I', 0), t('am', 2)],
|
||||
lang: 'EN',
|
||||
breakpoints: [{ pos: 1, kind: 'SPACE', priority: 10 }],
|
||||
availableWidth: 100,
|
||||
maxLines: 3,
|
||||
lineMode: 'FIXED',
|
||||
measure: {
|
||||
contextProfile: 'APP|ios|fixed',
|
||||
fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
|
||||
measureWidthImpl: impl,
|
||||
},
|
||||
scoring: {
|
||||
config: { weights: DEFAULT_WEIGHTS, idealWidthRatio: { APP: 0.9, WIDGET: 0.95 }, ellipsisToken: '…', tcParticleWhitelist: [] },
|
||||
lexicons: DEFAULT_LEXICONS,
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) expect(res.reason).toBe('NO_CANDIDATE');
|
||||
});
|
||||
|
||||
it('TC H6:断点导致下一行行首为标点时必须禁止', async () => {
|
||||
const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length);
|
||||
const res = await searchBestLayoutApp({
|
||||
tokens: [t('我', 0), t('好', 1), t(',', 2), t('累', 3)],
|
||||
lang: 'TC',
|
||||
// 若选择 pos=2,则第二行行首 token 为 ','(应禁止)
|
||||
breakpoints: [{ pos: 2, kind: 'PUNCT', priority: 30 }],
|
||||
availableWidth: 100,
|
||||
maxLines: 2,
|
||||
lineMode: 'FIXED',
|
||||
measure: {
|
||||
contextProfile: 'APP|ios|tc',
|
||||
fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
|
||||
measureWidthImpl: impl,
|
||||
},
|
||||
scoring: {
|
||||
config: { weights: DEFAULT_WEIGHTS, idealWidthRatio: { APP: 0.9, WIDGET: 0.95 }, ellipsisToken: '…', tcParticleWhitelist: [], tcPunctuations: [','] },
|
||||
lexicons: DEFAULT_LEXICONS,
|
||||
},
|
||||
});
|
||||
|
||||
// lineMode=FIXED 且唯一断点被禁止,因此无解
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) expect(res.reason).toBe('NO_CANDIDATE');
|
||||
});
|
||||
|
||||
it('TOO_LONG:超过阈值直接返回 TOO_LONG', async () => {
|
||||
const impl: MeasureWidthImpl = vi.fn(async ({ text }) => text.length);
|
||||
const tokens = Array.from({ length: 31 }).map((_, i) => t('a', i));
|
||||
const res = await searchBestLayoutApp({
|
||||
tokens,
|
||||
lang: 'EN',
|
||||
breakpoints: [],
|
||||
availableWidth: 100,
|
||||
maxLines: 2,
|
||||
lineMode: 'AUTO',
|
||||
measure: {
|
||||
contextProfile: 'APP|ios|toolong',
|
||||
fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
|
||||
measureWidthImpl: impl,
|
||||
},
|
||||
scoring: {
|
||||
config: { weights: DEFAULT_WEIGHTS, idealWidthRatio: { APP: 0.9, WIDGET: 0.95 }, ellipsisToken: '…', tcParticleWhitelist: [] },
|
||||
lexicons: DEFAULT_LEXICONS,
|
||||
},
|
||||
});
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) expect(res.reason).toBe('TOO_LONG');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user