Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit a6106f5

Browse files
committed
Add tests
Refactor tests to be in CallGuestLinkButton-test instead of the RoomHeader Signed-off-by: Timo K <[email protected]>
1 parent 48b923f commit a6106f5

File tree

4 files changed

+242
-116
lines changed

4 files changed

+242
-116
lines changed

src/components/views/rooms/RoomHeader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton";
5454
import { RoomKnocksBar } from "./RoomKnocksBar";
5555
import { isVideoRoom } from "../../../utils/video-rooms";
5656
import { notificationLevelToIndicator } from "../../../utils/notifications";
57-
import ExternalLinkButton from "./RoomHeader/ExternalLinkButton";
57+
import { CallGuestLinkButton } from "./RoomHeader/CallGuestLinkButton";
5858

5959
export default function RoomHeader({
6060
room,
@@ -314,7 +314,7 @@ export default function RoomHeader({
314314
);
315315
})}
316316

317-
{isViewingCall && <ExternalLinkButton room={room} />}
317+
{isViewingCall && <CallGuestLinkButton room={room} />}
318318
{((isConnectedToCall && isViewingCall) || isVideoRoom(room)) && <VideoRoomChatButton room={room} />}
319319

320320
{hasActiveCallSession && !isConnectedToCall && !isViewingCall ? (

src/components/views/rooms/RoomHeader/ExternalLinkButton.tsx renamed to src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,23 @@ import { calculateRoomVia } from "../../../../utils/permalinks/Permalinks";
3535
import { useEventEmitterState } from "../../../../hooks/useEventEmitter";
3636
import BaseDialog from "../../dialogs/BaseDialog";
3737

38-
interface Props {
39-
room: Room;
40-
}
41-
42-
export default function ExternalLinkButton({ room }: Props): JSX.Element {
38+
/**
39+
* Display a button to open a dialog to share a link to the call using a element call guest spa url (`element_call:guest_spa_url` in the EW config).
40+
* @param room
41+
* @returns Nothing if there is not the option to share a link (No guest_spa_url is set) or a button to open a dialog to share the link.
42+
*/
43+
export const CallGuestLinkButton: React.FC<{ room: Room }> = ({ room }) => {
4344
const guestSpaUrl = useMemo(() => {
4445
return SdkConfig.get("element_call").guest_spa_url;
4546
}, []);
4647

48+
// We use the direct function only in functions triggered by user interaction to avoid computation on every render.
4749
const isRoomJoinable = useCallback(
4850
() => room.getJoinRule() === JoinRule.Public || room.getJoinRule() === JoinRule.Knock,
4951
[room],
5052
);
5153

52-
const isRoomJoinableVar = useEventEmitterState(
53-
room,
54-
RoomStateEvent.Events,
55-
() => room.getJoinRule() === JoinRule.Public || room.getJoinRule() === JoinRule.Knock,
56-
);
54+
const isRoomJoinableState = useEventEmitterState(room, RoomStateEvent.Events, isRoomJoinable);
5755

5856
const canChangeJoinRule = useEventEmitterState(
5957
room,
@@ -72,7 +70,7 @@ export default function ExternalLinkButton({ room }: Props): JSX.Element {
7270
url.pathname = "/room/";
7371
// Set params for the sharable url
7472
url.searchParams.set("roomId", room.roomId);
75-
url.searchParams.set("perParticipantE2EE", "true");
73+
if (room.hasEncryptionStateEvent()) url.searchParams.set("perParticipantE2EE", "true");
7674
for (const server of calculateRoomVia(room)) {
7775
url.searchParams.set("viaServers", server);
7876
}
@@ -87,7 +85,7 @@ export default function ExternalLinkButton({ room }: Props): JSX.Element {
8785

8886
const showLinkModal = useCallback(() => {
8987
try {
90-
// generateCallLink throws if the permissions are not met
88+
// generateCallLink throws if the invite rules are not met
9189
const target = generateCallLink();
9290
Modal.createDialog(ShareDialog, {
9391
target,
@@ -107,14 +105,15 @@ export default function ExternalLinkButton({ room }: Props): JSX.Element {
107105
Modal.createDialog(JoinRuleDialog, {
108106
room,
109107
}).finished.then(() => {
108+
// we need to use the function here because the callback got called before the state was updated.
110109
if (isRoomJoinable()) showLinkModal();
111110
});
112111
}
113-
}, [room, isRoomJoinable, showLinkModal]);
112+
}, [showLinkModal, room, isRoomJoinable]);
114113

115114
return (
116115
<>
117-
{(canChangeJoinRule || isRoomJoinableVar) && (
116+
{(canChangeJoinRule || isRoomJoinableState) && guestSpaUrl && (
118117
<Tooltip label={_t("voip|get_call_link")}>
119118
<IconButton onClick={shareClick} aria-label={_t("voip|get_call_link")}>
120119
<ExternalLinkIcon />
@@ -123,13 +122,13 @@ export default function ExternalLinkButton({ room }: Props): JSX.Element {
123122
)}
124123
</>
125124
);
126-
}
125+
};
127126
interface JoinRuleDialogProps {
128127
onFinished(): void;
129128
room: Room;
130129
}
131130

132-
function JoinRuleDialog({ room, onFinished }: JoinRuleDialogProps): JSX.Element {
131+
export function JoinRuleDialog({ room, onFinished }: JoinRuleDialogProps): JSX.Element {
133132
const askToJoinEnabled = SettingsStore.getValue("feature_ask_to_join");
134133
const [isUpdating, setIsUpdating] = React.useState<undefined | JoinRule>(undefined);
135134
const changeJoinRule = useCallback(
@@ -144,6 +143,7 @@ function JoinRuleDialog({ room, onFinished }: JoinRuleDialogProps): JSX.Element
144143
} as IJoinRuleEventContent,
145144
"",
146145
);
146+
// Show the dialog for a bit to give the user feedback
147147
setTimeout(() => onFinished(), 500);
148148
},
149149
[isUpdating, onFinished, room.client, room.roomId],
@@ -152,14 +152,22 @@ function JoinRuleDialog({ room, onFinished }: JoinRuleDialogProps): JSX.Element
152152
<BaseDialog title={_t("update_room_access_modal|title")} onFinished={onFinished}>
153153
<div>
154154
<p>{_t("update_room_access_modal|description")}</p>
155-
<Button disabled={isUpdating === JoinRule.Public} onClick={() => changeJoinRule(JoinRule.Public)}>
156-
{_t("common|public")}
157-
</Button>
158155
{askToJoinEnabled && (
159156
<Button disabled={isUpdating === JoinRule.Knock} onClick={() => changeJoinRule(JoinRule.Knock)}>
160157
{_t("action|ask_to_join")}
161158
</Button>
162159
)}
160+
<Button
161+
// manually add destructive styles because they otherwise get overwritten by .mx_Dialog
162+
style={{
163+
borderColor: "var(--cpd-color-border-critical-subtle)",
164+
color: "var(--cpd-color-text-critical-primary)",
165+
}}
166+
disabled={isUpdating === JoinRule.Public}
167+
onClick={() => changeJoinRule(JoinRule.Public)}
168+
>
169+
{_t("common|public")}
170+
</Button>
163171
<br />
164172
<p>{_t("update_room_access_modal|dont_change_description")}</p>
165173
<Button

test/components/views/rooms/RoomHeader-test.tsx

Lines changed: 4 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ import { _t } from "../../../../src/languageHandler";
6161
import * as UseCall from "../../../../src/hooks/useCall";
6262
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
6363
import WidgetStore, { IApp } from "../../../../src/stores/WidgetStore";
64-
import ShareDialog from "../../../../src/components/views/dialogs/ShareDialog";
65-
import Modal from "../../../../src/Modal";
64+
6665
jest.mock("../../../../src/utils/ShieldUtils");
6766

6867
function getWrapper(): RenderOptions {
@@ -516,39 +515,12 @@ describe("RoomHeader", () => {
516515
const { container } = render(<RoomHeader room={room} />, getWrapper());
517516
getByLabelText(container, "Close lobby");
518517
});
519-
});
520-
521-
describe("External conference", () => {
522-
const oldGet = SdkConfig.get;
523-
beforeEach(() => {
524-
jest.spyOn(SdkConfig, "get").mockImplementation((key) => {
525-
if (key === "element_call") {
526-
return { guest_spa_url: "https://guest_spa_url.com", url: "https://spa_url.com" };
527-
}
528-
return oldGet(key);
529-
});
530-
mockRoomMembers(room, 3);
531-
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
532-
});
533-
534-
it("shows the external conference if the room has public join rules", () => {
535-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
536-
537-
const { container } = render(<RoomHeader room={room} />, getWrapper());
538-
expect(getByLabelText(container, _t("voip|get_call_link"))).toBeInTheDocument();
539-
});
540-
541-
it("shows the external conference if the room has Knock join rules", () => {
542-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
543-
544-
const { container } = render(<RoomHeader room={room} />, getWrapper());
545-
expect(getByLabelText(container, _t("voip|get_call_link"))).toBeInTheDocument();
546-
});
547518

548519
it("don't show external conference button if the call is not shown", () => {
549-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
550520
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(false);
551-
521+
jest.spyOn(SdkConfig, "get").mockImplementation((key) => {
522+
return { guest_spa_url: "https://guest_spa_url.com", url: "https://spa_url.com" };
523+
});
552524
let { container } = render(<RoomHeader room={room} />, getWrapper());
553525
expect(screen.queryByLabelText(_t("voip|get_call_link"))).not.toBeInTheDocument();
554526

@@ -558,69 +530,6 @@ describe("RoomHeader", () => {
558530

559531
expect(getByLabelText(container, _t("voip|get_call_link"))).toBeInTheDocument();
560532
});
561-
562-
it("don't show external conference button if now guest spa link is configured", () => {
563-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
564-
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
565-
566-
jest.spyOn(SdkConfig, "get").mockImplementation((key) => {
567-
if (key === "element_call") {
568-
return { url: "https://example2.com" };
569-
}
570-
return oldGet(key);
571-
});
572-
573-
render(<RoomHeader room={room} />, getWrapper());
574-
575-
// We only change the SdkConfig and show that this everything else is
576-
// configured so that the call link button is shown.
577-
578-
jest.spyOn(SdkConfig, "get").mockImplementation((key) => {
579-
if (key === "element_call") {
580-
return { guest_spa_url: "https://guest_spa_url.com", url: "https://example2.com" };
581-
}
582-
return oldGet(key);
583-
});
584-
585-
expect(screen.queryByLabelText(_t("voip|get_call_link"))).not.toBeInTheDocument();
586-
const { container } = render(<RoomHeader room={room} />, getWrapper());
587-
expect(getByLabelText(container, _t("voip|get_call_link"))).toBeInTheDocument();
588-
});
589-
it("opens the share dialog with the correct share link in an encrypted room", () => {
590-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
591-
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
592-
jest.spyOn(room, "hasEncryptionStateEvent").mockReturnValue(true);
593-
594-
const { container } = render(<RoomHeader room={room} />, getWrapper());
595-
const modalSpy = jest.spyOn(Modal, "createDialog");
596-
fireEvent.click(getByLabelText(container, _t("voip|get_call_link")));
597-
const target =
598-
"https://guest_spa_url.com/room/#/!1:example.org?roomId=%211%3Aexample.org&perParticipantE2EE=true&viaServers=example.org";
599-
expect(modalSpy).toHaveBeenCalled();
600-
const arg0 = modalSpy.mock.calls[0][0];
601-
const arg1 = modalSpy.mock.calls[0][1] as any;
602-
expect(arg0).toEqual(ShareDialog);
603-
const { customTitle, subtitle } = arg1;
604-
expect({ customTitle, subtitle }).toEqual({
605-
customTitle: "Conference invite link",
606-
subtitle: _t("share|share_call_subtitle"),
607-
});
608-
expect(arg1.target.toString()).toEqual(target);
609-
});
610-
611-
it("share dialog has correct link in an unencrypted room", () => {
612-
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
613-
jest.spyOn(room, "hasEncryptionStateEvent").mockReturnValue(false);
614-
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
615-
616-
const { container } = render(<RoomHeader room={room} />, getWrapper());
617-
const modalSpy = jest.spyOn(Modal, "createDialog");
618-
fireEvent.click(getByLabelText(container, _t("voip|get_call_link")));
619-
const target =
620-
"https://guest_spa_url.com/room/#/!1:example.org?roomId=%211%3Aexample.org&viaServers=example.org";
621-
const arg1 = modalSpy.mock.calls[0][1] as any;
622-
expect(arg1.target.toString()).toEqual(target);
623-
});
624533
});
625534

626535
describe("public room", () => {

0 commit comments

Comments
 (0)