Apply strictNullChecks
to src/components/views/settings
(#10724)
This commit is contained in:
parent
a4f0b80692
commit
1f4d857283
11 changed files with 772 additions and 48 deletions
250
test/components/views/settings/JoinRuleSettings-test.tsx
Normal file
250
test/components/views/settings/JoinRuleSettings-test.tsx
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import {
|
||||
EventType,
|
||||
GuestAccess,
|
||||
HistoryVisibility,
|
||||
JoinRule,
|
||||
MatrixEvent,
|
||||
Room,
|
||||
ClientEvent,
|
||||
RoomMember,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import {
|
||||
clearAllModals,
|
||||
flushPromises,
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../test-utils";
|
||||
import { filterBoolean } from "../../../../src/utils/arrays";
|
||||
import JoinRuleSettings, { JoinRuleSettingsProps } from "../../../../src/components/views/settings/JoinRuleSettings";
|
||||
import { PreferredRoomVersions } from "../../../../src/utils/PreferredRoomVersions";
|
||||
import SpaceStore from "../../../../src/stores/spaces/SpaceStore";
|
||||
|
||||
describe("<JoinRuleSettings />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
getRoom: jest.fn(),
|
||||
getLocalAliases: jest.fn().mockReturnValue([]),
|
||||
sendStateEvent: jest.fn(),
|
||||
upgradeRoom: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
invite: jest.fn().mockResolvedValue(undefined),
|
||||
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
||||
});
|
||||
const roomId = "!room:server.org";
|
||||
const newRoomId = "!roomUpgraded:server.org";
|
||||
|
||||
const defaultProps = {
|
||||
room: new Room(roomId, client, userId),
|
||||
closeSettingsFn: jest.fn(),
|
||||
onError: jest.fn(),
|
||||
};
|
||||
const getComponent = (props: Partial<JoinRuleSettingsProps> = {}) =>
|
||||
render(<JoinRuleSettings {...defaultProps} {...props} />);
|
||||
|
||||
const setRoomStateEvents = (
|
||||
room: Room,
|
||||
version = "9",
|
||||
joinRule?: JoinRule,
|
||||
guestAccess?: GuestAccess,
|
||||
history?: HistoryVisibility,
|
||||
): void => {
|
||||
const events = filterBoolean<MatrixEvent>([
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomCreate,
|
||||
content: { version },
|
||||
sender: userId,
|
||||
state_key: "",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
guestAccess &&
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomGuestAccess,
|
||||
content: { guest_access: guestAccess },
|
||||
sender: userId,
|
||||
state_key: "",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
history &&
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomHistoryVisibility,
|
||||
content: { history_visibility: history },
|
||||
sender: userId,
|
||||
state_key: "",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
joinRule &&
|
||||
new MatrixEvent({
|
||||
type: EventType.RoomJoinRules,
|
||||
content: { join_rule: joinRule },
|
||||
sender: userId,
|
||||
state_key: "",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
]);
|
||||
|
||||
room.currentState.setStateEvents(events);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
client.sendStateEvent.mockReset().mockResolvedValue({ event_id: "test" });
|
||||
client.isRoomEncrypted.mockReturnValue(false);
|
||||
client.upgradeRoom.mockResolvedValue({ replacement_room: newRoomId });
|
||||
client.getRoom.mockReturnValue(null);
|
||||
});
|
||||
|
||||
describe("Restricted rooms", () => {
|
||||
afterEach(async () => {
|
||||
await clearAllModals();
|
||||
});
|
||||
describe("When room does not support restricted rooms", () => {
|
||||
it("should not show restricted room join rule when upgrade not enabled", () => {
|
||||
// room that doesnt support restricted rooms
|
||||
const v8Room = new Room(roomId, client, userId);
|
||||
setRoomStateEvents(v8Room, "8");
|
||||
|
||||
getComponent({ room: v8Room, promptUpgrade: false });
|
||||
|
||||
expect(screen.queryByText("Space members")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show restricted room join rule when upgrade is enabled", () => {
|
||||
// room that doesnt support restricted rooms
|
||||
const v8Room = new Room(roomId, client, userId);
|
||||
setRoomStateEvents(v8Room, "8");
|
||||
|
||||
getComponent({ room: v8Room, promptUpgrade: true });
|
||||
|
||||
expect(screen.getByText("Space members")).toBeInTheDocument();
|
||||
expect(screen.getByText("Upgrade required")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("upgrades room when changing join rule to restricted", async () => {
|
||||
const deferredInvites: IDeferred<any>[] = [];
|
||||
// room that doesnt support restricted rooms
|
||||
const v8Room = new Room(roomId, client, userId);
|
||||
const parentSpace = new Room("!parentSpace:server.org", client, userId);
|
||||
jest.spyOn(SpaceStore.instance, "getKnownParents").mockReturnValue(new Set([parentSpace.roomId]));
|
||||
setRoomStateEvents(v8Room, "8");
|
||||
const memberAlice = new RoomMember(roomId, "@alice:server.org");
|
||||
const memberBob = new RoomMember(roomId, "@bob:server.org");
|
||||
const memberCharlie = new RoomMember(roomId, "@charlie:server.org");
|
||||
jest.spyOn(v8Room, "getMembersWithMembership").mockImplementation((membership) =>
|
||||
membership === "join" ? [memberAlice, memberBob] : [memberCharlie],
|
||||
);
|
||||
const upgradedRoom = new Room(newRoomId, client, userId);
|
||||
setRoomStateEvents(upgradedRoom);
|
||||
client.getRoom.mockImplementation((id) => {
|
||||
if (roomId === id) return v8Room;
|
||||
if (parentSpace.roomId === id) return parentSpace;
|
||||
return null;
|
||||
});
|
||||
|
||||
// resolve invites by hand
|
||||
// flushPromises is too blunt to test reliably
|
||||
client.invite.mockImplementation(() => {
|
||||
const p = defer<{}>();
|
||||
deferredInvites.push(p);
|
||||
return p.promise;
|
||||
});
|
||||
|
||||
getComponent({ room: v8Room, promptUpgrade: true });
|
||||
|
||||
fireEvent.click(screen.getByText("Space members"));
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
fireEvent.click(within(dialog).getByText("Upgrade"));
|
||||
|
||||
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, PreferredRoomVersions.RestrictedRooms);
|
||||
|
||||
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(within(dialog).getByText("Loading new room")).toBeInTheDocument();
|
||||
|
||||
// "create" our new room, have it come thru sync
|
||||
client.getRoom.mockImplementation((id) => {
|
||||
if (roomId === id) return v8Room;
|
||||
if (newRoomId === id) return upgradedRoom;
|
||||
if (parentSpace.roomId === id) return parentSpace;
|
||||
return null;
|
||||
});
|
||||
client.emit(ClientEvent.Room, upgradedRoom);
|
||||
|
||||
// invite users
|
||||
expect(await screen.findByText("Sending invites... (0 out of 2)")).toBeInTheDocument();
|
||||
deferredInvites.pop()!.resolve({});
|
||||
expect(await screen.findByText("Sending invites... (1 out of 2)")).toBeInTheDocument();
|
||||
deferredInvites.pop()!.resolve({});
|
||||
|
||||
// update spaces
|
||||
expect(await screen.findByText("Updating space...")).toBeInTheDocument();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// done, modal closed
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("upgrades room with no parent spaces or members when changing join rule to restricted", async () => {
|
||||
// room that doesnt support restricted rooms
|
||||
const v8Room = new Room(roomId, client, userId);
|
||||
setRoomStateEvents(v8Room, "8");
|
||||
const upgradedRoom = new Room(newRoomId, client, userId);
|
||||
setRoomStateEvents(upgradedRoom);
|
||||
|
||||
getComponent({ room: v8Room, promptUpgrade: true });
|
||||
|
||||
fireEvent.click(screen.getByText("Space members"));
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
fireEvent.click(within(dialog).getByText("Upgrade"));
|
||||
|
||||
expect(client.upgradeRoom).toHaveBeenCalledWith(roomId, PreferredRoomVersions.RestrictedRooms);
|
||||
|
||||
expect(within(dialog).getByText("Upgrading room")).toBeInTheDocument();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(within(dialog).getByText("Loading new room")).toBeInTheDocument();
|
||||
|
||||
// "create" our new room, have it come thru sync
|
||||
client.getRoom.mockImplementation((id) => {
|
||||
if (roomId === id) return v8Room;
|
||||
if (newRoomId === id) return upgradedRoom;
|
||||
return null;
|
||||
});
|
||||
client.emit(ClientEvent.Room, upgradedRoom);
|
||||
|
||||
await flushPromises();
|
||||
await flushPromises();
|
||||
|
||||
// done, modal closed
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -26,16 +26,18 @@ import {
|
|||
TweakName,
|
||||
ConditionKind,
|
||||
IPushRuleCondition,
|
||||
PushRuleKind,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids";
|
||||
import { act, fireEvent, getByTestId, render, screen, waitFor, within } from "@testing-library/react";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import Notifications from "../../../../src/components/views/settings/Notifications";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { StandardActions } from "../../../../src/notifications/StandardActions";
|
||||
import { getMockClientWithEventEmitter, mkMessage, mockClientMethodsUser } from "../../../test-utils";
|
||||
import { clearAllModals, getMockClientWithEventEmitter, mkMessage, mockClientMethodsUser } from "../../../test-utils";
|
||||
|
||||
// don't pollute test output with error logs from mock rejections
|
||||
jest.mock("matrix-js-sdk/src/logger");
|
||||
|
@ -257,6 +259,7 @@ describe("<Notifications />", () => {
|
|||
getPushers: jest.fn(),
|
||||
getThreePids: jest.fn(),
|
||||
setPusher: jest.fn(),
|
||||
removePusher: jest.fn(),
|
||||
setPushRuleEnabled: jest.fn(),
|
||||
setPushRuleActions: jest.fn(),
|
||||
getRooms: jest.fn().mockReturnValue([]),
|
||||
|
@ -274,10 +277,12 @@ describe("<Notifications />", () => {
|
|||
sendReadReceipt: jest.fn(),
|
||||
supportsThreads: jest.fn().mockReturnValue(true),
|
||||
isInitialSyncComplete: jest.fn().mockReturnValue(false),
|
||||
addPushRule: jest.fn().mockResolvedValue({}),
|
||||
deletePushRule: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
mockClient.getPushRules.mockResolvedValue(pushRules);
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
let i = 0;
|
||||
mocked(randomString).mockImplementation(() => {
|
||||
return "testid_" + i++;
|
||||
|
@ -286,9 +291,17 @@ describe("<Notifications />", () => {
|
|||
mockClient.getPushRules.mockClear().mockResolvedValue(pushRules);
|
||||
mockClient.getPushers.mockClear().mockResolvedValue({ pushers: [] });
|
||||
mockClient.getThreePids.mockClear().mockResolvedValue({ threepids: [] });
|
||||
mockClient.setPusher.mockClear().mockResolvedValue({});
|
||||
mockClient.setPusher.mockReset().mockResolvedValue({});
|
||||
mockClient.removePusher.mockClear().mockResolvedValue({});
|
||||
mockClient.setPushRuleActions.mockReset().mockResolvedValue({});
|
||||
mockClient.pushRules = pushRules;
|
||||
mockClient.getPushRules.mockClear().mockResolvedValue(pushRules);
|
||||
mockClient.addPushRule.mockClear();
|
||||
mockClient.deletePushRule.mockClear();
|
||||
|
||||
userEvent.setup();
|
||||
|
||||
await clearAllModals();
|
||||
});
|
||||
|
||||
it("renders spinner while loading", async () => {
|
||||
|
@ -392,21 +405,30 @@ describe("<Notifications />", () => {
|
|||
// force render
|
||||
await flushPromises();
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
expect(
|
||||
within(dialog).getByText("An error occurred whilst saving your notification preferences."),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// dismiss the dialog
|
||||
fireEvent.click(within(dialog).getByText("OK"));
|
||||
expect(screen.getByTestId("error-message")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("enables email notification when toggling off", async () => {
|
||||
const testPusher = { kind: "email", pushkey: "tester@test.com" } as unknown as IPusher;
|
||||
const testPusher = {
|
||||
kind: "email",
|
||||
pushkey: "tester@test.com",
|
||||
app_id: "testtest",
|
||||
} as unknown as IPusher;
|
||||
mockClient.getPushers.mockResolvedValue({ pushers: [testPusher] });
|
||||
await getComponentAndWait();
|
||||
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]')!;
|
||||
fireEvent.click(emailToggle);
|
||||
|
||||
expect(mockClient.setPusher).toHaveBeenCalledWith({
|
||||
...testPusher,
|
||||
kind: null,
|
||||
});
|
||||
expect(mockClient.removePusher).toHaveBeenCalledWith(testPusher.pushkey, testPusher.app_id);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -809,6 +831,66 @@ describe("<Notifications />", () => {
|
|||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("adds a new keyword", async () => {
|
||||
await getComponentAndWait();
|
||||
|
||||
await userEvent.type(screen.getByLabelText("Keyword"), "jest");
|
||||
expect(screen.getByLabelText("Keyword")).toHaveValue("jest");
|
||||
|
||||
fireEvent.click(screen.getByText("Add"));
|
||||
|
||||
expect(mockClient.addPushRule).toHaveBeenCalledWith("global", PushRuleKind.ContentSpecific, "jest", {
|
||||
actions: [PushRuleActionName.Notify, { set_tweak: "highlight", value: false }],
|
||||
pattern: "jest",
|
||||
});
|
||||
});
|
||||
|
||||
it("adds a new keyword with same actions as existing rules when keywords rule is off", async () => {
|
||||
const offContentRule = {
|
||||
...bananaRule,
|
||||
enabled: false,
|
||||
actions: [PushRuleActionName.Notify],
|
||||
};
|
||||
const pushRulesWithContentOff = {
|
||||
global: {
|
||||
...pushRules.global,
|
||||
content: [offContentRule],
|
||||
},
|
||||
};
|
||||
mockClient.pushRules = pushRulesWithContentOff;
|
||||
mockClient.getPushRules.mockClear().mockResolvedValue(pushRulesWithContentOff);
|
||||
|
||||
await getComponentAndWait();
|
||||
|
||||
const keywords = screen.getByTestId("vector_mentions_keywords");
|
||||
|
||||
expect(within(keywords).getByLabelText("Off")).toBeChecked();
|
||||
|
||||
await userEvent.type(screen.getByLabelText("Keyword"), "jest");
|
||||
expect(screen.getByLabelText("Keyword")).toHaveValue("jest");
|
||||
|
||||
fireEvent.click(screen.getByText("Add"));
|
||||
|
||||
expect(mockClient.addPushRule).toHaveBeenCalledWith("global", PushRuleKind.ContentSpecific, "jest", {
|
||||
actions: [PushRuleActionName.Notify, { set_tweak: TweakName.Highlight, value: false }],
|
||||
pattern: "jest",
|
||||
});
|
||||
});
|
||||
|
||||
it("removes keyword", async () => {
|
||||
await getComponentAndWait();
|
||||
|
||||
await userEvent.type(screen.getByLabelText("Keyword"), "jest");
|
||||
|
||||
const keyword = screen.getByText("banana");
|
||||
|
||||
fireEvent.click(within(keyword.parentElement!).getByLabelText("Remove"));
|
||||
|
||||
expect(mockClient.deletePushRule).toHaveBeenCalledWith("global", PushRuleKind.ContentSpecific, "banana");
|
||||
|
||||
await flushPromises();
|
||||
});
|
||||
});
|
||||
|
||||
describe("clear all notifications", () => {
|
||||
|
|
187
test/components/views/settings/SecureBackupPanel-test.tsx
Normal file
187
test/components/views/settings/SecureBackupPanel-test.tsx
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils";
|
||||
import SecureBackupPanel from "../../../../src/components/views/settings/SecureBackupPanel";
|
||||
import { accessSecretStorage } from "../../../../src/SecurityManager";
|
||||
|
||||
jest.mock("../../../../src/SecurityManager", () => ({
|
||||
accessSecretStorage: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<SecureBackupPanel />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
checkKeyBackup: jest.fn(),
|
||||
isKeyBackupKeyStored: jest.fn(),
|
||||
isSecretStorageReady: jest.fn(),
|
||||
getKeyBackupEnabled: jest.fn(),
|
||||
getKeyBackupVersion: jest.fn().mockReturnValue("1"),
|
||||
isKeyBackupTrusted: jest.fn().mockResolvedValue(true),
|
||||
getClientWellKnown: jest.fn(),
|
||||
deleteKeyBackupVersion: jest.fn(),
|
||||
});
|
||||
// @ts-ignore allow it
|
||||
client.crypto = {
|
||||
secretStorage: { hasKey: jest.fn() },
|
||||
getSessionBackupPrivateKey: jest.fn(),
|
||||
} as unknown as Crypto;
|
||||
|
||||
const getComponent = () => render(<SecureBackupPanel />);
|
||||
|
||||
beforeEach(() => {
|
||||
client.checkKeyBackup.mockResolvedValue({
|
||||
backupInfo: {
|
||||
version: "1",
|
||||
algorithm: "test",
|
||||
auth_data: {
|
||||
public_key: "1234",
|
||||
},
|
||||
},
|
||||
trustInfo: {
|
||||
usable: false,
|
||||
sigs: [],
|
||||
},
|
||||
});
|
||||
|
||||
mocked(client.crypto!.secretStorage.hasKey).mockClear().mockResolvedValue(false);
|
||||
client.deleteKeyBackupVersion.mockClear().mockResolvedValue();
|
||||
client.getKeyBackupVersion.mockClear();
|
||||
client.isKeyBackupTrusted.mockClear();
|
||||
|
||||
mocked(accessSecretStorage).mockClear().mockResolvedValue();
|
||||
});
|
||||
|
||||
it("displays a loader while checking keybackup", async () => {
|
||||
getComponent();
|
||||
expect(screen.getByRole("progressbar")).toBeInTheDocument();
|
||||
await flushPromises();
|
||||
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles null backup info", async () => {
|
||||
// checkKeyBackup can fail and return null for various reasons
|
||||
client.checkKeyBackup.mockResolvedValue(null);
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
// no backup info
|
||||
expect(screen.getByText("Back up your keys before signing out to avoid losing them.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("suggests connecting session to key backup when backup exists", async () => {
|
||||
const { container } = getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("displays when session is connected to key backup", async () => {
|
||||
client.getKeyBackupEnabled.mockReturnValue(true);
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.getByText("✅ This session is backing up your keys.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("asks for confirmation before deleting a backup", async () => {
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
fireEvent.click(screen.getByText("Delete Backup"));
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
expect(
|
||||
within(dialog).getByText(
|
||||
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(within(dialog).getByText("Cancel"));
|
||||
|
||||
expect(client.deleteKeyBackupVersion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("deletes backup after confirmation", async () => {
|
||||
client.checkKeyBackup
|
||||
.mockResolvedValueOnce({
|
||||
backupInfo: {
|
||||
version: "1",
|
||||
algorithm: "test",
|
||||
auth_data: {
|
||||
public_key: "1234",
|
||||
},
|
||||
},
|
||||
trustInfo: {
|
||||
usable: false,
|
||||
sigs: [],
|
||||
},
|
||||
})
|
||||
.mockResolvedValue(null);
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
fireEvent.click(screen.getByText("Delete Backup"));
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
expect(
|
||||
within(dialog).getByText(
|
||||
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.",
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(within(dialog).getByTestId("dialog-primary-button"));
|
||||
|
||||
expect(client.deleteKeyBackupVersion).toHaveBeenCalledWith("1");
|
||||
|
||||
// delete request
|
||||
await flushPromises();
|
||||
// refresh backup info
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
it("resets secret storage", async () => {
|
||||
mocked(client.crypto!.secretStorage.hasKey).mockClear().mockResolvedValue(true);
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
client.getKeyBackupVersion.mockClear();
|
||||
client.isKeyBackupTrusted.mockClear();
|
||||
|
||||
fireEvent.click(screen.getByText("Reset"));
|
||||
|
||||
// enter loading state
|
||||
expect(accessSecretStorage).toHaveBeenCalled();
|
||||
await flushPromises();
|
||||
|
||||
// backup status refreshed
|
||||
expect(client.getKeyBackupVersion).toHaveBeenCalled();
|
||||
expect(client.isKeyBackupTrusted).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SecureBackupPanel /> suggests connecting session to key backup when backup exists 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
This session is
|
||||
<b>
|
||||
not backing up your keys
|
||||
</b>
|
||||
, but you do have an existing backup you can restore from and add to going forward.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.
|
||||
</p>
|
||||
<details>
|
||||
<summary>
|
||||
Advanced
|
||||
</summary>
|
||||
<table
|
||||
class="mx_SecureBackupPanel_statusList"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Backup key stored:
|
||||
</th>
|
||||
<td>
|
||||
not stored
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Backup key cached:
|
||||
</th>
|
||||
<td>
|
||||
not found locally
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Secret storage public key:
|
||||
</th>
|
||||
<td>
|
||||
not found
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Secret storage:
|
||||
</th>
|
||||
<td>
|
||||
not ready
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Backup version:
|
||||
</th>
|
||||
<td>
|
||||
1
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Algorithm:
|
||||
</th>
|
||||
<td>
|
||||
test
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
Backup is not signed by any of your sessions
|
||||
</div>
|
||||
<div />
|
||||
</details>
|
||||
<div
|
||||
class="mx_SecureBackupPanel_buttonRow"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Connect this session to Key Backup
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Delete Backup
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -16,10 +16,11 @@ limitations under the License.
|
|||
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { render } from "@testing-library/react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
|
||||
import VoiceUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/VoiceUserSettingsTab";
|
||||
import MediaDeviceHandler from "../../../../../../src/MediaDeviceHandler";
|
||||
import MediaDeviceHandler, { IMediaDevices, MediaDeviceKindEnum } from "../../../../../../src/MediaDeviceHandler";
|
||||
import { flushPromises } from "../../../../../test-utils";
|
||||
|
||||
jest.mock("../../../../../../src/MediaDeviceHandler");
|
||||
const MediaDeviceHandlerMock = mocked(MediaDeviceHandler);
|
||||
|
@ -27,8 +28,69 @@ const MediaDeviceHandlerMock = mocked(MediaDeviceHandler);
|
|||
describe("<VoiceUserSettingsTab />", () => {
|
||||
const getComponent = (): React.ReactElement => <VoiceUserSettingsTab />;
|
||||
|
||||
const audioIn1 = {
|
||||
deviceId: "1",
|
||||
groupId: "g1",
|
||||
kind: MediaDeviceKindEnum.AudioInput,
|
||||
label: "Audio input test 1",
|
||||
};
|
||||
const videoIn1 = {
|
||||
deviceId: "2",
|
||||
groupId: "g1",
|
||||
kind: MediaDeviceKindEnum.VideoInput,
|
||||
label: "Video input test 1",
|
||||
};
|
||||
const videoIn2 = {
|
||||
deviceId: "3",
|
||||
groupId: "g1",
|
||||
kind: MediaDeviceKindEnum.VideoInput,
|
||||
label: "Video input test 2",
|
||||
};
|
||||
const defaultMediaDevices = {
|
||||
[MediaDeviceKindEnum.AudioOutput]: [],
|
||||
[MediaDeviceKindEnum.AudioInput]: [audioIn1],
|
||||
[MediaDeviceKindEnum.VideoInput]: [videoIn1, videoIn2],
|
||||
} as unknown as IMediaDevices;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
MediaDeviceHandlerMock.hasAnyLabeledDevices.mockResolvedValue(true);
|
||||
MediaDeviceHandlerMock.getDevices.mockResolvedValue(defaultMediaDevices);
|
||||
|
||||
// @ts-ignore bad mocking
|
||||
MediaDeviceHandlerMock.instance = { setDevice: jest.fn() };
|
||||
});
|
||||
|
||||
describe("devices", () => {
|
||||
it("renders dropdowns for input devices", async () => {
|
||||
render(getComponent());
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.getByLabelText("Microphone")).toHaveDisplayValue(audioIn1.label);
|
||||
expect(screen.getByLabelText("Camera")).toHaveDisplayValue(videoIn1.label);
|
||||
});
|
||||
|
||||
it("updates device", async () => {
|
||||
render(getComponent());
|
||||
await flushPromises();
|
||||
|
||||
fireEvent.change(screen.getByLabelText("Camera"), { target: { value: videoIn2.deviceId } });
|
||||
|
||||
expect(MediaDeviceHandlerMock.instance.setDevice).toHaveBeenCalledWith(
|
||||
videoIn2.deviceId,
|
||||
MediaDeviceKindEnum.VideoInput,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText("Camera")).toHaveDisplayValue(videoIn2.label);
|
||||
});
|
||||
|
||||
it("does not render dropdown when no devices exist for type", async () => {
|
||||
render(getComponent());
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.getByText("No Audio Outputs detected")).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText("Audio Output")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("renders audio processing settings", () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue