image.test.ts
1 import { describe, expect, it } from 'vitest'; 2 import type { IPage } from '@jackwener/opencli/types'; 3 import { __test__ } from './image.js'; 4 5 describe('grok image helpers', () => { 6 describe('isOnGrok', () => { 7 const fakePage = (url: string | Error): IPage => 8 ({ evaluate: () => url instanceof Error ? Promise.reject(url) : Promise.resolve(url) }) as unknown as IPage; 9 10 it('returns true for grok.com URLs', async () => { 11 expect(await __test__.isOnGrok(fakePage('https://grok.com/'))).toBe(true); 12 expect(await __test__.isOnGrok(fakePage('https://grok.com/chat/abc123'))).toBe(true); 13 }); 14 15 it('returns true for grok.com subdomains', async () => { 16 expect(await __test__.isOnGrok(fakePage('https://assets.grok.com/foo'))).toBe(true); 17 }); 18 19 it('returns false for non-grok domains', async () => { 20 expect(await __test__.isOnGrok(fakePage('https://fakegrok.com/'))).toBe(false); 21 expect(await __test__.isOnGrok(fakePage('about:blank'))).toBe(false); 22 }); 23 24 it('returns false when evaluate throws (detached tab)', async () => { 25 expect(await __test__.isOnGrok(fakePage(new Error('detached')))).toBe(false); 26 }); 27 }); 28 29 it('normalizes boolean flags', () => { 30 expect(__test__.normalizeBooleanFlag(true)).toBe(true); 31 expect(__test__.normalizeBooleanFlag('true')).toBe(true); 32 expect(__test__.normalizeBooleanFlag('1')).toBe(true); 33 expect(__test__.normalizeBooleanFlag('yes')).toBe(true); 34 expect(__test__.normalizeBooleanFlag('on')).toBe(true); 35 36 expect(__test__.normalizeBooleanFlag(false)).toBe(false); 37 expect(__test__.normalizeBooleanFlag('false')).toBe(false); 38 expect(__test__.normalizeBooleanFlag(undefined)).toBe(false); 39 }); 40 41 it('dedupes images by src', () => { 42 const deduped = __test__.dedupeBySrc([ 43 { src: 'https://a.example/1.jpg', w: 500, h: 500 }, 44 { src: 'https://a.example/1.jpg', w: 500, h: 500 }, 45 { src: 'https://a.example/2.jpg', w: 500, h: 500 }, 46 { src: '', w: 500, h: 500 }, 47 ]); 48 expect(deduped.map(i => i.src)).toEqual([ 49 'https://a.example/1.jpg', 50 'https://a.example/2.jpg', 51 ]); 52 }); 53 54 it('builds a deterministic-ish signature order-independent by src', () => { 55 const sigA = __test__.imagesSignature([ 56 { src: 'https://a.example/1.jpg', w: 1, h: 1 }, 57 { src: 'https://a.example/2.jpg', w: 1, h: 1 }, 58 ]); 59 const sigB = __test__.imagesSignature([ 60 { src: 'https://a.example/2.jpg', w: 1, h: 1 }, 61 { src: 'https://a.example/1.jpg', w: 1, h: 1 }, 62 ]); 63 expect(sigA).toBe(sigB); 64 }); 65 66 it('maps content-type to sensible image extensions', () => { 67 expect(__test__.extFromContentType('image/png')).toBe('png'); 68 expect(__test__.extFromContentType('image/webp')).toBe('webp'); 69 expect(__test__.extFromContentType('image/gif')).toBe('gif'); 70 expect(__test__.extFromContentType('image/jpeg')).toBe('jpg'); 71 expect(__test__.extFromContentType(undefined)).toBe('jpg'); 72 expect(__test__.extFromContentType('')).toBe('jpg'); 73 }); 74 75 it('builds filenames with a stable sha1 slice tied to the src', () => { 76 const a1 = __test__.buildFilename('https://a.example/1.jpg', 'image/jpeg'); 77 const a2 = __test__.buildFilename('https://a.example/1.jpg', 'image/jpeg'); 78 const b1 = __test__.buildFilename('https://a.example/2.jpg', 'image/png'); 79 // Same URL → same 12-char hash slice (timestamps may differ). 80 expect(a1.split('-')[2].split('.')[0]).toBe(a2.split('-')[2].split('.')[0]); 81 expect(a1.split('-')[2].split('.')[0]).not.toBe(b1.split('-')[2].split('.')[0]); 82 expect(a1.endsWith('.jpg')).toBe(true); 83 expect(b1.endsWith('.png')).toBe(true); 84 }); 85 86 it('only accepts image bubbles that appeared after the baseline', () => { 87 const candidate = __test__.pickLatestImageCandidate([ 88 [{ src: 'https://a.example/stale.jpg', w: 512, h: 512 }], 89 [], 90 [{ src: 'https://a.example/fresh.jpg', w: 1024, h: 1024 }], 91 ], 1); 92 93 expect(candidate).toEqual([ 94 { src: 'https://a.example/fresh.jpg', w: 1024, h: 1024 }, 95 ]); 96 }); 97 98 it('does not reuse stale images when no new image bubble appears after baseline', () => { 99 const candidate = __test__.pickLatestImageCandidate([ 100 [{ src: 'https://a.example/stale.jpg', w: 512, h: 512 }], 101 [], 102 [], 103 ], 1); 104 105 expect(candidate).toEqual([]); 106 }); 107 });