202 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
| /*
 | |
| Copyright 2023 The Matrix.org Foundation C.I.C.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| import { fireEvent, render } from "@testing-library/react";
 | |
| import { ClientEvent, PendingEventOrdering } from "matrix-js-sdk/src/client";
 | |
| import { Room } from "matrix-js-sdk/src/models/room";
 | |
| import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | |
| import React from "react";
 | |
| import { act } from "react-dom/test-utils";
 | |
| import { SyncState } from "matrix-js-sdk/src/sync";
 | |
| 
 | |
| import type { MatrixClient } from "matrix-js-sdk/src/client";
 | |
| import RoomContext from "../../../../src/contexts/RoomContext";
 | |
| import { getRoomContext } from "../../../test-utils/room";
 | |
| import { stubClient } from "../../../test-utils/test-utils";
 | |
| import BaseAvatar from "../../../../src/components/views/avatars/BaseAvatar";
 | |
| import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
 | |
| 
 | |
| type Props = React.ComponentPropsWithoutRef<typeof BaseAvatar>;
 | |
| 
 | |
| describe("<BaseAvatar />", () => {
 | |
|     let client: MatrixClient;
 | |
|     let room: Room;
 | |
|     let member: RoomMember;
 | |
| 
 | |
|     function getComponent(props: Partial<Props>) {
 | |
|         return (
 | |
|             <MatrixClientContext.Provider value={client}>
 | |
|                 <RoomContext.Provider value={getRoomContext(room, {})}>
 | |
|                     <BaseAvatar name="" {...props} />
 | |
|                 </RoomContext.Provider>
 | |
|             </MatrixClientContext.Provider>
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     function failLoadingImg(container: HTMLElement): void {
 | |
|         const img = container.querySelector<HTMLImageElement>("img")!;
 | |
|         expect(img).not.toBeNull();
 | |
|         act(() => {
 | |
|             fireEvent.error(img);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     function emitReconnect(): void {
 | |
|         act(() => {
 | |
|             client.emit(ClientEvent.Sync, SyncState.Prepared, SyncState.Reconnecting);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     beforeEach(() => {
 | |
|         client = stubClient();
 | |
| 
 | |
|         room = new Room("!room:example.com", client, client.getUserId() ?? "", {
 | |
|             pendingEventOrdering: PendingEventOrdering.Detached,
 | |
|         });
 | |
| 
 | |
|         member = new RoomMember(room.roomId, "@bob:example.org");
 | |
|         jest.spyOn(room, "getMember").mockReturnValue(member);
 | |
|     });
 | |
| 
 | |
|     it("renders with minimal properties", () => {
 | |
|         const { container } = render(getComponent({}));
 | |
| 
 | |
|         expect(container.querySelector(".mx_BaseAvatar")).not.toBeNull();
 | |
|     });
 | |
| 
 | |
|     it("matches snapshot (avatar)", () => {
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 name: "CoolUser22",
 | |
|                 title: "Hover title",
 | |
|                 url: "https://example.com/images/avatar.gif",
 | |
|                 className: "mx_SomethingArbitrary",
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         expect(container).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it("matches snapshot (avatar + click)", () => {
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 name: "CoolUser22",
 | |
|                 title: "Hover title",
 | |
|                 url: "https://example.com/images/avatar.gif",
 | |
|                 className: "mx_SomethingArbitrary",
 | |
|                 onClick: () => {},
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         expect(container).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it("matches snapshot (no avatar)", () => {
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 name: "xX_Element_User_Xx",
 | |
|                 title: ":kiss:",
 | |
|                 defaultToInitialLetter: true,
 | |
|                 className: "big-and-bold",
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         expect(container).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it("matches snapshot (no avatar + click)", () => {
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 name: "xX_Element_User_Xx",
 | |
|                 title: ":kiss:",
 | |
|                 defaultToInitialLetter: true,
 | |
|                 className: "big-and-bold",
 | |
|                 onClick: () => {},
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         expect(container).toMatchSnapshot();
 | |
|     });
 | |
| 
 | |
|     it("uses fallback images", () => {
 | |
|         const images = [...Array(10)].map((_, i) => `https://example.com/images/${i}.webp`);
 | |
| 
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 url: images[0],
 | |
|                 urls: images.slice(1),
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         for (const image of images) {
 | |
|             expect(container.querySelector("img")!.src).toBe(image);
 | |
|             failLoadingImg(container);
 | |
|         }
 | |
|     });
 | |
| 
 | |
|     it("re-renders on reconnect", () => {
 | |
|         const primary = "https://example.com/image.jpeg";
 | |
|         const fallback = "https://example.com/fallback.png";
 | |
|         const { container } = render(
 | |
|             getComponent({
 | |
|                 url: primary,
 | |
|                 urls: [fallback],
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         failLoadingImg(container);
 | |
|         expect(container.querySelector("img")!.src).toBe(fallback);
 | |
| 
 | |
|         emitReconnect();
 | |
|         expect(container.querySelector("img")!.src).toBe(primary);
 | |
|     });
 | |
| 
 | |
|     it("renders with an image", () => {
 | |
|         const url = "https://example.com/images/small/avatar.gif?size=realBig";
 | |
|         const { container } = render(getComponent({ url }));
 | |
| 
 | |
|         const img = container.querySelector("img");
 | |
|         expect(img!.src).toBe(url);
 | |
|     });
 | |
| 
 | |
|     it("renders the initial letter", () => {
 | |
|         const { container } = render(getComponent({ name: "Yellow", defaultToInitialLetter: true }));
 | |
| 
 | |
|         const avatar = container.querySelector<HTMLSpanElement>(".mx_BaseAvatar_initial")!;
 | |
|         expect(avatar.innerHTML).toBe("Y");
 | |
|     });
 | |
| 
 | |
|     it.each([{}, { name: "CoolUser22" }, { name: "XxElement_FanxX", defaultToInitialLetter: true }])(
 | |
|         "includes a click handler",
 | |
|         (props: Partial<Props>) => {
 | |
|             const onClick = jest.fn();
 | |
| 
 | |
|             const { container } = render(
 | |
|                 getComponent({
 | |
|                     ...props,
 | |
|                     onClick,
 | |
|                 }),
 | |
|             );
 | |
| 
 | |
|             act(() => {
 | |
|                 fireEvent.click(container.querySelector(".mx_BaseAvatar")!);
 | |
|             });
 | |
| 
 | |
|             expect(onClick).toHaveBeenCalled();
 | |
|         },
 | |
|     );
 | |
| });
 |