Files
PlantAI-rag-new/tests/frontend/unit/b2/figureLayout.test.js
jerryandClaude Fable 5 f30e2a9102 feat(chat): 连续两张图片即并排,取消同比例限制
- 实测发现同论文连续图(g001/g002)比例差异达 4.5%/62.5%,比例门禁把真实连续对全部拆散
- 布局改为纯 DOM 结构:连续相邻即配对包 figure-row,奇数最后一张落单
- 移除图片尺寸读取与 load 等待,布局时机简化为 mounted/updated
- 测试更新:任意连续两张并排、3 张=1对+1单、空 p 兼容、隐藏失败图跳过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:27:55 +08:00

131 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @vitest-environment jsdom
// figureLayout.test.js —— 聊天图片布局纯函数(2026-09-01 修订版)
// 需求:单图 50% 宽;只要是连续两张图片就并排一行各 50%(不做比例相同限制)。
import { describe, it, expect } from 'vitest';
import { pairFigures, isInlineAdjacent, layoutTextRoot } from '../../../../ui/src/components/chat/figureLayout.js';
const fig = () => document.createElement('figure');
describe('pairFigures 按 2 分块配对', () => {
const figs = [fig(), fig(), fig(), fig(), fig()];
it('2 张 → 1 对', () => {
const g = pairFigures([figs[0], figs[1]]);
expect(g).toHaveLength(1);
expect(g[0].row).toBe(true);
expect(g[0].figures).toEqual([figs[0], figs[1]]);
});
it('3 张 → 1 对 + 1 单(连续即配对,不论比例)', () => {
const g = pairFigures([figs[0], figs[1], figs[2]]);
expect(g).toHaveLength(2);
expect(g[0].row).toBe(true);
expect(g[1].row).toBe(false);
expect(g[1].figures).toEqual([figs[2]]);
});
it('5 张 → 2 对 + 1 单', () => {
const g = pairFigures(figs);
expect(g).toHaveLength(3);
expect(g.map((x) => x.row)).toEqual([true, true, false]);
});
it('空列表', () => {
expect(pairFigures([])).toEqual([]);
});
});
describe('isInlineAdjacent 连续性', () => {
it('同一 <p> 内仅空白相隔 → 相邻', () => {
const p = document.createElement('p');
const a = fig();
const b = fig();
p.append(a, document.createTextNode(' '), b);
expect(isInlineAdjacent(a, b)).toBe(true);
});
it('相邻兄弟 <p> → 相邻(markdown-it 逐图成段)', () => {
const wrap = document.createElement('div');
const pa = document.createElement('p');
const pb = document.createElement('p');
pa.append(fig());
pb.append(fig());
wrap.append(pa, pb);
expect(isInlineAdjacent(pa.firstChild, pb.firstChild)).toBe(true);
});
it('HTML 解析器把 figure 提出 p 留下的空 <p> 视为空白 → 相邻', () => {
const wrap = document.createElement('div');
const a = fig();
const emptyP = document.createElement('p');
const b = fig();
wrap.append(a, emptyP, b);
expect(isInlineAdjacent(a, b)).toBe(true);
});
it('中间有文字段落 → 不相邻', () => {
const wrap = document.createElement('div');
const pa = document.createElement('p');
const pm = document.createElement('p');
const pb = document.createElement('p');
pa.append(fig());
pm.textContent = '说明文字';
pb.append(fig());
wrap.append(pa, pm, pb);
expect(isInlineAdjacent(pa.firstChild, pb.firstChild)).toBe(false);
});
});
describe('layoutTextRoot 幂等布局', () => {
function makeRoot(html) {
const root = document.createElement('div');
root.className = 'md-text';
root.innerHTML = html;
return root;
}
it('单图 → half 类,无 row', () => {
const root = makeRoot('<p><figure class="md-figure"><img></figure></p>');
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(0);
expect(root.querySelector('.md-figure').classList.contains('half')).toBe(true);
});
it('连续两张(任意比例)→ 一个 figure-row,无 half', () => {
const root = makeRoot(
'<p><figure class="md-figure"><img></figure></p><p><figure class="md-figure"><img></figure></p>'
);
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(1);
const row = root.querySelector('.figure-row');
expect(row.children.length).toBe(2);
expect(root.querySelectorAll('.md-figure.half').length).toBe(0);
});
it('3 张连续 → 1 对 + 1 单', () => {
const root = makeRoot(
'<p><figure class="md-figure"><img></figure></p><p><figure class="md-figure"><img></figure></p><p><figure class="md-figure"><img></figure></p>'
);
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(1);
expect(root.querySelectorAll('.md-figure.half').length).toBe(1);
});
it('中间夹文字段落的双图 → 各自 half,无 row', () => {
const root = makeRoot(
'<p><figure class="md-figure"><img></figure></p><p>分隔文字</p><p><figure class="md-figure"><img></figure></p>'
);
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(0);
expect(root.querySelectorAll('.md-figure.half').length).toBe(2);
});
it('重复调用幂等(重排前拆旧 row / 清 half)', () => {
const root = makeRoot(
'<p><figure class="md-figure"><img></figure></p><p><figure class="md-figure"><img></figure></p>'
);
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(1);
layoutTextRoot(root);
expect(root.querySelectorAll('.figure-row').length).toBe(1);
expect(root.querySelectorAll('.md-figure.half').length).toBe(0);
});
it('display:none 的失败图不参与布局', () => {
const root = makeRoot(
'<p><figure class="md-figure" style="display:none"><img></figure></p><p><figure class="md-figure"><img></figure></p><p><figure class="md-figure"><img></figure></p>'
);
layoutTextRoot(root);
// 隐藏图被跳过,剩余两张可见图并排
expect(root.querySelectorAll('.figure-row').length).toBe(1);
expect(root.querySelectorAll('.md-figure.half').length).toBe(0);
});
});