import { TestBed } from '@angular/core/testing'; import { PluginToolResultComponent } from './plugin-tool-result.component'; import { PluginToolView } from '../../utils/plugin-tool-view.util'; describe('readable plugin receipts', () => { const makeView = (result: string): PluginToolView => ({ title: '观察电脑窗口', kind: '辅助观察', auxiliary: true, status: '已执行', args: [], result, rawResult: JSON.stringify({ result, call_id: 'original-call' }, null, 2), facts: [{ key: '窗口', value: 'Demo' }, { key: '截图', value: '1280 × 720' }] }); const mount = async (view: PluginToolView) => { await TestBed.configureTestingModule({ imports: [PluginToolResultComponent] }).compileComponents(); const fixture = TestBed.createComponent(PluginToolResultComponent); fixture.componentRef.setInput('view', view); fixture.detectChanges(); return fixture; }; it('keeps a long control tree and the original payload folded and fully accessible', async () => { const fullText = Array.from({ length: 200 }, (_, i) => `- [${i}] Button "Item ${i}"`).join('\n'); const view = makeView(fullText); const fixture = await mount(view); const root: HTMLElement = fixture.nativeElement; expect(root.querySelector('dl')!.textContent).toContain('1280 × 720'); expect(root.querySelector('.receipt-preview')!.textContent!.length).toBeLessThan(1100); const textDetails = root.querySelector('.receipt-text')!; const rawDetails = root.querySelector('.receipt-raw')!; expect(textDetails.open).toBeFalse(); expect(rawDetails.open).toBeFalse(); textDetails.querySelector('summary')!.click(); expect(textDetails.open).toBeTrue(); expect(textDetails.querySelector('pre')!.textContent).toBe(fullText); expect(rawDetails.querySelector('pre')!.textContent).toBe(view.rawResult); }); it('renders untrusted tool text as text and keeps short results readable', async () => { const text = '\n已观察,未验证业务结果'; const fixture = await mount(makeView(text)); const root: HTMLElement = fixture.nativeElement; expect(root.querySelector('.receipt-preview')!.textContent).toBe(text); expect(root.querySelector('img')).toBeNull(); expect(root.querySelector('.receipt-text')).toBeNull(); }); it('reflects a new receipt without resetting an open raw payload', async () => { const fixture = await mount(makeView('执行中')); const raw = fixture.nativeElement.querySelector('.receipt-raw') as HTMLDetailsElement; raw.querySelector('summary')!.click(); fixture.componentRef.setInput('view', makeView('已返回新的窗口观察')); fixture.detectChanges(); expect(raw.open).toBeTrue(); expect(fixture.nativeElement.querySelector('.receipt-preview').textContent).toBe('已返回新的窗口观察'); expect(raw.querySelector('pre')!.textContent).toContain('已返回新的窗口观察'); }); it('keeps long window labels and unbroken receipt text inside a narrow container', async () => { const view = makeView('long-unbroken-result'.repeat(200)); view.facts[0].value = 'VeryLongWindowTitle'.repeat(30); const fixture = await mount(view); const root = fixture.nativeElement as HTMLElement; root.style.width = '240px'; fixture.detectChanges(); expect(root.clientWidth).toBe(240); expect(root.scrollWidth).toBeLessThanOrEqual(root.clientWidth); const label = root.querySelector('dd')!; expect(label.scrollWidth).toBeLessThanOrEqual(label.clientWidth); }); });