From 9ff81979871c64529258e2186d51e47af04431a7 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 5 Nov 2024 17:00:58 +0000 Subject: [PATCH] Add tests for ReactionToggleButton --- src/button/ReactionToggleButton.test.tsx | 195 ++++ src/button/ReactionToggleButton.tsx | 10 +- .../ReactionToggleButton.test.tsx.snap | 839 ++++++++++++++++++ src/useReactions.test.tsx | 42 +- src/utils/testReactions.tsx | 17 +- 5 files changed, 1077 insertions(+), 26 deletions(-) create mode 100644 src/button/ReactionToggleButton.test.tsx create mode 100644 src/button/__snapshots__/ReactionToggleButton.test.tsx.snap diff --git a/src/button/ReactionToggleButton.test.tsx b/src/button/ReactionToggleButton.test.tsx new file mode 100644 index 00000000..79c236d6 --- /dev/null +++ b/src/button/ReactionToggleButton.test.tsx @@ -0,0 +1,195 @@ +import { act, render } from "@testing-library/react"; +import { expect, test } from "vitest"; +import { + MockRoom, + MockRTCSession, + TestReactionsWrapper, +} from "../utils/testReactions"; +import { ReactionToggleButton } from "./ReactionToggleButton"; +import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc"; +import { TooltipProvider } from "@vector-im/compound-web"; +import { ElementCallReactionEventType } from "../reactions"; +import { userEvent } from "@testing-library/user-event"; + +const memberUserIdAlice = "@alice:example.org"; +const memberEventAlice = "$membership-alice:example.org"; + +const membership: Record = { + [memberEventAlice]: memberUserIdAlice, +}; + +function TestComponent({ + rtcSession, + room, +}: { + rtcSession: MockRTCSession; + room: MockRoom; +}) { + return ( + + + + + + ); +} + +test("Can open menu", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, container } = render( + , + ); + act(() => getByRole("button").click()); + expect(container).toMatchSnapshot(); +}); + +test("Can close menu", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, container } = render( + , + ); + act(() => { + getByRole("button").click(); + }); + act(() => getByRole("button", { expanded: true }).click()); + expect(container).toMatchSnapshot(); +}); + +test("Can raise hand", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, getByText, container } = render( + , + ); + act(() => { + getByRole("button").click(); + }); + act(() => { + getByText("πŸ–οΈ").click(); + }); + expect(room.testSentEvents).toEqual([ + [ + undefined, + "m.reaction", + { + "m.relates_to": { + event_id: memberEventAlice, + key: "πŸ–οΈ", + rel_type: "m.annotation", + }, + }, + ], + ]); + expect(container).toMatchSnapshot(); +}); + +test("Can can lower hand", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, getByText, container } = render( + , + ); + const reactionEvent = room.testSendReaction(memberEventAlice, membership); + act(() => { + getByRole("button").click(); + }); + act(() => { + getByText("πŸ–οΈ").click(); + }); + expect(room.testRedactedEvents).toEqual([[undefined, reactionEvent]]); + expect(container).toMatchSnapshot(); +}); + +test("Can react with emoji", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, getByText } = render( + , + ); + act(() => { + getByRole("button").click(); + }); + act(() => { + getByText("🐢").click(); + }); + expect(room.testSentEvents).toEqual([ + [ + undefined, + ElementCallReactionEventType, + { + "m.relates_to": { + event_id: memberEventAlice, + rel_type: "m.reference", + }, + name: "dog", + emoji: "🐢", + }, + ], + ]); +}); + +test("Can search for and send emoji", async () => { + const user = userEvent.setup(); + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByText, getByRole, getByPlaceholderText, container } = render( + , + ); + act(() => { + getByRole("button").click(); + }); + act(() => { + getByRole("button", { + name: "Search", + }).click(); + }); + await act(async () => { + getByPlaceholderText("Search reactions…").focus(); + await user.keyboard("crickets"); + }); + expect(container).toMatchSnapshot(); + act(() => { + getByText("πŸ¦—").click(); + }); + expect(room.testSentEvents).toEqual([ + [ + undefined, + ElementCallReactionEventType, + { + "m.relates_to": { + event_id: memberEventAlice, + rel_type: "m.reference", + }, + name: "crickets", + emoji: "πŸ¦—", + }, + ], + ]); +}); + +test("Can close search", async () => { + const room = new MockRoom(memberUserIdAlice); + const rtcSession = new MockRTCSession(room, membership); + const { getByRole, container } = render( + , + ); + act(() => { + getByRole("button").click(); + }); + act(() => { + getByRole("button", { + name: "Search", + }).click(); + }); + act(() => { + getByRole("button", { + name: "close search", + }).click(); + }); + expect(container).toMatchSnapshot(); +}); diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index f21a88d4..3d77b9c5 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -53,6 +53,7 @@ const InnerButton: FC = ({ raised, ...props }) => { toggleRaisedHand()} @@ -124,6 +127,7 @@ export function ReactionPopupMenu({ /> setIsSearching(false)} @@ -134,12 +138,11 @@ export function ReactionPopupMenu({ ) : null} {filteredReactionSet.map((reaction) => ( -
  • +
  • sendRelation(reaction)} > @@ -153,6 +156,7 @@ export function ReactionPopupMenu({ setIsSearching(true)} @@ -196,7 +200,6 @@ export function ReactionToggleButton({ setBusy(true); await client.sendEvent( rtcSession.room.roomId, - null, ElementCallReactionEventType, { "m.relates_to": { @@ -206,7 +209,6 @@ export function ReactionToggleButton({ emoji: reaction.emoji, name: reaction.name, }, - undefined, ); // Do NOT close the menu after this. } catch (ex) { diff --git a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap new file mode 100644 index 00000000..45811dc0 --- /dev/null +++ b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap @@ -0,0 +1,839 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Can can lower hand 1`] = ` +
    + +
    +
    + +
    +
    +
    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    +
    +
    +`; + +exports[`Can close menu 1`] = ` +
    + +
    +`; + +exports[`Can close search 1`] = ` +
    + +
    +
    + +
    +
    +
    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    +
    +
    +`; + +exports[`Can open menu 1`] = ` +
    + +
    +
    + +
    +
    +
    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    +
    +
    +`; + +exports[`Can raise hand 1`] = ` +
    + +
    +
    + +
    +
    +
    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    +
    +
    +`; + +exports[`Can search for and send emoji 1`] = ` +
    + +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +`; diff --git a/src/useReactions.test.tsx b/src/useReactions.test.tsx index 6ea47514..0f0a9f6d 100644 --- a/src/useReactions.test.tsx +++ b/src/useReactions.test.tsx @@ -15,8 +15,8 @@ import { createRedaction, MockRoom, MockRTCSession, - TestComponentWrapper, -} from "./utils/test-reactions"; + TestReactionsWrapper, +} from "./utils/testReactions"; import { RoomEvent } from "matrix-js-sdk/src/matrix"; const memberUserIdAlice = "@alice:example.org"; @@ -61,9 +61,9 @@ describe("useReactions", () => { membership, ); const { queryByRole } = render( - + - , + , ); expect(queryByRole("list")?.children).to.have.lengthOf(0); }); @@ -71,9 +71,9 @@ describe("useReactions", () => { const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); const { queryByText } = render( - + - , + , ); await act(() => room.testSendReaction(memberEventAlice, membership)); expect(queryByText("Local reaction")).toBeTruthy(); @@ -82,9 +82,9 @@ describe("useReactions", () => { const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); await act(() => room.testSendReaction(memberEventAlice, membership)); expect(queryByRole("list")?.children).to.have.lengthOf(1); @@ -95,9 +95,9 @@ describe("useReactions", () => { const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); const reactionEventId = await act(() => room.testSendReaction(memberEventAlice, membership), @@ -119,9 +119,9 @@ describe("useReactions", () => { ]); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); expect(queryByRole("list")?.children).to.have.lengthOf(1); }); @@ -133,9 +133,9 @@ describe("useReactions", () => { ]); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); expect(queryByRole("list")?.children).to.have.lengthOf(1); act(() => rtcSession.testRemoveMember(memberUserIdAlice)); @@ -147,9 +147,9 @@ describe("useReactions", () => { ]); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); expect(queryByRole("list")?.children).to.have.lengthOf(1); // Simulate leaving and rejoining @@ -161,13 +161,13 @@ describe("useReactions", () => { }); test("ignores invalid sender for historic event", () => { const room = new MockRoom(memberUserIdAlice, [ - createReaction(memberEventAlice, membership), + createReaction(memberEventAlice, memberUserIdBob), ]); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); expect(queryByRole("list")?.children).to.have.lengthOf(0); }); @@ -175,9 +175,9 @@ describe("useReactions", () => { const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); const { queryByRole } = render( - + - , + , ); await act(() => room.testSendReaction(memberEventAlice, memberUserIdBob)); expect(queryByRole("list")?.children).to.have.lengthOf(0); diff --git a/src/utils/testReactions.tsx b/src/utils/testReactions.tsx index 0965379e..3390cad0 100644 --- a/src/utils/testReactions.tsx +++ b/src/utils/testReactions.tsx @@ -15,7 +15,7 @@ import { MatrixRTCSessionEvent, } from "matrix-js-sdk/src/matrixrtc"; -export const TestComponentWrapper = ({ +export const TestReactionsWrapper = ({ rtcSession, children, }: PropsWithChildren<{ @@ -98,6 +98,9 @@ export function createRedaction( } export class MockRoom extends EventEmitter { + public readonly testSentEvents: Parameters[] = []; + public readonly testRedactedEvents: Parameters[] = []; + public constructor( private readonly ownUserId: string, private readonly existingRelations: MatrixEvent[] = [], @@ -108,6 +111,18 @@ export class MockRoom extends EventEmitter { public get client(): MatrixClient { return { getUserId: (): string => this.ownUserId, + sendEvent: async ( + ...props: Parameters + ): ReturnType => { + this.testSentEvents.push(props); + return { event_id: randomUUID() }; + }, + redactEvent: async ( + ...props: Parameters + ): ReturnType => { + this.testRedactedEvents.push(props); + return { event_id: randomUUID() }; + }, } as unknown as MatrixClient; }