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('