import { beforeEach, describe, expect, it, vi } from "vitest"; import { useCompose } from "@/store/compose"; import { useMail } from "@/store/mail"; import { client } from "@/jmap/client"; import type { SharedContent } from "@/lib/shareTarget"; /** * What a share becomes once it reaches the composer. * * The signature is the part worth a test. `open()` only fits one when it is * given no body at all, so the obvious implementation -- pass the shared text * straight to `open()` -- silently drops the signature from every message that * started as a share, and nothing about the draft looks wrong. */ const IDENTITY = { id: "i1", name: "John", email: "john@example.org", replyTo: null, htmlSignature: "

--
John

", textSignature: "-- \nJohn", }; function share(over: Partial = {}): SharedContent { return { title: "", text: "", url: "", files: [], ...over }; } beforeEach(() => { useCompose.setState({ drafts: [], activeKey: null, pendingSends: {} }); useMail.setState({ accountId: "a1", identities: [IDENTITY] as never }); // addFiles uploads as it goes; nothing here is testing the upload, and a // real one would reach for the network. vi.spyOn(client, "upload").mockResolvedValue({ blobId: "b1", type: "image/png", size: 6 } as never); }); const draftFor = (key: string) => useCompose.getState().drafts.find((d) => d.key === key)!; describe("opening a share as a draft", () => { it("makes the shared title the subject and addresses nothing", () => { // A share says what to send, never who to. Anything else would be putting // a recipient in a field the sharer never filled in. const d = draftFor(useCompose.getState().openFromShare(share({ title: "Holiday plans" }))); expect(d.subject).toBe("Holiday plans"); expect(d.to).toEqual([]); expect(d.cc).toEqual([]); }); it("puts the shared text above the signature, not instead of it", () => { const d = draftFor(useCompose.getState().openFromShare(share({ text: "Look at this" }))); expect(d.text).toContain("Look at this"); expect(d.text).toContain("John"); expect(d.html).toContain("Look at this"); expect(d.html).toContain("-- "); // Above, not below: the reply goes where the caret lands. expect(d.html.indexOf("Look at this")).toBeLessThan(d.html.indexOf("-- ")); }); it("carries a shared link into the body", () => { const d = draftFor(useCompose.getState().openFromShare(share({ text: "worth reading", url: "https://example.com/a" }))); expect(d.text).toContain("https://example.com/a"); }); it("keeps the signature when a share carried nothing but files", () => { const key = useCompose.getState().openFromShare(share({ files: [new File(["pixels"], "beach.png", { type: "image/png" })] })); const d = draftFor(key); expect(d.html).toContain("John"); expect(d.attachments.map((a) => [a.name, a.type])).toEqual([["beach.png", "image/png"]]); }); it("attaches every shared file, and starts each one uploading", () => { const files = [ new File(["a"], "one.png", { type: "image/png" }), new File(["b"], "two.pdf", { type: "application/pdf" }), ]; const d = draftFor(useCompose.getState().openFromShare(share({ title: "Two things", files }))); expect(d.attachments).toHaveLength(2); expect(d.attachments.every((a) => a.error === null)).toBe(true); expect(client.upload).toHaveBeenCalledTimes(2); }); it("opens a plain draft for a share that carried only a subject", () => { const d = draftFor(useCompose.getState().openFromShare(share({ title: "Just this" }))); expect(d.subject).toBe("Just this"); expect(d.attachments).toEqual([]); }); });