const assert = require('assert'); const C = require('./clip.js'); // --- detectKind: every branch, precedence-correct --- assert.strictEqual(C.detectKind('https://example.com/page?x=1').kind, 'url'); assert.strictEqual(C.detectKind('#ff8800').kind, 'color'); assert.strictEqual(C.detectKind('#f80').kind, 'color'); // shorthand still a color assert.strictEqual(C.detectKind('/tmp/screenshot-12.png').kind, 'image'); // image beats path assert.strictEqual(C.detectKind('/home/u/file').kind, 'path'); // path, no image ext assert.strictEqual(C.detectKind('just some prose here').kind, 'text'); // icons match the detected kind assert.strictEqual(C.detectKind('#f80').icon, '\uF043'); assert.strictEqual(C.detectKind('https://x.io').icon, '\uF0C1'); assert.strictEqual(C.detectKind('/tmp/a.png').icon, '\uF03E'); // --- parseColor: shorthand expansion + alpha drop + rejection --- assert.strictEqual(C.parseColor('#f80'), '#ff8800'); // #rgb -> #rrggbb assert.strictEqual(C.parseColor('#ff8800'), '#ff8800'); // #rrggbb passthrough assert.strictEqual(C.parseColor('#f808'), '#ff8800'); // #rgba -> drop alpha assert.strictEqual(C.parseColor('#ff8800cc'), '#ff8800');// #rrggbbaa -> drop alpha assert.strictEqual(C.parseColor('rgb(255, 136, 0)'), '#ff8800'); assert.strictEqual(C.parseColor('rgba(255,136,0,0.5)'), '#ff8800'); assert.strictEqual(C.parseColor('#f808'), '#ff8800'); // (4-digit body is #rgba, valid) assert.strictEqual(C.parseColor('#ff888'), null); // 5-digit body -> invalid assert.strictEqual(C.parseColor('#fffffff'), null); // length 7 -> invalid assert.strictEqual(C.parseColor('hello'), null); assert.strictEqual(C.parseColor('orange'), null); // --- isImagePath --- assert.strictEqual(C.isImagePath('/tmp/x.png'), true); assert.strictEqual(C.isImagePath('/home/u/pic.JPEG'), true); assert.strictEqual(C.isImagePath('/home/u/notes.txt'), false); assert.strictEqual(C.isImagePath('not a path.png'), false); // has whitespace // --- sortRows: pinned floats above a NEWER unpinned; newest-first within a group --- const unsorted = [ { id: 'a', pinned: false, createdAt: 100 }, { id: 'b', pinned: true, createdAt: 50 }, // pinned but OLD { id: 'c', pinned: false, createdAt: 300 }, // newest unpinned { id: 'd', pinned: true, createdAt: 70 } // pinned, newer than b ]; const sorted = C.sortRows(unsorted); assert.deepStrictEqual(sorted.map(r => r.id), ['d', 'b', 'c', 'a']); // pinned ('d','b') precede the newer unpinned 'c'; within each group newest first assert.strictEqual(sorted[0].id, 'd'); assert.strictEqual(sorted[1].id, 'b'); assert.strictEqual(C.sortRows(unsorted).length, 4); // does not drop rows // --- filterRows: substring on title+body, ordered by sortRows afterwards --- const pool = [ { id: 'k1', title: 'hello world', body: '', pinned: false, createdAt: 10 }, { id: 'k2', title: 'unrelated', body: 'say hello', pinned: false, createdAt: 40 }, // matches via body { id: 'k3', title: 'HELLO again', body: '', pinned: false, createdAt: 20 }, // case-insensitive { id: 'k4', title: 'nope', body: 'nothing', pinned: false, createdAt: 99 } ]; const hits = C.filterRows(pool, 'hello'); assert.deepStrictEqual(hits.map(r => r.id), ['k2', 'k3', 'k1']); // newest-first, k4 excluded assert.strictEqual(C.filterRows(pool, '').length, 4); // empty query = all // --- ageLabel: bucket boundaries --- const now = 1000000000000; assert.strictEqual(C.ageLabel(now, now), 'just now'); // 0s assert.strictEqual(C.ageLabel(now - 90 * 1000, now), '1m'); // 90s -> 1m assert.strictEqual(C.ageLabel(now - 2 * 3600 * 1000, now), '2h'); // 2h assert.strictEqual(C.ageLabel(now - 3 * 86400 * 1000, now), '3d');// 3d // --- isReadableText: rejects a binary string, accepts prose --- let binary = ''; for (let i = 0; i < 64; i++) binary += String.fromCharCode(i % 7); // control bytes 0..6 assert.strictEqual(C.isReadableText(binary), false); assert.strictEqual(C.isReadableText('a normal clipboard line'), true); assert.strictEqual(C.isReadableText(''), false); // --- cloneRow: launcher-compatible shape + enriched view fields --- const cr = C.cloneRow({ id: 'x', body: '#f80', pinned: true, createdAt: now }, now); ['id', 'title', 'body', 'pinned', 'provider', 'route'].forEach(function (k) { assert.ok(cr.hasOwnProperty(k), 'cloneRow missing launcher key: ' + k); }); assert.strictEqual(cr.provider, 'clipboard'); assert.strictEqual(cr.route, 'clipboard'); assert.strictEqual(cr.kind, 'color'); assert.strictEqual(cr.swatch, '#ff8800'); assert.strictEqual(cr.isImage, false); assert.strictEqual(cr.imagePath, null); assert.strictEqual(cr.age, 'just now'); // image row: title forced to "Image", imagePath populated, swatch null const ci = C.cloneRow({ id: 'y', body: '/tmp/a.png', createdAt: now }, now); assert.strictEqual(ci.title, 'Image'); assert.strictEqual(ci.isImage, true); assert.strictEqual(ci.imagePath, '/tmp/a.png'); assert.strictEqual(ci.swatch, null); // --- persistedRow: durable storage shape --- const pr = C.persistedRow({ id: 'z', title: 't', body: 'b', pinned: true, createdAt: 5 }); assert.deepStrictEqual(pr, { id: 'z', title: 't', body: 'b', pinned: true, createdAt: 5 }); console.log('clip.test.js: ALL PASS');