Download apps/desktop/src/lib/preview-annotate/pack.test.ts from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 9.71 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/desktop/src/lib/preview-annotate/pack.test.ts
- Command line
-
hf download hf://SaylorTwift/hermes-agent/apps/desktop/src/lib/preview-annotate/pack.test.ts
-
curl -L -o pack.test.ts https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/desktop/src/lib/preview-annotate/pack.test.ts
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)') | |
| }) | |
| }) | |