Move session manager out of beta (#10968)
* remove old device manager * undo type fix for cypress crypto * update test case
This commit is contained in:
parent
e326526c10
commit
530197bfcd
21 changed files with 450 additions and 1673 deletions
|
@ -173,7 +173,7 @@ describe("<MatrixChat />", () => {
|
|||
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Security,
|
||||
initialTabId: UserTab.SessionManager,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -117,10 +117,7 @@ describe("<UserSettingsDialog />", () => {
|
|||
expect(getByTestId(`settings-tab-${UserTab.Voice}`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders session manager tab when enabled", () => {
|
||||
mockSettingsStore.getValue.mockImplementation((settingName): any => {
|
||||
return settingName === "feature_new_device_manager";
|
||||
});
|
||||
it("renders session manager tab", () => {
|
||||
const { getByTestId } = render(getComponent());
|
||||
expect(getByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy();
|
||||
});
|
||||
|
@ -153,28 +150,15 @@ describe("<UserSettingsDialog />", () => {
|
|||
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy();
|
||||
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[0][0]).toEqual("feature_mjolnir");
|
||||
expect(mockSettingsStore.watchSetting.mock.calls[1][0]).toEqual("feature_new_device_manager");
|
||||
|
||||
// call the watch setting callback
|
||||
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true);
|
||||
// tab is rendered now
|
||||
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();
|
||||
|
||||
// call the watch setting callback
|
||||
watchSettingCallbacks["feature_new_device_manager"](
|
||||
"feature_new_device_manager",
|
||||
"",
|
||||
SettingLevel.ACCOUNT,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
// tab is rendered now
|
||||
expect(queryByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy();
|
||||
|
||||
unmount();
|
||||
|
||||
// unwatches settings on unmount
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith("mock-watcher-id-feature_mjolnir");
|
||||
expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith("mock-watcher-id-feature_new_device_manager");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -128,6 +128,24 @@ NodeList [
|
|||
Security & Privacy
|
||||
</span>
|
||||
</li>,
|
||||
<li
|
||||
aria-controls="mx_tabpanel_USER_SESSION_MANAGER_TAB"
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_TabbedView_tabLabel"
|
||||
data-testid="settings-tab-USER_SESSION_MANAGER_TAB"
|
||||
role="tab"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="mx_TabbedView_maskedIcon mx_UserSettingsDialog_sessionsIcon"
|
||||
/>
|
||||
<span
|
||||
class="mx_TabbedView_tabLabel_text"
|
||||
id="mx_tabpanel_USER_SESSION_MANAGER_TAB_label"
|
||||
>
|
||||
Sessions
|
||||
</span>
|
||||
</li>,
|
||||
<li
|
||||
aria-controls="mx_tabpanel_USER_LABS_TAB"
|
||||
aria-selected="false"
|
||||
|
|
|
@ -1,246 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 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 { act, fireEvent, render } from "@testing-library/react";
|
||||
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { PUSHER_DEVICE_ID, PUSHER_ENABLED } from "matrix-js-sdk/src/@types/event";
|
||||
|
||||
import DevicesPanel from "../../../../src/components/views/settings/DevicesPanel";
|
||||
import { flushPromises, getMockClientWithEventEmitter, mkPusher, mockClientMethodsUser } from "../../../test-utils";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
describe("<DevicesPanel />", () => {
|
||||
const userId = "@alice:server.org";
|
||||
|
||||
// the local device
|
||||
const ownDevice = { device_id: "device_1" };
|
||||
|
||||
// a device which we have verified via cross-signing
|
||||
const verifiedDevice = { device_id: "device_2" };
|
||||
|
||||
// a device which we have *not* verified via cross-signing
|
||||
const unverifiedDevice = { device_id: "device_3" };
|
||||
|
||||
// a device which is returned by `getDevices` but getDeviceVerificationStatus returns `null` for
|
||||
// (as it would for a device with no E2E keys).
|
||||
const nonCryptoDevice = { device_id: "non_crypto" };
|
||||
|
||||
const mockCrypto = {
|
||||
getDeviceVerificationStatus: jest.fn().mockImplementation((_userId, deviceId) => {
|
||||
if (_userId !== userId) {
|
||||
throw new Error(`bad user id ${_userId}`);
|
||||
}
|
||||
if (deviceId === ownDevice.device_id || deviceId === verifiedDevice.device_id) {
|
||||
return { crossSigningVerified: true };
|
||||
} else if (deviceId === unverifiedDevice.device_id) {
|
||||
return {
|
||||
crossSigningVerified: false,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
};
|
||||
const mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
getDevices: jest.fn(),
|
||||
getDeviceId: jest.fn().mockReturnValue(ownDevice.device_id),
|
||||
deleteMultipleDevices: jest.fn(),
|
||||
getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo("id")),
|
||||
generateClientSecret: jest.fn(),
|
||||
getPushers: jest.fn(),
|
||||
setPusher: jest.fn(),
|
||||
getCrypto: jest.fn().mockReturnValue(mockCrypto),
|
||||
});
|
||||
|
||||
const getComponent = () => (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<DevicesPanel />
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockClient.getDevices
|
||||
.mockReset()
|
||||
.mockResolvedValue({ devices: [ownDevice, verifiedDevice, unverifiedDevice, nonCryptoDevice] });
|
||||
|
||||
mockClient.getPushers.mockReset().mockResolvedValue({
|
||||
pushers: [
|
||||
mkPusher({
|
||||
[PUSHER_DEVICE_ID.name]: ownDevice.device_id,
|
||||
[PUSHER_ENABLED.name]: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("renders device panel with devices", async () => {
|
||||
const { container } = render(getComponent());
|
||||
await flushPromises();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("device deletion", () => {
|
||||
const interactiveAuthError = { httpStatus: 401, data: { flows: [{ stages: ["m.login.password"] }] } };
|
||||
|
||||
const toggleDeviceSelection = (container: HTMLElement, deviceId: string) =>
|
||||
act(() => {
|
||||
const checkbox = container.querySelector(`#device-tile-checkbox-${deviceId}`)!;
|
||||
fireEvent.click(checkbox);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient.deleteMultipleDevices.mockReset();
|
||||
});
|
||||
|
||||
it("deletes selected devices when interactive auth is not required", async () => {
|
||||
mockClient.deleteMultipleDevices.mockResolvedValue({});
|
||||
mockClient.getDevices
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||
// pretend it was really deleted on refresh
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||
|
||||
const { container, getByTestId } = render(getComponent());
|
||||
await flushPromises();
|
||||
|
||||
expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(3);
|
||||
|
||||
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||
|
||||
mockClient.getDevices.mockClear();
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
||||
});
|
||||
|
||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], undefined);
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// devices refreshed
|
||||
expect(mockClient.getDevices).toHaveBeenCalled();
|
||||
// and rerendered
|
||||
expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(2);
|
||||
});
|
||||
|
||||
it("deletes selected devices when interactive auth is required", async () => {
|
||||
mockClient.deleteMultipleDevices
|
||||
// require auth
|
||||
.mockRejectedValueOnce(interactiveAuthError)
|
||||
// then succeed
|
||||
.mockResolvedValueOnce({});
|
||||
|
||||
mockClient.getDevices
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||
// pretend it was really deleted on refresh
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||
|
||||
const { container, getByTestId, getByLabelText } = render(getComponent());
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// reset mock count after initial load
|
||||
mockClient.getDevices.mockClear();
|
||||
|
||||
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
// modal rendering has some weird sleeps
|
||||
await sleep(100);
|
||||
|
||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], undefined);
|
||||
|
||||
const modal = document.getElementsByClassName("mx_Dialog");
|
||||
expect(modal).toMatchSnapshot();
|
||||
|
||||
// fill password and submit for interactive auth
|
||||
act(() => {
|
||||
fireEvent.change(getByLabelText("Password"), { target: { value: "topsecret" } });
|
||||
fireEvent.submit(getByLabelText("Password"));
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// called again with auth
|
||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], {
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: userId,
|
||||
},
|
||||
password: "",
|
||||
type: "m.login.password",
|
||||
user: userId,
|
||||
});
|
||||
// devices refreshed
|
||||
expect(mockClient.getDevices).toHaveBeenCalled();
|
||||
// and rerendered
|
||||
expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(2);
|
||||
});
|
||||
|
||||
it("clears loading state when interactive auth fail is cancelled", async () => {
|
||||
mockClient.deleteMultipleDevices
|
||||
// require auth
|
||||
.mockRejectedValueOnce(interactiveAuthError)
|
||||
// then succeed
|
||||
.mockResolvedValueOnce({});
|
||||
|
||||
mockClient.getDevices
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||
// pretend it was really deleted on refresh
|
||||
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||
|
||||
const { container, getByTestId } = render(getComponent());
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// reset mock count after initial load
|
||||
mockClient.getDevices.mockClear();
|
||||
|
||||
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
||||
});
|
||||
|
||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
||||
|
||||
await flushPromises();
|
||||
// modal rendering has some weird sleeps
|
||||
await sleep(20);
|
||||
|
||||
// close the modal without submission
|
||||
act(() => {
|
||||
const modalCloseButton = document.querySelector('[aria-label="Close dialog"]')!;
|
||||
fireEvent.click(modalCloseButton);
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// not refreshed
|
||||
expect(mockClient.getDevices).not.toHaveBeenCalled();
|
||||
// spinner removed
|
||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,506 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<DevicesPanel /> device deletion deletes selected devices when interactive auth is required 1`] = `
|
||||
HTMLCollection [
|
||||
<div
|
||||
class="mx_Dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_InteractiveAuthDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header mx_Dialog_headerWithCancel"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h2 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Authentication
|
||||
</h2>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Confirm your identity by entering your account password below.
|
||||
</p>
|
||||
<form
|
||||
class="mx_InteractiveAuthEntryComponents_passwordSection"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_Field_1"
|
||||
label="Password"
|
||||
name="passwordField"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_1"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="mx_button_row"
|
||||
>
|
||||
<input
|
||||
class="mx_Dialog_primary"
|
||||
disabled=""
|
||||
type="submit"
|
||||
value="Continue"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_DevicesPanel"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_header"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_title"
|
||||
>
|
||||
This device
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_device mx_DevicesPanel_myDevice"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_deviceTrust"
|
||||
>
|
||||
<span
|
||||
class="mx_DevicesPanel_icon mx_E2EIcon mx_E2EIcon_verified"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile"
|
||||
data-testid="device-tile-device_1"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Verified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon verified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
device_1
|
||||
</h4>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Verified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
device_1
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Sign Out
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Rename
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
class="mx_DevicesPanel_header"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_trust"
|
||||
>
|
||||
<span
|
||||
class="mx_DevicesPanel_header_icon mx_E2EIcon mx_E2EIcon_verified"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_title"
|
||||
>
|
||||
Verified devices
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_device"
|
||||
>
|
||||
<div
|
||||
class="mx_SelectableDeviceTile"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SelectableDeviceTile_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
data-testid="device-tile-checkbox-device_2"
|
||||
id="device-tile-checkbox-device_2"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-tile-checkbox-device_2"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_DeviceTile"
|
||||
data-testid="device-tile-device_2"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Verified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon verified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
device_2
|
||||
</h4>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Verified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
device_2
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Rename
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
class="mx_DevicesPanel_header"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_trust"
|
||||
>
|
||||
<span
|
||||
class="mx_DevicesPanel_header_icon mx_E2EIcon mx_E2EIcon_warning"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_title"
|
||||
>
|
||||
Unverified devices
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_device"
|
||||
>
|
||||
<div
|
||||
class="mx_SelectableDeviceTile"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SelectableDeviceTile_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
data-testid="device-tile-checkbox-device_3"
|
||||
id="device-tile-checkbox-device_3"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-tile-checkbox-device_3"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_DeviceTile"
|
||||
data-testid="device-tile-device_3"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Unverified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
device_3
|
||||
</h4>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Unverified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
device_3
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Verify
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Rename
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
class="mx_DevicesPanel_header"
|
||||
>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_trust"
|
||||
/>
|
||||
<div
|
||||
class="mx_DevicesPanel_header_title"
|
||||
>
|
||||
Devices without encryption support
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DevicesPanel_device"
|
||||
>
|
||||
<div
|
||||
class="mx_SelectableDeviceTile"
|
||||
>
|
||||
<span
|
||||
class="mx_Checkbox mx_SelectableDeviceTile_checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
data-testid="device-tile-checkbox-non_crypto"
|
||||
id="device-tile-checkbox-non_crypto"
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
for="device-tile-checkbox-non_crypto"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_background"
|
||||
>
|
||||
<div
|
||||
class="mx_Checkbox_checkmark"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_DeviceTile"
|
||||
data-testid="device-tile-non_crypto"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon"
|
||||
>
|
||||
<div
|
||||
class="mx_DeviceTypeIcon_deviceIconWrapper"
|
||||
>
|
||||
<div
|
||||
aria-label="Unknown session type"
|
||||
class="mx_DeviceTypeIcon_deviceIcon"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Unverified"
|
||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
||||
role="img"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_info"
|
||||
>
|
||||
<h4
|
||||
class="mx_Heading_h4"
|
||||
>
|
||||
non_crypto
|
||||
</h4>
|
||||
<div
|
||||
class="mx_DeviceTile_metadata"
|
||||
>
|
||||
<span
|
||||
data-testid="device-metadata-isVerified"
|
||||
>
|
||||
Unverified
|
||||
</span>
|
||||
·
|
||||
<span
|
||||
data-testid="device-metadata-deviceId"
|
||||
>
|
||||
non_crypto
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_DeviceTile_actions"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Rename
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
class="mx_AccessibleButton mx_DevicesPanel_deleteButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger_outline mx_AccessibleButton_disabled"
|
||||
data-testid="sign-out-devices-btn"
|
||||
disabled=""
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Sign out 0 selected devices
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -13,13 +13,11 @@ 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 { fireEvent, render } from "@testing-library/react";
|
||||
import { UNSTABLE_MSC3882_CAPABILITY } from "matrix-js-sdk/src/matrix";
|
||||
import { render } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
import SecurityUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/SecurityUserSettingsTab";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsServer,
|
||||
|
@ -27,7 +25,6 @@ import {
|
|||
mockClientMethodsCrypto,
|
||||
mockClientMethodsDevice,
|
||||
mockPlatformPeg,
|
||||
flushPromises,
|
||||
} from "../../../../../test-utils";
|
||||
|
||||
describe("<SecurityUserSettingsTab />", () => {
|
||||
|
@ -44,16 +41,6 @@ describe("<SecurityUserSettingsTab />", () => {
|
|||
...mockClientMethodsCrypto(),
|
||||
getRooms: jest.fn().mockReturnValue([]),
|
||||
getIgnoredUsers: jest.fn(),
|
||||
getVersions: jest.fn().mockResolvedValue({
|
||||
unstable_features: {
|
||||
"org.matrix.msc3886": true,
|
||||
},
|
||||
}),
|
||||
getCapabilities: jest.fn().mockResolvedValue({
|
||||
[UNSTABLE_MSC3882_CAPABILITY.name]: {
|
||||
enabled: true,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const getComponent = () => (
|
||||
|
@ -62,44 +49,14 @@ describe("<SecurityUserSettingsTab />", () => {
|
|||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
const settingsValueSpy = jest.spyOn(SettingsStore, "getValue");
|
||||
|
||||
beforeEach(() => {
|
||||
mockPlatformPeg();
|
||||
jest.clearAllMocks();
|
||||
settingsValueSpy.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it("renders sessions section when new session manager is disabled", () => {
|
||||
settingsValueSpy.mockReturnValue(false);
|
||||
const { getByTestId } = render(getComponent());
|
||||
it("renders security section", () => {
|
||||
const { container } = render(getComponent());
|
||||
|
||||
expect(getByTestId("devices-section")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not render sessions section when new session manager is enabled", () => {
|
||||
settingsValueSpy.mockReturnValue(true);
|
||||
const { queryByTestId } = render(getComponent());
|
||||
|
||||
expect(queryByTestId("devices-section")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("renders qr code login section", async () => {
|
||||
const { getByText } = render(getComponent());
|
||||
|
||||
// wait for versions call to settle
|
||||
await flushPromises();
|
||||
|
||||
expect(getByText("Sign in with QR code")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("enters qr code login section when show QR code button clicked", async () => {
|
||||
const { getByText, getByTestId } = render(getComponent());
|
||||
// wait for versions call to settle
|
||||
await flushPromises();
|
||||
|
||||
fireEvent.click(getByText("Show QR code"));
|
||||
|
||||
expect(getByTestId("login-with-qr")).toBeTruthy();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -79,59 +79,6 @@ exports[`<LabsUserSettingsTab /> renders settings marked as beta as beta cards 1
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BetaCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BetaCard_columns"
|
||||
>
|
||||
<div
|
||||
class="mx_BetaCard_columns_description"
|
||||
>
|
||||
<h3
|
||||
class="mx_BetaCard_title"
|
||||
>
|
||||
<span>
|
||||
New session manager
|
||||
</span>
|
||||
<span
|
||||
class="mx_BetaCard_betaPill"
|
||||
>
|
||||
Beta
|
||||
</span>
|
||||
</h3>
|
||||
<div
|
||||
class="mx_BetaCard_caption"
|
||||
>
|
||||
<p>
|
||||
Have greater visibility and control over all your sessions.
|
||||
</p>
|
||||
<p>
|
||||
Our new sessions manager provides better visibility of all your sessions, and greater control over them including the ability to remotely toggle push notifications.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BetaCard_buttons"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Join the beta
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BetaCard_columns_image_wrapper"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="mx_BetaCard_columns_image"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,399 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SecurityUserSettingsTab /> renders security section 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_SettingsTab"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsTab_sections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSection"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h2"
|
||||
>
|
||||
Encryption
|
||||
</h2>
|
||||
<div
|
||||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Secure Backup
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
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.
|
||||
</div>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
<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>
|
||||
</table>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Message search
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
<span>
|
||||
Element can't securely cache encrypted messages locally while running in a web browser. Use
|
||||
<a
|
||||
class="mx_ExternalLink"
|
||||
href="https://element.io/get-started"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Element Desktop
|
||||
<i
|
||||
class="mx_ExternalLink_icon"
|
||||
/>
|
||||
</a>
|
||||
for encrypted messages to appear in search results.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Cross-signing
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
<details>
|
||||
<summary>
|
||||
Advanced
|
||||
</summary>
|
||||
<table
|
||||
class="mx_CrossSigningPanel_statusList"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Cross-signing public keys:
|
||||
</th>
|
||||
<td>
|
||||
not found
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Cross-signing private keys:
|
||||
</th>
|
||||
<td>
|
||||
not found in storage
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Master private key:
|
||||
</th>
|
||||
<td>
|
||||
not found locally
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Self signing private key:
|
||||
</th>
|
||||
<td>
|
||||
not found locally
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
User signing private key:
|
||||
</th>
|
||||
<td>
|
||||
not found locally
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Homeserver feature support:
|
||||
</th>
|
||||
<td>
|
||||
not found
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Cryptography
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
<table
|
||||
class="mx_CryptographyPanel_sessionInfo"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Session ID:
|
||||
</th>
|
||||
<td>
|
||||
<code />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
scope="row"
|
||||
>
|
||||
Session key:
|
||||
</th>
|
||||
<td>
|
||||
<code>
|
||||
<b>
|
||||
<not supported>
|
||||
</b>
|
||||
</code>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Never send encrypted messages to unverified sessions from this session
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="false"
|
||||
aria-label="Never send encrypted messages to unverified sessions from this session"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSection"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h2"
|
||||
>
|
||||
Advanced
|
||||
</h2>
|
||||
<div
|
||||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Ignored users
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
You have no ignored users.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsectionHeading"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_SettingsSubsectionHeading_heading"
|
||||
>
|
||||
Encryption
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_content"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Manually verify all remote sessions
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="false"
|
||||
aria-label="Manually verify all remote sessions"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
Loading…
Add table
Add a link
Reference in a new issue