mirror of https://github.com/vector-im/riot-web
Fix display of devices without encryption support in Settings dialog (#10977)
* Update tests to demonstrate broken behaviour * Fixes and comments * Remove exception swallowing This seems like it causes more problems than it solves.pull/28788/head^2
parent
796ed35e75
commit
5593872b7a
|
@ -18,7 +18,13 @@ import { IMyDevice } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { ExtendedDeviceInformation } from "../../../../utils/device/parseUserAgent";
|
import { ExtendedDeviceInformation } from "../../../../utils/device/parseUserAgent";
|
||||||
|
|
||||||
export type DeviceWithVerification = IMyDevice & { isVerified: boolean | null };
|
export type DeviceWithVerification = IMyDevice & {
|
||||||
|
/**
|
||||||
|
* `null` if the device is unknown or has not published encryption keys; otherwise a boolean
|
||||||
|
* indicating whether the device has been cross-signed by a cross-signing key we trust.
|
||||||
|
*/
|
||||||
|
isVerified: boolean | null;
|
||||||
|
};
|
||||||
export type ExtendedDeviceAppInfo = {
|
export type ExtendedDeviceAppInfo = {
|
||||||
// eg Element Web
|
// eg Element Web
|
||||||
appName?: string;
|
appName?: string;
|
||||||
|
|
|
@ -22,15 +22,14 @@ import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
* @param client - reference to the MatrixClient
|
* @param client - reference to the MatrixClient
|
||||||
* @param deviceId - ID of the device to be checked
|
* @param deviceId - ID of the device to be checked
|
||||||
*
|
*
|
||||||
* @returns `true` if the device has been correctly cross-signed. `false` if the device is unknown or not correctly
|
* @returns `null` if the device is unknown or has not published encryption keys; otherwise a boolean
|
||||||
* cross-signed. `null` if there was an error fetching the device info.
|
* indicating whether the device has been cross-signed by a cross-signing key we trust.
|
||||||
*/
|
*/
|
||||||
export const isDeviceVerified = async (client: MatrixClient, deviceId: string): Promise<boolean | null> => {
|
export const isDeviceVerified = async (client: MatrixClient, deviceId: string): Promise<boolean | null> => {
|
||||||
try {
|
const trustLevel = await client.getCrypto()?.getDeviceVerificationStatus(client.getSafeUserId(), deviceId);
|
||||||
const trustLevel = await client.getCrypto()?.getDeviceVerificationStatus(client.getSafeUserId(), deviceId);
|
if (!trustLevel) {
|
||||||
return trustLevel?.crossSigningVerified ?? false;
|
// either no crypto, or an unknown/no-e2e device
|
||||||
} catch (e) {
|
|
||||||
console.error("Error getting device cross-signing info", e);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return trustLevel.crossSigningVerified;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,18 +25,40 @@ import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
|
|
||||||
describe("<DevicesPanel />", () => {
|
describe("<DevicesPanel />", () => {
|
||||||
const userId = "@alice:server.org";
|
const userId = "@alice:server.org";
|
||||||
const device1 = { device_id: "device_1" };
|
|
||||||
const device2 = { device_id: "device_2" };
|
// the local device
|
||||||
const device3 = { device_id: "device_3" };
|
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 = {
|
const mockCrypto = {
|
||||||
getDeviceVerificationStatus: jest.fn().mockResolvedValue({
|
getDeviceVerificationStatus: jest.fn().mockImplementation((_userId, deviceId) => {
|
||||||
crossSigningVerified: false,
|
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({
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
...mockClientMethodsUser(userId),
|
...mockClientMethodsUser(userId),
|
||||||
getDevices: jest.fn(),
|
getDevices: jest.fn(),
|
||||||
getDeviceId: jest.fn().mockReturnValue(device1.device_id),
|
getDeviceId: jest.fn().mockReturnValue(ownDevice.device_id),
|
||||||
deleteMultipleDevices: jest.fn(),
|
deleteMultipleDevices: jest.fn(),
|
||||||
getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo("id")),
|
getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo("id")),
|
||||||
generateClientSecret: jest.fn(),
|
generateClientSecret: jest.fn(),
|
||||||
|
@ -54,12 +76,14 @@ describe("<DevicesPanel />", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
mockClient.getDevices.mockReset().mockResolvedValue({ devices: [device1, device2, device3] });
|
mockClient.getDevices
|
||||||
|
.mockReset()
|
||||||
|
.mockResolvedValue({ devices: [ownDevice, verifiedDevice, unverifiedDevice, nonCryptoDevice] });
|
||||||
|
|
||||||
mockClient.getPushers.mockReset().mockResolvedValue({
|
mockClient.getPushers.mockReset().mockResolvedValue({
|
||||||
pushers: [
|
pushers: [
|
||||||
mkPusher({
|
mkPusher({
|
||||||
[PUSHER_DEVICE_ID.name]: device1.device_id,
|
[PUSHER_DEVICE_ID.name]: ownDevice.device_id,
|
||||||
[PUSHER_ENABLED.name]: true,
|
[PUSHER_ENABLED.name]: true,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -88,16 +112,16 @@ describe("<DevicesPanel />", () => {
|
||||||
it("deletes selected devices when interactive auth is not required", async () => {
|
it("deletes selected devices when interactive auth is not required", async () => {
|
||||||
mockClient.deleteMultipleDevices.mockResolvedValue({});
|
mockClient.deleteMultipleDevices.mockResolvedValue({});
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
.mockResolvedValueOnce({ devices: [device1, device2, device3] })
|
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||||
// pretend it was really deleted on refresh
|
// pretend it was really deleted on refresh
|
||||||
.mockResolvedValueOnce({ devices: [device1, device3] });
|
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||||
|
|
||||||
const { container, getByTestId } = render(getComponent());
|
const { container, getByTestId } = render(getComponent());
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(3);
|
expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(3);
|
||||||
|
|
||||||
toggleDeviceSelection(container, device2.device_id);
|
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||||
|
|
||||||
mockClient.getDevices.mockClear();
|
mockClient.getDevices.mockClear();
|
||||||
|
|
||||||
|
@ -106,7 +130,7 @@ describe("<DevicesPanel />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
||||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], undefined);
|
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], undefined);
|
||||||
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
|
@ -124,9 +148,9 @@ describe("<DevicesPanel />", () => {
|
||||||
.mockResolvedValueOnce({});
|
.mockResolvedValueOnce({});
|
||||||
|
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
.mockResolvedValueOnce({ devices: [device1, device2, device3] })
|
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||||
// pretend it was really deleted on refresh
|
// pretend it was really deleted on refresh
|
||||||
.mockResolvedValueOnce({ devices: [device1, device3] });
|
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||||
|
|
||||||
const { container, getByTestId, getByLabelText } = render(getComponent());
|
const { container, getByTestId, getByLabelText } = render(getComponent());
|
||||||
|
|
||||||
|
@ -135,7 +159,7 @@ describe("<DevicesPanel />", () => {
|
||||||
// reset mock count after initial load
|
// reset mock count after initial load
|
||||||
mockClient.getDevices.mockClear();
|
mockClient.getDevices.mockClear();
|
||||||
|
|
||||||
toggleDeviceSelection(container, device2.device_id);
|
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
||||||
|
@ -145,7 +169,7 @@ describe("<DevicesPanel />", () => {
|
||||||
// modal rendering has some weird sleeps
|
// modal rendering has some weird sleeps
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
|
|
||||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], undefined);
|
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], undefined);
|
||||||
|
|
||||||
const modal = document.getElementsByClassName("mx_Dialog");
|
const modal = document.getElementsByClassName("mx_Dialog");
|
||||||
expect(modal).toMatchSnapshot();
|
expect(modal).toMatchSnapshot();
|
||||||
|
@ -159,7 +183,7 @@ describe("<DevicesPanel />", () => {
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
// called again with auth
|
// called again with auth
|
||||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], {
|
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([verifiedDevice.device_id], {
|
||||||
identifier: {
|
identifier: {
|
||||||
type: "m.id.user",
|
type: "m.id.user",
|
||||||
user: userId,
|
user: userId,
|
||||||
|
@ -182,9 +206,9 @@ describe("<DevicesPanel />", () => {
|
||||||
.mockResolvedValueOnce({});
|
.mockResolvedValueOnce({});
|
||||||
|
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
.mockResolvedValueOnce({ devices: [device1, device2, device3] })
|
.mockResolvedValueOnce({ devices: [ownDevice, verifiedDevice, unverifiedDevice] })
|
||||||
// pretend it was really deleted on refresh
|
// pretend it was really deleted on refresh
|
||||||
.mockResolvedValueOnce({ devices: [device1, device3] });
|
.mockResolvedValueOnce({ devices: [ownDevice, unverifiedDevice] });
|
||||||
|
|
||||||
const { container, getByTestId } = render(getComponent());
|
const { container, getByTestId } = render(getComponent());
|
||||||
|
|
||||||
|
@ -193,7 +217,7 @@ describe("<DevicesPanel />", () => {
|
||||||
// reset mock count after initial load
|
// reset mock count after initial load
|
||||||
mockClient.getDevices.mockClear();
|
mockClient.getDevices.mockClear();
|
||||||
|
|
||||||
toggleDeviceSelection(container, device2.device_id);
|
toggleDeviceSelection(container, verifiedDevice.device_id);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
fireEvent.click(getByTestId("sign-out-devices-btn"));
|
||||||
|
|
|
@ -104,7 +104,7 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
class="mx_DevicesPanel_deviceTrust"
|
class="mx_DevicesPanel_deviceTrust"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="mx_DevicesPanel_icon mx_E2EIcon mx_E2EIcon_warning"
|
class="mx_DevicesPanel_icon mx_E2EIcon mx_E2EIcon_verified"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -124,8 +124,8 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-label="Unverified"
|
aria-label="Verified"
|
||||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
class="mx_DeviceTypeIcon_verificationIcon verified"
|
||||||
role="img"
|
role="img"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -143,7 +143,7 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
<span
|
<span
|
||||||
data-testid="device-metadata-isVerified"
|
data-testid="device-metadata-isVerified"
|
||||||
>
|
>
|
||||||
Unverified
|
Verified
|
||||||
</span>
|
</span>
|
||||||
·
|
·
|
||||||
<span
|
<span
|
||||||
|
@ -163,13 +163,6 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
>
|
>
|
||||||
Sign Out
|
Sign Out
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Verify
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||||
role="button"
|
role="button"
|
||||||
|
@ -188,24 +181,13 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
class="mx_DevicesPanel_header_trust"
|
class="mx_DevicesPanel_header_trust"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="mx_DevicesPanel_header_icon mx_E2EIcon mx_E2EIcon_warning"
|
class="mx_DevicesPanel_header_icon mx_E2EIcon mx_E2EIcon_verified"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_DevicesPanel_header_title"
|
class="mx_DevicesPanel_header_title"
|
||||||
>
|
>
|
||||||
Unverified devices
|
Verified devices
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DevicesPanel_header_button"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DevicesPanel_selectButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Select all
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -250,8 +232,8 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
aria-label="Unverified"
|
aria-label="Verified"
|
||||||
class="mx_DeviceTypeIcon_verificationIcon unverified"
|
class="mx_DeviceTypeIcon_verificationIcon verified"
|
||||||
role="img"
|
role="img"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -269,7 +251,7 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
<span
|
<span
|
||||||
data-testid="device-metadata-isVerified"
|
data-testid="device-metadata-isVerified"
|
||||||
>
|
>
|
||||||
Unverified
|
Verified
|
||||||
</span>
|
</span>
|
||||||
·
|
·
|
||||||
<span
|
<span
|
||||||
|
@ -296,6 +278,23 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
</span>
|
</span>
|
||||||
</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_warning"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_DevicesPanel_header_title"
|
||||||
|
>
|
||||||
|
Unverified devices
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_DevicesPanel_device"
|
class="mx_DevicesPanel_device"
|
||||||
>
|
>
|
||||||
|
@ -367,6 +366,114 @@ exports[`<DevicesPanel /> renders device panel with devices 1`] = `
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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
|
<div
|
||||||
class="mx_DeviceTile_actions"
|
class="mx_DeviceTile_actions"
|
||||||
>
|
>
|
||||||
|
|
|
@ -232,27 +232,6 @@ describe("<SessionManagerTab />", () => {
|
||||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy();
|
expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not fail when checking device verification fails", async () => {
|
|
||||||
const logSpy = jest.spyOn(console, "error").mockImplementation((e) => {});
|
|
||||||
mockClient.getDevices.mockResolvedValue({
|
|
||||||
devices: [alicesDevice, alicesMobileDevice],
|
|
||||||
});
|
|
||||||
const failError = new Error("non-specific failure");
|
|
||||||
mockCrypto.getDeviceVerificationStatus.mockImplementation(() => {
|
|
||||||
throw failError;
|
|
||||||
});
|
|
||||||
render(getComponent());
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
// called for each device despite error
|
|
||||||
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledWith(aliceId, alicesDevice.device_id);
|
|
||||||
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledWith(aliceId, alicesMobileDevice.device_id);
|
|
||||||
expect(logSpy).toHaveBeenCalledWith("Error getting device cross-signing info", failError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets device verification status correctly", async () => {
|
it("sets device verification status correctly", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({
|
mockClient.getDevices.mockResolvedValue({
|
||||||
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
||||||
|
@ -268,7 +247,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
return new DeviceVerificationStatus({});
|
return new DeviceVerificationStatus({});
|
||||||
}
|
}
|
||||||
// alicesOlderMobileDevice does not support encryption
|
// alicesOlderMobileDevice does not support encryption
|
||||||
throw new Error("encryption not supported");
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
@ -567,8 +546,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
|
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
|
||||||
}
|
}
|
||||||
// but alicesMobileDevice doesn't support encryption
|
// but alicesMobileDevice doesn't support encryption
|
||||||
// XXX this is not what happens if a device doesn't support encryption.
|
return null;
|
||||||
throw new Error("encryption not supported");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { getByTestId, queryByTestId } = render(getComponent());
|
const { getByTestId, queryByTestId } = render(getComponent());
|
||||||
|
|
Loading…
Reference in New Issue