Use context provided RoomViewStore within the RoomView component hierarchy (#31077)
* Update ContentMessages.ts Update ContentMessages.ts * update PlaybackQueue.ts * Update SpaceHierarchy.tsx * Update ThreadView.tsx * Update RoomCallBanner.tsx * Update useRoomCall.tsx * Update DateSeparator.tsx * Update TimelineCard.tsx * Update UserInfoBasicOptions * Update slask-commands/utils.ts * lint * Update PlaybackQueue, MVoiceMessageBody and UserInfoBasicOptionsView tests. * Update RoomHeader-test.tsx * lint * Add ts docs * Update utils-test.tsx * Update message-test.ts * coverage * lint * Improve naming --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -66,7 +66,7 @@ describe("SlashCommands", () => {
|
||||
|
||||
describe("/topic", () => {
|
||||
it("sets topic", async () => {
|
||||
const command = getCommand("/topic pizza");
|
||||
const command = getCommand(roomId, "/topic pizza");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd!.run(client, "room-id", null, command.args);
|
||||
@@ -75,7 +75,7 @@ describe("SlashCommands", () => {
|
||||
|
||||
it("should show topic modal if no args passed", async () => {
|
||||
const spy = jest.spyOn(Modal, "createDialog");
|
||||
const command = getCommand("/topic")!;
|
||||
const command = getCommand(roomId, "/topic")!;
|
||||
await command.cmd!.run(client, roomId, null);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
@@ -109,12 +109,12 @@ describe("SlashCommands", () => {
|
||||
describe("isEnabled", () => {
|
||||
it("should return true for Room", () => {
|
||||
setCurrentRoom();
|
||||
expect(command.isEnabled(client)).toBe(true);
|
||||
expect(command.isEnabled(client, roomId)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for LocalRoom", () => {
|
||||
setCurrentLocalRoom();
|
||||
expect(command.isEnabled(client)).toBe(false);
|
||||
expect(command.isEnabled(client, roomId)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -126,7 +126,7 @@ describe("SlashCommands", () => {
|
||||
});
|
||||
|
||||
it("should be enabled by default", () => {
|
||||
expect(command.isEnabled(client)).toBe(true);
|
||||
expect(command.isEnabled(client, roomId)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -199,11 +199,11 @@ describe("SlashCommands", () => {
|
||||
room2.getCanonicalAlias = jest.fn().mockReturnValue("#baz:bar");
|
||||
mocked(client.getRooms).mockReturnValue([room1, room2]);
|
||||
|
||||
const command = getCommand("/part #foo:bar");
|
||||
const command = getCommand(room1.roomId, "/part #foo:bar");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd!.run(client, "room-id", null, command.args);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything());
|
||||
await command.cmd!.run(client, room1.roomId, null, command.args);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith(room1.roomId, expect.anything());
|
||||
});
|
||||
|
||||
it("should part room matching alt alias if found", async () => {
|
||||
@@ -213,11 +213,11 @@ describe("SlashCommands", () => {
|
||||
room2.getAltAliases = jest.fn().mockReturnValue(["#baz:bar"]);
|
||||
mocked(client.getRooms).mockReturnValue([room1, room2]);
|
||||
|
||||
const command = getCommand("/part #foo:bar");
|
||||
const command = getCommand(room1.roomId, "/part #foo:bar");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd!.run(client, "room-id", null, command.args!);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything());
|
||||
await command.cmd!.run(client, room1.roomId, null, command.args!);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith(room1.roomId, expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { PlaybackQueue } from "../../../src/audio/PlaybackQueue";
|
||||
import { type Playback, PlaybackState } from "../../../src/audio/Playback";
|
||||
import { UPDATE_EVENT } from "../../../src/stores/AsyncStore";
|
||||
import { MockedPlayback } from "./MockedPlayback";
|
||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||
|
||||
describe("PlaybackQueue", () => {
|
||||
let playbackQueue: PlaybackQueue;
|
||||
@@ -21,7 +22,7 @@ describe("PlaybackQueue", () => {
|
||||
mockRoom = {
|
||||
getMember: jest.fn(),
|
||||
} as unknown as Mocked<Room>;
|
||||
playbackQueue = new PlaybackQueue(mockRoom);
|
||||
playbackQueue = new PlaybackQueue(mockRoom, SdkContextClass.instance.roomViewStore);
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -75,7 +76,7 @@ describe("PlaybackQueue", () => {
|
||||
`mx_voice_message_clocks_${mockRoom.roomId}`,
|
||||
JSON.stringify(Array.from(clockStates.entries())),
|
||||
);
|
||||
playbackQueue = new PlaybackQueue(mockRoom);
|
||||
playbackQueue = new PlaybackQueue(mockRoom, SdkContextClass.instance.roomViewStore);
|
||||
|
||||
// @ts-ignore
|
||||
expect(playbackQueue.clockStates.has("a")).toBe(true);
|
||||
|
||||
87
test/unit-tests/autocomplete/CommandProvider-test.ts
Normal file
87
test/unit-tests/autocomplete/CommandProvider-test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 { type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import CommandProvider from "../../../src/autocomplete/CommandProvider";
|
||||
import { stubClient } from "../../test-utils";
|
||||
import { Command } from "../../../src/slash-commands/command";
|
||||
import { CommandCategories } from "../../../src/slash-commands/interface";
|
||||
import { _td } from "../../../src/languageHandler";
|
||||
import * as SlashCommands from "../../../src/SlashCommands";
|
||||
|
||||
describe("CommandProvider", () => {
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
room = {
|
||||
roomId: "!room:server",
|
||||
} as Room;
|
||||
});
|
||||
|
||||
it("should filter out disabled commands when arguments are provided", async () => {
|
||||
// Create a disabled command
|
||||
const disabledCommand = new Command({
|
||||
command: "disabled",
|
||||
args: "<arg>",
|
||||
description: _td("slash_command|spoiler"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.messages,
|
||||
isEnabled: () => false,
|
||||
});
|
||||
|
||||
// Create an enabled command
|
||||
const enabledCommand = new Command({
|
||||
command: "enabled",
|
||||
args: "<arg>",
|
||||
description: _td("slash_command|shrug"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.messages,
|
||||
isEnabled: () => true,
|
||||
});
|
||||
|
||||
// Mock the Commands array and CommandMap
|
||||
Object.defineProperty(SlashCommands, "Commands", {
|
||||
value: [disabledCommand, enabledCommand],
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const mockCommandMap = new Map<string, Command>();
|
||||
mockCommandMap.set("disabled", disabledCommand);
|
||||
mockCommandMap.set("enabled", enabledCommand);
|
||||
|
||||
Object.defineProperty(SlashCommands, "CommandMap", {
|
||||
value: mockCommandMap,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const provider = new CommandProvider(room);
|
||||
|
||||
// When we search for a disabled command with arguments
|
||||
const completions = await provider.getCompletions("/disabled test", {
|
||||
beginning: true,
|
||||
start: 0,
|
||||
end: 14,
|
||||
});
|
||||
|
||||
// Then we should get no completions because the command is disabled
|
||||
expect(completions).toEqual([]);
|
||||
|
||||
// When we search for an enabled command with arguments
|
||||
const enabledCompletions = await provider.getCompletions("/enabled test", {
|
||||
beginning: true,
|
||||
start: 0,
|
||||
end: 13,
|
||||
});
|
||||
|
||||
// Then we should get the completion because the command is enabled
|
||||
// The completion preserves the arguments when the command matches
|
||||
expect(enabledCompletions.length).toBe(1);
|
||||
expect(enabledCompletions[0].completion).toBe("/enabled test");
|
||||
});
|
||||
});
|
||||
@@ -9,18 +9,19 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||
import { type HierarchyRoom, JoinRule, type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { type HierarchyRoom, JoinRule, MatrixError, type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { mkStubRoom, stubClient } from "../../../test-utils";
|
||||
import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import SpaceHierarchy, { showRoom, toLocalRoom } from "../../../../src/components/structures/SpaceHierarchy";
|
||||
import SpaceHierarchy, { showRoom, toLocalRoom, joinRoom } from "../../../../src/components/structures/SpaceHierarchy";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { type RoomViewStore } from "../../../../src/stores/RoomViewStore";
|
||||
|
||||
describe("SpaceHierarchy", () => {
|
||||
describe("showRoom", () => {
|
||||
@@ -66,6 +67,59 @@ describe("SpaceHierarchy", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("joinRoom", () => {
|
||||
let client: MatrixClient;
|
||||
let hierarchy: RoomHierarchy;
|
||||
let roomViewStore: RoomViewStore;
|
||||
let room: Room;
|
||||
const roomId = "!room:server";
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
client = MatrixClientPeg.safeGet();
|
||||
room = new Room("space-id", client, "@alice:example.com");
|
||||
hierarchy = new RoomHierarchy(room);
|
||||
roomViewStore = {
|
||||
showJoinRoomError: jest.fn(),
|
||||
} as unknown as RoomViewStore;
|
||||
|
||||
jest.spyOn(client, "isGuest").mockReturnValue(false);
|
||||
jest.spyOn(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should handle MatrixError exceptions when joining room", async () => {
|
||||
// Mock joinRoom to throw a MatrixError
|
||||
const matrixError = new MatrixError({ errcode: "M_FORBIDDEN", error: "Access denied" });
|
||||
mocked(client.joinRoom).mockRejectedValue(matrixError);
|
||||
|
||||
// Attempt to join the room
|
||||
await expect(joinRoom(client, roomViewStore, hierarchy, roomId)).rejects.toThrow(matrixError);
|
||||
|
||||
// Verify that showJoinRoomError was called with the MatrixError
|
||||
expect(roomViewStore.showJoinRoomError).toHaveBeenCalledWith(matrixError, roomId);
|
||||
});
|
||||
|
||||
it("should handle non-MatrixError exceptions when joining room", async () => {
|
||||
// Mock joinRoom to throw a non-MatrixError
|
||||
const customError = new Error("Custom error");
|
||||
mocked(client.joinRoom).mockRejectedValue(customError);
|
||||
|
||||
// Attempt to join the room
|
||||
await expect(joinRoom(client, roomViewStore, hierarchy, roomId)).rejects.toThrow("Custom error");
|
||||
|
||||
// Verify that showJoinRoomError was called with a MatrixError wrapper
|
||||
expect(roomViewStore.showJoinRoomError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
errcode: undefined,
|
||||
data: expect.objectContaining({
|
||||
error: "Unknown error",
|
||||
}),
|
||||
}),
|
||||
roomId,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toLocalRoom", () => {
|
||||
stubClient();
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
|
||||
@@ -204,7 +204,7 @@ describe("<UserOptionsSection />", () => {
|
||||
jest.spyOn(Modal, "createDialog");
|
||||
|
||||
const { result } = renderUserInfoBasicOptionsViewModelHook();
|
||||
result.current.onInviteUserButton(new Event("click"));
|
||||
result.current.onInviteUserButton("roomId", new Event("click"));
|
||||
|
||||
// check that we have called .invite
|
||||
expect(spy).toHaveBeenCalledWith([defaultMember.userId]);
|
||||
|
||||
@@ -30,7 +30,9 @@ import { CallStore } from "../../../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { ConnectionState } from "../../../../../src/models/Call";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext";
|
||||
import { type IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
|
||||
describe("<RoomCallBanner />", () => {
|
||||
let client: Mocked<MatrixClient>;
|
||||
@@ -42,6 +44,15 @@ describe("<RoomCallBanner />", () => {
|
||||
roomId: "!1:example.org",
|
||||
};
|
||||
|
||||
const mockRoomViewStore = {
|
||||
isViewingCall: jest.fn().mockReturnValue(false),
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
};
|
||||
|
||||
let roomContext: IRoomState;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
|
||||
@@ -59,6 +70,16 @@ describe("<RoomCallBanner />", () => {
|
||||
|
||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
|
||||
// Reset the mock RoomViewStore
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(false);
|
||||
|
||||
// Create a stable room context for this test
|
||||
roomContext = {
|
||||
...RoomContext,
|
||||
roomId: room.roomId,
|
||||
roomViewStore: mockRoomViewStore,
|
||||
} as unknown as IRoomState;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -66,7 +87,11 @@ describe("<RoomCallBanner />", () => {
|
||||
});
|
||||
|
||||
const renderBanner = async (props = {}): Promise<void> => {
|
||||
render(<RoomCallBanner {...defaultProps} {...props} />);
|
||||
render(
|
||||
<ScopedRoomContextProvider {...roomContext}>
|
||||
<RoomCallBanner {...defaultProps} {...props} />
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
await act(() => Promise.resolve()); // Let effects settle
|
||||
};
|
||||
|
||||
@@ -117,8 +142,7 @@ describe("<RoomCallBanner />", () => {
|
||||
});
|
||||
|
||||
it("doesn't show banner if the call is shown", async () => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall");
|
||||
mocked(SdkContextClass.instance.roomViewStore.isViewingCall).mockReturnValue(true);
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(true);
|
||||
await renderBanner();
|
||||
const banner = await screen.queryByText("Video call");
|
||||
expect(banner).toBeFalsy();
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 { render, screen } from "jest-matrix-react";
|
||||
|
||||
import SlashCommandHelpDialog from "../../../../../src/components/views/dialogs/SlashCommandHelpDialog";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { Command } from "../../../../../src/slash-commands/command";
|
||||
import { CommandCategories } from "../../../../../src/slash-commands/interface";
|
||||
import { _t, _td } from "../../../../../src/languageHandler";
|
||||
import * as SlashCommands from "../../../../../src/SlashCommands";
|
||||
|
||||
describe("SlashCommandHelpDialog", () => {
|
||||
const roomId = "!room:server";
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
});
|
||||
|
||||
it("should filter out disabled commands", () => {
|
||||
// Create commands with some enabled and some disabled
|
||||
const enabledCommand = new Command({
|
||||
command: "enabled",
|
||||
args: "<arg>",
|
||||
description: _td("slash_command|spoiler"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.messages,
|
||||
isEnabled: () => true,
|
||||
});
|
||||
|
||||
const disabledCommand = new Command({
|
||||
command: "disabled",
|
||||
args: "<arg>",
|
||||
description: _td("slash_command|shrug"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.messages,
|
||||
isEnabled: () => false,
|
||||
});
|
||||
|
||||
// Mock the Commands array by replacing the property
|
||||
Object.defineProperty(SlashCommands, "Commands", {
|
||||
value: [enabledCommand, disabledCommand],
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const onFinished = jest.fn();
|
||||
render(<SlashCommandHelpDialog roomId={roomId} onFinished={onFinished} />);
|
||||
|
||||
// The enabled command should be visible
|
||||
expect(screen.getByText("/enabled")).toBeInTheDocument();
|
||||
|
||||
// The disabled command should not be visible
|
||||
expect(screen.queryByText("/disabled")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should group commands by category", () => {
|
||||
const messageCommand = new Command({
|
||||
command: "msg",
|
||||
args: "",
|
||||
description: _td("slash_command|plain"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.messages,
|
||||
});
|
||||
|
||||
const adminCommand = new Command({
|
||||
command: "admin",
|
||||
args: "",
|
||||
description: _td("slash_command|upgraderoom"),
|
||||
runFn: jest.fn(),
|
||||
category: CommandCategories.admin,
|
||||
});
|
||||
|
||||
Object.defineProperty(SlashCommands, "Commands", {
|
||||
value: [messageCommand, adminCommand],
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const onFinished = jest.fn();
|
||||
render(<SlashCommandHelpDialog roomId={roomId} onFinished={onFinished} />);
|
||||
|
||||
// Both category headers should be present
|
||||
expect(screen.getByText(_t(CommandCategories.messages))).toBeInTheDocument();
|
||||
expect(screen.getByText(_t(CommandCategories.admin))).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,6 @@ import { type TimestampToEventResponse, ConnectionError, HTTPError, MatrixError
|
||||
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { type ViewRoomPayload } from "../../../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import { formatFullDateNoTime } from "../../../../../src/DateUtils";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { UIFeature } from "../../../../../src/settings/UIFeature";
|
||||
@@ -26,6 +25,9 @@ import {
|
||||
waitEnoughCyclesForModal,
|
||||
} from "../../../../test-utils";
|
||||
import DateSeparator from "../../../../../src/components/views/messages/DateSeparator";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext";
|
||||
import { type IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
|
||||
jest.mock("../../../../../src/settings/SettingsStore");
|
||||
|
||||
@@ -40,13 +42,25 @@ describe("DateSeparator", () => {
|
||||
roomId,
|
||||
};
|
||||
|
||||
const mockRoomViewStore = {
|
||||
getRoomId: jest.fn().mockReturnValue(roomId),
|
||||
};
|
||||
|
||||
const defaultRoomContext = {
|
||||
...RoomContext,
|
||||
roomId,
|
||||
roomViewStore: mockRoomViewStore,
|
||||
} as unknown as IRoomState;
|
||||
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
timestampToEvent: jest.fn(),
|
||||
});
|
||||
const getComponent = (props = {}) =>
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<DateSeparator {...defaultProps} {...props} />
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>
|
||||
<DateSeparator {...defaultProps} {...props} />
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
@@ -74,7 +88,7 @@ describe("DateSeparator", () => {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(roomId);
|
||||
mockRoomViewStore.getRoomId.mockReturnValue(roomId);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -200,7 +214,7 @@ describe("DateSeparator", () => {
|
||||
// network request is taking a while, so we got bored, switched rooms; we
|
||||
// shouldn't jump back to the previous room after the network request
|
||||
// happens to finish later.
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!some-other-room");
|
||||
mockRoomViewStore.getRoomId.mockReturnValue("!some-other-room");
|
||||
|
||||
// Jump to "last week"
|
||||
mockClient.timestampToEvent.mockResolvedValue({
|
||||
@@ -230,7 +244,7 @@ describe("DateSeparator", () => {
|
||||
// network request is taking a while, so we got bored, switched rooms; we
|
||||
// shouldn't jump back to the previous room after the network request
|
||||
// happens to finish later.
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!some-other-room");
|
||||
mockRoomViewStore.getRoomId.mockReturnValue("!some-other-room");
|
||||
|
||||
// Try to jump to "last week" but we want an error to occur and ensure that
|
||||
// we don't show an error dialog for it since we already switched away to
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { MediaEventHelper } from "../../../../../src/utils/MediaEventHelper
|
||||
import MVoiceMessageBody from "../../../../../src/components/views/messages/MVoiceMessageBody";
|
||||
import { PlaybackQueue } from "../../../../../src/audio/PlaybackQueue";
|
||||
import { createTestClient } from "../../../../test-utils";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
|
||||
describe("<MVvoiceMessageBody />", () => {
|
||||
let event: MatrixEvent;
|
||||
@@ -25,7 +26,7 @@ describe("<MVvoiceMessageBody />", () => {
|
||||
|
||||
const matrixClient = createTestClient();
|
||||
const room = new Room("!TESTROOM", matrixClient, "@alice:example.org");
|
||||
const playbackQueue = new PlaybackQueue(room);
|
||||
const playbackQueue = new PlaybackQueue(room, SdkContextClass.instance.roomViewStore);
|
||||
|
||||
jest.spyOn(PlaybackQueue, "forRoom").mockReturnValue(playbackQueue);
|
||||
jest.spyOn(playbackQueue, "unsortedEnqueue").mockReturnValue(undefined);
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("<UserOptionsSection />", () => {
|
||||
onInsertPillButton: () => jest.fn(),
|
||||
onReadReceiptButton: () => jest.fn(),
|
||||
onShareUserClick: () => jest.fn(),
|
||||
onInviteUserButton: (evt: Event) => Promise.resolve(),
|
||||
onInviteUserButton: (fallbackRoomId: string, evt: Event) => Promise.resolve(),
|
||||
onOpenDmForUser: (member: Member) => Promise.resolve(),
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ import * as TestUtils from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import EditorModel from "../../../../../src/editor/model";
|
||||
import { createPartCreator, createRenderer } from "../../../editor/mock";
|
||||
import { CommandPartCreator } from "../../../../../src/editor/parts";
|
||||
import DocumentOffset from "../../../../../src/editor/offset";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
|
||||
describe("BasicMessageComposer", () => {
|
||||
@@ -103,6 +106,25 @@ describe("BasicMessageComposer", () => {
|
||||
const placeholder = input[0].style.getPropertyValue("--placeholder");
|
||||
expect(placeholder).toMatch("'w\\\\e'");
|
||||
});
|
||||
|
||||
it("should not consider typing for unknown or disabled slash commands", async () => {
|
||||
// create a command part which represents a slash command the client doesn't recognise
|
||||
const commandPc = new CommandPartCreator(room as unknown as Room, client as unknown as MatrixClient, null);
|
||||
const commandPart = commandPc.command("/unknown do stuff");
|
||||
const model = new EditorModel([commandPart], commandPc, renderer);
|
||||
|
||||
// spy on typingStore.setSelfTyping
|
||||
const spy = jest.spyOn(SdkContextClass.instance.typingStore, "setSelfTyping");
|
||||
|
||||
render(<BasicMessageComposer model={model} room={room} />);
|
||||
|
||||
// simulate typing by updating the model - this will call the component's update callback
|
||||
await model.update(commandPart.text, "insertText", new DocumentOffset(commandPart.text.length, true));
|
||||
|
||||
// Since the command is not in CommandMap, it should not be considered typing
|
||||
expect(spy).toHaveBeenCalledWith(room.roomId, null, false);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
function generateMockDataTransferForString(string: string): DataTransfer {
|
||||
|
||||
@@ -40,6 +40,9 @@ import { filterConsole, stubClient } from "../../../../../test-utils";
|
||||
import RoomHeader from "../../../../../../src/components/views/rooms/RoomHeader/RoomHeader";
|
||||
import DMRoomMap from "../../../../../../src/utils/DMRoomMap";
|
||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../src/contexts/ScopedRoomContext";
|
||||
import { type IRoomState } from "../../../../../../src/components/structures/RoomView";
|
||||
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
||||
import RightPanelStore from "../../../../../../src/stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import LegacyCallHandler from "../../../../../../src/LegacyCallHandler";
|
||||
@@ -52,7 +55,6 @@ import * as ShieldUtils from "../../../../../../src/utils/ShieldUtils";
|
||||
import { Container, WidgetLayoutStore } from "../../../../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import { _t } from "../../../../../../src/languageHandler";
|
||||
import { SdkContextClass } from "../../../../../../src/contexts/SDKContext";
|
||||
import WidgetStore, { type IApp } from "../../../../../../src/stores/WidgetStore";
|
||||
import { UIFeature } from "../../../../../../src/settings/UIFeature";
|
||||
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
|
||||
@@ -65,14 +67,6 @@ jest.mock("../../../../../../src/hooks/right-panel/useCurrentPhase", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function getWrapper(): RenderOptions {
|
||||
return {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>{children}</MatrixClientContext.Provider>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
describe("RoomHeader", () => {
|
||||
filterConsole(
|
||||
"[getType] Room !1:example.org does not have an m.room.create event",
|
||||
@@ -84,6 +78,25 @@ describe("RoomHeader", () => {
|
||||
|
||||
let setCardSpy: jest.SpyInstance | undefined;
|
||||
|
||||
const mockRoomViewStore = {
|
||||
isViewingCall: jest.fn().mockReturnValue(false),
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
};
|
||||
|
||||
let roomContext: IRoomState;
|
||||
|
||||
function getWrapper(): RenderOptions {
|
||||
return {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
|
||||
<ScopedRoomContextProvider {...roomContext}>{children}</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
stubClient();
|
||||
room = new Room(ROOM_ID, MatrixClientPeg.get()!, "@alice:example.org", {
|
||||
@@ -99,6 +112,16 @@ describe("RoomHeader", () => {
|
||||
// Mock CallStore.instance.getCall to return null by default
|
||||
// Individual tests can override this when they need a specific Call object
|
||||
jest.spyOn(CallStore.instance, "getCall").mockReturnValue(null);
|
||||
|
||||
// Reset the mock RoomViewStore
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(false);
|
||||
|
||||
// Create a stable room context for this test
|
||||
roomContext = {
|
||||
...RoomContext,
|
||||
roomId: ROOM_ID,
|
||||
roomViewStore: mockRoomViewStore,
|
||||
} as unknown as IRoomState;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -581,7 +604,7 @@ describe("RoomHeader", () => {
|
||||
it("close lobby button is shown", async () => {
|
||||
mockRoomMembers(room, 3);
|
||||
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(true);
|
||||
render(<RoomHeader room={room} />, getWrapper());
|
||||
getByLabelText(document.body, "Close lobby");
|
||||
});
|
||||
@@ -590,21 +613,21 @@ describe("RoomHeader", () => {
|
||||
mockRoomMembers(room, 3);
|
||||
// Mock CallStore to return a call with 3 participants
|
||||
jest.spyOn(CallStore.instance, "getCall").mockReturnValue(createMockCall(ROOM_ID, 3));
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(true);
|
||||
|
||||
render(<RoomHeader room={room} />, getWrapper());
|
||||
getByLabelText(document.body, "Close lobby");
|
||||
});
|
||||
|
||||
it("don't show external conference button if the call is not shown", () => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(false);
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(false);
|
||||
jest.spyOn(SdkConfig, "get").mockImplementation((key) => {
|
||||
return { guest_spa_url: "https://guest_spa_url.com", url: "https://spa_url.com" };
|
||||
});
|
||||
render(<RoomHeader room={room} />, getWrapper());
|
||||
expect(screen.queryByLabelText(_t("voip|get_call_link"))).not.toBeInTheDocument();
|
||||
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "isViewingCall").mockReturnValue(true);
|
||||
mockRoomViewStore.isViewingCall.mockReturnValue(true);
|
||||
|
||||
render(<RoomHeader room={room} />, getWrapper());
|
||||
|
||||
|
||||
@@ -85,12 +85,13 @@ describe("handleClipboardEvent", () => {
|
||||
clipboardData: { files: ["something here"], types: [] },
|
||||
});
|
||||
const output = handleClipboardEvent(originalEvent, originalEvent.clipboardData, mockRoomState, mockClient);
|
||||
|
||||
const mockReplyToEvent = {} as unknown as MatrixEvent;
|
||||
expect(sendContentListToRoomSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendContentListToRoomSpy).toHaveBeenCalledWith(
|
||||
originalEvent.clipboardData?.files,
|
||||
mockRoom.roomId,
|
||||
undefined, // this is the event relation, an optional arg
|
||||
mockReplyToEvent,
|
||||
mockClient,
|
||||
mockRoomState.timelineRenderingType,
|
||||
);
|
||||
@@ -103,6 +104,7 @@ describe("handleClipboardEvent", () => {
|
||||
clipboardData: { files: ["something here"], types: [] },
|
||||
});
|
||||
const mockEventRelation = {} as unknown as IEventRelation;
|
||||
const mockReplyToEvent = {} as unknown as MatrixEvent;
|
||||
const output = handleClipboardEvent(
|
||||
originalEvent,
|
||||
originalEvent.clipboardData,
|
||||
@@ -116,6 +118,7 @@ describe("handleClipboardEvent", () => {
|
||||
originalEvent.clipboardData?.files,
|
||||
mockRoom.roomId,
|
||||
mockEventRelation, // this is the event relation, an optional arg
|
||||
mockReplyToEvent,
|
||||
mockClient,
|
||||
mockRoomState.timelineRenderingType,
|
||||
);
|
||||
|
||||
@@ -230,7 +230,7 @@ describe("message", () => {
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(getCommandSpy).toHaveBeenCalledWith(validCommand);
|
||||
expect(getCommandSpy).toHaveBeenCalledWith(mockRoom.roomId, validCommand);
|
||||
});
|
||||
|
||||
it("does not call getCommand for valid command with invalid prefix", async () => {
|
||||
|
||||
Reference in New Issue
Block a user