import { buildHandoffDoc } from '../src/features/datacollect/prompts/handoffDoc'; import { buildClaudeCommand } from '../src/features/claude/claudeLauncher'; import { parseBenchmarkArgs, BENCHMARK_MODE_PARTS } from '../src/features/datacollect/handlers'; const SCAN = { meta: { title: 'InteriorAI', description: 'Fire your interior designer', lang: 'en' }, design: { colors: { palette: [{ value: 'rgb(10,10,10)', count: 40 }, { value: 'rgb(255,87,51)', count: 12 }], background: 'rgb(255,255,255)', primaryText: 'rgb(10,10,10)', buttonBackground: 'rgb(255,87,51)', buttonText: 'rgb(255,255,255)', }, typography: { primaryFont: 'Inter', h1: { size: '48px', weight: 700 }, body: { size: '16px' } }, layout: { bodyMaxWidth: '1200px', sectionSpacing: '96px', borderRadiusScale: ['8px', '16px'] }, }, interactions: { hoverRules: ['button: background 0.2s ease'] }, microcopy: { headline: 'Fire your interior designer', ctaSamples: ['Get started $29/mo'] }, structure: { h1: 'Fire your interior designer' }, sitemap: { totalPages: 2, crawlDepth: 1, ascii: '/\n└── /pricing', pages: [ { url: '/', role: 'landing', h1: 'Fire your interior designer', primaryContentType: 'hero', imageCount: 6, ctaSamples: ['Get started'] }, { url: '/pricing', role: 'pricing', title: 'Pricing', primaryContentType: 'table', imageCount: 0, formFields: [{ name: 'email', type: 'email' }] }, ], }, }; describe('buildHandoffDoc — 측정치 무손실 인수인계 문서', () => { const doc = buildHandoffDoc(SCAN, 'https://interiorai.com', { crawlDepth: 1, maxPages: 8, userContent: '' }); test('측정된 원시 값이 그대로 들어간다 (rgb·px·카피 원문)', () => { expect(doc).toContain('rgb(255,87,51)(×12)'); expect(doc).toContain('1200px'); expect(doc).toContain('Fire your interior designer'); expect(doc).toContain('Get started $29/mo'); }); test('클라우드 모델용 지시문과 임의 대체 금지 규칙을 포함한다', () => { expect(doc).toContain('클라우드 모델용 지시문'); expect(doc).toContain('측정치를 그대로 사용하라'); expect(doc).toContain('추측하지 말고'); }); test('요구사항 미입력 시 채울 체크리스트를 제공한다', () => { expect(doc).toContain('- [ ] 서비스 이름'); expect(doc).toContain('- [ ] 뺄 기능'); }); test('사용자 변형 지시가 있으면 요구사항 우선 원칙과 함께 삽입된다', () => { const d2 = buildHandoffDoc(SCAN, 'https://interiorai.com', { crawlDepth: 1, maxPages: 8, userContent: '부동산 중개용으로 변형' }); expect(d2).toContain('부동산 중개용으로 변형'); expect(d2).toContain('요구사항이 우선한다'); }); test('페이지 인벤토리 표와 원시 JSON 부록을 포함한다', () => { expect(doc).toContain('| /pricing | pricing |'); expect(doc).toContain('원시 스캔 데이터 부록'); expect(doc).toContain('"primaryFont": "Inter"'); }); test('빈 스캔에도 문서가 깨지지 않는다', () => { const d = buildHandoffDoc({}, 'https://x.com', { crawlDepth: 0, maxPages: 1, userContent: '' }); expect(d).toContain('(스캔 데이터 없음)'); }); }); describe('handoff 모드 파싱·구성', () => { test('handoff 는 bare/mode= 모두 인식되고 합성 파트가 없다', () => { expect(parseBenchmarkArgs('https://a.com handoff').mode).toBe('handoff'); expect(parseBenchmarkArgs('https://a.com mode=handoff').mode).toBe('handoff'); expect(BENCHMARK_MODE_PARTS.handoff).toEqual([]); }); }); describe('buildClaudeCommand — 셸 안전 정규화', () => { test('지시문을 큰따옴표 한 줄 인자로 만든다', () => { expect(buildClaudeCommand('파일 읽고 개발해줘')).toBe('claude "파일 읽고 개발해줘"'); }); test('따옴표·POSIX 확장 문자를 무해화한다', () => { expect(buildClaudeCommand('say "hi" and $HOME `ls`')).toBe(`claude "say 'hi' and HOME ls"`); }); test('빈 지시문이면 대화형 실행', () => { expect(buildClaudeCommand(' ')).toBe('claude'); }); });