waybetter-for-x/apps/desktop/tests/searchOperators.test.ts
2026-10-01 06:01:13 +07:00

64 lines
3.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applySuggestion, buildSearchUrl, describeQuery, hasOperators, normalizeQuery, suggest, tokenAt } from '../src/core/searchOperators.ts';
import { toUrl } from '../src/shell/searchInput.ts';
import { buildItems } from '../src/shell/paletteModel.ts';
test('operator detection', () => {
for (const q of ['from:@jack hello', 'to:x', '@nasa', 'since:2024-01-01', 'cats filter:media', 'dogs -filter:replies', 'min_faves:100', 'lang:tr', 'list:123', 'url:github.com', '"exact phrase"', 'a OR b'])
assert.ok(hasOperators(q), q);
for (const q of ['hello world', 'from:', 'email me@', 'or maybe'])
assert.ok(!hasOperators(q), q);
});
test('from:@x is normalized and opens Latest with src=typed_query', () => {
assert.equal(normalizeQuery(' from:@jack hi -to:@x '), 'from:jack hi -to:x');
assert.equal(buildSearchUrl('from:@jack hello'), 'https://x.com/search?q=from%3Ajack%20hello&src=typed_query&f=live');
assert.equal(buildSearchUrl('plain words'), 'https://x.com/search?q=plain%20words&src=typed_query');
});
test('address bar input: operators search X, URLs and domains navigate', () => {
assert.equal(toUrl('from:@x cats'), 'https://x.com/search?q=from%3Ax%20cats&src=typed_query&f=live');
assert.equal(toUrl('url:github.com'), 'https://x.com/search?q=url%3Agithub.com&src=typed_query&f=live');
assert.equal(toUrl('github.com'), 'https://github.com');
assert.equal(toUrl('https://x.com/home'), 'https://x.com/home');
assert.equal(toUrl('just words'), 'https://x.com/search?q=just%20words');
});
test('palette: an operator query searches Latest and offers completions for the token', () => {
const context = { tabs: [], profiles: [], activeProfile: 'default' };
const items = buildItems(context, 'from:@jack');
assert.deepEqual(items.at(-1)!.action, { type: 'open', url: 'https://x.com/search?q=from%3Ajack&src=typed_query&f=live' });
const partial = buildItems(context, 'cats fil');
const complete = partial.filter(item => item.action.type === 'complete');
assert.ok(complete.some(item => item.action.type === 'complete' && item.action.text === 'cats filter:media '));
});
test('tokenAt finds the caret token', () => {
assert.deepEqual(tokenAt('cats fro dogs', 8), { start: 5, end: 8, text: 'fro' });
assert.deepEqual(tokenAt('cats ', 5), { start: 5, end: 5, text: '' });
});
test('suggest: partial operators, dates, languages and numbers', () => {
assert.deepEqual(suggest('fil', 3).map(s => s.insert), ['filter:media', 'filter:images', 'filter:videos', 'filter:links', 'filter:replies']);
assert.deepEqual(suggest('-fil', 4).map(s => s.insert), ['-filter:replies']);
const now = new Date(2026, 8, 27);
assert.deepEqual(suggest('since:', 6, now).map(s => s.insert).slice(0, 2), ['since:2026-09-27', 'since:2026-09-20']);
assert.deepEqual(suggest('lang:t', 6).map(s => s.insert), ['lang:tr']);
assert.deepEqual(suggest('min_faves:1', 11).map(s => s.insert), ['min_faves:10', 'min_faves:100', 'min_faves:1000', 'min_faves:10000']);
assert.ok(suggest('', 0).length > 0);
assert.deepEqual(suggest('from:jack', 9), []);
});
test('applySuggestion replaces the token and places the caret', () => {
assert.deepEqual(applySuggestion('cats fil', 8, 'filter:media'), { text: 'cats filter:media ', caret: 18 });
assert.deepEqual(applySuggestion('cats fr', 7, 'from:'), { text: 'cats from:', caret: 10 });
assert.deepEqual(applySuggestion('cats ', 5, '""'), { text: 'cats ""', caret: 6 });
assert.deepEqual(applySuggestion('cats ', 5, 'OR'), { text: 'cats OR ', caret: 8 });
});
test('describeQuery marks operators', () => {
assert.deepEqual(describeQuery('from:@a cats -dogs "big cat"'), [
{ text: 'from:a', operator: true }, { text: 'cats', operator: false }, { text: '-dogs', operator: true }, { text: '"big cat"', operator: true },
]);
});