mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-03 16:16:59 +00:00
99 lines
3.7 KiB
TypeScript
99 lines
3.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
attachSearchSnippets,
|
|
filterHasSnippetTerms,
|
|
hasMarkedSegment,
|
|
parseSearchSnippet,
|
|
snippetFilterFor,
|
|
} from '../search-snippet';
|
|
import type { Email } from '@/lib/jmap/types';
|
|
|
|
describe('parseSearchSnippet', () => {
|
|
it('splits marked and plain segments and decodes the escaped text', () => {
|
|
const segments = parseSearchSnippet('Re: <mark>Invoice</mark> & receipt <2026>');
|
|
expect(segments).toEqual([
|
|
{ text: 'Re: ', marked: false },
|
|
{ text: 'Invoice', marked: true },
|
|
{ text: ' & receipt <2026>', marked: false },
|
|
]);
|
|
expect(hasMarkedSegment(segments)).toBe(true);
|
|
});
|
|
|
|
it('treats any other tag as literal text', () => {
|
|
const segments = parseSearchSnippet('<script>x</script> <mark>hit</mark><b>bold</b>');
|
|
expect(segments).toEqual([
|
|
{ text: '<script>x</script> ', marked: false },
|
|
{ text: 'hit', marked: true },
|
|
{ text: '<b>bold</b>', marked: false },
|
|
]);
|
|
});
|
|
|
|
it('reports no highlight for a snippet without marks', () => {
|
|
const segments = parseSearchSnippet('plain subject');
|
|
expect(segments).toEqual([{ text: 'plain subject', marked: false }]);
|
|
expect(hasMarkedSegment(segments)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('filterHasSnippetTerms', () => {
|
|
it('finds text/subject/body terms at any nesting level', () => {
|
|
expect(filterHasSnippetTerms({ text: 'foo' })).toBe(true);
|
|
expect(filterHasSnippetTerms({ operator: 'AND', conditions: [{ inMailbox: 'x' }, { subject: 'foo' }] })).toBe(true);
|
|
expect(
|
|
filterHasSnippetTerms({
|
|
operator: 'AND',
|
|
conditions: [{ inMailbox: 'x' }, { operator: 'NOT', conditions: [{ body: 'foo' }] }],
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('ignores structural-only filters and empty terms', () => {
|
|
expect(filterHasSnippetTerms(undefined)).toBe(false);
|
|
expect(filterHasSnippetTerms({ inMailbox: 'x', hasKeyword: '$seen' })).toBe(false);
|
|
expect(filterHasSnippetTerms({ from: 'alice' })).toBe(false);
|
|
expect(filterHasSnippetTerms({ text: ' ' })).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('snippetFilterFor', () => {
|
|
it('strips the prefix-search wildcard from highlightable terms only', () => {
|
|
expect(
|
|
snippetFilterFor({
|
|
operator: 'AND',
|
|
conditions: [{ inMailbox: 'x' }, { text: 'inv* rece*' }, { from: 'ali*' }],
|
|
}),
|
|
).toEqual({
|
|
operator: 'AND',
|
|
conditions: [{ inMailbox: 'x' }, { text: 'inv rece' }, { from: 'ali*' }],
|
|
});
|
|
});
|
|
|
|
it('leaves a lone wildcard and quoted phrases alone', () => {
|
|
expect(snippetFilterFor({ text: '*' })).toEqual({ text: '*' });
|
|
expect(snippetFilterFor({ subject: '"exact phrase"' })).toEqual({ subject: '"exact phrase"' });
|
|
});
|
|
});
|
|
|
|
describe('attachSearchSnippets', () => {
|
|
const email = (id: string): Email => ({ id, subject: `s-${id}` }) as Email;
|
|
|
|
it('attaches snippets by email id and drops ones without a highlight', () => {
|
|
const emails = [email('a'), email('b'), email('c')];
|
|
attachSearchSnippets(emails, [
|
|
{ emailId: 'a', subject: '<mark>hit</mark> a', preview: null },
|
|
{ emailId: 'b', subject: 's-b', preview: 'nothing marked' },
|
|
{ emailId: 'c', subject: null, preview: 'body <mark>hit</mark>' },
|
|
]);
|
|
expect(emails[0].searchSnippet).toEqual({ subject: '<mark>hit</mark> a', preview: null });
|
|
expect(emails[1].searchSnippet).toBeUndefined();
|
|
expect(emails[2].searchSnippet).toEqual({ subject: null, preview: 'body <mark>hit</mark>' });
|
|
});
|
|
|
|
it('is a no-op without snippets', () => {
|
|
const emails = [email('a')];
|
|
attachSearchSnippets(emails, undefined);
|
|
attachSearchSnippets(emails, []);
|
|
expect(emails[0].searchSnippet).toBeUndefined();
|
|
});
|
|
});
|