Replace MatrixClient.isRoomEncrypted
by MatrixClient.CryptoApi.isEncryptionEnabledInRoom
in RoomView (#28278)
* Replace `MatrixClient.isRoomEncrypted` by `MatrixClient.CryptoApi.isEncryptionEnabledInRoom` in RoomView * Add `isRoomEncrypted` to room * Update e2eStatus and urlPreview when isRoomEncrypted is computed * Fix e2e test * Add tests when user verification change * Reduced abusive timeout in e2e test
This commit is contained in:
parent
de5ddcf6f7
commit
af846f8be9
8 changed files with 775 additions and 106 deletions
|
@ -67,6 +67,9 @@ test.describe("Cryptography", function () {
|
||||||
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
|
||||||
await app.viewRoomByName("Test room");
|
await app.viewRoomByName("Test room");
|
||||||
|
|
||||||
|
// In this case, the call to cryptoApi.isEncryptionEnabledInRoom is taking a long time to resolve
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
// There should be two historical events in the timeline
|
// There should be two historical events in the timeline
|
||||||
const tiles = await page.locator(".mx_EventTile").all();
|
const tiles = await page.locator(".mx_EventTile").all();
|
||||||
expect(tiles.length).toBeGreaterThanOrEqual(2);
|
expect(tiles.length).toBeGreaterThanOrEqual(2);
|
||||||
|
|
|
@ -9,7 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, ComponentProps, createRef, ReactElement, ReactNode, RefObject, useContext } from "react";
|
import React, {
|
||||||
|
ChangeEvent,
|
||||||
|
ComponentProps,
|
||||||
|
createRef,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
RefObject,
|
||||||
|
useContext,
|
||||||
|
JSX,
|
||||||
|
} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {
|
import {
|
||||||
IRecommendedVersion,
|
IRecommendedVersion,
|
||||||
|
@ -29,6 +38,7 @@ import {
|
||||||
MatrixError,
|
MatrixError,
|
||||||
ISearchResults,
|
ISearchResults,
|
||||||
THREAD_RELATION_TYPE,
|
THREAD_RELATION_TYPE,
|
||||||
|
MatrixClient,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -233,6 +243,11 @@ export interface IRoomState {
|
||||||
liveTimeline?: EventTimeline;
|
liveTimeline?: EventTimeline;
|
||||||
narrow: boolean;
|
narrow: boolean;
|
||||||
msc3946ProcessDynamicPredecessor: boolean;
|
msc3946ProcessDynamicPredecessor: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the room is encrypted or not.
|
||||||
|
* If null, we are still determining the encryption status.
|
||||||
|
*/
|
||||||
|
isRoomEncrypted: boolean | null;
|
||||||
|
|
||||||
canAskToJoin: boolean;
|
canAskToJoin: boolean;
|
||||||
promptAskToJoin: boolean;
|
promptAskToJoin: boolean;
|
||||||
|
@ -417,6 +432,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
canAskToJoin: this.askToJoinEnabled,
|
canAskToJoin: this.askToJoinEnabled,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,7 +863,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
return isManuallyShown && widgets.length > 0;
|
return isManuallyShown && widgets.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public async componentDidMount(): Promise<void> {
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
|
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
|
@ -1342,13 +1358,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
||||||
|
|
||||||
this.calculatePeekRules(room);
|
this.calculatePeekRules(room);
|
||||||
this.updatePreviewUrlVisibility(room);
|
|
||||||
this.loadMembersIfJoined(room);
|
this.loadMembersIfJoined(room);
|
||||||
this.calculateRecommendedVersion(room);
|
this.calculateRecommendedVersion(room);
|
||||||
this.updateE2EStatus(room);
|
|
||||||
this.updatePermissions(room);
|
this.updatePermissions(room);
|
||||||
this.checkWidgets(room);
|
this.checkWidgets(room);
|
||||||
this.loadVirtualRoom(room);
|
this.loadVirtualRoom(room);
|
||||||
|
this.updateRoomEncrypted(room);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.getMainSplitContentType(room) !== MainSplitContentType.Timeline &&
|
this.getMainSplitContentType(room) !== MainSplitContentType.Timeline &&
|
||||||
|
@ -1377,6 +1392,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
return room?.currentState.getStateEvents(EventType.RoomTombstone, "") ?? undefined;
|
return room?.currentState.getStateEvents(EventType.RoomTombstone, "") ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getIsRoomEncrypted(roomId = this.state.roomId): Promise<boolean> {
|
||||||
|
const crypto = this.context.client?.getCrypto();
|
||||||
|
if (!crypto || !roomId) return false;
|
||||||
|
|
||||||
|
return await crypto.isEncryptionEnabledInRoom(roomId);
|
||||||
|
}
|
||||||
|
|
||||||
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
||||||
const upgradeRecommendation = await room.getRecommendedVersion();
|
const upgradeRecommendation = await room.getRecommendedVersion();
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
@ -1409,12 +1431,15 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePreviewUrlVisibility({ roomId }: Room): void {
|
private updatePreviewUrlVisibility(room: Room): void {
|
||||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
this.setState(({ isRoomEncrypted }) => ({
|
||||||
const key = this.context.client?.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted),
|
||||||
this.setState({
|
}));
|
||||||
showUrlPreview: SettingsStore.getValue(key, roomId),
|
}
|
||||||
});
|
|
||||||
|
private getPreviewUrlVisibility({ roomId }: Room, isRoomEncrypted: boolean | null): boolean {
|
||||||
|
const key = isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
||||||
|
return SettingsStore.getValue(key, roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoom = (room: Room): void => {
|
private onRoom = (room: Room): void => {
|
||||||
|
@ -1456,7 +1481,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private async updateE2EStatus(room: Room): Promise<void> {
|
private async updateE2EStatus(room: Room): Promise<void> {
|
||||||
if (!this.context.client?.isRoomEncrypted(room.roomId)) return;
|
if (!this.context.client || !this.state.isRoomEncrypted) return;
|
||||||
|
|
||||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
// If crypto is not currently enabled, we aren't tracking devices at all,
|
||||||
// so we don't know what the answer is. Let's error on the safe side and show
|
// so we don't know what the answer is. Let's error on the safe side and show
|
||||||
|
@ -1467,33 +1492,54 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
if (this.context.client.getCrypto()) {
|
if (this.context.client.getCrypto()) {
|
||||||
/* At this point, the user has encryption on and cross-signing on */
|
/* At this point, the user has encryption on and cross-signing on */
|
||||||
e2eStatus = await shieldStatusForRoom(this.context.client, room);
|
e2eStatus = await this.cacheAndGetE2EStatus(room, this.context.client);
|
||||||
RoomView.e2eStatusCache.set(room.roomId, e2eStatus);
|
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
this.setState({ e2eStatus });
|
this.setState({ e2eStatus });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async cacheAndGetE2EStatus(room: Room, client: MatrixClient): Promise<E2EStatus> {
|
||||||
|
const e2eStatus = await shieldStatusForRoom(client, room);
|
||||||
|
RoomView.e2eStatusCache.set(room.roomId, e2eStatus);
|
||||||
|
return e2eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
private onUrlPreviewsEnabledChange = (): void => {
|
private onUrlPreviewsEnabledChange = (): void => {
|
||||||
if (this.state.room) {
|
if (this.state.room) {
|
||||||
this.updatePreviewUrlVisibility(this.state.room);
|
this.updatePreviewUrlVisibility(this.state.room);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateEvents = (ev: MatrixEvent, state: RoomState): void => {
|
private onRoomStateEvents = async (ev: MatrixEvent, state: RoomState): Promise<void> => {
|
||||||
// ignore if we don't have a room yet
|
// ignore if we don't have a room yet
|
||||||
if (!this.state.room || this.state.room.roomId !== state.roomId) return;
|
if (!this.state.room || this.state.room.roomId !== state.roomId || !this.context.client) return;
|
||||||
|
|
||||||
switch (ev.getType()) {
|
switch (ev.getType()) {
|
||||||
case EventType.RoomTombstone:
|
case EventType.RoomTombstone:
|
||||||
this.setState({ tombstone: this.getRoomTombstone() });
|
this.setState({ tombstone: this.getRoomTombstone() });
|
||||||
break;
|
break;
|
||||||
|
case EventType.RoomEncryption: {
|
||||||
|
await this.updateRoomEncrypted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
this.updatePermissions(this.state.room);
|
this.updatePermissions(this.state.room);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async updateRoomEncrypted(room = this.state.room): Promise<void> {
|
||||||
|
if (!room || !this.context.client) return;
|
||||||
|
|
||||||
|
const isRoomEncrypted = await this.getIsRoomEncrypted(room.roomId);
|
||||||
|
const newE2EStatus = isRoomEncrypted ? await this.cacheAndGetE2EStatus(room, this.context.client) : null;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isRoomEncrypted,
|
||||||
|
showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted),
|
||||||
|
...(newE2EStatus && { e2eStatus: newE2EStatus }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private onRoomStateUpdate = (state: RoomState): void => {
|
private onRoomStateUpdate = (state: RoomState): void => {
|
||||||
// ignore members in other rooms
|
// ignore members in other rooms
|
||||||
if (state.roomId !== this.state.room?.roomId) {
|
if (state.roomId !== this.state.room?.roomId) {
|
||||||
|
@ -2027,6 +2073,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
public render(): ReactNode {
|
public render(): ReactNode {
|
||||||
if (!this.context.client) return null;
|
if (!this.context.client) return null;
|
||||||
|
const { isRoomEncrypted } = this.state;
|
||||||
|
const isRoomEncryptionLoading = isRoomEncrypted === null;
|
||||||
|
|
||||||
if (this.state.room instanceof LocalRoom) {
|
if (this.state.room instanceof LocalRoom) {
|
||||||
if (this.state.room.state === LocalRoomState.CREATING) {
|
if (this.state.room.state === LocalRoomState.CREATING) {
|
||||||
|
@ -2242,14 +2290,16 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
let aux: JSX.Element | undefined;
|
let aux: JSX.Element | undefined;
|
||||||
let previewBar;
|
let previewBar;
|
||||||
if (this.state.timelineRenderingType === TimelineRenderingType.Search) {
|
if (this.state.timelineRenderingType === TimelineRenderingType.Search) {
|
||||||
aux = (
|
if (!isRoomEncryptionLoading) {
|
||||||
<RoomSearchAuxPanel
|
aux = (
|
||||||
searchInfo={this.state.search}
|
<RoomSearchAuxPanel
|
||||||
onCancelClick={this.onCancelSearchClick}
|
searchInfo={this.state.search}
|
||||||
onSearchScopeChange={this.onSearchScopeChange}
|
onCancelClick={this.onCancelSearchClick}
|
||||||
isRoomEncrypted={this.context.client.isRoomEncrypted(this.state.room.roomId)}
|
onSearchScopeChange={this.onSearchScopeChange}
|
||||||
/>
|
isRoomEncrypted={isRoomEncrypted}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (showRoomUpgradeBar) {
|
} else if (showRoomUpgradeBar) {
|
||||||
aux = <RoomUpgradeWarningBar room={this.state.room} />;
|
aux = <RoomUpgradeWarningBar room={this.state.room} />;
|
||||||
} else if (myMembership !== KnownMembership.Join) {
|
} else if (myMembership !== KnownMembership.Join) {
|
||||||
|
@ -2325,8 +2375,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
|
|
||||||
let messageComposer;
|
let messageComposer;
|
||||||
const showComposer =
|
const showComposer =
|
||||||
|
!isRoomEncryptionLoading &&
|
||||||
// joined and not showing search results
|
// joined and not showing search results
|
||||||
myMembership === KnownMembership.Join && !this.state.search;
|
myMembership === KnownMembership.Join &&
|
||||||
|
!this.state.search;
|
||||||
if (showComposer) {
|
if (showComposer) {
|
||||||
messageComposer = (
|
messageComposer = (
|
||||||
<MessageComposer
|
<MessageComposer
|
||||||
|
@ -2367,34 +2419,37 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
highlightedEventId = this.state.initialEventId;
|
highlightedEventId = this.state.initialEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagePanel = (
|
let messagePanel: JSX.Element | undefined;
|
||||||
<TimelinePanel
|
if (!isRoomEncryptionLoading) {
|
||||||
ref={this.gatherTimelinePanelRef}
|
messagePanel = (
|
||||||
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
<TimelinePanel
|
||||||
overlayTimelineSet={this.state.virtualRoom?.getUnfilteredTimelineSet()}
|
ref={this.gatherTimelinePanelRef}
|
||||||
overlayTimelineSetFilter={isCallEvent}
|
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
||||||
showReadReceipts={this.state.showReadReceipts}
|
overlayTimelineSet={this.state.virtualRoom?.getUnfilteredTimelineSet()}
|
||||||
manageReadReceipts={!this.state.isPeeking}
|
overlayTimelineSetFilter={isCallEvent}
|
||||||
sendReadReceiptOnLoad={!this.state.wasContextSwitch}
|
showReadReceipts={this.state.showReadReceipts}
|
||||||
manageReadMarkers={!this.state.isPeeking}
|
manageReadReceipts={!this.state.isPeeking}
|
||||||
hidden={hideMessagePanel}
|
sendReadReceiptOnLoad={!this.state.wasContextSwitch}
|
||||||
highlightedEventId={highlightedEventId}
|
manageReadMarkers={!this.state.isPeeking}
|
||||||
eventId={this.state.initialEventId}
|
hidden={hideMessagePanel}
|
||||||
eventScrollIntoView={this.state.initialEventScrollIntoView}
|
highlightedEventId={highlightedEventId}
|
||||||
eventPixelOffset={this.state.initialEventPixelOffset}
|
eventId={this.state.initialEventId}
|
||||||
onScroll={this.onMessageListScroll}
|
eventScrollIntoView={this.state.initialEventScrollIntoView}
|
||||||
onEventScrolledIntoView={this.resetJumpToEvent}
|
eventPixelOffset={this.state.initialEventPixelOffset}
|
||||||
onReadMarkerUpdated={this.updateTopUnreadMessagesBar}
|
onScroll={this.onMessageListScroll}
|
||||||
showUrlPreview={this.state.showUrlPreview}
|
onEventScrolledIntoView={this.resetJumpToEvent}
|
||||||
className={this.messagePanelClassNames}
|
onReadMarkerUpdated={this.updateTopUnreadMessagesBar}
|
||||||
membersLoaded={this.state.membersLoaded}
|
showUrlPreview={this.state.showUrlPreview}
|
||||||
permalinkCreator={this.permalinkCreator}
|
className={this.messagePanelClassNames}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
membersLoaded={this.state.membersLoaded}
|
||||||
showReactions={true}
|
permalinkCreator={this.permalinkCreator}
|
||||||
layout={this.state.layout}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
editState={this.state.editState}
|
showReactions={true}
|
||||||
/>
|
layout={this.state.layout}
|
||||||
);
|
editState={this.state.editState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let topUnreadMessagesBar: JSX.Element | undefined;
|
let topUnreadMessagesBar: JSX.Element | undefined;
|
||||||
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
||||||
|
@ -2415,7 +2470,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showRightPanel = this.state.room && this.state.showRightPanel;
|
const showRightPanel = !isRoomEncryptionLoading && this.state.room && this.state.showRightPanel;
|
||||||
|
|
||||||
const rightPanel = showRightPanel ? (
|
const rightPanel = showRightPanel ? (
|
||||||
<RightPanel
|
<RightPanel
|
||||||
|
|
|
@ -75,6 +75,7 @@ const RoomContext = createContext<
|
||||||
canAskToJoin: false,
|
canAskToJoin: false,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: null,
|
||||||
});
|
});
|
||||||
RoomContext.displayName = "RoomContext";
|
RoomContext.displayName = "RoomContext";
|
||||||
export default RoomContext;
|
export default RoomContext;
|
||||||
|
|
|
@ -85,7 +85,7 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
|
||||||
canAskToJoin: false,
|
canAskToJoin: false,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: false,
|
||||||
...override,
|
...override,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ export function createTestClient(): MatrixClient {
|
||||||
|
|
||||||
getCrypto: jest.fn().mockReturnValue({
|
getCrypto: jest.fn().mockReturnValue({
|
||||||
getOwnDeviceKeys: jest.fn(),
|
getOwnDeviceKeys: jest.fn(),
|
||||||
getUserDeviceInfo: jest.fn(),
|
getUserDeviceInfo: jest.fn().mockResolvedValue(new Map()),
|
||||||
getUserVerificationStatus: jest.fn(),
|
getUserVerificationStatus: jest.fn(),
|
||||||
getDeviceVerificationStatus: jest.fn(),
|
getDeviceVerificationStatus: jest.fn(),
|
||||||
resetKeyBackup: jest.fn(),
|
resetKeyBackup: jest.fn(),
|
||||||
|
|
|
@ -10,18 +10,19 @@ import React, { createRef, RefObject } from "react";
|
||||||
import { mocked, MockedObject } from "jest-mock";
|
import { mocked, MockedObject } from "jest-mock";
|
||||||
import {
|
import {
|
||||||
ClientEvent,
|
ClientEvent,
|
||||||
|
EventTimeline,
|
||||||
|
EventType,
|
||||||
|
IEvent,
|
||||||
|
JoinRule,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
|
MatrixError,
|
||||||
|
MatrixEvent,
|
||||||
Room,
|
Room,
|
||||||
RoomEvent,
|
RoomEvent,
|
||||||
EventType,
|
|
||||||
JoinRule,
|
|
||||||
MatrixError,
|
|
||||||
RoomStateEvent,
|
RoomStateEvent,
|
||||||
MatrixEvent,
|
|
||||||
SearchResult,
|
SearchResult,
|
||||||
IEvent,
|
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
import { CryptoApi, UserVerificationStatus, CryptoEvent } from "matrix-js-sdk/src/crypto-api";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import {
|
import {
|
||||||
fireEvent,
|
fireEvent,
|
||||||
|
@ -34,6 +35,7 @@ import {
|
||||||
cleanup,
|
cleanup,
|
||||||
} from "jest-matrix-react";
|
} from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { defer } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
stubClient,
|
stubClient,
|
||||||
|
@ -87,8 +89,7 @@ describe("RoomView", () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockPlatformPeg({ reload: () => {} });
|
mockPlatformPeg({ reload: () => {} });
|
||||||
stubClient();
|
cli = mocked(stubClient());
|
||||||
cli = mocked(MatrixClientPeg.safeGet());
|
|
||||||
|
|
||||||
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
||||||
jest.spyOn(room, "findPredecessor");
|
jest.spyOn(room, "findPredecessor");
|
||||||
|
@ -247,8 +248,9 @@ describe("RoomView", () => {
|
||||||
|
|
||||||
it("updates url preview visibility on encryption state change", async () => {
|
it("updates url preview visibility on encryption state change", async () => {
|
||||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
// we should be starting unencrypted
|
// we should be starting unencrypted
|
||||||
expect(cli.isRoomEncrypted(room.roomId)).toEqual(false);
|
expect(await cli.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)).toEqual(false);
|
||||||
|
|
||||||
const roomViewInstance = await getRoomViewInstance();
|
const roomViewInstance = await getRoomViewInstance();
|
||||||
|
|
||||||
|
@ -263,23 +265,38 @@ describe("RoomView", () => {
|
||||||
expect(roomViewInstance.state.showUrlPreview).toBe(true);
|
expect(roomViewInstance.state.showUrlPreview).toBe(true);
|
||||||
|
|
||||||
// now enable encryption
|
// now enable encryption
|
||||||
cli.isRoomEncrypted.mockReturnValue(true);
|
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||||
|
|
||||||
// and fake an encryption event into the room to prompt it to re-check
|
// and fake an encryption event into the room to prompt it to re-check
|
||||||
await act(() =>
|
act(() => {
|
||||||
room.addLiveEvents([
|
const encryptionEvent = new MatrixEvent({
|
||||||
new MatrixEvent({
|
type: EventType.RoomEncryption,
|
||||||
type: "m.room.encryption",
|
sender: cli.getUserId()!,
|
||||||
sender: cli.getUserId()!,
|
content: {},
|
||||||
content: {},
|
event_id: "someid",
|
||||||
event_id: "someid",
|
room_id: room.roomId,
|
||||||
room_id: room.roomId,
|
});
|
||||||
}),
|
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||||
]),
|
cli.emit(RoomStateEvent.Events, encryptionEvent, roomState, null);
|
||||||
);
|
});
|
||||||
|
|
||||||
// URL previews should now be disabled
|
// URL previews should now be disabled
|
||||||
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
await waitFor(() => expect(roomViewInstance.state.showUrlPreview).toBe(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not display the timeline when the room encryption is loading", async () => {
|
||||||
|
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||||
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
|
const deferred = defer<boolean>();
|
||||||
|
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockImplementation(() => deferred.promise);
|
||||||
|
|
||||||
|
const { asFragment, container } = await mountRoomView();
|
||||||
|
expect(container.querySelector(".mx_RoomView_messagePanel")).toBeNull();
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
|
deferred.resolve(true);
|
||||||
|
await waitFor(() => expect(container.querySelector(".mx_RoomView_messagePanel")).not.toBeNull());
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates live timeline when a timeline reset happens", async () => {
|
it("updates live timeline when a timeline reset happens", async () => {
|
||||||
|
@ -290,6 +307,32 @@ describe("RoomView", () => {
|
||||||
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should update when the e2e status when the user verification changed", async () => {
|
||||||
|
room.currentState.setStateEvents([
|
||||||
|
mkRoomMemberJoinEvent(cli.getSafeUserId(), room.roomId),
|
||||||
|
mkRoomMemberJoinEvent("user@example.com", room.roomId),
|
||||||
|
]);
|
||||||
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
// Not all the calls to cli.isRoomEncrypted are migrated, so we need to mock both.
|
||||||
|
mocked(cli.isRoomEncrypted).mockReturnValue(true);
|
||||||
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(
|
||||||
|
new UserVerificationStatus(false, false, false),
|
||||||
|
);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "getUserDeviceInfo").mockResolvedValue(
|
||||||
|
new Map([["user@example.com", new Map<string, any>()]]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container } = await renderRoomView();
|
||||||
|
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_normal")).toBeInTheDocument());
|
||||||
|
|
||||||
|
const verificationStatus = new UserVerificationStatus(true, true, false);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(verificationStatus);
|
||||||
|
cli.emit(CryptoEvent.UserTrustStatusChanged, cli.getSafeUserId(), verificationStatus);
|
||||||
|
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_verified")).toBeInTheDocument());
|
||||||
|
});
|
||||||
|
|
||||||
describe("with virtual rooms", () => {
|
describe("with virtual rooms", () => {
|
||||||
it("checks for a virtual room on initial load", async () => {
|
it("checks for a virtual room on initial load", async () => {
|
||||||
const { container } = await renderRoomView();
|
const { container } = await renderRoomView();
|
||||||
|
@ -427,7 +470,8 @@ describe("RoomView", () => {
|
||||||
]);
|
]);
|
||||||
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId());
|
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId());
|
||||||
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
|
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
|
||||||
mocked(cli).isRoomEncrypted.mockReturnValue(true);
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||||
await renderRoomView();
|
await renderRoomView();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby=":rbc:"
|
aria-labelledby=":rg4:"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
@ -78,7 +78,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Voice call"
|
aria-label="Voice call"
|
||||||
aria-labelledby=":rbh:"
|
aria-labelledby=":rg9:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -103,7 +103,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby=":rbm:"
|
aria-labelledby=":rge:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -128,7 +128,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":rbr:"
|
aria-labelledby=":rgj:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -157,7 +157,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="2 members"
|
aria-label="2 members"
|
||||||
aria-labelledby=":rc0:"
|
aria-labelledby=":rgo:"
|
||||||
class="mx_AccessibleButton mx_FacePile"
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -280,7 +280,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby=":rca:"
|
aria-labelledby=":rh2:"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
@ -296,7 +296,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Voice call"
|
aria-label="Voice call"
|
||||||
aria-labelledby=":rcf:"
|
aria-labelledby=":rh7:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -321,7 +321,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby=":rck:"
|
aria-labelledby=":rhc:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -346,7 +346,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":rcp:"
|
aria-labelledby=":rhh:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -375,7 +375,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="2 members"
|
aria-label="2 members"
|
||||||
aria-labelledby=":rcu:"
|
aria-labelledby=":rhm:"
|
||||||
class="mx_AccessibleButton mx_FacePile"
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -583,7 +583,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby=":r70:"
|
aria-labelledby=":rbo:"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
@ -599,7 +599,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Voice call"
|
aria-label="Voice call"
|
||||||
aria-labelledby=":r75:"
|
aria-labelledby=":rbt:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -624,7 +624,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby=":r7a:"
|
aria-labelledby=":rc2:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -649,7 +649,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r7f:"
|
aria-labelledby=":rc7:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -678,7 +678,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="2 members"
|
aria-label="2 members"
|
||||||
aria-labelledby=":r7k:"
|
aria-labelledby=":rcc:"
|
||||||
class="mx_AccessibleButton mx_FacePile"
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -963,7 +963,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
style="--cpd-icon-button-size: 100%;"
|
style="--cpd-icon-button-size: 100%;"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-labelledby=":r96:"
|
aria-labelledby=":rdu:"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
@ -979,7 +979,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
<button
|
<button
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-label="Voice call"
|
aria-label="Voice call"
|
||||||
aria-labelledby=":r9b:"
|
aria-labelledby=":re3:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1004,7 +1004,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby=":r9g:"
|
aria-labelledby=":re8:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1029,7 +1029,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r9l:"
|
aria-labelledby=":red:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1058,7 +1058,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="2 members"
|
aria-label="2 members"
|
||||||
aria-labelledby=":r9q:"
|
aria-labelledby=":rei:"
|
||||||
class="mx_AccessibleButton mx_FacePile"
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -1276,6 +1276,571 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`RoomView should not display the timeline when the room encryption is loading 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView"
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
aria-hidden="true"
|
||||||
|
height="768"
|
||||||
|
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||||
|
width="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="mx_MainSplit"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_body mx_MainSplit_timeline"
|
||||||
|
data-layout="group"
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
class="mx_Flex mx_RoomHeader light-panel"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Open room settings"
|
||||||
|
aria-live="off"
|
||||||
|
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||||
|
data-color="1"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
data-type="round"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-avatar-size: 40px;"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
class="mx_RoomHeader_infoWrapper"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Box mx_RoomHeader_info mx_Box--flex"
|
||||||
|
style="--mx-box-flex: 1;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-level="1"
|
||||||
|
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
|
||||||
|
dir="auto"
|
||||||
|
role="heading"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||||
|
>
|
||||||
|
!5:example.org
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="mx_Flex"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-label="There's no one here to call"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-labelledby=":r2c:"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-label="There's no one here to call"
|
||||||
|
aria-labelledby=":r2h:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r2m:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Threads"
|
||||||
|
aria-labelledby=":r2r:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="0 members"
|
||||||
|
aria-labelledby=":r30:"
|
||||||
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_stacked-avatars_mcap2_111"
|
||||||
|
/>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div
|
||||||
|
class="mx_AutoHideScrollbar mx_AuxPanel"
|
||||||
|
role="region"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<main
|
||||||
|
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-label="Room status bar"
|
||||||
|
class="mx_RoomView_statusArea"
|
||||||
|
role="region"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_statusAreaBox"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_statusAreaBox_line"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`RoomView should not display the timeline when the room encryption is loading 2`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView"
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
aria-hidden="true"
|
||||||
|
height="768"
|
||||||
|
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||||
|
width="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="mx_MainSplit"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_body mx_MainSplit_timeline"
|
||||||
|
data-layout="group"
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
class="mx_Flex mx_RoomHeader light-panel"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Open room settings"
|
||||||
|
aria-live="off"
|
||||||
|
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||||
|
data-color="1"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
data-type="round"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-avatar-size: 40px;"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
class="mx_RoomHeader_infoWrapper"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_Box mx_RoomHeader_info mx_Box--flex"
|
||||||
|
style="--mx-box-flex: 1;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-level="1"
|
||||||
|
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
|
||||||
|
dir="auto"
|
||||||
|
role="heading"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||||
|
>
|
||||||
|
!5:example.org
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="mx_Flex"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-label="There's no one here to call"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-labelledby=":r2c:"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-label="There's no one here to call"
|
||||||
|
aria-labelledby=":r2h:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Room info"
|
||||||
|
aria-labelledby=":r2m:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-label="Threads"
|
||||||
|
aria-labelledby=":r2r:"
|
||||||
|
class="_icon-button_bh2qc_17"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_indicator-icon_133tf_26"
|
||||||
|
style="--cpd-icon-button-size: 100%;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="0 members"
|
||||||
|
aria-labelledby=":r30:"
|
||||||
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_stacked-avatars_mcap2_111"
|
||||||
|
/>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div
|
||||||
|
class="mx_AutoHideScrollbar mx_AuxPanel"
|
||||||
|
role="region"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<main
|
||||||
|
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_messageListWrapper"
|
||||||
|
>
|
||||||
|
<ol
|
||||||
|
aria-live="polite"
|
||||||
|
class="mx_RoomView_MessageList"
|
||||||
|
style="height: 400px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<div
|
||||||
|
aria-label="Room status bar"
|
||||||
|
class="mx_RoomView_statusArea"
|
||||||
|
role="region"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_statusAreaBox"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_RoomView_statusAreaBox_line"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-label="Message composer"
|
||||||
|
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
|
||||||
|
role="region"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_wrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_e2eIconWrapper"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-labelledby=":r3e:"
|
||||||
|
class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_SendMessageComposer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_BasicMessageComposer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Formatting"
|
||||||
|
class="mx_MessageComposerFormatBar"
|
||||||
|
role="toolbar"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Bold"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Italics"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Strikethrough"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Code block"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Quote"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Insert link"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-label="Send an encrypted message…"
|
||||||
|
aria-multiline="true"
|
||||||
|
class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty"
|
||||||
|
contenteditable="true"
|
||||||
|
data-testid="basicmessagecomposer"
|
||||||
|
dir="auto"
|
||||||
|
role="textbox"
|
||||||
|
style="--placeholder: 'Send\\ an\\ encrypted\\ message…';"
|
||||||
|
tabindex="0"
|
||||||
|
translate="no"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_MessageComposer_actions"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Emoji"
|
||||||
|
class="mx_AccessibleButton mx_EmojiButton mx_MessageComposer_button mx_EmojiButton_icon"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-label="Attachment"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-label="More options"
|
||||||
|
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
multiple=""
|
||||||
|
style="display: none;"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`RoomView should show error view if failed to look up room alias 1`] = `
|
exports[`RoomView should show error view if failed to look up room alias 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
<div
|
<div
|
||||||
|
@ -1332,7 +1897,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
aria-label="Open room settings"
|
aria-label="Open room settings"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||||
data-color="3"
|
data-color="5"
|
||||||
data-testid="avatar-img"
|
data-testid="avatar-img"
|
||||||
data-type="round"
|
data-type="round"
|
||||||
role="button"
|
role="button"
|
||||||
|
@ -1359,7 +1924,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
<span
|
<span
|
||||||
class="mx_RoomHeader_truncated mx_lineClamp"
|
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||||
>
|
>
|
||||||
!10:example.org
|
!12:example.org
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1370,7 +1935,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Room info"
|
aria-label="Room info"
|
||||||
aria-labelledby=":r2k:"
|
aria-labelledby=":r7c:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1395,7 +1960,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Chat"
|
aria-label="Chat"
|
||||||
aria-labelledby=":r2p:"
|
aria-labelledby=":r7h:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1420,7 +1985,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
aria-label="Threads"
|
aria-label="Threads"
|
||||||
aria-labelledby=":r2u:"
|
aria-labelledby=":r7m:"
|
||||||
class="_icon-button_bh2qc_17"
|
class="_icon-button_bh2qc_17"
|
||||||
role="button"
|
role="button"
|
||||||
style="--cpd-icon-button-size: 32px;"
|
style="--cpd-icon-button-size: 32px;"
|
||||||
|
@ -1449,7 +2014,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="0 members"
|
aria-label="0 members"
|
||||||
aria-labelledby=":r33:"
|
aria-labelledby=":r7r:"
|
||||||
class="mx_AccessibleButton mx_FacePile"
|
class="mx_AccessibleButton mx_FacePile"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
@ -1487,7 +2052,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
aria-labelledby=":r3c:"
|
aria-labelledby=":r84:"
|
||||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||||
data-testid="base-card-close-button"
|
data-testid="base-card-close-button"
|
||||||
role="button"
|
role="button"
|
||||||
|
|
|
@ -77,6 +77,7 @@ describe("<SendMessageComposer/>", () => {
|
||||||
canAskToJoin: false,
|
canAskToJoin: false,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: false,
|
||||||
};
|
};
|
||||||
describe("createMessageContent", () => {
|
describe("createMessageContent", () => {
|
||||||
it("sends plaintext messages correctly", () => {
|
it("sends plaintext messages correctly", () => {
|
||||||
|
|
Loading…
Reference in a new issue