Files
element-web/test/viewmodels/profile/DisambiguatedProfileViewModel-test.tsx
Zack 7e05552325 Refactor DisambiguatedProfile to shared-components (#31835)
* Refactoring of DisambiguatedProfile into shared components

* correct values and refactoring

* Add username color classes to Storybook and clean up DisambiguatedProfile stories

* Refactor DisambiguatedProfileView to use class component and enhance props structure

* Refactor DisambiguatedProfile components to use member object and enhance props structure

* Update copyright year to 2026 and adjust the tests to fit the correct memberinfro interface

* Add DisambiguatedProfileViewModel class

* Refactor DisambiguatedProfileViewModel to use member object and the rest of the props

* Refactor SenderProfile to use DisambiguatedProfileViewModel and update DisambiguatedProfile styles

* Refactor DisambiguatedProfileView to enhance  interface documentation

* Refactor DisambiguatedProfileView to use CSS modules for styling

* Updated css + tests to fit the new changes

* Update of the test snap to fit the current tests

* Adjusted RoomMemberTitleView and SenderProfile to use the new viewmodel, removed the old component.

* Implemented new viewmodel test for DisambiguatedProfileViewModel

* Update copyright text

* update css class names

* update to correct snapshot after css name changes.

* Apply suggestion from @florianduros

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>

* Moved logic to viewmodel instead of having it in the view. Removed unessecery functions and css.

* removed unessecery file that I copied from root folder, this is no longed needed as I use the root file instead in the viewmodel

* Better Formatting

* Fix issues after merging develop

* FIxed issues with eslint

* Added Visible, non-interactive elements with click handlers must have at least one keyboard listener from eslint docs

* Updated snapshot the fit the latest update with eslint button requirment

* Update snapshot screens for new tests.

* Update tests to reflect snapshots

* Update snapshot due of outdated CSS module classes

* Add useEffect to call setProps on the DisambiguatedProfileViewModel
when props change, ensuring the view updates with the correct display
name. Update LayoutSwitcher snapshot for new CSS classes.

* Fix Playwright editing tests by adding exact match for Edit button selector
The DisambiguatedProfile refactoring added role="button" to the component,
causing the selector { name: "Edit" } to match both the user "Edith" and
the actual Edit button.

* Fix ForwardDialog location tests for async hook rendering The SenderProfile component now uses hooks that trigger async state updates.

* Fix SenderProfile useEffect to only update changeable props

* Added letter spacing

* Added ClassName prop

* Update snapshot

* Update letter-spacing

* Update snapshot screenshots

* Update Snapshots

* Update snapshot

* Removal of letter spacing to test CI

* Apply suggestion from @florianduros

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>

* Added closing brackets + added back letter-spacing

* Update snapshots

* Update snapshot

* Update span to correctly apply to the CI tests, it wasn't possible to use classname as a prop

* Update snapshot

* Added comment to explain the span classNames

* DisambiguatedProfileViewModel.setProps to runtime-changing props

* replace DisambiguatedProfileViewModel setProps with explicit setters and update call sites

* Update Setters

* Prettier FIx

* Update Setters

* update DisambiguatedProfileViewModel setters and tests

* Update SenderProfile to show connect display name

* clone snapshot in setters to trigger reactive updates

* use snapshot.merge in DisambiguatedProfileViewModel setters

* emove duplicated logic in DisambiguatedProfileViewModel

* Change snapshot name

* Update viewmodel

* Updated Tests

* typo

* Update src/viewmodels/profile/DisambiguatedProfileViewModel.ts

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>

* Removal of unused function

* Update snapshots

* Update tests to pass coverage

* Update Eslint

---------

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
2026-02-11 15:31:06 +00:00

159 lines
4.9 KiB
TypeScript

/*
* Copyright 2026 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 { DisambiguatedProfileViewModel } from "../../../src/viewmodels/profile/DisambiguatedProfileViewModel";
describe("DisambiguatedProfileViewModel", () => {
const member = {
userId: "@alice:example.org",
roomId: "!room:example.org",
rawDisplayName: "Alice",
disambiguate: true,
};
const nonDisambiguatedMember = {
...member,
disambiguate: false,
};
it("should return the snapshot from props", () => {
const vm = new DisambiguatedProfileViewModel({
member,
fallbackName: "Fallback",
colored: true,
emphasizeDisplayName: true,
withTooltip: true,
});
expect(vm.getSnapshot()).toEqual({
displayName: "Alice",
colorClass: "mx_Username_color3",
className: undefined,
displayIdentifier: "@alice:example.org",
title: "Alice (@alice:example.org)",
emphasizeDisplayName: true,
});
});
it("should default member fields when member is null", () => {
const vm = new DisambiguatedProfileViewModel({
member: null,
fallbackName: "Fallback",
});
expect(vm.getSnapshot()).toMatchObject({
displayName: "Fallback",
colorClass: undefined,
className: undefined,
displayIdentifier: undefined,
title: undefined,
emphasizeDisplayName: undefined,
});
});
it("should pass through className prop", () => {
const vm = new DisambiguatedProfileViewModel({
member,
fallbackName: "Fallback",
className: "mx_DisambiguatedProfile",
});
expect(vm.getSnapshot().className).toBe("mx_DisambiguatedProfile");
});
it("should delegate onClick without emitting a snapshot update", () => {
const onClick = jest.fn();
const vm = new DisambiguatedProfileViewModel({
member,
fallbackName: "Fallback",
onClick,
});
const prevSnapshot = vm.getSnapshot();
const subscriber = jest.fn();
vm.subscribe(subscriber);
onClick({} as never);
expect(onClick).toHaveBeenCalledTimes(1);
expect(subscriber).not.toHaveBeenCalled();
expect(vm.getSnapshot()).toBe(prevSnapshot);
});
it("should emit snapshot update when fallbackName changes", () => {
const vm = new DisambiguatedProfileViewModel({
member: null,
fallbackName: "Fallback",
});
const subscriber = jest.fn();
vm.subscribe(subscriber);
vm.setMember("Updated");
expect(subscriber).toHaveBeenCalledTimes(1);
expect(vm.getSnapshot().displayName).toBe("Updated");
});
it("should emit snapshot update when setMember is called even if fallbackName is unchanged", () => {
const vm = new DisambiguatedProfileViewModel({
member: null,
fallbackName: "Fallback",
});
const subscriber = jest.fn();
vm.subscribe(subscriber);
vm.setMember("Fallback");
expect(subscriber).toHaveBeenCalledTimes(1);
});
it("should compute tooltip title from constructor props when withTooltip is true", () => {
const vm = new DisambiguatedProfileViewModel({
member,
fallbackName: "Fallback",
withTooltip: true,
});
expect(vm.getSnapshot().title).toBe("Alice (@alice:example.org)");
});
it("should compute tooltip title even when disambiguation is not needed", () => {
const vm = new DisambiguatedProfileViewModel({
member: nonDisambiguatedMember,
fallbackName: "Fallback",
withTooltip: true,
});
expect(vm.getSnapshot().title).toBe("Alice (@alice:example.org)");
});
it("should emit snapshot update when member changes via setMember", () => {
const vm = new DisambiguatedProfileViewModel({
member: null,
fallbackName: "Fallback",
});
const subscriber = jest.fn();
vm.subscribe(subscriber);
vm.setMember("Fallback", member);
expect(subscriber).toHaveBeenCalledTimes(1);
expect(vm.getSnapshot().displayName).toBe("Alice");
});
it("should emit snapshot update when setMember is called with unchanged member", () => {
const vm = new DisambiguatedProfileViewModel({
member,
fallbackName: "Fallback",
});
const subscriber = jest.fn();
vm.subscribe(subscriber);
vm.setMember("Fallback", member);
expect(subscriber).toHaveBeenCalledTimes(1);
});
});