SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
28ad89a verified
Raw History Blame Contribute Delete
9.71 kB
import { describe, expect, it, vi } from 'vitest'
import { flushAnnotateStack } from './flush'
import { compactIdentity } from './identity'
import { annotateFlushPrompt, packageAnnotatePin, packageAnnotateStack } from './pack'
import { addAnnotatePin, type AnnotatePin, emptyAnnotateStack } from './stack'
import { ANNOTATE_HTML_BUDGET } from './tokens'
const png =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
function pin(partial: Partial<AnnotatePin> = {}): AnnotatePin {
return {
id: 'annotate-1',
imageDataUrl: png,
kind: 'element',
note: 'This button overflows on mobile.',
number: 1,
pageTitle: 'Pricing',
pageUrl: 'http://127.0.0.1:4173/',
rect: { height: 40, width: 120, x: 8, y: 8 },
identity: {
css: { color: 'rgb(24, 24, 24)', 'font-size': '14px' },
html: '<button class="plan">Select plan</button>',
selector: 'button.plan',
tag: 'button',
text: 'Select plan'
},
...partial
}
}
describe('packageAnnotatePin', () => {
it('carries the selector, markup, and computed styles the agent needs to find the source', () => {
const packed = packageAnnotatePin(pin())
expect(packed.prompt).toContain('Selector: button.plan')
expect(packed.prompt).toContain('HTML: <button class="plan">Select plan</button>')
expect(packed.prompt).toContain('color: rgb(24, 24, 24)')
expect(packed.prompt).toContain('font-size: 14px')
})
it('keeps the target line prose while the DOM detail rides its own labelled lines', () => {
const text = 'ื’ื ื‘ืงื™ื‘ื•ืฅ ื—ื•ืœื™ืช ื”ืงื˜ืŸ ื™ืฉ ื™ืœื“ ืฉืขื•ืฉื” ืืช ื”ืฆืขื“ ื”ืจืืฉื•ืŸ ื‘ืžืขืจื›ืช ื”ื—ื™ื ื•ืš'
const packed = packageAnnotatePin(
pin({
identity: {
css: { color: 'rgb(0, 0, 0)', 'font-family': 'Moses, NarkisBlock', 'font-size': '18px' },
html: '<div class="text_editor_paragraph rtl">โ€ฆ</div>',
selector:
'div.DraftEditor-editorContainer>div.public-DraftEditor-content>div>div.text_editor_paragraph.rtl:nth-of-type(9)',
tag: 'div',
text
},
note: 'ืชืกื›ื ืืช ื–ื”'
})
)
const target = packed.prompt.split('\n').find(line => line.startsWith('Target:'))
expect(target).toBe(`Target: "${text}"`)
expect(target).not.toContain('div')
expect(target).not.toContain('DraftEditor')
expect(packed.prompt).toContain('Note: ืชืกื›ื ืืช ื–ื”')
expect(packed.prompt).toContain('Selector: div.DraftEditor-editorContainer')
})
it('packs a numbered crop, compact identity, and the note', () => {
const packed = packageAnnotatePin(pin())
expect(packed.number).toBe(1)
expect(packed.imageDataUrl).toBe(png)
expect(packed.note).toContain('overflows')
expect(packed.prompt).toContain('Comment 1')
expect(packed.prompt).toContain('Target: button "Select plan"')
expect(packed.prompt).toContain('Image 1 marks the target in blue.')
expect(packed.prompt).toContain('Note: This button overflows on mobile.')
expect(packed.prompt).not.toContain('<html')
})
it('keeps a selector when an element has no readable text', () => {
const packed = packageAnnotatePin(
pin({
identity: {
css: { display: 'block' },
html: '<div id="sales-chart"></div>',
selector: '#sales-chart',
tag: 'div',
text: ''
},
note: 'Use the same scale as the chart above.'
})
)
expect(packed.prompt).toContain('Target: #sales-chart')
})
it('invents no element detail for an area pin', () => {
const packed = packageAnnotatePin(pin({ identity: undefined, kind: 'area', note: 'too tight' }))
expect(packed.prompt).toContain('area')
expect(packed.prompt).toContain('120ร—40px')
expect(packed.prompt).toContain('too tight')
expect(packed.prompt).not.toContain('Selector:')
expect(packed.prompt).not.toContain('HTML:')
expect(packed.prompt).not.toContain('Styles:')
})
})
describe('compactIdentity', () => {
it('never keeps the whole document and clips long text', () => {
const compact = compactIdentity({
css: { color: 'red', display: 'none', margin: '0px' },
selector: 'html>body>div>div>button.submit',
tag: 'BUTTON',
text: 'x'.repeat(200)
})
expect(compact.tag).toBe('button')
expect(compact.text.endsWith('โ€ฆ')).toBe(true)
expect(compact.text.length).toBeLessThanOrEqual(80)
expect(compact.css.display).toBeUndefined()
expect(compact.css.margin).toBeUndefined()
expect(compact.css.color).toBe('red')
})
it('clips markup to the budget rather than pasting a whole section', () => {
const compact = compactIdentity({
css: {},
html: `<section>${'<p>filler</p>'.repeat(400)}</section>`,
selector: 'section',
tag: 'section',
text: ''
})
expect(compact.html.length).toBeLessThanOrEqual(ANNOTATE_HTML_BUDGET)
expect(compact.html.startsWith('<section>')).toBe(true)
expect(compact.html.endsWith('โ€ฆ')).toBe(true)
})
it('tolerates a snapshot with no markup', () => {
expect(compactIdentity({ css: {}, selector: 'div', tag: 'div', text: '' }).html).toBe('')
})
})
describe('flushAnnotateStack', () => {
it('attaches one composer item per pin and does not invoke send', async () => {
const first = pin()
const second = pin({ id: 'annotate-2', kind: 'area', identity: undefined, note: 'align the chart', number: 2 })
let stack = emptyAnnotateStack()
stack = addAnnotatePin(stack, {
imageDataUrl: first.imageDataUrl,
identity: first.identity,
kind: first.kind,
note: first.note,
pageTitle: first.pageTitle,
pageUrl: first.pageUrl,
rect: first.rect
})
stack = addAnnotatePin(stack, {
imageDataUrl: second.imageDataUrl,
kind: 'area',
note: second.note,
pageTitle: second.pageTitle,
pageUrl: second.pageUrl,
rect: second.rect
})
const attachImage = vi.fn()
const insertText = vi.fn()
const send = vi.fn()
const result = await flushAnnotateStack(stack.pins, { attachImage, insertText, send }, 'http://127.0.0.1:4173/')
expect(result.sent).toBe(false)
expect(result.count).toBe(2)
expect(send).not.toHaveBeenCalled()
expect(attachImage).toHaveBeenCalledTimes(2)
expect(attachImage.mock.calls[0]?.[0]).toBeInstanceOf(File)
expect((attachImage.mock.calls[0]?.[0] as File).name).toBe('Comment_1.png')
expect((attachImage.mock.calls[1]?.[0] as File).name).toBe('Comment_2.png')
expect(insertText).toHaveBeenCalledOnce()
expect(insertText.mock.calls[0]?.[0]).toContain('I left 2 comments')
expect(insertText.mock.calls[0]?.[0]).toContain('Comment 1')
expect(insertText.mock.calls[0]?.[0]).toContain('Comment 2')
})
it('a pin save is stacking, not flushing', () => {
const stacked = packageAnnotateStack([pin(), pin({ id: 'annotate-2', number: 2, note: 'second' })])
expect(stacked).toHaveLength(2)
expect(annotateFlushPrompt(stacked)).toContain('2 comments')
})
})
describe('annotateFlushPrompt batching', () => {
function at(number: number, selector: string): AnnotatePin {
return pin({
id: `annotate-${number}`,
number,
identity: { css: {}, html: '', selector, tag: 'div', text: '' }
})
}
const batch = packageAnnotateStack([
at(1, 'body>main>section.hero>h1'),
at(2, 'body>main>section.hero>p'),
at(3, 'body>main>section.pricing>button'),
at(4, 'body>main>section.pricing>span'),
at(5, 'body>main>section.faq>li')
])
it('heads each region so a long batch is fewer pieces of work than comments', () => {
const prompt = annotateFlushPrompt(batch, 'http://localhost:5173/')
expect(prompt).toContain('Group 1 โ€” `section.hero` (2 comments)')
expect(prompt).toContain('Group 2 โ€” `section.pricing` (2 comments)')
expect(prompt).toContain('Group 3 โ€” `section.faq` (1 comment)')
expect(prompt).toContain('Work them as 3 pieces of work, not 5.')
})
it('warns against the theme split that would put workers in the same files', () => {
const prompt = annotateFlushPrompt(batch)
expect(prompt).toContain('delegate whole groups')
expect(prompt).toContain('never form new groups by theme')
expect(prompt).toContain('Regroup if the code disagrees')
})
it('still lists every comment exactly once', () => {
const prompt = annotateFlushPrompt(batch)
for (const item of batch) {
expect(prompt.split(`Comment ${item.number}\n`)).toHaveLength(2)
}
})
it('leaves a short batch flat โ€” grouping two comments is noise', () => {
const prompt = annotateFlushPrompt(batch.slice(0, 2))
expect(prompt).not.toContain('Group 1')
expect(prompt).not.toContain('pieces of work')
})
it('leaves a batch flat when every comment is in one region', () => {
const prompt = annotateFlushPrompt(
packageAnnotateStack([
at(1, 'body>div.card>h1'),
at(2, 'body>div.card>p'),
at(3, 'body>div.card>a'),
at(4, 'body>div.card>span')
])
)
expect(prompt).not.toContain('Group 1')
})
it('gives dragged areas their own section instead of a guessed region', () => {
const prompt = annotateFlushPrompt(
packageAnnotateStack([
at(1, 'body>main>section.hero>h1'),
at(2, 'body>main>section.pricing>button'),
pin({ id: 'annotate-3', identity: undefined, kind: 'area', number: 3 }),
at(4, 'body>main>section.faq>li')
])
)
expect(prompt).toContain('Unanchored (dragged areas) (1 comment)')
})
})