Files
ihasmail-inbuxa/web/src/lib/__tests__/identityVisibility.test.ts
T
jcoffey-dev 133036a6c5 Hide identities from the compose picker
An account using a unique address per service, on a server with an alias
domain, ends up with every local part twice over and a From picker
nobody can use -- while only ever sending from a handful (#73).

Identities can now be hidden from that picker, from Identities &
signatures. Hiding is presentation only: the identity still exists,
still receives, and stays listed and editable, the way an unsubscribed
folder is still a folder. That framing is mbunkus's own, and it is the
right one -- this is a UI preference, not a change to the account.

Three things it refuses to do, because a sender picker with nothing
usable in it is worse than a cluttered one:

  - it will not hide the identity a draft is already using, which would
    leave the select with no matching option and move the From line
    under the writer
  - it will not hide the default, which is what a new draft starts on;
    the button is disabled there and says why
  - if every identity is somehow hidden -- reachable only through
    settings sync, since the UI will not do it -- they are all offered
    again

The setting syncs, so the picker looks the same on every device, which
follows from DEVICE_KEYS being a list of exceptions rather than a list
of what travels.

Verified against the mock with four identities and one hidden: the
picker offers the other three, the hidden address is gone from
composing, the default's hide button is disabled, and the row says the
identity still receives.
2026-08-26 15:36:35 -07:00

61 lines
2.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { isAlwaysVisible, visibleIdentities } from "@/lib/identityVisibility";
/**
* Issue #73: a unique address per service, on a server with an alias domain,
* gives every local part twice and a compose picker nobody can use — while only
* a handful are ever sent from.
*
* The interesting cases are not the hiding. They are the three refusals, all of
* which exist because a sender picker with nothing usable in it is worse than a
* cluttered one.
*/
const ids = (n: number) => Array.from({ length: n }, (_, i) => ({ id: `i${i + 1}`, email: `a${i + 1}@example.com` }));
describe("hiding identities from the picker", () => {
it("removes the hidden ones", () => {
expect(visibleIdentities(ids(4), ["i2", "i4"]).map((i) => i.id)).toEqual(["i1", "i3"]);
});
it("changes nothing when none are hidden", () => {
const all = ids(3);
expect(visibleIdentities(all, [])).toBe(all);
});
});
describe("what it refuses to hide", () => {
it("keeps the identity the draft is already using", () => {
// Otherwise the select has no matching option and the From line moves
// under the writer.
expect(visibleIdentities(ids(3), ["i2"], ["i2"]).map((i) => i.id)).toEqual(["i1", "i2", "i3"]);
});
it("keeps the default, which a new draft starts on", () => {
expect(visibleIdentities(ids(3), ["i1", "i3"], [null, "i1"]).map((i) => i.id)).toEqual(["i1", "i2"]);
});
it("shows everything rather than nothing when all are hidden", () => {
const all = ids(3);
expect(visibleIdentities(all, ["i1", "i2", "i3"]).map((i) => i.id)).toEqual(["i1", "i2", "i3"]);
});
it("ignores an id for an identity that no longer exists", () => {
// A deleted identity leaves its id behind in the setting; it must not
// silently hide anything else or empty the list.
expect(visibleIdentities(ids(2), ["gone"]).map((i) => i.id)).toEqual(["i1", "i2"]);
});
it("tolerates nulls among the ids to keep", () => {
expect(visibleIdentities(ids(2), ["i1"], [null, undefined]).map((i) => i.id)).toEqual(["i2"]);
});
});
describe("what the settings row may offer", () => {
it("refuses to offer hiding for an always-visible identity", () => {
expect(isAlwaysVisible("i1", ["i1"])).toBe(true);
expect(isAlwaysVisible("i2", ["i1"])).toBe(false);
expect(isAlwaysVisible("i2", [null, undefined])).toBe(false);
});
});