import { convertSilkeCommandsToString, escapeHtml, filterAndSortCommands, getActiveCommandIndex, getCommandMatchScore, getItemLength, getTextLength, isAttachment, isFile, isImage, isSilkeCommand, isText, SilkeCommand, SilkeCommandTextFieldValue, SilkeCommandType, SilkeFileValue, SilkeImageValue, valueToHtml, } from './utils'; describe('escapeHtml', () => { test('escapes HTML special characters', () => { expect(escapeHtml('')).toBe( '<script>alert("xss")</script>', ); }); test('escapes ampersand', () => { expect(escapeHtml('foo & bar')).toBe('foo & bar'); }); test('escapes single quotes', () => { expect(escapeHtml("it's")).toBe('it's'); }); test('returns empty string for empty input', () => { expect(escapeHtml('')).toBe(''); }); test('returns text unchanged if no special characters', () => { expect(escapeHtml('Hello world 123')).toBe('Hello world 123'); }); }); describe('getActiveCommandIndex', () => { const testValue: SilkeCommandTextFieldValue[] = [ { type: 'text', value: 'Hello ' }, { type: SilkeCommandType.MENTION, value: 'user' }, { type: 'text', value: ' world' }, ]; test('returns 0 for empty array', () => { expect(getActiveCommandIndex(0, [])).toBe(0); }); test('returns 0 for caret at start', () => { expect(getActiveCommandIndex(0, testValue)).toBe(0); }); test('returns correct index for caret in text segment', () => { expect(getActiveCommandIndex(3, testValue)).toBe(0); }); test('returns command index when caret at boundary (start of command)', () => { // Position 6 is at '@', the first char of the command expect(getActiveCommandIndex(6, testValue)).toBe(1); }); test('returns correct index for caret at command', () => { // "Hello " = 6 chars, "@user" = 5 chars (@ + user), caret at 8 is inside @user expect(getActiveCommandIndex(8, testValue)).toBe(1); }); test('returns correct index for caret after command', () => { // "Hello " (6) + "@user" (5) + " " (1) = 12, caret at 13 is in " world" expect(getActiveCommandIndex(13, testValue)).toBe(2); }); test('returns last index for caret at end', () => { // Total: "Hello " (6) + "@user" (5) + " world" (6) = 17 expect(getActiveCommandIndex(17, testValue)).toBe(2); }); test('handles undefined value array', () => { expect(getActiveCommandIndex(0, undefined as unknown as SilkeCommandTextFieldValue[])).toBe(0); }); }); describe('getTextLength', () => { const testValue: SilkeCommandTextFieldValue[] = [ { type: 'text', value: 'Hello ' }, { type: SilkeCommandType.MENTION, value: 'user' }, { type: 'text', value: ' world' }, ]; test('returns 0 for empty array', () => { expect(getTextLength([])).toBe(0); }); test('returns 0 for index 0', () => { expect(getTextLength(testValue, 0)).toBe(0); }); test('returns correct length for text segment', () => { expect(getTextLength(testValue, 1)).toBe(6); // "Hello " }); test('returns correct length including command (with @ symbol)', () => { expect(getTextLength(testValue, 2)).toBe(11); // "Hello " + "@user" }); test('returns total length for full array', () => { expect(getTextLength(testValue)).toBe(17); // "Hello " + "@user" + " world" }); test('handles negative index', () => { expect(getTextLength(testValue, -1)).toBe(0); }); test('handles undefined value array', () => { expect(getTextLength(undefined as unknown as SilkeCommandTextFieldValue[])).toBe(0); }); }); describe('getItemLength', () => { test('returns correct length for text', () => { expect(getItemLength({ type: 'text', value: 'hello' })).toBe(5); }); test('returns correct length for command (value + 1 for symbol)', () => { expect(getItemLength({ type: SilkeCommandType.MENTION, value: 'user' })).toBe(5); }); test('returns 0 for undefined', () => { expect(getItemLength(undefined as unknown as SilkeCommandTextFieldValue)).toBe(0); }); }); describe('valueToHtml', () => { const mockStyles = { action: 'action-class', mention: 'mention-class', partial: 'partial-class', tag: 'tag-class', }; test('returns empty string for empty array', () => { expect(valueToHtml([], mockStyles)).toBe(''); }); test('wraps text in span', () => { const value: SilkeCommandTextFieldValue[] = [{ type: 'text', value: 'hello' }]; expect(valueToHtml(value, mockStyles)).toBe('hello'); }); test('renders mention command with correct classes', () => { const value: SilkeCommandTextFieldValue[] = [{ type: SilkeCommandType.MENTION, value: 'user' }]; const html = valueToHtml(value, mockStyles); expect(html).toContain('tag-class'); expect(html).toContain('mention-class'); expect(html).toContain('@user'); }); test('renders action command with correct classes', () => { const value: SilkeCommandTextFieldValue[] = [{ type: SilkeCommandType.ACTION, value: 'cmd' }]; const html = valueToHtml(value, mockStyles); expect(html).toContain('tag-class'); expect(html).toContain('action-class'); expect(html).toContain('/cmd'); }); test('adds partial class for partial commands', () => { const value: SilkeCommandTextFieldValue[] = [ { partialMatch: true, type: SilkeCommandType.MENTION, value: 'unknown' }, ]; const html = valueToHtml(value, mockStyles); expect(html).toContain('partial-class'); }); test('escapes HTML in text values', () => { const value: SilkeCommandTextFieldValue[] = [ { type: 'text', value: '' }, ]; const html = valueToHtml(value, mockStyles); expect(html).not.toContain('