100 lines
3.0 KiB
TypeScript
100 lines
3.0 KiB
TypeScript
import { buildChatChannelDetailVM, type ChatResolvedAuthorVM } from "@hivegrid/view-models";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { overlayChatAttachmentSendAttempt, type ChatAttachmentSendAttempt } from "./chatAttachmentOptimistic";
|
|
|
|
const currentUser: ChatResolvedAuthorVM = {
|
|
userId: "user_1",
|
|
displayName: "Raffael",
|
|
avatar: { imageUrl: null, fallbackLabel: "RS" },
|
|
isCurrentUser: true,
|
|
};
|
|
|
|
function vm() {
|
|
return buildChatChannelDetailVM({
|
|
header: {
|
|
id: "chat_1",
|
|
kind: "group",
|
|
title: "Team",
|
|
subtitle: null,
|
|
avatar: { imageUrl: null, fallbackLabel: "T" },
|
|
project: null,
|
|
memberCount: 3,
|
|
isPrivate: true,
|
|
isArchived: false,
|
|
isAuditMode: false,
|
|
canManage: false,
|
|
settingsHref: null,
|
|
pinnedHref: null,
|
|
searchHref: "/demo/chat/search",
|
|
},
|
|
messages: [],
|
|
pinnedMessages: [],
|
|
typingLabels: [],
|
|
history: { hasOlder: false, olderCursor: null },
|
|
composer: {
|
|
disabledReasonKey: null,
|
|
canSend: true,
|
|
canAttachFiles: true,
|
|
canRecordVoice: true,
|
|
canMention: true,
|
|
canMarkAsTask: true,
|
|
draftStorageKey: "chat:draft:chat_1",
|
|
maxBodyLength: 4_000,
|
|
},
|
|
connection: { state: "online", reconnectAttempt: 0, retryAtIso: null },
|
|
});
|
|
}
|
|
|
|
function attempt(deliveryState: "sending" | "failed", errorKey: string | null): ChatAttachmentSendAttempt {
|
|
return {
|
|
payloadKey: "payload",
|
|
requestId: "request_stable",
|
|
clientMessageId: "client_stable",
|
|
fileIds: ["file_1"],
|
|
body: "Montagefoto",
|
|
attachments: [
|
|
{
|
|
clientId: "upload_1",
|
|
fileId: "file_1",
|
|
fileName: "camera.jpg",
|
|
mimeType: "image/jpeg",
|
|
sizeBytes: 1024,
|
|
kind: "image",
|
|
status: "ready",
|
|
progress: 100,
|
|
errorKey: null,
|
|
durationSeconds: null,
|
|
},
|
|
],
|
|
createdAtIso: "2026-07-16T08:00:00.000Z",
|
|
deliveryState,
|
|
errorKey,
|
|
};
|
|
}
|
|
|
|
describe("attachment optimistic message overlay", () => {
|
|
it("shows a local sending echo with the stable client id and attachment metadata", () => {
|
|
const result = overlayChatAttachmentSendAttempt(vm(), attempt("sending", null), currentUser);
|
|
expect(result.status).toBe("ready");
|
|
if (result.status !== "ready") return;
|
|
expect(result.data.messages[0]).toMatchObject({
|
|
id: "local:client_stable",
|
|
clientMessageId: "client_stable",
|
|
source: "local_echo",
|
|
deliveryState: "sending",
|
|
attachments: [{ fileId: "file_1", fileName: "camera.jpg" }],
|
|
});
|
|
});
|
|
|
|
it("keeps a failed send element-local and retryable without changing its identity", () => {
|
|
const result = overlayChatAttachmentSendAttempt(vm(), attempt("failed", "chat.errors.invalidInput"), currentUser);
|
|
expect(result.status === "ready" ? result.data.messages[0] : null).toMatchObject({
|
|
clientMessageId: "client_stable",
|
|
deliveryState: "failed",
|
|
retryable: true,
|
|
failureReasonKey: "chat.errors.invalidInput",
|
|
});
|
|
});
|
|
});
|