85 lines
3.4 KiB
TypeScript
85 lines
3.4 KiB
TypeScript
/*
|
|||
|
|
Copyright 2026 hayaksi1
|
||
|
|
|
||
|
|
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 React, { type PropsWithChildren } from "react";
|
||
|
|
import { act, renderHook, waitFor } from "jest-matrix-react";
|
||
|
|
import { EventType, type MatrixClient, MatrixEvent, RelationType, Room } from "matrix-js-sdk/src/matrix";
|
||
|
|
|
||
|
|
import { useFetchedPinnedEvents } from "../../../src/hooks/usePinnedEvents";
|
||
|
|
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||
|
|
import { stubClient } from "../../test-utils";
|
||
|
|
|
||
|
|
describe("useFetchedPinnedEvents", () => {
|
||
|
|
const roomId = "!room:server";
|
||
|
|
const userId = "@alice:server";
|
||
|
|
const pinnedEventId = "$pinned:server";
|
||
|
|
|
||
|
|
let client: MatrixClient;
|
||
|
|
let room: Room;
|
||
|
|
// Stable identity: the hook memoises on this array, so a fresh literal per render would
|
||
|
|
// refetch forever.
|
||
|
|
const pinnedIds = [pinnedEventId];
|
||
|
|
|
||
|
|
const makeEvent = (id: string, content: object): MatrixEvent =>
|
||
|
|
new MatrixEvent({
|
||
|
|
type: EventType.RoomMessage,
|
||
|
|
sender: userId,
|
||
|
|
room_id: roomId,
|
||
|
|
event_id: id,
|
||
|
|
origin_server_ts: 0,
|
||
|
|
content,
|
||
|
|
});
|
||
|
|
|
||
|
|
const edit = (targetId: string, id: string): MatrixEvent =>
|
||
|
|
makeEvent(id, {
|
||
|
|
"msgtype": "m.text",
|
||
|
|
"body": "* edited",
|
||
|
|
"m.new_content": { msgtype: "m.text", body: "edited" },
|
||
|
|
"m.relates_to": { rel_type: RelationType.Replace, event_id: targetId },
|
||
|
|
});
|
||
|
|
|
||
|
|
const wrapper = ({ children }: PropsWithChildren): React.JSX.Element => (
|
||
|
|
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||
|
|
);
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
client = stubClient();
|
||
|
|
room = new Room(roomId, client, userId);
|
||
|
|
// The pinned event is deliberately not in the local timeline, so the hook holds a copy of
|
||
|
|
// its own that nothing else keeps up to date.
|
||
|
|
jest.spyOn(client, "fetchRoomEvent").mockResolvedValue(
|
||
|
|
makeEvent(pinnedEventId, { msgtype: "m.text", body: "original" }).event as never,
|
||
|
|
);
|
||
|
|
jest.spyOn(client, "relations").mockResolvedValue({ events: [] });
|
||
|
|
});
|
||
|
|
|
||
|
|
it("gives the edited content for a pinned event that is edited", async () => {
|
||
|
|
const { result } = renderHook(() => useFetchedPinnedEvents(room, pinnedIds), { wrapper });
|
||
|
|
await waitFor(() => expect(result.current).toHaveLength(1));
|
||
|
|
expect(result.current[0].getContent().body).toBe("original");
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
room.addLiveEvents([edit(pinnedEventId, "$edit:server")], { addToState: false });
|
||
|
|
});
|
||
|
|
|
||
|
|
await waitFor(() => expect(result.current[0].getContent().body).toBe("edited"));
|
||
|
|
});
|
||
|
|
|
||
|
|
it("leaves a pinned event alone when a different event is edited", async () => {
|
||
|
|
const { result } = renderHook(() => useFetchedPinnedEvents(room, pinnedIds), { wrapper });
|
||
|
|
await waitFor(() => expect(result.current).toHaveLength(1));
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
room.addLiveEvents([edit("$somethingElse:server", "$edit2:server")], { addToState: false });
|
||
|
|
});
|
||
|
|
|
||
|
|
// Wait long enough that an edit would have been applied, then confirm none was.
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
||
|
|
expect(result.current[0].getContent().body).toBe("original");
|
||
|
|
});
|
||
|
|
});
|