From e936b38e7ba93bd275072cc9ac9d0e73df44dde1 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 30 Nov 2022 16:24:33 +1300 Subject: [PATCH 1/6] test bulk unverified sessions toast behaviour --- test/DeviceListener-test.ts | 263 ++++++++++++++++++++++++++---------- 1 file changed, 189 insertions(+), 74 deletions(-) diff --git a/test/DeviceListener-test.ts b/test/DeviceListener-test.ts index 8d0dd48570e..2ee2b64adf4 100644 --- a/test/DeviceListener-test.ts +++ b/test/DeviceListener-test.ts @@ -15,10 +15,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventEmitter } from "events"; -import { mocked } from "jest-mock"; -import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; +import { Mocked, mocked } from "jest-mock"; +import { MatrixEvent, Room, MatrixClient } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; +import { CrossSigningInfo, DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { CryptoEvent } from "matrix-js-sdk/src/crypto"; +import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; import DeviceListener from "../src/DeviceListener"; import { MatrixClientPeg } from "../src/MatrixClientPeg"; @@ -30,7 +33,7 @@ import dis from "../src/dispatcher/dispatcher"; import { Action } from "../src/dispatcher/actions"; import SettingsStore from "../src/settings/SettingsStore"; import { SettingLevel } from "../src/settings/SettingLevel"; -import { mockPlatformPeg } from "./test-utils"; +import { getMockClientWithEventEmitter, mockPlatformPeg } from "./test-utils"; // don't litter test console with logs jest.mock("matrix-js-sdk/src/logger"); @@ -44,35 +47,13 @@ jest.mock("../src/SecurityManager", () => ({ isSecretStorageBeingAccessed: jest.fn(), accessSecretStorage: jest.fn(), })); +const userId = '@user:server'; const deviceId = 'my-device-id'; - -class MockClient extends EventEmitter { - isGuest = jest.fn(); - getUserId = jest.fn(); - getKeyBackupVersion = jest.fn().mockResolvedValue(undefined); - getRooms = jest.fn().mockReturnValue([]); - doesServerSupportUnstableFeature = jest.fn().mockResolvedValue(true); - isCrossSigningReady = jest.fn().mockResolvedValue(true); - isSecretStorageReady = jest.fn().mockResolvedValue(true); - isCryptoEnabled = jest.fn().mockReturnValue(true); - isInitialSyncComplete = jest.fn().mockReturnValue(true); - getKeyBackupEnabled = jest.fn(); - getStoredDevicesForUser = jest.fn().mockReturnValue([]); - getCrossSigningId = jest.fn(); - getStoredCrossSigningForUser = jest.fn(); - waitForClientWellKnown = jest.fn(); - downloadKeys = jest.fn(); - isRoomEncrypted = jest.fn(); - getClientWellKnown = jest.fn(); - getDeviceId = jest.fn().mockReturnValue(deviceId); - setAccountData = jest.fn(); - getAccountData = jest.fn(); -} const mockDispatcher = mocked(dis); const flushPromises = async () => await new Promise(process.nextTick); describe('DeviceListener', () => { - let mockClient; + let mockClient: Mocked | undefined; // spy on various toasts' hide and show functions // easier than mocking @@ -88,7 +69,29 @@ describe('DeviceListener', () => { mockPlatformPeg({ getAppVersion: jest.fn().mockResolvedValue('1.2.3'), }); - mockClient = new MockClient(); + mockClient = getMockClientWithEventEmitter({ + isGuest: jest.fn(), + getUserId: jest.fn().mockReturnValue(userId), + getKeyBackupVersion: jest.fn().mockResolvedValue(undefined), + getRooms: jest.fn().mockReturnValue([]), + doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(true), + isCrossSigningReady: jest.fn().mockResolvedValue(true), + isSecretStorageReady: jest.fn().mockResolvedValue(true), + isCryptoEnabled: jest.fn().mockReturnValue(true), + isInitialSyncComplete: jest.fn().mockReturnValue(true), + getKeyBackupEnabled: jest.fn(), + getStoredDevicesForUser: jest.fn().mockReturnValue([]), + getCrossSigningId: jest.fn(), + getStoredCrossSigningForUser: jest.fn(), + waitForClientWellKnown: jest.fn(), + downloadKeys: jest.fn(), + isRoomEncrypted: jest.fn(), + getClientWellKnown: jest.fn(), + getDeviceId: jest.fn().mockReturnValue(deviceId), + setAccountData: jest.fn(), + getAccountData: jest.fn(), + checkDeviceTrust: jest.fn().mockReturnValue(new DeviceTrustLevel(false, false, false, false)), + }); jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); }); @@ -124,7 +127,7 @@ describe('DeviceListener', () => { it('saves client information on start', async () => { await createAndStart(); - expect(mockClient.setAccountData).toHaveBeenCalledWith( + expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, { name: 'Element', url: 'localhost', version: '1.2.3' }, ); @@ -133,7 +136,7 @@ describe('DeviceListener', () => { it('catches error and logs when saving client information fails', async () => { const errorLogSpy = jest.spyOn(logger, 'error'); const error = new Error('oups'); - mockClient.setAccountData.mockRejectedValue(error); + mockClient!.setAccountData.mockRejectedValue(error); // doesn't throw await createAndStart(); @@ -147,14 +150,14 @@ describe('DeviceListener', () => { it('saves client information on logged in action', async () => { const instance = await createAndStart(); - mockClient.setAccountData.mockClear(); + mockClient!.setAccountData.mockClear(); // @ts-ignore calling private function instance.onAction({ action: Action.OnLoggedIn }); await flushPromises(); - expect(mockClient.setAccountData).toHaveBeenCalledWith( + expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, { name: 'Element', url: 'localhost', version: '1.2.3' }, ); @@ -169,30 +172,30 @@ describe('DeviceListener', () => { beforeEach(() => { jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); - mockClient.getAccountData.mockReturnValue(undefined); + mockClient!.getAccountData.mockReturnValue(undefined); }); it('does not save client information on start', async () => { await createAndStart(); - expect(mockClient.setAccountData).not.toHaveBeenCalled(); + expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); it('removes client information on start if it exists', async () => { - mockClient.getAccountData.mockReturnValue(clientInfoEvent); + mockClient!.getAccountData.mockReturnValue(clientInfoEvent); await createAndStart(); - expect(mockClient.setAccountData).toHaveBeenCalledWith( + expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, {}, ); }); it('does not try to remove client info event that are already empty', async () => { - mockClient.getAccountData.mockReturnValue(emptyClientInfoEvent); + mockClient!.getAccountData.mockReturnValue(emptyClientInfoEvent); await createAndStart(); - expect(mockClient.setAccountData).not.toHaveBeenCalled(); + expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); it('does not save client information on logged in action', async () => { @@ -203,7 +206,7 @@ describe('DeviceListener', () => { await flushPromises(); - expect(mockClient.setAccountData).not.toHaveBeenCalled(); + expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); it('saves client information after setting is enabled', async () => { @@ -218,7 +221,7 @@ describe('DeviceListener', () => { await flushPromises(); - expect(mockClient.setAccountData).toHaveBeenCalledWith( + expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, { name: 'Element', url: 'localhost', version: '1.2.3' }, ); @@ -228,22 +231,22 @@ describe('DeviceListener', () => { describe('recheck', () => { it('does nothing when cross signing feature is not supported', async () => { - mockClient.doesServerSupportUnstableFeature.mockResolvedValue(false); + mockClient!.doesServerSupportUnstableFeature.mockResolvedValue(false); await createAndStart(); - expect(mockClient.isCrossSigningReady).not.toHaveBeenCalled(); + expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); it('does nothing when crypto is not enabled', async () => { - mockClient.isCryptoEnabled.mockReturnValue(false); + mockClient!.isCryptoEnabled.mockReturnValue(false); await createAndStart(); - expect(mockClient.isCrossSigningReady).not.toHaveBeenCalled(); + expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); it('does nothing when initial sync is not complete', async () => { - mockClient.isInitialSyncComplete.mockReturnValue(false); + mockClient!.isInitialSyncComplete.mockReturnValue(false); await createAndStart(); - expect(mockClient.isCrossSigningReady).not.toHaveBeenCalled(); + expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); describe('set up encryption', () => { @@ -253,15 +256,15 @@ describe('DeviceListener', () => { ] as unknown as Room[]; beforeEach(() => { - mockClient.isCrossSigningReady.mockResolvedValue(false); - mockClient.isSecretStorageReady.mockResolvedValue(false); - mockClient.getRooms.mockReturnValue(rooms); - mockClient.isRoomEncrypted.mockReturnValue(true); + mockClient!.isCrossSigningReady.mockResolvedValue(false); + mockClient!.isSecretStorageReady.mockResolvedValue(false); + mockClient!.getRooms.mockReturnValue(rooms); + mockClient!.isRoomEncrypted.mockReturnValue(true); }); it('hides setup encryption toast when cross signing and secret storage are ready', async () => { - mockClient.isCrossSigningReady.mockResolvedValue(true); - mockClient.isSecretStorageReady.mockResolvedValue(true); + mockClient!.isCrossSigningReady.mockResolvedValue(true); + mockClient!.isSecretStorageReady.mockResolvedValue(true); await createAndStart(); expect(SetupEncryptionToast.hideToast).toHaveBeenCalled(); }); @@ -277,49 +280,49 @@ describe('DeviceListener', () => { mocked(isSecretStorageBeingAccessed).mockReturnValue(true); await createAndStart(); - expect(mockClient.downloadKeys).not.toHaveBeenCalled(); + expect(mockClient!.downloadKeys).not.toHaveBeenCalled(); expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled(); }); it('does not do any checks or show any toasts when no rooms are encrypted', async () => { - mockClient.isRoomEncrypted.mockReturnValue(false); + mockClient!.isRoomEncrypted.mockReturnValue(false); await createAndStart(); - expect(mockClient.downloadKeys).not.toHaveBeenCalled(); + expect(mockClient!.downloadKeys).not.toHaveBeenCalled(); expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled(); }); describe('when user does not have a cross signing id on this device', () => { beforeEach(() => { - mockClient.getCrossSigningId.mockReturnValue(undefined); + mockClient!.getCrossSigningId.mockReturnValue(undefined); }); it('shows verify session toast when account has cross signing', async () => { - mockClient.getStoredCrossSigningForUser.mockReturnValue(true); + mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId)); await createAndStart(); - expect(mockClient.downloadKeys).toHaveBeenCalled(); + expect(mockClient!.downloadKeys).toHaveBeenCalled(); expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith( SetupEncryptionToast.Kind.VERIFY_THIS_SESSION); }); it('checks key backup status when when account has cross signing', async () => { - mockClient.getCrossSigningId.mockReturnValue(undefined); - mockClient.getStoredCrossSigningForUser.mockReturnValue(true); + mockClient!.getCrossSigningId.mockReturnValue(undefined); + mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId)); await createAndStart(); - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalled(); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); }); }); describe('when user does have a cross signing id on this device', () => { beforeEach(() => { - mockClient.getCrossSigningId.mockReturnValue('abc'); + mockClient!.getCrossSigningId.mockReturnValue('abc'); }); it('shows upgrade encryption toast when user has a key backup available', async () => { // non falsy response - mockClient.getKeyBackupVersion.mockResolvedValue({}); + mockClient!.getKeyBackupVersion.mockResolvedValue({} as unknown as IKeyBackupInfo); await createAndStart(); expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith( @@ -332,51 +335,163 @@ describe('DeviceListener', () => { it('checks keybackup status when cross signing and secret storage are ready', async () => { // default mocks set cross signing and secret storage to ready await createAndStart(); - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalled(); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); expect(mockDispatcher.dispatch).not.toHaveBeenCalled(); }); it('checks keybackup status when setup encryption toast has been dismissed', async () => { - mockClient.isCrossSigningReady.mockResolvedValue(false); + mockClient!.isCrossSigningReady.mockResolvedValue(false); const instance = await createAndStart(); instance.dismissEncryptionSetup(); await flushPromises(); - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalled(); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); }); it('does not dispatch keybackup event when key backup check is not finished', async () => { // returns null when key backup status hasn't finished being checked - mockClient.getKeyBackupEnabled.mockReturnValue(null); + mockClient!.getKeyBackupEnabled.mockReturnValue(null); await createAndStart(); expect(mockDispatcher.dispatch).not.toHaveBeenCalled(); }); it('dispatches keybackup event when key backup is not enabled', async () => { - mockClient.getKeyBackupEnabled.mockReturnValue(false); + mockClient!.getKeyBackupEnabled.mockReturnValue(false); await createAndStart(); expect(mockDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ReportKeyBackupNotEnabled }); }); it('does not check key backup status again after check is complete', async () => { - mockClient.getKeyBackupEnabled.mockReturnValue(null); + mockClient!.getKeyBackupEnabled.mockReturnValue(null); const instance = await createAndStart(); - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalled(); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); // keyback check now complete - mockClient.getKeyBackupEnabled.mockReturnValue(true); + mockClient!.getKeyBackupEnabled.mockReturnValue(true); // trigger a recheck instance.dismissEncryptionSetup(); await flushPromises(); - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalledTimes(2); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalledTimes(2); // trigger another recheck instance.dismissEncryptionSetup(); await flushPromises(); // not called again, check was complete last time - expect(mockClient.getKeyBackupEnabled).toHaveBeenCalledTimes(2); + expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalledTimes(2); + }); + }); + + describe('unverified sessions toasts', () => { + const currentDevice = new DeviceInfo(deviceId); + const device2 = new DeviceInfo('d2'); + const device3 = new DeviceInfo('d3'); + + const deviceTrustVerified = new DeviceTrustLevel(true, false, false, false); + const deviceTrustUnverified = new DeviceTrustLevel(false, false, false, false); + + beforeEach(() => { + mockClient!.isCrossSigningReady.mockResolvedValue(true); + mockClient!.getStoredDevicesForUser.mockReturnValue([ + currentDevice, device2, device3, + ]); + // all devices verified by default + mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustVerified); + mockClient!.deviceId = currentDevice.deviceId; + }); + describe('bulk unverified sessions toasts', () => { + it('hides toast when cross signing is not ready', async () => { + mockClient!.isCrossSigningReady.mockResolvedValue(false); + await createAndStart(); + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + }); + + it('hides toast when all devices at app start are verified', async () => { + await createAndStart(); + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + }); + + it('hides toast when only unverified device is the current device', async () => { + mockClient!.getStoredDevicesForUser.mockReturnValue([ + currentDevice, + ]); + mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustUnverified); + await createAndStart(); + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + }); + + it('shows toast with unverified devices at app start', async () => { + // currentDevice, device2 are verified, device3 is unverified + mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { + switch (deviceId) { + case currentDevice.deviceId: + case device2.deviceId: + return deviceTrustVerified; + default: + return deviceTrustUnverified; + } + }); + await createAndStart(); + expect(BulkUnverifiedSessionsToast.showToast).toHaveBeenCalledWith( + new Set([device3.deviceId]), + ); + }); + + it('hides toast when unverified sessions at app start have been dismissed', async () => { + // currentDevice, device2 are verified, device3 is unverified + mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { + switch (deviceId) { + case currentDevice.deviceId: + case device2.deviceId: + return deviceTrustVerified; + default: + return deviceTrustUnverified; + } + }); + const instance = await createAndStart(); + expect(BulkUnverifiedSessionsToast.showToast).toHaveBeenCalledWith( + new Set([device3.deviceId]), + ); + + await instance.dismissUnverifiedSessions([device3.deviceId]); + await flushPromises(); + + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + }); + + it('hides toast when unverified sessions are added after app start', async () => { + // currentDevice, device2 are verified, device3 is unverified + mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { + switch (deviceId) { + case currentDevice.deviceId: + case device2.deviceId: + return deviceTrustVerified; + default: + return deviceTrustUnverified; + } + }); + mockClient!.getStoredDevicesForUser.mockReturnValue([ + currentDevice, device2, + ]); + await createAndStart(); + + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + + // add an unverified device + mockClient!.getStoredDevicesForUser.mockReturnValue([ + currentDevice, device2, device3, + ]); + // trigger a recheck + mockClient!.emit(CryptoEvent.DevicesUpdated, [mockClient!.getUserId()], false); + await flushPromises(); + + // bulk unverified sessions toast only shown for devices that were + // there at app start + // individual nags are shown for new unverified devices + expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalledTimes(2); + expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); + }); }); }); }); From 2b9e71fb5daed494fd470e57873b40d55cf375ec Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 30 Nov 2022 13:58:06 +1300 Subject: [PATCH 2/6] unverified sessions toast text tweak --- src/i18n/strings/en_EN.json | 2 +- src/toasts/BulkUnverifiedSessionsToast.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c2f34afca99..b8983322dde 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -808,7 +808,7 @@ "Yes": "Yes", "No": "No", "Help improve %(analyticsOwner)s": "Help improve %(analyticsOwner)s", - "You have unverified logins": "You have unverified logins", + "You have unverified sessions": "You have unverified sessions", "Review to ensure your account is safe": "Review to ensure your account is safe", "Review": "Review", "Later": "Later", diff --git a/src/toasts/BulkUnverifiedSessionsToast.ts b/src/toasts/BulkUnverifiedSessionsToast.ts index 0113f2f030a..ae512df7ed4 100644 --- a/src/toasts/BulkUnverifiedSessionsToast.ts +++ b/src/toasts/BulkUnverifiedSessionsToast.ts @@ -38,7 +38,7 @@ export const showToast = (deviceIds: Set) => { ToastStore.sharedInstance().addOrReplaceToast({ key: TOAST_KEY, - title: _t("You have unverified logins"), + title: _t("You have unverified sessions"), icon: "verification_warning", props: { description: _t("Review to ensure your account is safe"), From c6621170bb60d53943781cde08087f54dbc23767 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Wed, 30 Nov 2022 16:43:06 +1300 Subject: [PATCH 3/6] only show bulk unverified sessions toast when current device is verified --- src/DeviceListener.ts | 6 +++++- test/DeviceListener-test.ts | 15 ++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 1f49c3b34d6..d749b396b6c 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -306,6 +306,9 @@ export default class DeviceListener { // Unverified devices that have appeared since then const newUnverifiedDeviceIds = new Set(); + const isCurrentDeviceTrusted = crossSigningReady && + await (cli.checkDeviceTrust(cli.getUserId(), cli.deviceId)).isCrossSigningVerified(); + // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts if (crossSigningReady) { @@ -329,7 +332,8 @@ export default class DeviceListener { logger.debug("Currently showing toasts for: " + Array.from(this.displayingToastsForDeviceIds).join(',')); // Display or hide the batch toast for old unverified sessions - if (oldUnverifiedDeviceIds.size > 0) { + // don't show the toast if the current device is unverified + if (oldUnverifiedDeviceIds.size > 0 && isCurrentDeviceTrusted) { showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds); } else { hideBulkUnverifiedSessionsToast(); diff --git a/test/DeviceListener-test.ts b/test/DeviceListener-test.ts index 2ee2b64adf4..e5555845531 100644 --- a/test/DeviceListener-test.ts +++ b/test/DeviceListener-test.ts @@ -412,11 +412,16 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); }); - it('hides toast when only unverified device is the current device', async () => { - mockClient!.getStoredDevicesForUser.mockReturnValue([ - currentDevice, - ]); - mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustUnverified); + it('hides toast when current device is unverified', async () => { + // device2 verified, current and device3 unverified + mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { + switch (deviceId) { + case device2.deviceId: + return deviceTrustVerified; + default: + return deviceTrustUnverified; + } + }); await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); }); From adc37e7707b511e2ff10e67fca5cfc717f29c1b9 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 1 Dec 2022 09:55:48 +1300 Subject: [PATCH 4/6] add more assertions for show/hide toast, fix strict errors --- test/DeviceListener-test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/DeviceListener-test.ts b/test/DeviceListener-test.ts index 2ee2b64adf4..3e76af5bb79 100644 --- a/test/DeviceListener-test.ts +++ b/test/DeviceListener-test.ts @@ -294,7 +294,7 @@ describe('DeviceListener', () => { describe('when user does not have a cross signing id on this device', () => { beforeEach(() => { - mockClient!.getCrossSigningId.mockReturnValue(undefined); + mockClient!.getCrossSigningId.mockReturnValue(null); }); it('shows verify session toast when account has cross signing', async () => { @@ -307,7 +307,7 @@ describe('DeviceListener', () => { }); it('checks key backup status when when account has cross signing', async () => { - mockClient!.getCrossSigningId.mockReturnValue(undefined); + mockClient!.getCrossSigningId.mockReturnValue(null); mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId)); await createAndStart(); @@ -405,11 +405,13 @@ describe('DeviceListener', () => { mockClient!.isCrossSigningReady.mockResolvedValue(false); await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); it('hides toast when all devices at app start are verified', async () => { await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); it('hides toast when only unverified device is the current device', async () => { @@ -419,6 +421,7 @@ describe('DeviceListener', () => { mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustUnverified); await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); + expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); it('shows toast with unverified devices at app start', async () => { @@ -436,6 +439,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.showToast).toHaveBeenCalledWith( new Set([device3.deviceId]), ); + expect(BulkUnverifiedSessionsToast.hideToast).not.toHaveBeenCalled(); }); it('hides toast when unverified sessions at app start have been dismissed', async () => { @@ -483,7 +487,7 @@ describe('DeviceListener', () => { currentDevice, device2, device3, ]); // trigger a recheck - mockClient!.emit(CryptoEvent.DevicesUpdated, [mockClient!.getUserId()], false); + mockClient!.emit(CryptoEvent.DevicesUpdated, [userId], false); await flushPromises(); // bulk unverified sessions toast only shown for devices that were From 967c522792dffdf1a0e03d07b3f2b420ba5243bf Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 1 Dec 2022 10:01:00 +1300 Subject: [PATCH 5/6] fix strict error --- src/DeviceListener.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index d749b396b6c..8a15b539c8b 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -307,7 +307,7 @@ export default class DeviceListener { const newUnverifiedDeviceIds = new Set(); const isCurrentDeviceTrusted = crossSigningReady && - await (cli.checkDeviceTrust(cli.getUserId(), cli.deviceId)).isCrossSigningVerified(); + await (cli.checkDeviceTrust(cli.getUserId()!, cli.deviceId)).isCrossSigningVerified(); // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts From 3ecbd43f901c2a95b1dc6897ab53642f6084db15 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 1 Dec 2022 11:10:44 +1300 Subject: [PATCH 6/6] really fix strict error --- src/DeviceListener.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 8a15b539c8b..6142621c311 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -307,7 +307,7 @@ export default class DeviceListener { const newUnverifiedDeviceIds = new Set(); const isCurrentDeviceTrusted = crossSigningReady && - await (cli.checkDeviceTrust(cli.getUserId()!, cli.deviceId)).isCrossSigningVerified(); + await (cli.checkDeviceTrust(cli.getUserId()!, cli.deviceId!)).isCrossSigningVerified(); // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts @@ -316,7 +316,7 @@ export default class DeviceListener { for (const device of devices) { if (device.deviceId === cli.deviceId) continue; - const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId); + const deviceTrust = await cli.checkDeviceTrust(cli.getUserId()!, device.deviceId!); if (!deviceTrust.isCrossSigningVerified() && !this.dismissed.has(device.deviceId)) { if (this.ourDeviceIdsAtStart.has(device.deviceId)) { oldUnverifiedDeviceIds.add(device.deviceId);