import { describe, expect, it } from 'vitest'; import { attachmentError, linkify, MAX_ATTACHMENT_BYTES } from './feedback'; describe('attachmentError', () => { it('accepts allowed extensions within the size cap', () => { expect(attachmentError('shot.png', 1000)).toBe(''); expect(attachmentError('Photo.JPG', 1000)).toBe(''); // case-insensitive expect(attachmentError('report.pdf', 1000)).toBe(''); expect(attachmentError('my.notes.docx', 1000)).toBe(''); // dotted name expect(attachmentError('logs.7z', MAX_ATTACHMENT_BYTES)).toBe(''); }); it('rejects disallowed or extensionless names', () => { expect(attachmentError('evil.exe', 10)).toBe('type'); expect(attachmentError('x.svg', 10)).toBe('type'); // XSS vector, not allowed expect(attachmentError('x.html', 10)).toBe('type'); expect(attachmentError('README', 10)).toBe('type'); expect(attachmentError('', 10)).toBe('type'); }); it('rejects too-large files', () => { expect(attachmentError('shot.png', MAX_ATTACHMENT_BYTES + 1)).toBe('size'); }); }); describe('linkify', () => { it('returns a single text segment for plain text', () => { expect(linkify('just text')).toEqual([{ text: 'just text' }]); }); it('marks an http(s) URL as a link', () => { expect(linkify('see https://example.com now')).toEqual([ { text: 'see ' }, { text: 'https://example.com', href: 'https://example.com' }, { text: ' now' }, ]); }); it('keeps trailing punctuation out of the link', () => { expect(linkify('go to https://example.com.')).toEqual([ { text: 'go to ' }, { text: 'https://example.com', href: 'https://example.com' }, { text: '.' }, ]); expect(linkify('(see https://example.com/path)')).toEqual([ { text: '(see ' }, { text: 'https://example.com/path', href: 'https://example.com/path' }, { text: ')' }, ]); }); it('handles multiple links', () => { const segs = linkify('a http://x.io b https://y.io'); expect(segs.filter((s) => s.href).map((s) => s.href)).toEqual(['http://x.io', 'https://y.io']); }); it('linkifies whitelisted non-http schemes (ftp, mailto, tel)', () => { expect(linkify('ftp://files.example.com/x')).toContainEqual({ text: 'ftp://files.example.com/x', href: 'ftp://files.example.com/x', }); expect(linkify('write to mailto:help@example.com please')).toContainEqual({ text: 'mailto:help@example.com', href: 'mailto:help@example.com', }); expect(linkify('call tel:+1234')).toContainEqual({ text: 'tel:+1234', href: 'tel:+1234' }); }); it('never linkifies dangerous schemes', () => { const segs = linkify('javascript:alert(1) data:text/html,x vbscript:msgbox'); expect(segs.some((s) => s.href)).toBe(false); }); it('does not match a scheme inside a word (hotel:, email:)', () => { expect(linkify('the hotel:foo and email:bar').some((s) => s.href)).toBe(false); }); });