Tidy up snapshot, actions and vm names according to MVVM doc (#32819)

* chore: rename snapshot, actions and vm according to MVVM doc

* doc: add naming conventions

* chore: fix UrlGroupView naming, folders and children

* chore: remove `Example` column

* refactor: rename `UrlPreviewGroupViewPreview` into `UrlPreview`
This commit is contained in:
Florian Duros
2026-03-17 14:20:52 +00:00
committed by GitHub
parent 6339bcda15
commit 136bb78c15
54 changed files with 194 additions and 162 deletions
@@ -9,8 +9,8 @@ import { expect } from "@jest/globals";
import type { MockedObject } from "jest-mock";
import type { MatrixClient, IPreviewUrlResponse } from "matrix-js-sdk/src/matrix";
import { UrlPreviewViewModel } from "../../../src/viewmodels/message-body/UrlPreviewViewModel";
import type { UrlPreviewViewSnapshotPreview } from "@element-hq/web-shared-components";
import { UrlPreviewGroupViewModel } from "../../../src/viewmodels/message-body/UrlPreviewGroupViewModel";
import type { UrlPreview } from "@element-hq/web-shared-components";
import { getMockClientWithEventEmitter, mkEvent } from "../../test-utils";
const IMAGE_MXC = "mxc://example.org/abc";
@@ -23,16 +23,16 @@ const BASIC_PREVIEW_OGDATA = {
};
function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible: true }): {
vm: UrlPreviewViewModel;
vm: UrlPreviewGroupViewModel;
client: MockedObject<MatrixClient>;
onImageClicked: jest.Mock<void, [UrlPreviewViewSnapshotPreview]>;
onImageClicked: jest.Mock<void, [UrlPreview]>;
} {
const client = getMockClientWithEventEmitter({
getUrlPreview: jest.fn(),
mxcUrlToHttp: jest.fn(),
});
const onImageClicked = jest.fn<void, [UrlPreviewViewSnapshotPreview]>();
const vm = new UrlPreviewViewModel({
const onImageClicked = jest.fn<void, [UrlPreview]>();
const vm = new UrlPreviewGroupViewModel({
client,
mediaVisible,
visible,
@@ -48,7 +48,7 @@ function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible:
return { vm, client, onImageClicked };
}
describe("UrlPreviewViewModel", () => {
describe("UrlPreviewGroupViewModel", () => {
it("should return no previews by default", () => {
expect(getViewModel().vm.getSnapshot()).toMatchSnapshot();
});
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:description': 'A description',\\n 'og:title': ''\\n} 1`] = `
exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:description': 'A description',\\n 'og:title': ''\\n} 1`] = `
{
"description": undefined,
"image": undefined,
@@ -11,7 +11,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n
}
`;
exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:site_name': 'Site name',\\n 'og:title': ''\\n} 1`] = `
exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:site_name': 'Site name',\\n 'og:title': ''\\n} 1`] = `
{
"description": undefined,
"image": undefined,
@@ -22,7 +22,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n
}
`;
exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Basic title'\\n} 1`] = `
exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Basic title'\\n} 1`] = `
{
"description": undefined,
"image": undefined,
@@ -33,7 +33,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n
}
`;
exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Cool blog',\\n 'og:site_name': 'Cool site'\\n} 1`] = `
exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Cool blog',\\n 'og:site_name': 'Cool site'\\n} 1`] = `
{
"description": undefined,
"image": undefined,
@@ -44,7 +44,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n
}
`;
exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 1024,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = `
exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 1024,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = `
{
"description": undefined,
"image": {
@@ -61,7 +61,7 @@ exports[`UrlPreviewViewModel handles different kinds of opengraph responses {\\n
}
`;
exports[`UrlPreviewViewModel should deduplicate multiple versions of the same URL 1`] = `
exports[`UrlPreviewGroupViewModel should deduplicate multiple versions of the same URL 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -80,7 +80,7 @@ exports[`UrlPreviewViewModel should deduplicate multiple versions of the same UR
}
`;
exports[`UrlPreviewViewModel should handle being hidden and shown by the user 1`] = `
exports[`UrlPreviewGroupViewModel should handle being hidden and shown by the user 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -90,7 +90,7 @@ exports[`UrlPreviewViewModel should handle being hidden and shown by the user 1`
}
`;
exports[`UrlPreviewViewModel should handle being hidden and shown by the user 2`] = `
exports[`UrlPreviewGroupViewModel should handle being hidden and shown by the user 2`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -109,7 +109,7 @@ exports[`UrlPreviewViewModel should handle being hidden and shown by the user 2`
}
`;
exports[`UrlPreviewViewModel should hide preview when invisible 1`] = `
exports[`UrlPreviewGroupViewModel should hide preview when invisible 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -119,7 +119,7 @@ exports[`UrlPreviewViewModel should hide preview when invisible 1`] = `
}
`;
exports[`UrlPreviewViewModel should ignore failed previews 1`] = `
exports[`UrlPreviewGroupViewModel should ignore failed previews 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -129,7 +129,7 @@ exports[`UrlPreviewViewModel should ignore failed previews 1`] = `
}
`;
exports[`UrlPreviewViewModel should ignore media when mediaVisible is false 1`] = `
exports[`UrlPreviewGroupViewModel should ignore media when mediaVisible is false 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -148,7 +148,7 @@ exports[`UrlPreviewViewModel should ignore media when mediaVisible is false 1`]
}
`;
exports[`UrlPreviewViewModel should preview a URL with media 1`] = `
exports[`UrlPreviewGroupViewModel should preview a URL with media 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -173,7 +173,7 @@ exports[`UrlPreviewViewModel should preview a URL with media 1`] = `
}
`;
exports[`UrlPreviewViewModel should preview a single valid URL 1`] = `
exports[`UrlPreviewGroupViewModel should preview a single valid URL 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -192,7 +192,7 @@ exports[`UrlPreviewViewModel should preview a single valid URL 1`] = `
}
`;
exports[`UrlPreviewViewModel should return no previews by default 1`] = `
exports[`UrlPreviewGroupViewModel should return no previews by default 1`] = `
{
"compactLayout": false,
"overPreviewLimit": false,
@@ -16,7 +16,7 @@ import dispatcher from "../../../src/dispatcher/dispatcher";
import { Action } from "../../../src/dispatcher/actions";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
import DMRoomMap from "../../../src/utils/DMRoomMap";
import { RoomListViewViewModel } from "../../../src/viewmodels/room-list/RoomListViewViewModel";
import { RoomListViewModel } from "../../../src/viewmodels/room-list/RoomListViewModel";
import { hasCreateRoomRights } from "../../../src/viewmodels/room-list/utils";
jest.mock("../../../src/viewmodels/room-list/utils", () => ({
@@ -25,12 +25,12 @@ jest.mock("../../../src/viewmodels/room-list/utils", () => ({
hasAccessToNotificationMenu: jest.fn().mockReturnValue(true),
}));
describe("RoomListViewViewModel", () => {
describe("RoomListViewModel", () => {
let matrixClient: MatrixClient;
let room1: Room;
let room2: Room;
let room3: Room;
let viewModel: RoomListViewViewModel;
let viewModel: RoomListViewModel;
beforeEach(() => {
matrixClient = createTestClient();
@@ -63,7 +63,7 @@ describe("RoomListViewViewModel", () => {
describe("Initialization", () => {
it("should initialize with correct snapshot", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const snapshot = viewModel.getSnapshot();
expect(snapshot.sections[0].roomIds).toEqual(["!room1:server", "!room2:server", "!room3:server"]);
@@ -80,7 +80,7 @@ describe("RoomListViewViewModel", () => {
rooms: [],
});
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
expect(viewModel.getSnapshot().sections[0].roomIds).toEqual([]);
expect(viewModel.getSnapshot().isRoomListEmpty).toBe(true);
@@ -88,7 +88,7 @@ describe("RoomListViewViewModel", () => {
it("should set canCreateRoom based on user rights", () => {
mocked(hasCreateRoomRights).mockReturnValue(true);
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
expect(viewModel.getSnapshot().canCreateRoom).toBe(true);
});
@@ -96,7 +96,7 @@ describe("RoomListViewViewModel", () => {
describe("Room list updates", () => {
it("should update room list when ListsUpdate event fires", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const newRoom = mkStubRoom("!room4:server", "Room 4", matrixClient);
jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({
@@ -116,7 +116,7 @@ describe("RoomListViewViewModel", () => {
it("should update loading state when ListsLoaded event fires", () => {
jest.spyOn(RoomListStoreV3.instance, "isLoadingRooms", "get").mockReturnValue(true);
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
expect(viewModel.getSnapshot().isLoadingRooms).toBe(true);
@@ -127,7 +127,7 @@ describe("RoomListViewViewModel", () => {
// This test ensures that the room list item vms are preserved when the room list is changing
it("should keep existing view model when ListsUpdate event fires", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
// Create view model for room1
const room1VM = viewModel.getRoomItemViewModel("!room1:server");
@@ -142,7 +142,7 @@ describe("RoomListViewViewModel", () => {
describe("Space switching", () => {
it("should update room list when space changes", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const spaceRoomList = [room1, room2];
@@ -160,7 +160,7 @@ describe("RoomListViewViewModel", () => {
});
it("should clear view models when space changes", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
// Get view models for visible rooms
const vm1 = viewModel.getRoomItemViewModel("!room1:server");
@@ -184,7 +184,7 @@ describe("RoomListViewViewModel", () => {
describe("Active room tracking", () => {
it("should update active room index when room is selected", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server");
@@ -200,7 +200,7 @@ describe("RoomListViewViewModel", () => {
});
it("should return undefined active room index when no room is selected", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(null);
@@ -218,7 +218,7 @@ describe("RoomListViewViewModel", () => {
describe("Sticky room behavior", () => {
it("should keep selected room at same index when room list updates", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
// Select room at index 1
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server");
@@ -244,7 +244,7 @@ describe("RoomListViewViewModel", () => {
});
it("should not apply sticky behavior when user changes rooms", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
// Select room at index 1
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server");
@@ -270,7 +270,7 @@ describe("RoomListViewViewModel", () => {
describe("Filters", () => {
it("should toggle filter on", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
expect(viewModel.getSnapshot().activeFilterId).toBeUndefined();
@@ -287,7 +287,7 @@ describe("RoomListViewViewModel", () => {
});
it("should toggle filter off", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
// Turn filter on
jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({
@@ -317,7 +317,7 @@ describe("RoomListViewViewModel", () => {
describe("Room item view models", () => {
it("should create room item view model on demand", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const itemViewModel = viewModel.getRoomItemViewModel("!room1:server");
@@ -326,7 +326,7 @@ describe("RoomListViewViewModel", () => {
});
it("should reuse existing room item view model", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const itemViewModel1 = viewModel.getRoomItemViewModel("!room1:server");
const itemViewModel2 = viewModel.getRoomItemViewModel("!room1:server");
@@ -335,7 +335,7 @@ describe("RoomListViewViewModel", () => {
});
it("should throw error when requesting view model for non-existent room", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
expect(() => {
viewModel.getRoomItemViewModel("!nonexistent:server");
@@ -343,7 +343,7 @@ describe("RoomListViewViewModel", () => {
});
it("should dispose view models for rooms no longer visible", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const vm1 = viewModel.getRoomItemViewModel("!room1:server");
const vm2 = viewModel.getRoomItemViewModel("!room2:server");
@@ -366,7 +366,7 @@ describe("RoomListViewViewModel", () => {
describe("Room creation", () => {
it("should dispatch CreateChat action when createChatRoom is called", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const dispatchSpy = jest.spyOn(dispatcher, "fire");
@@ -376,7 +376,7 @@ describe("RoomListViewViewModel", () => {
});
it("should dispatch CreateRoom action without parent space", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
@@ -391,7 +391,7 @@ describe("RoomListViewViewModel", () => {
const spaceRoom = mkStubRoom("!space:server", "Space", matrixClient);
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(spaceRoom);
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
@@ -411,7 +411,7 @@ describe("RoomListViewViewModel", () => {
});
it("should navigate to next room when delta is 1", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room1:server");
@@ -434,7 +434,7 @@ describe("RoomListViewViewModel", () => {
});
it("should navigate to previous room when delta is -1", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room2:server");
@@ -457,7 +457,7 @@ describe("RoomListViewViewModel", () => {
});
it("should wrap around to last room when navigating backwards from first room", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!room1:server");
@@ -480,7 +480,7 @@ describe("RoomListViewViewModel", () => {
});
it("should not navigate when current room is not found", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!unknown:server");
@@ -504,7 +504,7 @@ describe("RoomListViewViewModel", () => {
});
it("should not navigate when no room is selected", async () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(null);
@@ -529,7 +529,7 @@ describe("RoomListViewViewModel", () => {
describe("Cleanup", () => {
it("should dispose all room item view models on dispose", () => {
viewModel = new RoomListViewViewModel({ client: matrixClient });
viewModel = new RoomListViewModel({ client: matrixClient });
const vm1 = viewModel.getRoomItemViewModel("!room1:server");
const vm2 = viewModel.getRoomItemViewModel("!room2:server");