From 59af13eff0cc1a8455bb71e6e7b18887001149e4 Mon Sep 17 00:00:00 2001 From: Martin Varmuza Date: Thu, 22 Aug 2024 10:59:01 +0200 Subject: [PATCH] test(connect): add tests for TrezorConnect.cancel --- .../connect/e2e/tests/device/cancel.test.ts | 155 ++++++++++++++++++ packages/trezor-user-env-link/src/api.ts | 2 +- scripts/ci/connect-test-matrix-generator.js | 2 +- 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 packages/connect/e2e/tests/device/cancel.test.ts diff --git a/packages/connect/e2e/tests/device/cancel.test.ts b/packages/connect/e2e/tests/device/cancel.test.ts new file mode 100644 index 000000000000..7026762bde22 --- /dev/null +++ b/packages/connect/e2e/tests/device/cancel.test.ts @@ -0,0 +1,155 @@ +import { SetupEmu } from '@trezor/trezor-user-env-link'; +import TrezorConnect from '../../../src'; + +const { getController, setup, initTrezorConnect } = global.Trezor; + +const getAddress = (showOnTrezor: boolean) => { + return TrezorConnect.getAddress({ + path: "m/44'/1'/0'/0/0", + showOnTrezor, + }); +}; + +const passphraseHandler = (value: string) => () => { + TrezorConnect.uiResponse({ + type: 'ui-receive_passphrase', + payload: { + passphraseOnDevice: false, + value, + }, + }); + TrezorConnect.removeAllListeners('ui-request_passphrase'); +}; + +const addressHandler = () => () => { + TrezorConnect.uiResponse({ + type: 'ui-receive_confirmation', + payload: true, + }); + TrezorConnect.removeAllListeners('ui-request_confirmation'); +}; + +const assertGetAddressWorks = async () => { + // validate that further communication is possible without any glitch + TrezorConnect.on('ui-request_passphrase', passphraseHandler('a')); + TrezorConnect.on('ui-request_confirmation', addressHandler()); + const getAddressResponse = await getAddress(false); + expect(getAddressResponse).toMatchObject({ + payload: { address: 'ms1TJk4b4s7aisyL3jfrkCqwznttWwiS4r' }, + }); +}; + +describe('TrezorConnect.cancel', () => { + const controller = getController(); + + const setupTest = async ({ setupParams }: { setupParams: SetupEmu; bridgeVersion: string }) => { + await setup(controller, setupParams); + await TrezorConnect.dispose(); + await initTrezorConnect(controller, { debug: false }); + }; + + afterAll(async () => { + controller.dispose(); + await TrezorConnect.dispose(); + }); + + [ + '2.0.27', + '2.0.30', + // todo: add support in trezor-user-env-link + // '3.0.0' + ].forEach(bridgeVersion => { + describe(`Bridge ${bridgeVersion}`, () => { + TrezorConnect.removeAllListeners(); + // the goal is to run this test couple of times to uncover possible race conditions/flakiness + test(`GetAddress - ButtonRequest_Address - Cancel `, async () => { + await setupTest({ + setupParams: { + mnemonic: 'mnemonic_all', + }, + bridgeVersion, + }); + + const getAddressCall = getAddress(true); + await new Promise(resolve => { + TrezorConnect.on('button', event => { + if (event.code === 'ButtonRequest_Address') { + resolve(undefined); + } + }); + }); + + TrezorConnect.cancel('my custom message'); + + const response = await getAddressCall; + + expect(response).toMatchObject({ + success: false, + payload: { + error: 'my custom message', + code: 'Method_Cancel', + }, + }); + + // TODO: this sometimes fails with, probably a race condition + // success: false, + // payload: { + // error: 'Initialize failed: Unexpected message, code: Failure_UnexpectedMessage', + // code: 'Device_InitializeFailed' + // } + // await assertGetAddressWorks(); + }); + + // todo: this doesn't seem to work + // await getAddressCal does not resolve + test.skip('Synchronous Cancel', async () => { + await setupTest({ + setupParams: { + mnemonic: 'mnemonic_all', + }, + bridgeVersion, + }); + + const getAddressCall = getAddress(true); + + TrezorConnect.cancel(); + + const response = await getAddressCall; + + // This looks like a bug. there was showOnTrezor: true but we bypassed it by sending cancel? + expect(response).toMatchObject({ + success: true, + payload: { + address: 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q', + }, + }); + }); + + test('Passphrase request - Cancel', async () => { + await setupTest({ + setupParams: { + mnemonic: 'mnemonic_all', + passphrase_protection: true, + }, + bridgeVersion, + }); + + const getAddressCall = getAddress(true); + await new Promise(resolve => { + TrezorConnect.on('UI_EVENT', event => { + if (event.type === 'ui-request_passphrase') { + resolve(undefined); + } + }); + }); + TrezorConnect.cancel(); + + const response = await getAddressCall; + + expect(response.success).toEqual(false); + + await assertGetAddressWorks(); + }); + }); + }); +}); diff --git a/packages/trezor-user-env-link/src/api.ts b/packages/trezor-user-env-link/src/api.ts index 51042fba79a5..75bdef7433cc 100644 --- a/packages/trezor-user-env-link/src/api.ts +++ b/packages/trezor-user-env-link/src/api.ts @@ -5,7 +5,7 @@ import { TypedEmitter } from '@trezor/utils'; import { Model, Firmwares } from './types'; import { WebsocketClient, WebsocketClientEvents } from './websocket-client'; -interface SetupEmu { +export interface SetupEmu { mnemonic?: string; pin?: string; passphrase_protection?: boolean; diff --git a/scripts/ci/connect-test-matrix-generator.js b/scripts/ci/connect-test-matrix-generator.js index ee297766bbae..76d0ea1b4cdb 100644 --- a/scripts/ci/connect-test-matrix-generator.js +++ b/scripts/ci/connect-test-matrix-generator.js @@ -4,7 +4,7 @@ const groups = { api: { name: 'api', pattern: - 'init authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy override checkFirmwareAuthenticity', + 'init authorizeCoinjoin cancelCoinjoinAuthorization passphrase unlockPath setBusy override checkFirmwareAuthenticity cancel.test', methods: '', }, management: {