Files
markdown-converter/tests/inline-assist.test.js
T
Amit Haridas cb0b2eac3d feat(ai-assist): pure prompt builder + result applier
buildAssistPrompt(action, selection) returns the {system, messages}
payload for rewrite/shorten/expand. Enforces 8KB selection cap and
typed errors with codes (unknown_action, bad_selection,
empty_selection, selection_too_large) so the IPC layer can map them
to user-friendly messages.

applyAssistResult(original, newText) trims the response and rejects
empty/unchanged payloads so a no-op streaming chunk doesn't churn
the editor.

15 new tests cover action coverage, prompt content, all four error
codes, cap-edge (exactly 8KB OK, 8KB+1 rejected), and trim semantics.

Amit Haridas
2026-09-30 20:01:54 +05:30

129 lines
4.0 KiB
JavaScript

/**
* @jest-environment node
*
* Inline AI assist pure module.
*/
const {
buildAssistPrompt,
applyAssistResult,
ACTIONS,
ACTIONS_LIST,
MAX_SELECTION_CHARS,
} = require('../src/ai-assist/inline-assist');
describe('ACTIONS', () => {
test('the three actions are exposed', () => {
expect(ACTIONS_LIST).toEqual(['rewrite', 'shorten', 'expand']);
expect(ACTIONS.rewrite.label).toBe('Rewrite');
expect(ACTIONS.shorten.label).toBe('Shorten');
expect(ACTIONS.expand.label).toBe('Expand');
});
test('every action has a non-empty system prompt', () => {
for (const a of ACTIONS_LIST) {
expect(typeof ACTIONS[a].systemPrompt).toBe('string');
expect(ACTIONS[a].systemPrompt.length).toBeGreaterThan(20);
}
});
});
describe('buildAssistPrompt', () => {
test('returns {system, messages} for each action', () => {
for (const action of ACTIONS_LIST) {
const out = buildAssistPrompt(action, 'hello world');
expect(out.system).toBe(ACTIONS[action].systemPrompt);
expect(out.messages).toHaveLength(1);
expect(out.messages[0].role).toBe('user');
expect(out.messages[0].content).toBe('hello world');
}
});
test('preserves the exact selection content', () => {
const selection = 'A\nmultiline\nselection with unicode éàü ✓ and trailing spaces ';
const out = buildAssistPrompt('rewrite', selection);
expect(out.messages[0].content).toBe(selection);
});
test('throws on unknown action with unknown_action code', () => {
try {
buildAssistPrompt('polish', 'hi');
throw new Error('expected to throw');
} catch (err) {
expect(err.code).toBe('unknown_action');
}
});
test('throws on non-string selection with bad_selection code', () => {
try {
buildAssistPrompt('rewrite', null);
throw new Error('expected to throw');
} catch (err) {
expect(err.code).toBe('bad_selection');
}
try {
buildAssistPrompt('rewrite', 42);
throw new Error('expected to throw');
} catch (err) {
expect(err.code).toBe('bad_selection');
}
});
test('throws on empty selection with empty_selection code', () => {
try {
buildAssistPrompt('rewrite', '');
throw new Error('expected to throw');
} catch (err) {
expect(err.code).toBe('empty_selection');
}
});
test('throws on oversized selection with selection_too_large code', () => {
const huge = 'a'.repeat(MAX_SELECTION_CHARS + 1);
try {
buildAssistPrompt('rewrite', huge);
throw new Error('expected to throw');
} catch (err) {
expect(err.code).toBe('selection_too_large');
}
});
test('accepts a selection at exactly the cap', () => {
const exact = 'a'.repeat(MAX_SELECTION_CHARS);
expect(() => buildAssistPrompt('rewrite', exact)).not.toThrow();
});
test('MAX_SELECTION_CHARS is 8KB', () => {
expect(MAX_SELECTION_CHARS).toBe(8 * 1024);
});
});
describe('applyAssistResult', () => {
test('returns the trimmed new text when different', () => {
expect(applyAssistResult('hello', 'HELLO')).toBe('HELLO');
expect(applyAssistResult('hello', ' goodbye ')).toBe('goodbye');
});
test('returns null when newText is unchanged from original (after trim)', () => {
expect(applyAssistResult('hello', 'hello')).toBeNull();
expect(applyAssistResult('hello', ' hello ')).toBeNull();
});
test('returns null when newText is empty or whitespace-only', () => {
expect(applyAssistResult('hello', '')).toBeNull();
expect(applyAssistResult('hello', ' ')).toBeNull();
expect(applyAssistResult('hello', '\n\t ')).toBeNull();
});
test('returns null when newText is not a string', () => {
expect(applyAssistResult('hello', null)).toBeNull();
expect(applyAssistResult('hello', undefined)).toBeNull();
expect(applyAssistResult('hello', 42)).toBeNull();
});
test('trims surrounding whitespace before comparing/applying', () => {
// Even if the LLM adds stray newlines, we strip them before writing
expect(applyAssistResult('x', '\n\nresult\n\n')).toBe('result');
});
});