Files
blap/apps/web/test/viewmodels/event-tiles/EventTileReceiptState-test.ts
T
rbondesson 03360eb260 Refactor EventTile using the MVVM pattern - #5a (#33587)
* Extract EventTile receipt state

* Extract EventTile thread state

* Extract EventTile reaction relation state

* Extract EventTile reply-chain state

* Keep reply chain collapse wiring in EventTile

* Reuse message event eligibility for receipts
2026-05-22 18:25:32 +00:00

141 lines
4.5 KiB
TypeScript

/*
Copyright 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 { EventStatus, EventType, M_POLL_END, M_POLL_START, type MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
import { mkEvent } from "../../test-utils";
import { TimelineRenderingType } from "../../../src/contexts/RoomContext";
import {
getEventTileReceiptState,
isEligibleForSpecialReceipt,
type EventTileReceiptStateInput,
} from "../../../src/viewmodels/room/timeline/event-tile/EventTileReceiptState";
const roomId = "!room:example.org";
const ownUserId = "@alice:example.org";
const otherUserId = "@bob:example.org";
function makeEvent({
type = EventType.RoomMessage,
user = ownUserId,
}: {
type?: string;
user?: string;
} = {}): MatrixEvent {
return mkEvent({
event: true,
type,
room: roomId,
user,
content: {
msgtype: MsgType.Text,
body: "Hello",
},
});
}
function makeInput(overrides: Partial<EventTileReceiptStateInput> = {}): EventTileReceiptStateInput {
return {
mxEvent: makeEvent(),
hasRoom: true,
ownUserId,
lastSuccessful: true,
timelineRenderingType: TimelineRenderingType.Room,
...overrides,
};
}
describe("EventTileReceiptState", () => {
it.each([
EventType.RoomMessage,
EventType.RoomMessageEncrypted,
EventType.Sticker,
M_POLL_START.name,
M_POLL_END.name,
])("treats %s as eligible for special receipts", (type) => {
expect(isEligibleForSpecialReceipt(makeEvent({ type }))).toBe(true);
});
it("does not treat state events as eligible for special receipts", () => {
expect(isEligibleForSpecialReceipt(makeEvent({ type: EventType.RoomName }))).toBe(false);
});
it("shows a sent receipt for the last successful own event", () => {
const state = getEventTileReceiptState(makeInput());
expect(state).toMatchObject({
isEligibleForSpecialReceipt: true,
shouldShowSentReceipt: true,
shouldShowSendingReceipt: false,
shouldListenForReceipts: true,
});
});
it("shows a sent receipt for an explicitly sent event", () => {
const state = getEventTileReceiptState(makeInput({ eventSendStatus: EventStatus.SENT }));
expect(state.shouldShowSentReceipt).toBe(true);
expect(state.shouldListenForReceipts).toBe(true);
});
it("shows a sending receipt for pending send states", () => {
for (const eventSendStatus of [EventStatus.QUEUED, EventStatus.SENDING, EventStatus.ENCRYPTING]) {
const state = getEventTileReceiptState(makeInput({ eventSendStatus }));
expect(state.shouldShowSentReceipt).toBe(false);
expect(state.shouldShowSendingReceipt).toBe(true);
expect(state.shouldListenForReceipts).toBe(true);
}
});
it("does not show a sent receipt in the thread list", () => {
const state = getEventTileReceiptState(
makeInput({
timelineRenderingType: TimelineRenderingType.ThreadsList,
}),
);
expect(state.shouldShowSentReceipt).toBe(false);
expect(state.shouldListenForReceipts).toBe(false);
});
it("does not show special receipts once read receipts are present", () => {
const state = getEventTileReceiptState(
makeInput({
readReceipts: [
{
userId: otherUserId,
},
],
}),
);
expect(state.isEligibleForSpecialReceipt).toBe(false);
expect(state.shouldShowSentReceipt).toBe(false);
expect(state.shouldShowSendingReceipt).toBe(false);
expect(state.shouldListenForReceipts).toBe(false);
});
it("does not show special receipts for another sender", () => {
const state = getEventTileReceiptState(
makeInput({
mxEvent: makeEvent({ user: otherUserId }),
}),
);
expect(state.isEligibleForSpecialReceipt).toBe(false);
expect(state.shouldListenForReceipts).toBe(false);
});
it("does not show special receipts when the room is not available", () => {
const state = getEventTileReceiptState(makeInput({ hasRoom: false }));
expect(state.isEligibleForSpecialReceipt).toBe(false);
expect(state.shouldListenForReceipts).toBe(false);
});
});