mirror of
https://github.com/root-fr/jmap-webmail.git
synced 2026-09-23 00:01:13 +00:00
135 lines
4.4 KiB
TypeScript
135 lines
4.4 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { getMailboxFullPath, buildMailboxTree } from '../utils';
|
|
import { UNIFIED_INBOX_ID } from '../jmap/search-utils';
|
|
import type { Mailbox } from '../jmap/types';
|
|
|
|
const mb = (id: string, name: string, parentId?: string, role?: string): Mailbox => ({
|
|
id,
|
|
name,
|
|
parentId,
|
|
role,
|
|
sortOrder: 0,
|
|
totalEmails: 0,
|
|
unreadEmails: 0,
|
|
totalThreads: 0,
|
|
unreadThreads: 0,
|
|
myRights: {
|
|
mayReadItems: true,
|
|
mayAddItems: true,
|
|
mayRemoveItems: true,
|
|
maySetSeen: true,
|
|
maySetKeywords: true,
|
|
mayCreateChild: true,
|
|
mayRename: true,
|
|
mayDelete: true,
|
|
maySubmit: true,
|
|
},
|
|
isSubscribed: true,
|
|
});
|
|
|
|
describe('getMailboxFullPath', () => {
|
|
it('returns the leaf name for a top-level mailbox', () => {
|
|
const inbox = mb('a', 'Inbox', undefined, 'inbox');
|
|
expect(getMailboxFullPath([inbox], 'a')).toBe('Inbox');
|
|
});
|
|
|
|
it('walks parentId and joins with the hierarchy delimiter', () => {
|
|
const inbox = mb('a', 'Inbox', undefined, 'inbox');
|
|
const projects = mb('b', 'Projects', 'a');
|
|
const foo = mb('c', 'Foo', 'b');
|
|
expect(getMailboxFullPath([inbox, projects, foo], 'c')).toBe('Inbox/Projects/Foo');
|
|
});
|
|
|
|
it('returns the full path regardless of mailbox list order', () => {
|
|
const foo = mb('c', 'Foo', 'b');
|
|
const projects = mb('b', 'Projects', 'a');
|
|
const inbox = mb('a', 'Inbox', undefined, 'inbox');
|
|
expect(getMailboxFullPath([foo, projects, inbox], 'c')).toBe('Inbox/Projects/Foo');
|
|
});
|
|
|
|
it('returns empty string for unknown id', () => {
|
|
expect(getMailboxFullPath([mb('a', 'Inbox')], 'missing')).toBe('');
|
|
});
|
|
|
|
it('stops at an orphaned parent without infinite-looping', () => {
|
|
const orphan = mb('c', 'Foo', 'gone');
|
|
expect(getMailboxFullPath([orphan], 'c')).toBe('Foo');
|
|
});
|
|
|
|
it('does not loop on a self-referential parentId', () => {
|
|
const cyclic = mb('a', 'Loop', 'a');
|
|
expect(getMailboxFullPath([cyclic], 'a')).toBe('Loop');
|
|
});
|
|
});
|
|
|
|
const accountInbox = (
|
|
accountId: string,
|
|
unreadEmails: number,
|
|
overrides: Partial<Mailbox> = {},
|
|
): Mailbox => ({
|
|
...mb(`${accountId}:inbox`, 'Inbox', undefined, 'inbox'),
|
|
accountId,
|
|
accountName: `${accountId}@example.com`,
|
|
unreadEmails,
|
|
totalEmails: unreadEmails,
|
|
...overrides,
|
|
});
|
|
|
|
describe('buildMailboxTree unified inbox node', () => {
|
|
const primaryInbox = accountInbox('acc-primary', 3, { id: 'inbox-1', isShared: false });
|
|
const teamInbox = accountInbox('acc-team', 5, { isShared: true });
|
|
const supportInbox = accountInbox('acc-support', 2, { isShared: true });
|
|
|
|
it('is absent when no unified options are passed', () => {
|
|
const tree = buildMailboxTree([primaryInbox, teamInbox]);
|
|
expect(tree.some((n) => n.id === UNIFIED_INBOX_ID)).toBe(false);
|
|
});
|
|
|
|
it('renders first with the given name and aggregated unread count', () => {
|
|
const tree = buildMailboxTree([primaryInbox, teamInbox, supportInbox], {
|
|
name: 'All Inboxes',
|
|
excludedAccountIds: [],
|
|
});
|
|
expect(tree[0].id).toBe(UNIFIED_INBOX_ID);
|
|
expect(tree[0].name).toBe('All Inboxes');
|
|
expect(tree[0].unreadEmails).toBe(10);
|
|
expect(tree[0].depth).toBe(0);
|
|
expect(tree[0].children).toEqual([]);
|
|
});
|
|
|
|
it('drops excluded accounts from the badge sum', () => {
|
|
const tree = buildMailboxTree([primaryInbox, teamInbox, supportInbox], {
|
|
name: 'All Inboxes',
|
|
excludedAccountIds: ['acc-support'],
|
|
});
|
|
expect(tree[0].id).toBe(UNIFIED_INBOX_ID);
|
|
expect(tree[0].unreadEmails).toBe(8);
|
|
});
|
|
|
|
it('hides itself when fewer than two included accounts expose an inbox', () => {
|
|
const single = buildMailboxTree([primaryInbox], {
|
|
name: 'All Inboxes',
|
|
excludedAccountIds: [],
|
|
});
|
|
expect(single.some((n) => n.id === UNIFIED_INBOX_ID)).toBe(false);
|
|
|
|
const excludedDown = buildMailboxTree([primaryInbox, teamInbox], {
|
|
name: 'All Inboxes',
|
|
excludedAccountIds: ['acc-team'],
|
|
});
|
|
expect(excludedDown.some((n) => n.id === UNIFIED_INBOX_ID)).toBe(false);
|
|
});
|
|
|
|
it('grants read-only rights so rename/drag/drop stay disabled', () => {
|
|
const tree = buildMailboxTree([primaryInbox, teamInbox], {
|
|
name: 'All Inboxes',
|
|
excludedAccountIds: [],
|
|
});
|
|
expect(tree[0].myRights.mayReadItems).toBe(true);
|
|
expect(tree[0].myRights.mayRename).toBe(false);
|
|
expect(tree[0].myRights.mayAddItems).toBe(false);
|
|
expect(tree[0].myRights.mayCreateChild).toBe(false);
|
|
expect(tree[0].myRights.mayDelete).toBe(false);
|
|
});
|
|
});
|