mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 12:16:53 +02:00
WIP
This commit is contained in:
parent
c31d4fea8d
commit
fff9fd9798
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
@ -98,7 +98,7 @@ declare global {
|
||||
mxToastStore: ToastStore;
|
||||
mxDeviceListener: DeviceListener;
|
||||
mxRoomListStore: RoomListStore;
|
||||
mxRoomListStoreV3: RoomListStoreV3Class;
|
||||
getRoomListStoreV3: () => RoomListStoreV3Class;
|
||||
mxRoomListLayoutStore: RoomListLayoutStore;
|
||||
mxPlatformPeg: PlatformPeg;
|
||||
mxIntegrationManagers: typeof IntegrationManagers;
|
||||
|
||||
@ -14,7 +14,7 @@ import Timer from "../../utils/Timer";
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
import { getKeyBindingsManager } from "../../KeyBindingsManager";
|
||||
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
|
||||
import { SDKContext } from "../../contexts/SDKContext";
|
||||
import type { SDKContext } from "../../contexts/SDKContext";
|
||||
|
||||
// The amount of extra scroll distance to allow prior to unfilling.
|
||||
// See getExcessHeight.
|
||||
@ -184,7 +184,6 @@ export default class ScrollPanel extends React.Component<IProps> {
|
||||
private heightUpdateInProgress = false;
|
||||
public divScroll: HTMLDivElement | null = null;
|
||||
|
||||
public static contextType = SDKContext;
|
||||
declare public context: React.ContextType<typeof SDKContext>;
|
||||
|
||||
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
|
||||
|
||||
52
src/modules/AccountDataApi.ts
Normal file
52
src/modules/AccountDataApi.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { Watchable, type AccountDataApi as IAccountDataApi } from "@element-hq/element-web-module-api";
|
||||
import { ClientEvent, type MatrixEvent, type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { getSafeCli } from "./common";
|
||||
|
||||
export class AccountDataApi implements IAccountDataApi {
|
||||
public get(eventType: string): Watchable<unknown> {
|
||||
const cli = getSafeCli();
|
||||
return new AccountDataWatchable(cli, eventType);
|
||||
}
|
||||
|
||||
public async set(eventType: string, content: any): Promise<void> {
|
||||
//@ts-expect-error: JS-SDK accepts known event-types, intentionally allow arbitrary types.
|
||||
await getSafeCli().setAccountData(eventType, content);
|
||||
}
|
||||
|
||||
public async delete(eventType: string): Promise<void> {
|
||||
//@ts-expect-error: JS-SDK accepts known event-types, intentionally allow arbitrary types.
|
||||
getSafeCli().deleteAccountData(eventType);
|
||||
}
|
||||
}
|
||||
|
||||
class AccountDataWatchable extends Watchable<unknown> {
|
||||
public constructor(
|
||||
private cli: MatrixClient,
|
||||
private eventType: string,
|
||||
) {
|
||||
//@ts-expect-error: JS-SDK accepts known event-types, intentionally allow arbitrary types.
|
||||
super(cli.getAccountData(eventType)?.getContent());
|
||||
}
|
||||
|
||||
private onAccountData = (event: MatrixEvent): void => {
|
||||
if (event.getType() === this.eventType) {
|
||||
this.value = event.getContent();
|
||||
}
|
||||
};
|
||||
|
||||
protected onFirstWatch(): void {
|
||||
this.cli.on(ClientEvent.AccountData, this.onAccountData);
|
||||
}
|
||||
|
||||
protected onLastWatch(): void {
|
||||
this.cli.off(ClientEvent.AccountData, this.onAccountData);
|
||||
}
|
||||
}
|
||||
21
src/modules/ActionsApi.ts
Normal file
21
src/modules/ActionsApi.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
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 { ActionsApi as IActionsApi } from "@element-hq/element-web-module-api";
|
||||
import type { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
|
||||
import dispatcher from "../dispatcher/dispatcher";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
|
||||
export class ActionsApi implements IActionsApi {
|
||||
public openRoom(roomId: string): void {
|
||||
dispatcher.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
room_id: roomId,
|
||||
metricsTrigger: undefined, // other
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,10 @@ import { NavigationApi } from "./Navigation.ts";
|
||||
import { openDialog } from "./Dialog.tsx";
|
||||
import { overwriteAccountAuth } from "./Auth.ts";
|
||||
import { ElementWebExtrasApi } from "./ExtrasApi.ts";
|
||||
import { ElementWebBuiltinsApi } from "./BuiltinsApi.ts";
|
||||
import { ElementWebBuiltinsApi } from "./BuiltinsApi.tsx";
|
||||
import { StoresApi } from "./StoresApi.ts";
|
||||
import { ClientApi } from "./ClientApi.ts";
|
||||
import { ActionsApi } from "./ActionsApi.ts";
|
||||
|
||||
const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => {
|
||||
let used = false;
|
||||
@ -84,6 +87,9 @@ export class ModuleApi implements Api {
|
||||
public readonly extras = new ElementWebExtrasApi();
|
||||
public readonly builtins = new ElementWebBuiltinsApi();
|
||||
public readonly rootNode = document.getElementById("matrixchat")!;
|
||||
public readonly stores = new StoresApi();
|
||||
public readonly client = new ClientApi();
|
||||
public readonly actions = new ActionsApi();
|
||||
|
||||
public createRoot(element: Element): Root {
|
||||
return createRoot(element);
|
||||
|
||||
@ -5,8 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { type RoomViewProps, type BuiltinsApi } from "@element-hq/element-web-module-api";
|
||||
|
||||
import RoomAvatar from "../components/views/avatars/RoomAvatar";
|
||||
import { getSafeCli } from "./common";
|
||||
|
||||
export class ElementWebBuiltinsApi implements BuiltinsApi {
|
||||
private _roomView?: React.ComponentType<RoomViewProps>;
|
||||
|
||||
@ -30,4 +34,17 @@ export class ElementWebBuiltinsApi implements BuiltinsApi {
|
||||
|
||||
return this._roomView;
|
||||
}
|
||||
|
||||
public renderRoomView(roomId: string): React.ReactNode {
|
||||
const Component = this.getRoomViewComponent();
|
||||
return <Component roomId={roomId} />;
|
||||
}
|
||||
|
||||
public renderRoomAvatar(roomId: string, size?: string): React.ReactNode {
|
||||
const room = getSafeCli().getRoom(roomId);
|
||||
if (!room) {
|
||||
throw new Error(`No room such room: ${roomId}`);
|
||||
}
|
||||
return <RoomAvatar room={room} size={size} />;
|
||||
}
|
||||
}
|
||||
27
src/modules/ClientApi.ts
Normal file
27
src/modules/ClientApi.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 { ClientApi as IClientApi, Room } from "@element-hq/element-web-module-api";
|
||||
import { Room as ModuleRoom } from "./models/Room";
|
||||
import { AccountDataApi } from "./AccountDataApi";
|
||||
import { getSafeCli } from "./common";
|
||||
|
||||
export class ClientApi implements IClientApi {
|
||||
private accountDataApi?: AccountDataApi;
|
||||
|
||||
public getRoom(roomId: string): Room | null {
|
||||
const sdkRoom = getSafeCli().getRoom(roomId);
|
||||
if (sdkRoom) return new ModuleRoom(sdkRoom);
|
||||
return null;
|
||||
}
|
||||
|
||||
public get accountData(): AccountDataApi {
|
||||
if (!this.accountDataApi) {
|
||||
this.accountDataApi = new AccountDataApi();
|
||||
}
|
||||
return this.accountDataApi;
|
||||
}
|
||||
}
|
||||
65
src/modules/StoresApi.ts
Normal file
65
src/modules/StoresApi.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 StoresApi as IStoresApi,
|
||||
type RoomListStoreApi as IRoomListStore,
|
||||
type Room,
|
||||
Watchable,
|
||||
} from "@element-hq/element-web-module-api";
|
||||
|
||||
import RoomListStoreV3, { LISTS_LOADED_EVENT, LISTS_UPDATE_EVENT } from "../stores/room-list-v3/RoomListStoreV3";
|
||||
import { Room as ModuleRoom } from "./models/Room";
|
||||
|
||||
class RoomListStoreApi implements IRoomListStore {
|
||||
public getRooms(): RoomsWatchable {
|
||||
return new RoomsWatchable();
|
||||
}
|
||||
|
||||
public async waitForReady(): Promise<void> {
|
||||
// Check if RLS is already loaded
|
||||
if (!RoomListStoreV3.instance.isLoadingRooms) return;
|
||||
|
||||
// Return a promise that resolves when RLS has loaded
|
||||
let resolve: () => void;
|
||||
const promise: Promise<void> = new Promise((_resolve) => {
|
||||
resolve = _resolve;
|
||||
});
|
||||
RoomListStoreV3.instance.once(LISTS_LOADED_EVENT, () => {
|
||||
resolve();
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomsWatchable extends Watchable<Room[]> {
|
||||
public constructor() {
|
||||
super(RoomListStoreV3.instance.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom)));
|
||||
}
|
||||
|
||||
private onRlsUpdate = (): void => {
|
||||
this.value = RoomListStoreV3.instance.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom));
|
||||
};
|
||||
|
||||
protected onFirstWatch(): void {
|
||||
RoomListStoreV3.instance.on(LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
}
|
||||
|
||||
protected onLastWatch(): void {
|
||||
RoomListStoreV3.instance.off(LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class StoresApi implements IStoresApi {
|
||||
private roomListStoreApi?: IRoomListStore;
|
||||
|
||||
public get roomListStore(): IRoomListStore {
|
||||
if (!this.roomListStoreApi) {
|
||||
this.roomListStoreApi = new RoomListStoreApi();
|
||||
}
|
||||
return this.roomListStoreApi;
|
||||
}
|
||||
}
|
||||
22
src/modules/common.ts
Normal file
22
src/modules/common.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { SdkContextClass } from "../contexts/SDKContext";
|
||||
|
||||
/**
|
||||
* Get MatrixClient instance from SdkContextClass.
|
||||
* @throws Will throw error if cli is not instantiated in SdkContextClass
|
||||
* @returns MatrixClient object
|
||||
*/
|
||||
export function getSafeCli(): MatrixClient {
|
||||
const cli = SdkContextClass.instance.client;
|
||||
if (!cli) {
|
||||
throw new Error("Could not get MatrixClient from SdkContextClass");
|
||||
}
|
||||
return cli;
|
||||
}
|
||||
45
src/modules/models/Room.ts
Normal file
45
src/modules/models/Room.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
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 as IRoom, Watchable } from "@element-hq/element-web-module-api";
|
||||
import { RoomEvent, type Room as SdkRoom } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
export class Room implements IRoom {
|
||||
public name: Watchable<string>;
|
||||
|
||||
public constructor(private sdkRoom: SdkRoom) {
|
||||
this.name = new WatchableName(sdkRoom);
|
||||
}
|
||||
|
||||
public getLastActiveTimestamp(): number {
|
||||
return this.sdkRoom.getLastActiveTimestamp();
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this.sdkRoom.roomId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom watchable for room name.
|
||||
*/
|
||||
class WatchableName extends Watchable<string> {
|
||||
public constructor(private sdkRoom: SdkRoom) {
|
||||
super(sdkRoom.name);
|
||||
}
|
||||
|
||||
private onNameUpdate = (): void => {
|
||||
super.value = this.sdkRoom.name;
|
||||
};
|
||||
protected onFirstWatch(): void {
|
||||
this.sdkRoom.on(RoomEvent.Name, this.onNameUpdate);
|
||||
}
|
||||
|
||||
protected onLastWatch(): void {
|
||||
this.sdkRoom.off(RoomEvent.Name, this.onNameUpdate);
|
||||
}
|
||||
}
|
||||
@ -373,4 +373,4 @@ export default class RoomListStoreV3 {
|
||||
}
|
||||
}
|
||||
|
||||
window.mxRoomListStoreV3 = RoomListStoreV3.instance;
|
||||
window.getRoomListStoreV3 = () => RoomListStoreV3.instance;
|
||||
|
||||
@ -650,6 +650,7 @@ export function mkStubRoom(
|
||||
getJoinedMembers: jest.fn().mockReturnValue([]),
|
||||
getLiveTimeline: jest.fn().mockReturnValue(stubTimeline),
|
||||
getLastLiveEvent: jest.fn().mockReturnValue(undefined),
|
||||
getLastActiveTimestamp: jest.fn().mockReturnValue(1183140000),
|
||||
getMember: jest.fn().mockReturnValue({
|
||||
userId: "@member:domain.bla",
|
||||
name: "Member",
|
||||
|
||||
39
test/unit-tests/modules/AccountDataApi-test.ts
Normal file
39
test/unit-tests/modules/AccountDataApi-test.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { AccountDataApi } from "../../../src/modules/AccountDataApi";
|
||||
import * as utils from "../../../src/modules/common";
|
||||
import { mkEvent, stubClient } from "../../test-utils/test-utils";
|
||||
|
||||
describe("AccountDataApi", () => {
|
||||
it("should return content of account data event on get()", () => {
|
||||
const cli = stubClient();
|
||||
jest.spyOn(utils, "getSafeCli").mockReturnValue(cli);
|
||||
const api = new AccountDataApi();
|
||||
// Mock cli to return a event
|
||||
const content = { foo: "bar" };
|
||||
const event = mkEvent({ content, type: "m.test", user: "@foobar:matrix.org", event: true });
|
||||
cli.getAccountData = () => event;
|
||||
expect(api.get("m.test").value).toStrictEqual(content);
|
||||
});
|
||||
|
||||
it("should set account data via js-sdk on set()", async () => {
|
||||
const cli = stubClient();
|
||||
jest.spyOn(utils, "getSafeCli").mockReturnValue(cli);
|
||||
const api = new AccountDataApi();
|
||||
await api.set("m.test", { foo: "bar" });
|
||||
expect(cli.setAccountData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should delete account data via js-sdk on set()", async () => {
|
||||
const cli = stubClient();
|
||||
jest.spyOn(utils, "getSafeCli").mockReturnValue(cli);
|
||||
const api = new AccountDataApi();
|
||||
await api.delete("m.test");
|
||||
expect(cli.deleteAccountData).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
27
test/unit-tests/modules/ActionsApi-test.ts
Normal file
27
test/unit-tests/modules/ActionsApi-test.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { waitFor } from "jest-matrix-react";
|
||||
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import dispatcher from "../../../src/dispatcher/dispatcher";
|
||||
import { ActionsApi } from "../../../src/modules/ActionsApi";
|
||||
|
||||
describe("ActionsApi", () => {
|
||||
it("should dispatch view room action", async () => {
|
||||
const api = new ActionsApi();
|
||||
const fn = jest.fn();
|
||||
dispatcher.register(fn);
|
||||
api.openRoom("!foo:m.org");
|
||||
await waitFor(() =>
|
||||
expect(fn).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: "!foo:m.org",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,17 +0,0 @@
|
||||
/*
|
||||
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 { ElementWebBuiltinsApi } from "../../../src/modules/BuiltinsApi";
|
||||
|
||||
describe("ElementWebBuiltinsApi", () => {
|
||||
it("returns the RoomView component thats been set", () => {
|
||||
const builtinsApi = new ElementWebBuiltinsApi();
|
||||
const sentinel = {};
|
||||
builtinsApi.setRoomViewComponent(sentinel as any);
|
||||
expect(builtinsApi.getRoomViewComponent()).toBe(sentinel);
|
||||
});
|
||||
});
|
||||
55
test/unit-tests/modules/BuiltinsApi-test.tsx
Normal file
55
test/unit-tests/modules/BuiltinsApi-test.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 } from "jest-matrix-react";
|
||||
|
||||
import { ElementWebBuiltinsApi } from "../../../src/modules/BuiltinsApi";
|
||||
import { stubClient } from "../../test-utils/test-utils";
|
||||
import * as utils from "../../../src/modules/common";
|
||||
|
||||
jest.mock("../../../src/components/views/avatars/RoomAvatar", () => {
|
||||
const Avatar: React.FC<{ room: { roomId: string }; size: string }> = ({ room, size }) => {
|
||||
return (
|
||||
<div>
|
||||
Avatar, {room.roomId}, {size}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return {
|
||||
__esModule: true,
|
||||
default: Avatar,
|
||||
};
|
||||
});
|
||||
|
||||
describe("ElementWebBuiltinsApi", () => {
|
||||
it("returns the RoomView component thats been set", () => {
|
||||
const builtinsApi = new ElementWebBuiltinsApi();
|
||||
const sentinel = {};
|
||||
builtinsApi.setRoomViewComponent(sentinel as any);
|
||||
expect(builtinsApi.getRoomViewComponent()).toBe(sentinel);
|
||||
});
|
||||
|
||||
it("returns rendered RoomView component", () => {
|
||||
const builtinsApi = new ElementWebBuiltinsApi();
|
||||
const RoomView = () => <div>hello world</div>;
|
||||
builtinsApi.setRoomViewComponent(RoomView as any);
|
||||
const { container } = render(<> {builtinsApi.renderRoomView("!foo:m.org")}</>);
|
||||
expect(container).toHaveTextContent("hello world");
|
||||
});
|
||||
|
||||
it("returns rendered RoomAvatar component", () => {
|
||||
const cli = stubClient();
|
||||
jest.spyOn(utils, "getSafeCli").mockReturnValue(cli);
|
||||
|
||||
const builtinsApi = new ElementWebBuiltinsApi();
|
||||
const { container } = render(<> {builtinsApi.renderRoomAvatar("!foo:m.org", "50")}</>);
|
||||
expect(container).toHaveTextContent("Avatar");
|
||||
expect(container).toHaveTextContent("!foo:m.org");
|
||||
expect(container).toHaveTextContent("50");
|
||||
});
|
||||
});
|
||||
22
test/unit-tests/modules/ClientApi-test.ts
Normal file
22
test/unit-tests/modules/ClientApi-test.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { ClientApi } from "../../../src/modules/ClientApi";
|
||||
import * as utils from "../../../src/modules/common";
|
||||
import { Room } from "../../../src/modules/models/Room";
|
||||
import { stubClient } from "../../test-utils/test-utils";
|
||||
|
||||
describe("ClientApi", () => {
|
||||
it("should return module room from getRoom()", () => {
|
||||
const cli = stubClient();
|
||||
jest.spyOn(utils, "getSafeCli").mockReturnValue(cli);
|
||||
const client = new ClientApi();
|
||||
const moduleRoom = client.getRoom("!foo:matrix.org");
|
||||
expect(moduleRoom).toBeInstanceOf(Room);
|
||||
expect(moduleRoom?.id).toStrictEqual("!foo:matrix.org");
|
||||
});
|
||||
});
|
||||
77
test/unit-tests/modules/StoresApi-test.ts
Normal file
77
test/unit-tests/modules/StoresApi-test.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { waitFor } from "jest-matrix-react";
|
||||
|
||||
import { StoresApi } from "../../../src/modules/StoresApi";
|
||||
import RoomListStoreV3, {
|
||||
LISTS_LOADED_EVENT,
|
||||
LISTS_UPDATE_EVENT,
|
||||
} from "../../../src/stores/room-list-v3/RoomListStoreV3";
|
||||
import { mkRoom, stubClient } from "../../test-utils/test-utils";
|
||||
import { Room } from "../../../src/modules/models/Room";
|
||||
import {} from "../../../src/stores/room-list/algorithms/Algorithm";
|
||||
|
||||
describe("StoresApi", () => {
|
||||
describe("RoomListStoreApi", () => {
|
||||
it("should return promise that resolves when RLS is ready", async () => {
|
||||
jest.spyOn(RoomListStoreV3.instance, "isLoadingRooms", "get").mockReturnValue(true);
|
||||
const store = new StoresApi();
|
||||
let hasResolved = false;
|
||||
// The following async function will set hasResolved to false
|
||||
// only when waitForReady resolves.
|
||||
(async () => {
|
||||
await store.roomListStore.waitForReady();
|
||||
hasResolved = true;
|
||||
})();
|
||||
// Shouldn't have resolved yet.
|
||||
expect(hasResolved).toStrictEqual(false);
|
||||
// Emit the loaded event.
|
||||
RoomListStoreV3.instance.emit(LISTS_LOADED_EVENT);
|
||||
// Should resolve now.
|
||||
await waitFor(() => {
|
||||
expect(hasResolved).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRooms()", () => {
|
||||
it("should return rooms from RLS", () => {
|
||||
const cli = stubClient();
|
||||
const room1 = mkRoom(cli, "!foo1:m.org");
|
||||
const room2 = mkRoom(cli, "!foo2:m.org");
|
||||
const room3 = mkRoom(cli, "!foo3:m.org");
|
||||
jest.spyOn(RoomListStoreV3.instance, "getSortedRooms").mockReturnValue([room1, room2, room3]);
|
||||
|
||||
const store = new StoresApi();
|
||||
const watchable = store.roomListStore.getRooms();
|
||||
expect(watchable.value).toHaveLength(3);
|
||||
expect(watchable.value[0]).toBeInstanceOf(Room);
|
||||
});
|
||||
|
||||
it("should update from RLS", () => {
|
||||
const cli = stubClient();
|
||||
const room1 = mkRoom(cli, "!foo1:m.org");
|
||||
const room2 = mkRoom(cli, "!foo2:m.org");
|
||||
const rooms = [room1, room2];
|
||||
|
||||
jest.spyOn(RoomListStoreV3.instance, "getSortedRooms").mockReturnValue(rooms);
|
||||
|
||||
const store = new StoresApi();
|
||||
const watchable = store.roomListStore.getRooms();
|
||||
const fn = jest.fn();
|
||||
watchable.watch(fn);
|
||||
expect(watchable.value).toHaveLength(2);
|
||||
|
||||
const room3 = mkRoom(cli, "!foo3:m.org");
|
||||
rooms.push(room3);
|
||||
RoomListStoreV3.instance.emit(LISTS_UPDATE_EVENT);
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
expect(watchable.value).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
50
test/unit-tests/modules/models/Room-test.ts
Normal file
50
test/unit-tests/modules/models/Room-test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations 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 { Room } from "../../../../src/modules/models/Room";
|
||||
import { mkRoom, stubClient } from "../../../test-utils";
|
||||
|
||||
describe("Room", () => {
|
||||
it("should return id from sdk room", () => {
|
||||
const cli = stubClient();
|
||||
const sdkRoom = mkRoom(cli, "!foo:m.org");
|
||||
const room = new Room(sdkRoom);
|
||||
expect(room.id).toStrictEqual("!foo:m.org");
|
||||
});
|
||||
|
||||
it("should return last timestamp from sdk room", () => {
|
||||
const cli = stubClient();
|
||||
const sdkRoom = mkRoom(cli, "!foo:m.org");
|
||||
const room = new Room(sdkRoom);
|
||||
expect(room.getLastActiveTimestamp()).toStrictEqual(sdkRoom.getLastActiveTimestamp());
|
||||
});
|
||||
|
||||
describe("watchableName", () => {
|
||||
it("should return name from sdkRoom", () => {
|
||||
const cli = stubClient();
|
||||
const sdkRoom = mkRoom(cli, "!foo:m.org");
|
||||
sdkRoom.name = "Foo Name";
|
||||
const room = new Room(sdkRoom);
|
||||
expect(room.name.value).toStrictEqual("Foo Name");
|
||||
});
|
||||
|
||||
it("should add/remove event listener on sdk room", () => {
|
||||
const cli = stubClient();
|
||||
const sdkRoom = mkRoom(cli, "!foo:m.org");
|
||||
sdkRoom.name = "Foo Name";
|
||||
|
||||
const room = new Room(sdkRoom);
|
||||
const fn = jest.fn();
|
||||
|
||||
room.name.watch(fn);
|
||||
expect(sdkRoom.on).toHaveBeenCalledTimes(1);
|
||||
|
||||
room.name.unwatch(fn);
|
||||
expect(sdkRoom.off).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user