Files
webmail/lib/__tests__/plugin-host-vacation-forward.test.ts
T

94 lines
3.9 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { InstalledPlugin } from '../plugin-types';
import type { IJMAPClient } from '../jmap/client-interface';
import { useAuthStore } from '@/stores/auth-store';
import { useVacationStore } from '@/stores/vacation-store';
vi.mock('@/stores/toast-store', () => ({
toast: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() },
}));
import { dispatchApiCall } from '../plugin-sandbox/host-api';
// The out-of-office's forward and the host: vacation.get says it can't tell
// when the filters couldn't be read (instead of "no forward"), and no plugin
// may switch the reply on while it forwards mail or while that isn't known.
function plugin(perms: string[]): InstalledPlugin {
return {
id: 'ooo', name: 'ooo', version: '1.0.0', author: 'test', description: '',
type: 'hook', permissions: perms, grantedPermissions: perms, entrypoint: 'index.js',
enabled: true, status: 'running', settings: {},
};
}
const P = plugin(['vacation:read', 'vacation:write']);
const call = (method: string, ...args: unknown[]) => dispatchApiCall(P, method, args);
const REPLY = { id: 'singleton', isEnabled: false, fromDate: null, toDate: null, subject: 'Away', textBody: 'Back soon', htmlBody: null };
const FORWARDING = [
'/* @metadata:begin',
JSON.stringify({ version: 2, rules: [], includeVacation: true, vacationForward: { enabled: true, to: 'x@example.com', keepCopy: true } }),
'@metadata:end */',
'require ["include"];',
'include :personal :optional "vacation";',
].join('\n');
let script: string | null;
let sieveFails: boolean;
let client: IJMAPClient;
beforeEach(() => {
script = null;
sieveFails = false;
client = {
getAccountId: () => 'acc1',
supportsVacationResponse: () => true,
supportsSieve: () => true,
getSieveAccountId: () => 'acc1',
getSieveCapabilities: () => ({ sieveExtensions: ['include', 'vacation', 'copy'] }),
getVacationResponse: vi.fn(async () => REPLY),
setVacationResponse: vi.fn(async () => {}),
getSieveScripts: vi.fn(async () => {
if (sieveFails) throw new Error('SieveScript/get failed');
return script ? [{ id: 's1', name: 'filters', blobId: 'b1', isActive: true }] : [];
}),
getSieveScriptContent: vi.fn(async () => script ?? ''),
} as unknown as IJMAPClient;
useAuthStore.setState({ client });
useVacationStore.getState().clearState();
});
afterEach(() => {
useAuthStore.setState({ client: null });
});
describe('vacation.get', () => {
it('says whether the out-of-office forwards, and null when the filters can\'t be read', async () => {
expect(await call('vacation.get')).toMatchObject({ forwardActive: false });
script = FORWARDING;
expect(await call('vacation.get')).toMatchObject({ forwardActive: true });
sieveFails = true;
expect(await call('vacation.get')).toMatchObject({ forwardActive: null });
});
});
describe('vacation.set', () => {
it('refuses to switch the reply on while it forwards mail, for any plugin', async () => {
script = FORWARDING;
await expect(call('vacation.set', { isEnabled: true, subject: 'Away' })).rejects.toThrow(/forwards mail to another address/);
expect(client.setVacationResponse).not.toHaveBeenCalled();
});
it('refuses while that can\'t be read, and still lets a plugin switch it off', async () => {
sieveFails = true;
await expect(call('vacation.set', { isEnabled: true, subject: 'Away' })).rejects.toThrow(/can't be read/);
expect(client.setVacationResponse).not.toHaveBeenCalled();
await call('vacation.set', { isEnabled: false });
expect(client.setVacationResponse).toHaveBeenCalledTimes(1);
});
it('switches it on when no forward is set', async () => {
await call('vacation.set', { isEnabled: true, subject: 'Away' });
expect(client.setVacationResponse).toHaveBeenCalledWith(expect.objectContaining({ isEnabled: true }), undefined);
});
});