import { beforeEach, describe, expect, it, vi } from "vitest"; import { CreateFlowUploadValidationError } from "../../lib/create/createFlowUploadValidation"; const readPendingCommunityAvatarFile = vi.fn(); const clearPendingCommunityAvatarFile = vi.fn(); const uploadCreateFlowFile = vi.fn(); vi.mock("../../lib/create/pendingCommunityAvatarUpload", () => ({ readPendingCommunityAvatarFile: (...args: unknown[]) => readPendingCommunityAvatarFile(...args), clearPendingCommunityAvatarFile: (...args: unknown[]) => clearPendingCommunityAvatarFile(...args), })); vi.mock("../../lib/create/uploadToServer", () => ({ uploadCreateFlowFile: (...args: unknown[]) => uploadCreateFlowFile(...args), })); import { flushPendingCommunityAvatar, forgetFlushedCommunityAvatarUrl, stateWithPendingCommunityAvatar, } from "../../lib/create/flushPendingCommunityAvatar"; const pending = new File([new Uint8Array([1, 2, 3])], "logo.png", { type: "image/png", }); describe("flushPendingCommunityAvatar", () => { beforeEach(() => { readPendingCommunityAvatarFile.mockReset(); clearPendingCommunityAvatarFile.mockReset(); uploadCreateFlowFile.mockReset(); clearPendingCommunityAvatarFile.mockResolvedValue(undefined); forgetFlushedCommunityAvatarUrl(); }); it("does nothing when no file is staged", async () => { readPendingCommunityAvatarFile.mockResolvedValue(null); await expect(flushPendingCommunityAvatar()).resolves.toEqual({ status: "none", }); expect(uploadCreateFlowFile).not.toHaveBeenCalled(); }); it("uploads once and remembers the url after the file is cleared", async () => { readPendingCommunityAvatarFile .mockResolvedValueOnce(pending) .mockResolvedValue(null); uploadCreateFlowFile.mockResolvedValue({ url: "/api/uploads/logo" }); const [first, second] = await Promise.all([ flushPendingCommunityAvatar(), flushPendingCommunityAvatar(), ]); expect(first).toEqual({ status: "uploaded", url: "/api/uploads/logo" }); expect(second).toEqual(first); expect(uploadCreateFlowFile).toHaveBeenCalledTimes(1); expect(clearPendingCommunityAvatarFile).toHaveBeenCalledTimes(1); await expect(flushPendingCommunityAvatar()).resolves.toEqual({ status: "uploaded", url: "/api/uploads/logo", }); expect(uploadCreateFlowFile).toHaveBeenCalledTimes(1); }); it("drops an invalid file and leaves a transient failure staged", async () => { readPendingCommunityAvatarFile.mockResolvedValue(pending); uploadCreateFlowFile.mockRejectedValueOnce( new CreateFlowUploadValidationError("tooLarge"), ); await expect(flushPendingCommunityAvatar()).resolves.toEqual({ status: "invalid", }); expect(clearPendingCommunityAvatarFile).toHaveBeenCalledTimes(1); uploadCreateFlowFile.mockRejectedValueOnce(new Error("network")); await expect(flushPendingCommunityAvatar()).resolves.toEqual({ status: "failed", }); expect(clearPendingCommunityAvatarFile).toHaveBeenCalledTimes(1); }); it("copies a successful upload onto publish state and blocks on failure", async () => { readPendingCommunityAvatarFile.mockResolvedValue(pending); uploadCreateFlowFile.mockResolvedValue({ url: "/api/uploads/logo" }); await expect( stateWithPendingCommunityAvatar({ title: "Garden" }), ).resolves.toEqual({ ok: true, state: { title: "Garden", communityAvatarUrl: "/api/uploads/logo" }, }); forgetFlushedCommunityAvatarUrl(); uploadCreateFlowFile.mockRejectedValueOnce(new Error("network")); await expect( stateWithPendingCommunityAvatar({ title: "Garden", communityAvatarFileName: "logo.png", }), ).resolves.toEqual({ ok: false, reason: "upload_failed" }); }); });