Takes the flat module count from 66 to 42, continuing what admin/ and
calendar/ started.
lib/mailbox/ archiveDate, emptyFolder, folderMove, labelTree,
mailboxName, mailboxRoute
lib/sieve/ sieve, sieveApply, sieveFolders
lib/input/ keyboard, swipe, touch, listSelection, dropUpload
lib/notify/ notify, webpush, webpushEnable
lib/sw/ swCache, swFacts, staleBuild
lib/text/ html, markdown, text, emlName
FOUR THINGS THE FILENAMES GET WRONG, each checked by reading the file
rather than trusting what it is called:
- appFolder is not a mailbox. It is the `ihasmail` folder in JMAP
*Files*, where the client keeps signature images and synced settings.
It stays flat.
- format holds no formatting of text. It re-exports the date and clock
formatters, so it belongs with dates/datetime, not with text/.
- preview is the file viewer deciding what it can show without
downloading, and source is where to point someone asking for this
instance's AGPL source. Neither is about text.
- notify is not Web Push. It is the tab title, the favicon badge and
the new-mail sound -- in-app notification, which is why it sits with
webpush rather than under sw/ with the service worker's own concerns.
threadScroll stays flat too: it decides where a conversation opens, which
is view state rather than a gesture, and input/ is honest only if
everything in it interprets something the reader did.
No behavior change. Almost every reference was on the @/ alias; eight
relative imports in files that did not move, or that moved away from a
sibling, needed rewriting by hand.
69 lines
3.0 KiB
TypeScript
69 lines
3.0 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { isLocalizedName, mailboxDisplayName, mailboxDisplayPath } from "@/lib/mailbox/mailboxName";
|
|
import { setCatalog, type Catalog } from "@/lib/i18n";
|
|
import type { Mailbox } from "@/jmap/types";
|
|
|
|
/**
|
|
* Stalwart names the standard folders once, at account creation, and never
|
|
* renames them — so a German reader on an English-provisioned account would
|
|
* otherwise see "Deleted Items" in an otherwise German app. The role is what
|
|
* lets ihasmail say "Papierkorb" without writing anything to the server.
|
|
*/
|
|
const de: Catalog = {
|
|
strings: { Inbox: "Posteingang", "Deleted Items": "Papierkorb", Drafts: "Entwürfe" },
|
|
plurals: {},
|
|
};
|
|
const mb = (id: string, name: string, role: string | null = null, parentId: string | null = null) =>
|
|
({ id, name, role, parentId } as unknown as Mailbox);
|
|
|
|
afterEach(() => setCatalog("en", { strings: {}, plurals: {} }));
|
|
|
|
describe("mailboxDisplayName", () => {
|
|
it("is the server's name until a catalog says otherwise", () => {
|
|
expect(mailboxDisplayName(mb("1", "Deleted Items", "trash"))).toBe("Deleted Items");
|
|
});
|
|
|
|
it("follows the interface language for a folder carrying a role", () => {
|
|
setCatalog("de", de);
|
|
expect(mailboxDisplayName(mb("1", "Deleted Items", "trash"))).toBe("Papierkorb");
|
|
expect(mailboxDisplayName(mb("2", "Inbox", "inbox"))).toBe("Posteingang");
|
|
});
|
|
|
|
it("leaves a folder somebody made alone", () => {
|
|
// "Newsletters" is their word. Translating it would name a folder they
|
|
// never created, and it would not match what any other client shows.
|
|
setCatalog("de", de);
|
|
expect(mailboxDisplayName(mb("3", "Newsletters"))).toBe("Newsletters");
|
|
expect(mailboxDisplayName(mb("4", "Work", "subscribed"))).toBe("Work");
|
|
});
|
|
|
|
it("survives a missing mailbox rather than printing undefined", () => {
|
|
expect(mailboxDisplayName(null)).toBe("");
|
|
expect(mailboxDisplayName(undefined)).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("isLocalizedName", () => {
|
|
it("tells an editor when the name on screen is not the server's", () => {
|
|
// A rename box prefilled with "Papierkorb" would rename the folder to that
|
|
// the moment somebody pressed Save — a real change made by accident.
|
|
expect(isLocalizedName(mb("1", "Deleted Items", "trash"))).toBe(true);
|
|
expect(isLocalizedName(mb("2", "Newsletters"))).toBe(false);
|
|
expect(isLocalizedName(mb("3", "Work", "subscribed"))).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("mailboxDisplayPath", () => {
|
|
it("localizes each part that has a role and leaves the rest", () => {
|
|
setCatalog("de", de);
|
|
const all = { a: mb("a", "Inbox", "inbox"), b: mb("b", "Projects", null, "a") };
|
|
expect(mailboxDisplayPath(all.b!, all)).toBe("Posteingang / Projects");
|
|
});
|
|
|
|
it("stops rather than looping on a parent cycle", () => {
|
|
// A malformed tree from the server must not hang the folder picker.
|
|
const all: Record<string, Mailbox> = { a: mb("a", "A", null, "b"), b: mb("b", "B", null, "a") };
|
|
expect(mailboxDisplayPath(all.a!, all)).toBe("B / A");
|
|
});
|
|
});
|