/* * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial * Please see LICENSE files in the repository root for full details. */ import { type IEventRelation, type MatrixClient, type Room, RoomEvent } from "matrix-js-sdk/src/matrix"; import type { MockedObject } from "jest-mock"; import { RoomUploadViewModel } from "../../../src/viewmodels/room/RoomUploadViewModel"; import { mkEvent, mkStubRoom, stubClient } from "../../test-utils"; import { TimelineRenderingType } from "../../../src/contexts/RoomContext"; import type { MatrixDispatcher } from "../../../src/dispatcher/dispatcher"; import ContentMessages from "../../../src/ContentMessages"; const sendContentListToRoomSpy = jest.spyOn(ContentMessages.sharedInstance(), "sendContentListToRoom"); describe("RoomUploadViewModel", () => { let client: MockedObject; let room: MockedObject; let dis: MockedObject; beforeEach(() => { jest.clearAllMocks(); client = stubClient() as MockedObject; room = mkStubRoom("!room", undefined, undefined) as MockedObject; dis = { dispatch: jest.fn(), } as Partial as MockedObject; }); afterAll(() => { jest.restoreAllMocks(); }); it.each([true, false])("handles state of mayUpload when room.maySendMessage = %s", (maySendMessage) => { room.maySendMessage.mockReturnValue(maySendMessage); const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Room, dis, undefined, undefined, () => {}, ); expect(vm.getSnapshot().mayUpload).toEqual(maySendMessage); room.maySendMessage.mockReturnValue(!maySendMessage); room.emit(RoomEvent.CurrentStateUpdated, room, null as any, null as any); expect(vm.getSnapshot().mayUpload).toEqual(!maySendMessage); }); describe("uploads via input", () => { it("redirected if guest", async () => { client.isGuest.mockReturnValue(true); const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Room, dis, undefined, undefined, () => {}, ); await vm.initiateViaInputFiles([] as unknown as FileList); expect(dis.dispatch).toHaveBeenCalledWith({ action: "require_registration" }); }); it("skips empty files", async () => { const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Room, dis, undefined, undefined, () => {}, ); await vm.initiateViaInputFiles([] as unknown as FileList); expect(dis.dispatch).not.toHaveBeenCalled(); }); it("uploads with correct context", async () => { sendContentListToRoomSpy.mockResolvedValue(undefined); const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Thread, dis, undefined, undefined, () => {}, ); const replyEvent = mkEvent({ event: true, type: "anything", user: "anyone", content: {} }); vm.setReplyToEvent(replyEvent); const threadRelation: IEventRelation = { key: "foo" }; vm.setThreadRelation(threadRelation); const fileList = [ { name: "fake.png", size: 1024, type: "image/png", }, ] as unknown as FileList; await vm.initiateViaInputFiles(fileList); expect(sendContentListToRoomSpy).toHaveBeenCalledWith( fileList, room.roomId, threadRelation, replyEvent, client, TimelineRenderingType.Thread, ); }); }); describe("uploads via data transfer", () => { it("redirected if guest", async () => { client.isGuest.mockReturnValue(true); const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Room, dis, undefined, undefined, () => {}, ); await vm.initiateViaDataTransfer({} as DataTransfer); expect(dis.dispatch).toHaveBeenCalledWith({ action: "require_registration" }); }); it("skips empty files", async () => { const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Room, dis, undefined, undefined, () => {}, ); await vm.initiateViaDataTransfer({ files: [] as unknown as FileList } as DataTransfer); expect(dis.dispatch).not.toHaveBeenCalled(); }); it("uploads with correct context", async () => { sendContentListToRoomSpy.mockResolvedValue(undefined); const vm = new RoomUploadViewModel( room, client, TimelineRenderingType.Thread, dis, undefined, undefined, () => {}, ); const replyEvent = mkEvent({ event: true, type: "anything", user: "anyone", content: {} }); vm.setReplyToEvent(replyEvent); const threadRelation: IEventRelation = { key: "foo" }; vm.setThreadRelation(threadRelation); const files = [ { name: "fake.png", size: 1024, type: "image/png", }, ] as unknown as FileList; await vm.initiateViaDataTransfer({ files } as DataTransfer); expect(sendContentListToRoomSpy).toHaveBeenCalledWith( files, room.roomId, threadRelation, replyEvent, client, TimelineRenderingType.Thread, ); }); }); });