mirror of
https://github.com/priyanshujain/margin-mail.git
synced 2026-10-02 19:17:05 +00:00
The client itself: the sync engine and mirror, the Gmail and IMAP providers, the screener, the three boxes and two piles, compose and send, the sanitiser and tracker stripping, contacts, clips, backup, notifications and the guide. The pipeline that ships it. Three runners now build in parallel and the publish gate wants all four platform keys in latest.json before a release leaves draft. Linux gets two more packages Tauri does not build: a flatpak repackaged from the deb against GNOME 48, and a Nix package that relinks the deb against nixpkgs so it runs as a native Wayland client rather than through Xwayland. CI runs the Rust suite on all three desktops rather than on Linux alone, and rebuilds the flatpak manifest on every push to main. Two things that were only ever exercised on macOS and were wrong everywhere else. The menu bar was built by adjusting the submenus macOS is given, so on a platform whose default menu has no File or View the new File menu landed after Edit, Check for Updates landed nowhere, and the three places and the reading pane had no menu at all. The deb declared libwebkit2gtk-4.1-0 and libgtk-3-0 twice, because tauri.conf.json named the dependencies Tauri already emits. The updater key exists now, so releases can sign their artifacts.
108 lines
4.0 KiB
TypeScript
108 lines
4.0 KiB
TypeScript
// Where an address goes, decided twice: from the domain, and from the servers discovery found.
|
|
//
|
|
// These are the decisions with a right answer. A gmail.com address that ended up on a password
|
|
// panel would be a screen that fails on purpose, and a work domain hosted by Google that was not
|
|
// recognised would be the same failure one lookup later.
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
import type { MailConfig } from "./ipc";
|
|
import { closedName, domainOf, passwordHint, providerName, routeFor, routeOf } from "./providers";
|
|
|
|
function found(imapHost: string, displayName: string | null = null): MailConfig {
|
|
const leg = (host: string) => ({
|
|
host,
|
|
port: 993,
|
|
security: "tls" as const,
|
|
auth: "password" as const,
|
|
username: "[email protected]",
|
|
});
|
|
return { imap: leg(imapHost), smtp: leg(imapHost), source: "ispdb", displayName };
|
|
}
|
|
|
|
describe("routeFor", () => {
|
|
it("sends Google's own domains to the browser without a lookup", () => {
|
|
expect(routeFor("[email protected]")).toBe("google");
|
|
expect(routeFor("[email protected]")).toBe("google");
|
|
});
|
|
|
|
it("recognises Microsoft's consumer domains under any country's ending", () => {
|
|
for (const address of [
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"[email protected]",
|
|
"[email protected]",
|
|
]) {
|
|
expect(routeFor(address), address).toBe("microsoft");
|
|
}
|
|
});
|
|
|
|
it("knows the providers that have no IMAP at all, by name", () => {
|
|
expect(routeFor("[email protected]")).toBe("closed");
|
|
expect(routeFor("[email protected]")).toBe("closed");
|
|
expect(closedName("[email protected]")).toBe("HEY");
|
|
expect(closedName("[email protected]")).toBe("Tuta");
|
|
});
|
|
|
|
it("looks everything else up, including domains that merely contain a provider's name", () => {
|
|
expect(routeFor("[email protected]")).toBe("lookup");
|
|
expect(routeFor("[email protected]")).toBe("lookup");
|
|
expect(routeFor("[email protected]")).toBe("lookup");
|
|
expect(routeFor("[email protected]")).toBe("lookup");
|
|
});
|
|
});
|
|
|
|
describe("routeOf", () => {
|
|
it("knows a custom domain by the servers it was found on", () => {
|
|
expect(routeOf(found("imap.gmail.com"))).toBe("google");
|
|
expect(routeOf(found("imap.googlemail.com"))).toBe("google");
|
|
expect(routeOf(found("outlook.office365.com"))).toBe("microsoft");
|
|
expect(routeOf(found("imap-mail.outlook.com"))).toBe("microsoft");
|
|
});
|
|
|
|
it("leaves everyone else on the password", () => {
|
|
expect(routeOf(found("imap.fastmail.com"))).toBe("password");
|
|
expect(routeOf(found("127.0.0.1"))).toBe("password");
|
|
expect(routeOf(found("mail.gmail.example"))).toBe("password");
|
|
});
|
|
});
|
|
|
|
describe("providerName", () => {
|
|
it("is what the provider calls itself, or the domain when it said nothing", () => {
|
|
expect(providerName(found("imap.fastmail.com", "Fastmail"), "[email protected]")).toBe(
|
|
"Fastmail",
|
|
);
|
|
expect(providerName(found("mail.northgate.example"), "[email protected]")).toBe(
|
|
"northgate.example",
|
|
);
|
|
expect(providerName(null, "nonsense")).toBe("this address");
|
|
});
|
|
});
|
|
|
|
describe("passwordHint", () => {
|
|
it("names the kind of password a provider wants before it is typed", () => {
|
|
expect(passwordHint(found("imap.fastmail.com"))).toContain("app password");
|
|
expect(passwordHint(found("imap.mail.me.com"))).toContain("app-specific password");
|
|
expect(passwordHint(found("imap.mail.yahoo.com"))).toContain("Yahoo");
|
|
expect(passwordHint(found("imap.aol.com"))).toContain("AOL");
|
|
});
|
|
|
|
it("says a loopback is a bridge with a password of its own", () => {
|
|
expect(passwordHint(found("127.0.0.1"))).toContain("Bridge shows under Mailbox details");
|
|
});
|
|
|
|
it("has nothing to add for a server it knows nothing about", () => {
|
|
expect(passwordHint(found("mail.northgate.example"))).toBeNull();
|
|
expect(passwordHint(null)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("domainOf", () => {
|
|
it("is the lowercased half after the last @", () => {
|
|
expect(domainOf("[email protected] ")).toBe("example.com");
|
|
expect(domainOf("nonsense")).toBe("");
|
|
});
|
|
});
|