Improve display of edited events

This commit is contained in:
Richard van der Hoff 2022-11-25 13:54:06 +00:00
parent fa01a6211e
commit dc29317445
4 changed files with 330 additions and 10 deletions

View file

@ -14,19 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import * as React from "react";
import { act, render, screen, waitFor } from "@testing-library/react";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { IEncryptedEventInfo } from "matrix-js-sdk/src/crypto/api";
import EventTile, { EventTileProps } from "../../../../src/components/views/rooms/EventTile";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import SettingsStore from "../../../../src/settings/SettingsStore";
import { getRoomContext, mkEvent, mkMessage, stubClient } from "../../../test-utils";
import { getRoomContext, mkEncryptedEvent, mkEvent, mkMessage, stubClient } from "../../../test-utils";
import { mkThread } from "../../../test-utils/threads";
describe("EventTile", () => {
@ -34,6 +37,7 @@ describe("EventTile", () => {
let mxEvent: MatrixEvent;
let room: Room;
let client: MatrixClient;
// let changeEvent: (event: MatrixEvent) => void;
function TestEventTile(props: Partial<EventTileProps>) {
@ -67,7 +71,7 @@ describe("EventTile", () => {
stubClient();
client = MatrixClientPeg.get();
room = new Room(ROOM_ID, client, client.getUserId(), {
room = new Room(ROOM_ID, client, client.getUserId()!, {
pendingEventOrdering: PendingEventOrdering.Detached,
});
@ -140,18 +144,194 @@ describe("EventTile", () => {
expect(container.getElementsByClassName("mx_NotificationBadge")).toHaveLength(0);
act(() => {
room.setThreadUnreadNotificationCount(mxEvent.getId(), NotificationCountType.Total, 3);
room.setThreadUnreadNotificationCount(mxEvent.getId()!, NotificationCountType.Total, 3);
});
expect(container.getElementsByClassName("mx_NotificationBadge")).toHaveLength(1);
expect(container.getElementsByClassName("mx_NotificationBadge_highlighted")).toHaveLength(0);
act(() => {
room.setThreadUnreadNotificationCount(mxEvent.getId(), NotificationCountType.Highlight, 1);
room.setThreadUnreadNotificationCount(mxEvent.getId()!, NotificationCountType.Highlight, 1);
});
expect(container.getElementsByClassName("mx_NotificationBadge")).toHaveLength(1);
expect(container.getElementsByClassName("mx_NotificationBadge_highlighted")).toHaveLength(1);
});
});
describe("Event verification", () => {
// data for our stubbed getEventEncryptionInfo: a map from event id to result
const eventToEncryptionInfoMap = new Map<string, IEncryptedEventInfo>();
const TRUSTED_DEVICE = DeviceInfo.fromStorage({}, "TRUSTED_DEVICE");
const UNTRUSTED_DEVICE = DeviceInfo.fromStorage({}, "UNTRUSTED_DEVICE");
beforeEach(() => {
eventToEncryptionInfoMap.clear();
// a mocked version of getEventEncryptionInfo which will pick its result from `eventToEncryptionInfoMap`
client.getEventEncryptionInfo = (event) => eventToEncryptionInfoMap.get(event.getId()!)!;
// a mocked version of checkUserTrust which always says the user is trusted (we do our testing via
// unverified devices).
const trustedUserTrustLevel = new UserTrustLevel(true, true, true);
client.checkUserTrust = (_userId) => trustedUserTrustLevel;
// a version of checkDeviceTrust which says that TRUSTED_DEVICE is trusted, and others are not.
const trustedDeviceTrustLevel = DeviceTrustLevel.fromUserTrustLevel(trustedUserTrustLevel, true, false);
const untrustedDeviceTrustLevel = DeviceTrustLevel.fromUserTrustLevel(trustedUserTrustLevel, false, false);
client.checkDeviceTrust = (userId, deviceId) => {
if (deviceId === TRUSTED_DEVICE.deviceId) {
return trustedDeviceTrustLevel;
} else {
return untrustedDeviceTrustLevel;
}
};
});
it("shows a warning for an event from an unverified device", async () => {
mxEvent = await mkEncryptedEvent({
plainContent: { msgtype: "m.text", body: "msg1" },
plainType: "m.room.message",
user: "@alice:example.org",
room: room.roomId,
});
eventToEncryptionInfoMap.set(mxEvent.getId()!, {
authenticated: true,
sender: UNTRUSTED_DEVICE,
} as IEncryptedEventInfo);
const { container } = getComponent();
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
const eventTile = eventTiles[0];
expect(eventTile.classList).toContain("mx_EventTile_unverified");
// there should be a warning shield
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(1);
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")[0].classList).toContain(
"mx_EventTile_e2eIcon_warning",
);
});
it("shows no shield for a verified event", async () => {
mxEvent = await mkEncryptedEvent({
plainContent: { msgtype: "m.text", body: "msg1" },
plainType: "m.room.message",
user: "@alice:example.org",
room: room.roomId,
});
eventToEncryptionInfoMap.set(mxEvent.getId()!, {
authenticated: true,
sender: TRUSTED_DEVICE,
} as IEncryptedEventInfo);
const { container } = getComponent();
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
const eventTile = eventTiles[0];
expect(eventTile.classList).toContain("mx_EventTile_verified");
// there should be no warning
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(0);
});
it("should update the warning when the event is edited", async () => {
// we start out with an event from the trusted device
mxEvent = await mkEncryptedEvent({
plainContent: { msgtype: "m.text", body: "msg1" },
plainType: "m.room.message",
user: "@alice:example.org",
room: room.roomId,
});
eventToEncryptionInfoMap.set(mxEvent.getId()!, {
authenticated: true,
sender: TRUSTED_DEVICE,
} as IEncryptedEventInfo);
const { container } = getComponent();
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
const eventTile = eventTiles[0];
expect(eventTile.classList).toContain("mx_EventTile_verified");
// there should be no warning
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(0);
// then we replace the event with one from the unverified device
const replacementEvent = await mkEncryptedEvent({
plainContent: { msgtype: "m.text", body: "msg1" },
plainType: "m.room.message",
user: "@alice:example.org",
room: room.roomId,
});
eventToEncryptionInfoMap.set(replacementEvent.getId()!, {
authenticated: true,
sender: UNTRUSTED_DEVICE,
} as IEncryptedEventInfo);
act(() => {
mxEvent.makeReplaced(replacementEvent);
});
// check it was updated
expect(eventTile.classList).toContain("mx_EventTile_unverified");
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(1);
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")[0].classList).toContain(
"mx_EventTile_e2eIcon_warning",
);
});
it("should update the warning when the event is replaced with an unencrypted one", async () => {
jest.spyOn(client, "isRoomEncrypted").mockReturnValue(true);
// we start out with an event from the trusted device
mxEvent = await mkEncryptedEvent({
plainContent: { msgtype: "m.text", body: "msg1" },
plainType: "m.room.message",
user: "@alice:example.org",
room: room.roomId,
});
eventToEncryptionInfoMap.set(mxEvent.getId()!, {
authenticated: true,
sender: TRUSTED_DEVICE,
} as IEncryptedEventInfo);
const { container } = getComponent();
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
const eventTile = eventTiles[0];
expect(eventTile.classList).toContain("mx_EventTile_verified");
// there should be no warning
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(0);
// then we replace the event with an unencrypted one
const replacementEvent = await mkMessage({
msg: "msg2",
user: "@alice:example.org",
room: room.roomId,
event: true,
});
act(() => {
mxEvent.makeReplaced(replacementEvent);
});
// check it was updated
expect(eventTile.classList).not.toContain("mx_EventTile_verified");
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(1);
expect(container.getElementsByClassName("mx_EventTile_e2eIcon")[0].classList).toContain(
"mx_EventTile_e2eIcon_warning",
);
});
});
});