File size: 6,032 Bytes
a0ebf39 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | import { describe, expect, test } from 'vitest';
import { buildWhiteboardConflicts } from '@/lib/orchestration/summarizers/whiteboard-conflicts';
// Minimal PPTElement stand-ins β the summarizer only reads geometry fields.
const text = (id: string, left: number, top: number, width: number, height: number) => ({
type: 'text',
id,
left,
top,
width,
height,
content: '<p>sample</p>',
});
const table = (id: string, left: number, top: number, width: number, height: number) => ({
type: 'table',
id,
left,
top,
width,
height,
data: [[{ text: 'a' }]],
});
const line = (
id: string,
left: number,
top: number,
start: [number, number],
end: [number, number],
) => ({ type: 'line', id, left, top, start, end });
describe('buildWhiteboardConflicts β no conflicts', () => {
test('empty element list returns empty string', () => {
expect(buildWhiteboardConflicts([])).toBe('');
});
test('two well-separated elements return empty string', () => {
const out = buildWhiteboardConflicts([
text('t1', 20, 20, 200, 60),
text('t2', 400, 200, 200, 60),
]);
expect(out).toBe('');
});
test('just-touching bboxes (intersection area = 0) are not reported', () => {
const out = buildWhiteboardConflicts([
text('t1', 0, 0, 100, 100),
text('t2', 100, 0, 100, 100), // shares only the x=100 edge
]);
expect(out).toBe('');
});
test('line routed clear of all elements produces no conflict', () => {
const out = buildWhiteboardConflicts([
text('t1', 100, 100, 200, 60),
line('l1', 0, 0, [50, 50], [50, 400]),
]);
expect(out).toBe('');
});
});
describe('buildWhiteboardConflicts β bbox overlap', () => {
test('one element fully inside another reports ~100% overlap', () => {
const out = buildWhiteboardConflicts([
table('big', 0, 0, 500, 400),
text('small', 50, 50, 100, 80), // entirely inside the table
]);
expect(out).toContain('OVERLAP:');
expect(out).toContain('100%');
});
test('50% overlap is reported; 10% is not (30% threshold)', () => {
// Each bbox 100Γ100; smaller area = 10000. Overlap area = 50Γ100 = 5000 β 50%.
const overlapping = buildWhiteboardConflicts([
text('a', 0, 0, 100, 100),
text('b', 50, 0, 100, 100),
]);
expect(overlapping).toContain('OVERLAP:');
expect(overlapping).toContain('50%');
// Overlap area = 10Γ100 = 1000 β 10% β below threshold.
const tiny = buildWhiteboardConflicts([text('a', 0, 0, 100, 100), text('b', 90, 0, 100, 100)]);
expect(tiny).toBe('');
});
test('non-line elements without width/height are skipped, not crashed', () => {
const out = buildWhiteboardConflicts([
text('t1', 0, 0, 100, 100),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{ type: 'text', id: 'broken', left: 10, top: 10 } as any, // missing width/height
]);
// Only one valid element remaining β no overlap to report.
expect(out).toBe('');
});
});
describe('buildWhiteboardConflicts β line crossing elements', () => {
test('line passing through the middle of a text box is reported', () => {
const out = buildWhiteboardConflicts([
text('t1', 100, 100, 200, 60), // covers xβ[100,300], yβ[100,160]
line('l1', 0, 0, [0, 130], [400, 130]), // horizontal line through y=130, cuts the box
]);
expect(out).toContain('LINE CROSSES:');
expect(out).toContain('t1');
});
test('line whose endpoint is inside a bbox is reported', () => {
const out = buildWhiteboardConflicts([
text('t1', 100, 100, 200, 60),
line('l1', 0, 0, [50, 50], [200, 130]), // endpoint (200,130) is inside t1
]);
expect(out).toContain('LINE CROSSES:');
});
test('line with endpoints on opposite sides of a box but path above the box is clean', () => {
const out = buildWhiteboardConflicts([
text('t1', 100, 100, 200, 60),
line('l1', 0, 0, [50, 50], [400, 50]), // y=50, above the box (yβ[100,160])
]);
expect(out).toBe('');
});
});
describe('buildWhiteboardConflicts β canvas edge clipping', () => {
test('element extending past right edge is reported', () => {
const out = buildWhiteboardConflicts([text('wide', 900, 100, 200, 60)]);
expect(out).toContain('OUT OF CANVAS:');
expect(out).toContain('right edge by 100px');
});
test('element extending past bottom edge is reported (canvas height = 563)', () => {
const out = buildWhiteboardConflicts([text('tall', 100, 500, 100, 80)]);
expect(out).toContain('OUT OF CANVAS:');
expect(out).toContain('bottom edge by 17px'); // 500+80-563 = 17
});
test('element with negative left is reported', () => {
const out = buildWhiteboardConflicts([text('negx', -10, 100, 50, 50)]);
expect(out).toContain('OUT OF CANVAS:');
expect(out).toContain('left edge by 10px');
});
test('element exactly at right edge (x+w == 1000) is NOT reported', () => {
const out = buildWhiteboardConflicts([text('edge', 900, 100, 100, 60)]);
expect(out).toBe('');
});
test('element exactly at bottom edge (y+h == 563) is NOT reported', () => {
const out = buildWhiteboardConflicts([text('edge', 100, 500, 100, 63)]);
expect(out).toBe('');
});
});
describe('buildWhiteboardConflicts β output format', () => {
test('renders a single markdown block with a header and bullet list', () => {
const out = buildWhiteboardConflicts([text('a', 0, 0, 100, 100), text('b', 50, 0, 100, 100)]);
expect(out).toMatch(/## β Layout Conflicts Detected/);
expect(out).toMatch(/\n {2}- OVERLAP:/);
});
test('lists multiple conflicts in one block', () => {
const out = buildWhiteboardConflicts([
text('a', 0, 0, 100, 100),
text('b', 50, 0, 100, 100), // overlap with a
text('outside', 950, 100, 200, 60), // out of canvas
]);
const bullets = out.split('\n').filter((l) => l.trim().startsWith('- '));
expect(bullets.length).toBeGreaterThanOrEqual(2);
});
});
|