mirror of
https://github.com/vector-im/element-web.git
synced 2026-03-05 05:21:15 +01:00
mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts
And a couple of gitignore tweaks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
297 lines
11 KiB
TypeScript
297 lines
11 KiB
TypeScript
/*
|
|
* Copyright 2025 New Vector 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 React from "react";
|
|
import { MatrixWidgetType } from "matrix-widget-api";
|
|
import { type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
|
|
|
import {
|
|
WidgetContextMenuViewModel,
|
|
type WidgetContextMenuViewModelProps,
|
|
} from "../../../src/viewmodels/right-panel/WidgetContextMenuViewModel";
|
|
import { stubClient } from "../../test-utils";
|
|
import WidgetUtils from "../../../src/utils/WidgetUtils";
|
|
import { type IApp } from "../../../src/utils/WidgetUtils-types";
|
|
import { Container, WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
|
|
import * as livestream from "../../../src/Livestream";
|
|
import Modal from "../../../src/Modal";
|
|
import SettingsStore from "../../../src/settings/SettingsStore";
|
|
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
|
import * as widgetStore from "../../../src/stores/WidgetStore";
|
|
import { WidgetMessagingStore } from "../../../src/stores/widgets/WidgetMessagingStore";
|
|
import { type WidgetMessaging } from "../../../src/stores/widgets/WidgetMessaging";
|
|
|
|
describe("WidgetContextMenuViewModel", () => {
|
|
const widgetId = "w1";
|
|
const eventId = "e1";
|
|
const roomId = "r1";
|
|
const userId = "@user-id:server";
|
|
|
|
const app: IApp = {
|
|
id: widgetId,
|
|
eventId,
|
|
roomId,
|
|
type: MatrixWidgetType.Custom,
|
|
url: "https://example.com",
|
|
name: "Example 1",
|
|
creatorUserId: userId,
|
|
avatar_url: undefined,
|
|
};
|
|
|
|
let client: MatrixClient;
|
|
const defaultProps: WidgetContextMenuViewModelProps = {
|
|
menuDisplayed: true,
|
|
room: undefined,
|
|
roomId,
|
|
cli: stubClient(),
|
|
app,
|
|
showUnpin: true,
|
|
userWidget: true,
|
|
trigger: <></>,
|
|
onEditClick: jest.fn(),
|
|
onDeleteClick: jest.fn(),
|
|
onFinished: jest.fn(),
|
|
};
|
|
|
|
beforeEach(() => {
|
|
jest.spyOn(WidgetUtils, "canUserModifyWidgets").mockReturnValue(true);
|
|
jest.spyOn(WidgetUtils, "isManagedByManager").mockReturnValue(true);
|
|
jest.spyOn(WidgetUtils, "editWidget").mockReturnValue();
|
|
const mockMessaging = {
|
|
on: () => {},
|
|
off: () => {},
|
|
stop: () => {},
|
|
widgetApi: {
|
|
hasCapability: jest.fn(),
|
|
},
|
|
} as unknown as WidgetMessaging;
|
|
jest.spyOn(WidgetMessagingStore.instance, "getMessagingForUid").mockReturnValue(mockMessaging);
|
|
client = stubClient();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it("should return the snapshot", () => {
|
|
const vm = new WidgetContextMenuViewModel(defaultProps);
|
|
expect(vm.getSnapshot()).toMatchObject({
|
|
showStreamAudioStreamButton: false, // because widget type is custom and not jitsi
|
|
showEditButton: true, // because default mock return true on canUserModifyWidgets and isManagedByManager
|
|
showRevokeButton: false,
|
|
showDeleteButton: true,
|
|
showSnapshotButton: false, // because no default value for sdkconfig "enableWidgetScreenshots"
|
|
showMoveButtons: [false, false],
|
|
canModify: true,
|
|
isMenuOpened: true,
|
|
trigger: <></>,
|
|
});
|
|
});
|
|
|
|
it("should call edit widget no custom edit function passed and room exist", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
onEditClick: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onEditClick();
|
|
expect(WidgetUtils.editWidget).toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call custom onEditClick if passed as props and room exist", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onEditClick();
|
|
|
|
expect(props.onEditClick).toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should just call finish if no custom onEditClick is passed as props and does not room exist", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
room: undefined,
|
|
onEditClick: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onEditClick();
|
|
|
|
expect(WidgetUtils.editWidget).not.toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should move widget position when onmovebutton is called", () => {
|
|
jest.spyOn(WidgetLayoutStore.instance, "moveWithinContainer").mockReturnValue();
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onMoveButton(1);
|
|
|
|
expect(WidgetLayoutStore.instance.moveWithinContainer).toHaveBeenCalledWith(
|
|
props.room,
|
|
Container.Top,
|
|
props.app,
|
|
1,
|
|
);
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should throw error when onmovebutton is called and no room is given", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
room: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
|
|
expect(() => vm.onMoveButton(1)).toThrow();
|
|
});
|
|
|
|
it("should startJitsiAudioLivestream when onStreamAudioClick button is clicked", async () => {
|
|
jest.spyOn(livestream, "startJitsiAudioLivestream").mockImplementation(jest.fn());
|
|
jest.spyOn(livestream, "getConfigLivestreamUrl").mockReturnValue("https://url");
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onStreamAudioClick();
|
|
await expect(livestream.startJitsiAudioLivestream).toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should show modal when startJitsiAudioLivestream is on error and onStreamAudioClick button is clicked", async () => {
|
|
jest.spyOn(livestream, "startJitsiAudioLivestream").mockImplementation(() => {
|
|
console.log("failllllled");
|
|
throw new Error("Failed");
|
|
});
|
|
jest.spyOn(livestream, "getConfigLivestreamUrl").mockReturnValue("https://url");
|
|
jest.spyOn(Modal, "createDialog").mockReturnValue({
|
|
finished: Promise.resolve([true, true, false]),
|
|
close: jest.fn(),
|
|
});
|
|
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
await vm.onStreamAudioClick();
|
|
expect(Modal.createDialog).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should throw when no room is given and onStreamAudioClick button is clicked", async () => {
|
|
jest.spyOn(livestream, "startJitsiAudioLivestream").mockImplementation(jest.fn());
|
|
jest.spyOn(livestream, "getConfigLivestreamUrl").mockReturnValue("https://url");
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
await vm.onStreamAudioClick();
|
|
// nothing happened
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call custom delete function when it is given in props", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
vm.onDeleteClick();
|
|
expect(props.onDeleteClick).toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should display modal when no custom function is provided and a room is given", () => {
|
|
jest.spyOn(Modal, "createDialog").mockReturnValue({
|
|
finished: Promise.resolve([true, true, false]),
|
|
close: jest.fn(),
|
|
});
|
|
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
onDeleteClick: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
|
|
vm.onDeleteClick();
|
|
|
|
expect(Modal.createDialog).toHaveBeenCalled();
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should do nothing when onDeleteClick and no custom function and no room is provided", () => {
|
|
const props = {
|
|
...defaultProps,
|
|
room: undefined,
|
|
onDeleteClick: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
|
|
vm.onDeleteClick();
|
|
|
|
expect(props.onFinished).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should set new level for allowedwidget when onrevoke button is clicked", () => {
|
|
const current = { [eventId]: true };
|
|
jest.spyOn(SettingsStore, "getValue").mockReturnValue(current);
|
|
jest.spyOn(SettingsStore, "firstSupportedLevel").mockReturnValue(SettingLevel.DEFAULT);
|
|
jest.spyOn(SettingsStore, "setValue").mockResolvedValue();
|
|
jest.spyOn(widgetStore, "isAppWidget").mockReturnValue(true);
|
|
const props = {
|
|
...defaultProps,
|
|
room: new Room(roomId, client, userId),
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
|
|
vm.onRevokeClick();
|
|
|
|
expect(SettingsStore.setValue).toHaveBeenCalledWith(
|
|
"allowedWidgets",
|
|
props.roomId,
|
|
SettingLevel.DEFAULT,
|
|
current,
|
|
);
|
|
|
|
const current2 = { [eventId]: false };
|
|
jest.spyOn(SettingsStore, "getValue").mockReturnValue(current2);
|
|
jest.spyOn(SettingsStore, "firstSupportedLevel").mockReturnValue(SettingLevel.DEFAULT);
|
|
jest.spyOn(SettingsStore, "setValue").mockResolvedValue();
|
|
jest.spyOn(widgetStore, "isAppWidget").mockReturnValue(false);
|
|
|
|
vm.onRevokeClick();
|
|
|
|
expect(SettingsStore.setValue).toHaveBeenCalledWith(
|
|
"allowedWidgets",
|
|
props.roomId,
|
|
SettingLevel.DEFAULT,
|
|
current2,
|
|
);
|
|
});
|
|
|
|
it("should throw an error when first supported level is not set", () => {
|
|
jest.spyOn(SettingsStore, "firstSupportedLevel").mockReturnValue(null);
|
|
const props = {
|
|
...defaultProps,
|
|
room: undefined,
|
|
onDeleteClick: undefined,
|
|
};
|
|
const vm = new WidgetContextMenuViewModel(props);
|
|
|
|
expect(() => vm.onRevokeClick()).toThrow();
|
|
});
|
|
});
|