196 lines · 6.3 KB · TypeScript
RawDownload
import { describe, expect, it, vi } from "@effect/vitest";import {  PROVIDER_SEND_TURN_MAX_ATTACHMENTS,  PROVIDER_SEND_TURN_MAX_IMAGE_BYTES,} from "@t3tools/contracts";import type { ResolvedSharePayload, SharePayload } from "expo-sharing"; import { buildIncomingShareDraft, hasIncomingShareContent } from "./incoming-share-model"; describe("incoming native shares", () => {  it("converts shared text, URLs, and images into a durable composer draft", async () => {    const image: SharePayload = {      shareType: "image",      value: "file:///shared/Screenshot.png",      mimeType: "image/png",    };    const payloads: SharePayload[] = [      { shareType: "text", value: "Please explain this error" },      { shareType: "url", value: "https://example.com/issue/1" },      { shareType: "text", value: "Please explain this error" },      image,    ];    const resolvedImage: ResolvedSharePayload = {      ...image,      contentUri: image.value,      contentType: "image",      contentMimeType: "image/png",      contentSize: 3,      originalName: "Screenshot.png",    };    const removeOwnedFile = vi.fn(() => Promise.resolve());     const result = await buildIncomingShareDraft({      id: "share-1",      createdAt: "2026-07-15T10:00:00.000Z",      payloads,      resolvedPayloads: [resolvedImage],      fileReader: {        readBase64: async () => "YWJj",        removeOwnedFile,      },    });     expect(result).toEqual({      schemaVersion: 1,      id: "share-1",      createdAt: "2026-07-15T10:00:00.000Z",      text: "Please explain this error\n\nhttps://example.com/issue/1",      attachments: [        {          id: "share-1:image:3",          type: "image",          name: "Screenshot.png",          mimeType: "image/png",          sizeBytes: 3,          dataUrl: "data:image/png;base64,YWJj",          previewUri: "data:image/png;base64,YWJj",        },      ],      warnings: [],    });    expect(removeOwnedFile).toHaveBeenCalledWith(image.value);    expect(hasIncomingShareContent(result)).toBe(true);  });   it("skips oversized images and releases the temporary native file", async () => {    const image: SharePayload = {      shareType: "image",      value: "file:///shared/huge.png",      mimeType: "image/png",    };    const readBase64 = vi.fn(async () => "unused");    const removeOwnedFile = vi.fn(() => Promise.resolve());     const result = await buildIncomingShareDraft({      id: "share-2",      createdAt: "2026-07-15T10:00:00.000Z",      payloads: [image],      resolvedPayloads: [        {          ...image,          contentUri: image.value,          contentType: "image",          contentMimeType: "image/png",          contentSize: PROVIDER_SEND_TURN_MAX_IMAGE_BYTES + 1,          originalName: "huge.png",        },      ],      fileReader: { readBase64, removeOwnedFile },    });     expect(result.attachments).toEqual([]);    expect(result.warnings).toEqual(["'huge.png' exceeds the 10 MB attachment limit."]);    expect(readBase64).not.toHaveBeenCalled();    expect(removeOwnedFile).toHaveBeenCalledWith(image.value);    expect(hasIncomingShareContent(result)).toBe(false);  });   it("releases every temporary file when a share exceeds the attachment limit", async () => {    const payloads = Array.from({ length: PROVIDER_SEND_TURN_MAX_ATTACHMENTS + 1 }, (_, index) => ({      shareType: "image" as const,      value: `file:///shared/${index}.png`,      mimeType: "image/png",    }));    const removeOwnedFile = vi.fn(() => Promise.resolve());    const readBase64 = vi.fn(async () => "YWJj");     const result = await buildIncomingShareDraft({      id: "share-3",      createdAt: "2026-07-15T10:00:00.000Z",      payloads,      resolvedPayloads: [],      fileReader: { readBase64, removeOwnedFile },    });     expect(result.attachments).toHaveLength(PROVIDER_SEND_TURN_MAX_ATTACHMENTS);    expect(result.warnings).toEqual([      `Only the first ${PROVIDER_SEND_TURN_MAX_ATTACHMENTS} shared images were attached.`,    ]);    expect(readBase64).toHaveBeenCalledTimes(PROVIDER_SEND_TURN_MAX_ATTACHMENTS);    expect(removeOwnedFile).toHaveBeenCalledTimes(payloads.length);  });   it("maps duplicate image payloads to distinct resolved files", async () => {    const duplicate: SharePayload = {      shareType: "image",      value: "content://shared/screenshot",      mimeType: "image/png",    };    const resolvedPayloads: ResolvedSharePayload[] = [      {        ...duplicate,        contentUri: "file:///cache/first.png",        contentType: "image",        contentMimeType: "image/png",        contentSize: 3,        originalName: "first.png",      },      {        ...duplicate,        contentUri: "file:///cache/second.png",        contentType: "image",        contentMimeType: "image/png",        contentSize: 3,        originalName: "second.png",      },    ];    const readBase64 = vi.fn(async (uri: string) =>      uri.includes("first") ? "Zmlyc3Q=" : "c2Vjb25k",    );    const removeOwnedFile = vi.fn(async () => undefined);     const result = await buildIncomingShareDraft({      id: "share-duplicates",      createdAt: "2026-07-16T08:00:00.000Z",      payloads: [duplicate, duplicate],      resolvedPayloads,      fileReader: { readBase64, removeOwnedFile },    });     expect(readBase64.mock.calls.map(([uri]) => uri)).toEqual([      "file:///cache/first.png",      "file:///cache/second.png",    ]);    expect(result.attachments.map((attachment) => attachment.name)).toEqual([      "first.png",      "second.png",    ]);    expect(removeOwnedFile).toHaveBeenCalledWith("file:///cache/first.png");    expect(removeOwnedFile).toHaveBeenCalledWith("file:///cache/second.png");  });   it("keeps imported content when temporary-file cleanup fails", async () => {    const image: SharePayload = {      shareType: "image",      value: "file:///shared/screenshot.png",      mimeType: "image/png",    };     const result = await buildIncomingShareDraft({      id: "share-cleanup-failure",      createdAt: "2026-07-16T08:00:00.000Z",      payloads: [image],      resolvedPayloads: [],      fileReader: {        readBase64: async () => "YWJj",        removeOwnedFile: async () => {          throw new Error("file is busy");        },      },    });     expect(result.attachments).toHaveLength(1);    expect(result.warnings).toEqual([]);  });});

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t