Files
webmail/lib/__tests__/settings-search.test.ts
T

158 lines
5.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import en from '@/locales/en/common.json';
import {
type SettingsSearchTab,
type SubResult,
collectSubResults,
flattenStrings,
getByPath,
tabSearchPaths,
withAppName,
} from '../settings-search';
function subResultsFor(tab: SettingsSearchTab): SubResult[] {
const list: SubResult[] = [];
for (const path of tabSearchPaths[tab]) {
collectSubResults(getByPath(en, path), list);
}
return list;
}
function haystackFor(tab: SettingsSearchTab): string {
const strings: string[] = [];
for (const path of tabSearchPaths[tab]) {
flattenStrings(getByPath(en, path), strings);
}
return strings.join(' ').toLowerCase();
}
function labelsFor(tab: SettingsSearchTab): string[] {
return subResultsFor(tab).map((r) => r.label);
}
describe('collectSubResults', () => {
it('emits flat `key` / `key_desc` string pairs as a labelled sub-result', () => {
const list: SubResult[] = [];
collectSubResults(
{
title: 'Calendar settings',
free_scroll: 'Free scrolling',
free_scroll_desc: 'Scroll continuously',
hover_preview_off: 'Disabled',
},
list
);
expect(list).toEqual([
{ label: 'Calendar settings', description: undefined },
{ label: 'Free scrolling', description: 'Scroll continuously' },
]);
});
it('still emits label/description objects and *_label keys', () => {
const list: SubResult[] = [];
collectSubResults(
{
name_label: 'Name',
nested: { label: 'Theme', description: 'Pick one', dark: 'Dark' },
},
list
);
expect(list).toEqual([
{ label: 'Name' },
{ label: 'Theme', description: 'Pick one' },
]);
});
});
describe('settings search index (English messages)', () => {
it('every tab path resolves to a translation subtree', () => {
const missing: string[] = [];
for (const paths of Object.values(tabSearchPaths)) {
for (const path of paths) {
if (getByPath(en, path) === undefined) missing.push(path);
}
}
expect(missing).toEqual([]);
});
it('lists the calendar toggles as sub-results', () => {
const labels = labelsFor('calendar');
expect(labels).toContain('Free scrolling');
expect(labels).toContain('Show week numbers');
expect(labels).toContain('Organize invitations as');
expect(labels).toContain('Parse email invitations');
expect(labelsFor('notifications')).toContain('Unread Count on Tab and App Icon');
expect(labelsFor('notifications')).not.toContain('Parse email invitations');
expect(labels).toContain('Contact birthday calendar');
// Option values are not settings of their own.
expect(labels).not.toContain('Disabled');
});
it('finds the newer mail settings on their tabs', () => {
expect(labelsFor('message_list')).toContain('Clear search when switching folders');
expect(labelsFor('message_list')).toContain('Swipe right action (mobile)');
expect(labelsFor('message_list')).toContain('Message list order');
expect(labelsFor('reading')).toContain('Trusted Senders');
expect(labelsFor('reading')).toContain('Email (.eml) filename');
expect(labelsFor('reading')).not.toContain('Delete Action');
expect(labelsFor('composing')).toContain('Undo send / send delay');
expect(labelsFor('composing')).toContain('Request read receipts by default');
expect(labelsFor('composing')).toContain('Email Templates');
expect(labelsFor('composing')).toContain('Quick Reply');
expect(labelsFor('composing')).not.toContain('Sub-Address Delimiter');
expect(labelsFor('appearance')).toContain('Pro Interface (Experimental)');
expect(labelsFor('account')).toContain('Tell accounts apart');
expect(labelsFor('account')).toContain('Your Identities');
expect(labelsFor('appearance')).toContain('Themes');
expect(labelsFor('general')).toContain('Time zone');
expect(labelsFor('general')).toContain('Email links');
expect(labelsFor('organize')).toContain('Nested Tags');
expect(haystackFor('organize')).toContain('standard folder roles');
});
it('matches the calendar tab for a free-scroll query', () => {
expect(haystackFor('calendar')).toContain('free scrolling');
expect(haystackFor('general')).toContain('time zone');
});
});
describe('{appName} in the index (#1314)', () => {
const index = (appName: string) => {
const list: SubResult[] = [];
const strings: string[] = [];
for (const path of tabSearchPaths.general) {
const node = withAppName(getByPath(en, path), appName);
collectSubResults(node, list);
flattenStrings(node, strings);
}
return { list, haystack: strings.join(' ').toLowerCase() };
};
it('never keeps the raw placeholder', () => {
for (const tab of Object.keys(tabSearchPaths) as SettingsSearchTab[]) {
for (const path of tabSearchPaths[tab]) {
expect(JSON.stringify(withAppName(getByPath(en, path), 'Bulwark')) ?? '').not.toContain('{appName}');
}
}
});
it('finds Default apps by "bulwark" on a stock install', () => {
const { list, haystack } = index('Bulwark');
expect(haystack).toContain('bulwark');
expect(list.some((r) => r.description?.includes('Bulwark'))).toBe(true);
});
it('finds Default apps by the configured name', () => {
const { list, haystack } = index('Rath Mail');
expect(haystack).toContain('rath mail');
const links: string[] = [];
flattenStrings(withAppName(getByPath(en, 'protocol_handlers'), 'Rath Mail'), links);
expect(links.join(' ').toLowerCase()).not.toContain('bulwark');
expect(list.some((r) => r.description?.includes('Rath Mail'))).toBe(true);
});
it('leaves other values alone', () => {
expect(withAppName({ a: 'x {appName} y', b: ['{appName}'], c: 3 }, 'N')).toEqual({ a: 'x N y', b: ['N'], c: 3 });
});
});