import { describe, expect, it } from 'vitest'; import { buildBoardCommentAttachments, buildVisualAnnotationAttachment, commentsToAttachments, historyWithCommentAttachmentContext, liveSnapshotForComment, mergeAttachedComments, messageContentWithCommentAttachments, overlayBoundsFromSnapshot, removeAttachedComment, targetFromSnapshot, } from '../src/comments'; import type { ChatMessage, PreviewComment } from '../src/types'; describe('preview comment attachment helpers', () => { it('builds compact target context from an iframe snapshot', () => { const target = targetFromSnapshot({ filePath: 'index.html', elementId: 'hero-title', selector: '[data-od-id="hero-title"]', label: 'h1.hero-title', text: ` ${'Title '.repeat(80)} `, htmlHint: `

${'x'.repeat(240)}

`, position: { x: 10.4, y: 20.5, width: 300.2, height: 88.8 }, style: { color: 'rgb(26, 25, 22)', fontSize: '13.5px', fontFamily: 'Inter, sans-serif', }, }); expect(target.text.length).toBeLessThanOrEqual(160); expect(target.htmlHint.length).toBeLessThanOrEqual(180); expect(target.position).toEqual({ x: 10, y: 21, width: 300, height: 89 }); expect(target.style).toMatchObject({ color: 'rgb(26, 25, 22)', fontSize: '13.5px', fontFamily: 'Inter, sans-serif', }); }); it('creates ordered compact send payloads from attached comments', () => { const attachments = commentsToAttachments([ comment({ id: 'c1', elementId: 'hero-title', note: 'Shorten this title' }), comment({ id: 'c2', elementId: 'chart', note: 'Make it feel real' }), ]); expect(attachments).toMatchObject([ { id: 'c1', order: 1, elementId: 'hero-title', comment: 'Shorten this title' }, { id: 'c2', order: 2, elementId: 'chart', comment: 'Make it feel real' }, ]); }); it('builds grouped board payloads for pod selections', () => { const attachments = buildBoardCommentAttachments({ target: { filePath: 'atlas.html', elementId: 'pod-1', selector: '[data-od-id="hero"], [data-od-id="chart"]', label: 'Hero and chart', text: 'Hero title Chart value', position: { x: 10, y: 20, width: 300, height: 200 }, htmlHint: '
', selectionKind: 'pod', memberCount: 2, podMembers: [ { elementId: 'hero', selector: '[data-od-id="hero"]', label: 'section.hero', text: 'Hero title', position: { x: 10, y: 20, width: 200, height: 100 }, htmlHint: '
', }, { elementId: 'chart', selector: '[data-od-id="chart"]', label: 'section.chart', text: 'Chart value', position: { x: 120, y: 80, width: 190, height: 120 }, htmlHint: '
', }, ], }, notes: ['Tighten the hierarchy', 'Make the chart feel premium'], }); expect(attachments).toHaveLength(2); expect(attachments[0]).toMatchObject({ selectionKind: 'pod', memberCount: 2, source: 'board-batch', comment: 'Tighten the hierarchy', }); expect(messageContentWithCommentAttachments('', attachments)).toContain('memberCount: 2'); }); it('builds visual annotation payloads without requiring a selector', () => { const attachment = buildVisualAnnotationAttachment({ order: 1, screenshotPath: 'uploads/drawing.png', markKind: 'stroke', note: '', bounds: { x: 12, y: 24, width: 140, height: 80 }, target: { filePath: 'index.html', position: { x: 12, y: 24, width: 140, height: 80 }, }, }); expect(attachment).toMatchObject({ selectionKind: 'visual', screenshotPath: 'uploads/drawing.png', markKind: 'stroke', selector: '', comment: expect.stringContaining('red strokes'), intent: expect.stringContaining('red strokes'), }); expect(messageContentWithCommentAttachments('', [attachment])).toContain('targetKind: visual'); expect(messageContentWithCommentAttachments('', [attachment])).toContain('screenshot: uploads/drawing.png'); expect(messageContentWithCommentAttachments('', [attachment])).toContain('markKind: stroke'); expect(messageContentWithCommentAttachments('', [attachment])).not.toContain('selector: '); }); it('keeps large queued board-note batches ordered in one send payload', () => { const notes = Array.from({ length: 8 }, (_, index) => `Note ${index + 1}`); const attachments = buildBoardCommentAttachments({ target: { filePath: 'atlas.html', elementId: 'pod-2', selector: '[data-od-id="card"]', label: 'Card pod', text: 'Heading Body CTA', position: { x: 20, y: 30, width: 240, height: 160 }, htmlHint: '
', selectionKind: 'pod', memberCount: 3, podMembers: [ { elementId: 'card-heading', selector: '[data-od-id="card-heading"]', label: 'h2.card-heading', text: 'Heading', position: { x: 24, y: 34, width: 100, height: 32 }, htmlHint: '

', }, { elementId: 'card-body', selector: '[data-od-id="card-body"]', label: 'p.card-body', text: 'Body', position: { x: 24, y: 72, width: 180, height: 48 }, htmlHint: '

', }, { elementId: 'card-cta', selector: '[data-od-id="card-cta"]', label: 'button.card-cta', text: 'CTA', position: { x: 24, y: 128, width: 96, height: 32 }, htmlHint: '