Files
webmail/lib/__tests__/plugin-slot-props.test.ts
T

64 lines
2.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import type { InstalledPlugin, Permission } from '../plugin-types';
import { slotPropsFor } from '../plugin-sandbox/permissions';
function plugin(perms: Permission[]): InstalledPlugin {
return {
id: 'p1', name: 'P', version: '1.0.0', author: '', description: '', type: 'ui-extension',
permissions: perms, grantedPermissions: perms, entrypoint: 'index.js',
enabled: true, status: 'running', settings: {},
};
}
const EMAIL = { id: 'm1', subject: 'Salary review', text: 'confidential body' };
describe('slot props follow read permissions', () => {
it('withholds the opened message from an email-banner plugin without email:read', () => {
// ui:email-banner only lets a plugin draw a banner. Handing it the body,
// headers and source would make it as good as email:read.
const props = slotPropsFor(plugin(['ui:email-banner']), 'email-banner', { email: EMAIL });
expect(props.email).toBeUndefined();
});
it('passes the message once the plugin also holds email:read', () => {
const props = slotPropsFor(plugin(['ui:email-banner', 'email:read']), 'email-banner', { email: EMAIL });
expect(props.email).toEqual(EMAIL);
});
it.each([
['email-details-section', 'email', 'email:read'],
['email-detail-sidebar', 'email', 'email:read'],
['calendar-event-actions', 'eventData', 'calendar:read'],
['contact-cryptokeys', 'cryptoKeys', 'contacts:read'],
['app-top-banner', 'username', 'account:read'],
['app-top-banner', 'serverUrl', 'account:read'],
] as const)('%s.%s needs %s', (slot, key, perm) => {
const without = slotPropsFor(plugin([]), slot, { [key]: 'value' });
const withPerm = slotPropsFor(plugin([perm]), slot, { [key]: 'value' });
expect(without[key]).toBeUndefined();
expect(withPerm[key]).toBe('value');
});
it('drops props a slot does not declare', () => {
const props = slotPropsFor(plugin(['email:read']), 'email-footer', { email: EMAIL, extra: 1 });
expect(props).toEqual({});
});
it('keeps host callbacks that are part of the slot contract', () => {
const onAttach = () => {};
const props = slotPropsFor(plugin([]), 'composer-attachment-source', { onAttach });
expect(props.onAttach).toBe(onAttach);
});
it('passes a plugin dialog its own props unchanged', () => {
const props = slotPropsFor(plugin([]), 'plugin-dialog', { anything: { nested: true }, onResult: () => {} });
expect(Object.keys(props).sort()).toEqual(['anything', 'onResult']);
});
});