From b0084da720a9125cf7ee7707924441ffa4ff54bb Mon Sep 17 00:00:00 2001 From: "Valentin D. Pinkman" Date: Mon, 2 Sep 2024 17:23:10 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(core):=20Event=20based=20device=20?= =?UTF-8?q?session=20refresher=20using=20rxjs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/popular-spies-allow.md | 5 ++ .../src/providers/DeviceSdkProvider/index.tsx | 3 +- .../GetDeviceStatusDeviceAction.test.ts | 5 -- .../GoToDashboardDeviceAction.test.ts | 3 - .../OpenAppDeviceAction.test.ts | 1 - .../api/device-session/DeviceSessionState.ts | 3 +- .../device-session/model/DeviceSession.ts | 55 ++++++++++--------- .../model/DeviceSessionRefresher.test.ts | 42 +++++++++----- .../model/DeviceSessionRefresher.ts | 51 ++++------------- .../service/DefaultEventDispatcher.test.ts | 23 ++++++++ .../service/DefaultEventDispatcher.ts | 30 ++++++++++ .../service/EventDispatcher.ts | 8 +++ .../__mocks__/DefaultEventDispatcher.ts | 6 ++ .../task/BuildEIP712ContextTask.test.ts | 3 - 14 files changed, 142 insertions(+), 96 deletions(-) create mode 100644 .changeset/popular-spies-allow.md create mode 100644 packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.test.ts create mode 100644 packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.ts create mode 100644 packages/core/src/internal/event-dispatcher/service/EventDispatcher.ts create mode 100644 packages/core/src/internal/event-dispatcher/service/__mocks__/DefaultEventDispatcher.ts diff --git a/.changeset/popular-spies-allow.md b/.changeset/popular-spies-allow.md new file mode 100644 index 000000000..6f5c36878 --- /dev/null +++ b/.changeset/popular-spies-allow.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/device-sdk-core": minor +--- + +Add new EventDispatcher and implement it in the device session refresher diff --git a/apps/sample/src/providers/DeviceSdkProvider/index.tsx b/apps/sample/src/providers/DeviceSdkProvider/index.tsx index 6f93b833c..3111805e8 100644 --- a/apps/sample/src/providers/DeviceSdkProvider/index.tsx +++ b/apps/sample/src/providers/DeviceSdkProvider/index.tsx @@ -3,10 +3,11 @@ import { ConsoleLogger, DeviceSdk, DeviceSdkBuilder, + LogLevel, } from "@ledgerhq/device-sdk-core"; export const sdk = new DeviceSdkBuilder() - .addLogger(new ConsoleLogger()) + .addLogger(new ConsoleLogger(LogLevel.Info)) .build(); const SdkContext = createContext(sdk); diff --git a/packages/core/src/api/device-action/os/GetDeviceStatus/GetDeviceStatusDeviceAction.test.ts b/packages/core/src/api/device-action/os/GetDeviceStatus/GetDeviceStatusDeviceAction.test.ts index 20d0f53f8..c0b140348 100644 --- a/packages/core/src/api/device-action/os/GetDeviceStatus/GetDeviceStatusDeviceAction.test.ts +++ b/packages/core/src/api/device-action/os/GetDeviceStatus/GetDeviceStatusDeviceAction.test.ts @@ -106,7 +106,6 @@ describe("GetDeviceStatusDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.LOCKED, currentApp: "mockedCurrentApp", - installedApps: [], }); apiGetDeviceSessionStateObservableMock.mockImplementation( @@ -120,7 +119,6 @@ describe("GetDeviceStatusDeviceAction", () => { DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "mockedCurrentApp", - installedApps: [], }); o.complete(); } else { @@ -129,7 +127,6 @@ describe("GetDeviceStatusDeviceAction", () => { DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.LOCKED, currentApp: "mockedCurrentApp", - installedApps: [], }); } }, @@ -382,7 +379,6 @@ describe("GetDeviceStatusDeviceAction", () => { DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.LOCKED, currentApp: "mockedCurrentApp", - installedApps: [], }); }, }); @@ -647,7 +643,6 @@ describe("GetDeviceStatusDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "mockedCurrentApp", - installedApps: [], }); sendCommandMock.mockResolvedValue( diff --git a/packages/core/src/api/device-action/os/GoToDashboard/GoToDashboardDeviceAction.test.ts b/packages/core/src/api/device-action/os/GoToDashboard/GoToDashboardDeviceAction.test.ts index 56354843c..970f6d50f 100644 --- a/packages/core/src/api/device-action/os/GoToDashboard/GoToDashboardDeviceAction.test.ts +++ b/packages/core/src/api/device-action/os/GoToDashboard/GoToDashboardDeviceAction.test.ts @@ -54,7 +54,6 @@ describe("GoToDashboardDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "BOLOS", - installedApps: [], }); const expectedStates: Array = [ @@ -104,7 +103,6 @@ describe("GoToDashboardDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "Bitcoin", - installedApps: [], }); sendCommandMock @@ -292,7 +290,6 @@ describe("GoToDashboardDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "BOLOS", - installedApps: [], }); const expectedStates: Array = [ diff --git a/packages/core/src/api/device-action/os/OpenAppDeviceAction/OpenAppDeviceAction.test.ts b/packages/core/src/api/device-action/os/OpenAppDeviceAction/OpenAppDeviceAction.test.ts index 3d88046cf..3f4ffbe06 100644 --- a/packages/core/src/api/device-action/os/OpenAppDeviceAction/OpenAppDeviceAction.test.ts +++ b/packages/core/src/api/device-action/os/OpenAppDeviceAction/OpenAppDeviceAction.test.ts @@ -48,7 +48,6 @@ describe("OpenAppDeviceAction", () => { sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, currentApp: "Bitcoin", - installedApps: [], }); sendCommandMock.mockResolvedValueOnce( diff --git a/packages/core/src/api/device-session/DeviceSessionState.ts b/packages/core/src/api/device-session/DeviceSessionState.ts index d4843b62e..25e04dc8b 100644 --- a/packages/core/src/api/device-session/DeviceSessionState.ts +++ b/packages/core/src/api/device-session/DeviceSessionState.ts @@ -1,6 +1,5 @@ import { BatteryStatusFlags } from "@api/command/os/GetBatteryStatusCommand"; import { DeviceStatus } from "@api/device/DeviceStatus"; -import { Application } from "@internal/manager-api/model/ManagerApiType"; /** * The battery status of a device. @@ -77,7 +76,7 @@ type DeviceSessionReadyState = { /** * The current applications installed on the device. */ - readonly installedApps: Application[]; + // readonly installedApps: Application[]; }; /** diff --git a/packages/core/src/internal/device-session/model/DeviceSession.ts b/packages/core/src/internal/device-session/model/DeviceSession.ts index 286292766..8780e506c 100644 --- a/packages/core/src/internal/device-session/model/DeviceSession.ts +++ b/packages/core/src/internal/device-session/model/DeviceSession.ts @@ -1,4 +1,3 @@ -import { BehaviorSubject } from "rxjs"; import { v4 as uuidv4 } from "uuid"; import { Command } from "@api/command/Command"; @@ -17,6 +16,8 @@ import { } from "@api/device-session/DeviceSessionState"; import { DeviceSessionId } from "@api/device-session/types"; import { SdkError } from "@api/Error"; +import { DefaultEventDispatcher } from "@internal/event-dispatcher/service/DefaultEventDispatcher"; +import { EventDispatcher } from "@internal/event-dispatcher/service/EventDispatcher"; import { LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; import { type ManagerApiService } from "@internal/manager-api/service/ManagerApiService"; import { InternalConnectedDevice } from "@internal/usb/model/InternalConnectedDevice"; @@ -34,9 +35,9 @@ export type SessionConstructorArgs = { export class DeviceSession { private readonly _id: DeviceSessionId; private readonly _connectedDevice: InternalConnectedDevice; - private readonly _deviceState: BehaviorSubject; private readonly _refresher: DeviceSessionRefresher; private readonly _managerApiService: ManagerApiService; + private readonly _deviceState: EventDispatcher; constructor( { connectedDevice, id = uuidv4() }: SessionConstructorArgs, @@ -45,26 +46,25 @@ export class DeviceSession { ) { this._id = id; this._connectedDevice = connectedDevice; - this._deviceState = new BehaviorSubject({ + + this._deviceState = new DefaultEventDispatcher({ sessionStateType: DeviceSessionStateType.Connected, deviceStatus: DeviceStatus.CONNECTED, }); + this._refresher = new DeviceSessionRefresher( { refreshInterval: 1000, - deviceStatus: DeviceStatus.CONNECTED, + deviceState: this._deviceState, sendApduFn: (rawApdu: Uint8Array) => this.sendApdu(rawApdu, { isPolling: true, triggersDisconnection: false, }), - updateStateFn: (callback) => { - const state = this._deviceState.getValue(); - this.setDeviceSessionState(callback(state)); - }, }, loggerModuleFactory("device-session-refresher"), ); + this._managerApiService = managerApiService; } @@ -77,20 +77,11 @@ export class DeviceSession { } public get state() { - return this._deviceState.asObservable(); + return this._deviceState.listen(); } public setDeviceSessionState(state: DeviceSessionState) { - this._deviceState.next(state); - } - - private updateDeviceStatus(deviceStatus: DeviceStatus) { - const sessionState = this._deviceState.getValue(); - this._refresher.setDeviceStatus(deviceStatus); - this._deviceState.next({ - ...sessionState, - deviceStatus, - }); + this._deviceState.dispatch(state); } async sendApdu( @@ -100,7 +91,11 @@ export class DeviceSession { triggersDisconnection: false, }, ) { - if (!options.isPolling) this.updateDeviceStatus(DeviceStatus.BUSY); + if (!options.isPolling) { + this._deviceState.dispatch({ + deviceStatus: DeviceStatus.BUSY, + }); + } const errorOrResponse = await this._connectedDevice.sendApdu( rawApdu, @@ -109,9 +104,13 @@ export class DeviceSession { return errorOrResponse.ifRight((response) => { if (CommandUtils.isLockedDeviceResponse(response)) { - this.updateDeviceStatus(DeviceStatus.LOCKED); + this._deviceState.dispatch({ + deviceStatus: DeviceStatus.LOCKED, + }); } else { - this.updateDeviceStatus(DeviceStatus.CONNECTED); + this._deviceState.dispatch({ + deviceStatus: DeviceStatus.CONNECTED, + }); } }); } @@ -146,11 +145,11 @@ export class DeviceSession { sendCommand: async ( command: Command, ) => this.sendCommand(command), - getDeviceSessionState: () => this._deviceState.getValue(), + getDeviceSessionState: () => this._deviceState.get(), getDeviceSessionStateObservable: () => this.state, setDeviceSessionState: (state: DeviceSessionState) => { this.setDeviceSessionState(state); - return this._deviceState.getValue(); + return this._deviceState.get(); }, getMetadataForAppHashes: (apps: ListAppsResponse) => this._managerApiService.getAppsByHash(apps), @@ -163,7 +162,11 @@ export class DeviceSession { } close() { - this.updateDeviceStatus(DeviceStatus.NOT_CONNECTED); - this._deviceState.complete(); + this._deviceState.dispatch({ + deviceStatus: DeviceStatus.NOT_CONNECTED, + }); + + this._refresher.stop(); + this._deviceState.close(); } } diff --git a/packages/core/src/internal/device-session/model/DeviceSessionRefresher.test.ts b/packages/core/src/internal/device-session/model/DeviceSessionRefresher.test.ts index 75819b7ce..9318c9234 100644 --- a/packages/core/src/internal/device-session/model/DeviceSessionRefresher.test.ts +++ b/packages/core/src/internal/device-session/model/DeviceSessionRefresher.test.ts @@ -7,19 +7,24 @@ import { } from "@api/command/os/GetAppAndVersionCommand"; import { DeviceStatus } from "@api/device/DeviceStatus"; import { ApduResponse } from "@api/device-session/ApduResponse"; +import { DeviceSessionState } from "@api/device-session/DeviceSessionState"; +import { DefaultEventDispatcher } from "@internal/event-dispatcher/service/DefaultEventDispatcher"; import { DefaultLoggerPublisherService } from "@internal/logger-publisher/service/DefaultLoggerPublisherService"; import { LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; import { DeviceSessionRefresher } from "./DeviceSessionRefresher"; -const mockSendApduFn = jest.fn().mockResolvedValue(Right({} as ApduResponse)); -const mockUpdateStateFn = jest.fn().mockImplementation(() => undefined); - +jest.mock>( + "@internal/event-dispatcher/service/DefaultEventDispatcher", +); jest.useFakeTimers(); +const mockSendApduFn = jest.fn().mockResolvedValue(Right({} as ApduResponse)); + describe("DeviceSessionRefresher", () => { let deviceSessionRefresher: DeviceSessionRefresher; let logger: LoggerPublisherService; + let deviceState: DefaultEventDispatcher; beforeEach(() => { jest @@ -31,16 +36,25 @@ describe("DeviceSessionRefresher", () => { } as GetAppAndVersionResponse, }), ); + logger = new DefaultLoggerPublisherService( [], "DeviceSessionRefresherTest", ); + + deviceState = new DefaultEventDispatcher({ + deviceStatus: DeviceStatus.CONNECTED, + } as DeviceSessionState); + + jest.spyOn(deviceState, "get").mockReturnValue({ + deviceStatus: DeviceStatus.CONNECTED, + } as DeviceSessionState); + deviceSessionRefresher = new DeviceSessionRefresher( { refreshInterval: 1000, - deviceStatus: DeviceStatus.CONNECTED, + deviceState, sendApduFn: mockSendApduFn, - updateStateFn: mockUpdateStateFn, }, logger, ); @@ -60,7 +74,9 @@ describe("DeviceSessionRefresher", () => { }); it("should not poll when device is busy", () => { - deviceSessionRefresher.setDeviceStatus(DeviceStatus.BUSY); + jest.spyOn(deviceState, "get").mockReturnValue({ + deviceStatus: DeviceStatus.BUSY, + } as DeviceSessionState); jest.advanceTimersByTime(1000); @@ -68,7 +84,9 @@ describe("DeviceSessionRefresher", () => { }); it("should not poll when device is disconnected", () => { - deviceSessionRefresher.setDeviceStatus(DeviceStatus.NOT_CONNECTED); + jest.spyOn(deviceState, "get").mockReturnValue({ + deviceStatus: DeviceStatus.NOT_CONNECTED, + } as DeviceSessionState); jest.advanceTimersByTime(1000); @@ -79,7 +97,7 @@ describe("DeviceSessionRefresher", () => { jest.advanceTimersByTime(1000); expect(await mockSendApduFn()).toEqual(Right({})); - expect(mockUpdateStateFn).toHaveBeenCalled(); + expect(deviceState.dispatch).toHaveBeenCalled(); }); it("should not update device session state with failed polling response", async () => { @@ -89,16 +107,10 @@ describe("DeviceSessionRefresher", () => { jest.advanceTimersByTime(1000); await mockSendApduFn(); - expect(mockUpdateStateFn).not.toHaveBeenCalled(); + expect(deviceState.dispatch).not.toHaveBeenCalled(); expect(spy).toHaveBeenCalled(); }); - it("should stop the refresher when device is disconnected", () => { - const spy = jest.spyOn(deviceSessionRefresher, "stop"); - deviceSessionRefresher.setDeviceStatus(DeviceStatus.NOT_CONNECTED); - expect(spy).toHaveBeenCalledTimes(1); - }); - it("should not throw error if stop is called on a stopped refresher", () => { deviceSessionRefresher.stop(); expect(() => deviceSessionRefresher.stop()).not.toThrow(); diff --git a/packages/core/src/internal/device-session/model/DeviceSessionRefresher.ts b/packages/core/src/internal/device-session/model/DeviceSessionRefresher.ts index 360e445fa..e3f8430bc 100644 --- a/packages/core/src/internal/device-session/model/DeviceSessionRefresher.ts +++ b/packages/core/src/internal/device-session/model/DeviceSessionRefresher.ts @@ -11,6 +11,7 @@ import { DeviceSessionStateType, } from "@api/device-session/DeviceSessionState"; import { SdkError } from "@api/Error"; +import { EventDispatcher } from "@internal/event-dispatcher/service/EventDispatcher"; import { type LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; /** @@ -23,24 +24,14 @@ export type DeviceSessionRefresherArgs = { refreshInterval: number; /** - * The current device status when the refresher is created. + * The device state EventDispatcher. */ - deviceStatus: Exclude; + deviceState: EventDispatcher; /** * The function used to send APDU commands to the device. */ sendApduFn: (rawApdu: Uint8Array) => Promise>; - - /** - * Callback that updates the state of the device session with - * polling response. - * @param callback - A function that will take the previous state and return the new state. - * @returns void - */ - updateStateFn( - callback: (state: DeviceSessionState) => DeviceSessionState, - ): void; }; /** @@ -50,26 +41,20 @@ export type DeviceSessionRefresherArgs = { export class DeviceSessionRefresher { private readonly _logger: LoggerPublisherService; private readonly _getAppAndVersionCommand = new GetAppAndVersionCommand(); - private _deviceStatus: DeviceStatus; private _subscription: Subscription; - + private _deviceState: EventDispatcher; constructor( - { - refreshInterval, - deviceStatus, - sendApduFn, - updateStateFn, - }: DeviceSessionRefresherArgs, + { refreshInterval, deviceState, sendApduFn }: DeviceSessionRefresherArgs, logger: LoggerPublisherService, ) { - this._deviceStatus = deviceStatus; + this._deviceState = deviceState; this._logger = logger; this._subscription = interval(refreshInterval) .pipe( filter( () => ![DeviceStatus.BUSY, DeviceStatus.NOT_CONNECTED].includes( - this._deviceStatus, + this._deviceState.get().deviceStatus, ), ), switchMap(() => { @@ -102,29 +87,15 @@ export class DeviceSessionRefresher { if (!isSuccessCommandResult(parsedResponse)) { return; } - // `batteryStatus` and `firmwareVersion` are not available in the polling response. - updateStateFn((state) => ({ - ...state, + + this._deviceState.dispatch({ sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, - deviceStatus: this._deviceStatus, + deviceStatus: this._deviceState.get().deviceStatus, currentApp: parsedResponse.data.name, - installedApps: "installedApps" in state ? state.installedApps : [], - })); + }); }); } - /** - * Maintain a device status to prevent sending APDU when the device is busy. - * - * @param {DeviceStatus} deviceStatus - The new device status. - */ - setDeviceStatus(deviceStatus: DeviceStatus) { - if (deviceStatus === DeviceStatus.NOT_CONNECTED) { - this.stop(); - } - this._deviceStatus = deviceStatus; - } - /** * Stops the session refresher. * The refresher will no longer send commands to refresh the session. diff --git a/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.test.ts b/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.test.ts new file mode 100644 index 000000000..00fa21472 --- /dev/null +++ b/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.test.ts @@ -0,0 +1,23 @@ +import { Observable } from "rxjs"; + +import { DefaultEventDispatcher } from "./DefaultEventDispatcher"; + +describe("DefaultEventDispatcher", () => { + it("should dispatch an event", () => { + const dispatcher = new DefaultEventDispatcher({ a: 1 }); + dispatcher.dispatch({ a: 2 }); + expect(dispatcher.get()).toEqual({ a: 2 }); + }); + + it("should listen to an event", () => { + const dispatcher = new DefaultEventDispatcher({ a: 1 }); + const subscription = dispatcher.listen(); + expect(subscription).toBeInstanceOf(Observable); + }); + + it("should close the event dispatcher", () => { + const dispatcher = new DefaultEventDispatcher({ a: 1 }); + dispatcher.close(); + expect(dispatcher.get()).toEqual({ a: 1 }); + }); +}); diff --git a/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.ts b/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.ts new file mode 100644 index 000000000..23c0cba3b --- /dev/null +++ b/packages/core/src/internal/event-dispatcher/service/DefaultEventDispatcher.ts @@ -0,0 +1,30 @@ +import { BehaviorSubject, Observable } from "rxjs"; + +import { EventDispatcher } from "./EventDispatcher"; + +export class DefaultEventDispatcher> + implements EventDispatcher +{ + private obs: BehaviorSubject; + + constructor(e: T) { + this.obs = new BehaviorSubject(e); + } + + get(): T { + return this.obs.getValue(); + } + + listen(): Observable { + return this.obs.asObservable(); + } + + dispatch(event: Partial): void { + const currentState = this.get(); + this.obs.next({ ...currentState, ...event }); + } + + close(): void { + this.obs.complete(); + } +} diff --git a/packages/core/src/internal/event-dispatcher/service/EventDispatcher.ts b/packages/core/src/internal/event-dispatcher/service/EventDispatcher.ts new file mode 100644 index 000000000..d568f3c8f --- /dev/null +++ b/packages/core/src/internal/event-dispatcher/service/EventDispatcher.ts @@ -0,0 +1,8 @@ +import { Observable } from "rxjs"; + +export interface EventDispatcher> { + listen(): Observable; + dispatch(event: Partial): void; + get(): T; + close(): void; +} diff --git a/packages/core/src/internal/event-dispatcher/service/__mocks__/DefaultEventDispatcher.ts b/packages/core/src/internal/event-dispatcher/service/__mocks__/DefaultEventDispatcher.ts new file mode 100644 index 000000000..26f449ed8 --- /dev/null +++ b/packages/core/src/internal/event-dispatcher/service/__mocks__/DefaultEventDispatcher.ts @@ -0,0 +1,6 @@ +export class DefaultEventDispatcher { + get = jest.fn(); + listen = jest.fn(); + dispatch = jest.fn(); + close = jest.fn(); +} diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/BuildEIP712ContextTask.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildEIP712ContextTask.test.ts index e623dca02..cb3a56341 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/task/BuildEIP712ContextTask.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildEIP712ContextTask.test.ts @@ -139,7 +139,6 @@ describe("BuildEIP712ContextTask", () => { apiMock.getDeviceSessionState.mockReturnValueOnce({ sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, - installedApps: [], currentApp: "Bitcoin", }); // WHEN @@ -171,7 +170,6 @@ describe("BuildEIP712ContextTask", () => { apiMock.getDeviceSessionState.mockReturnValueOnce({ sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, - installedApps: [], currentApp: "Ethereum", }); contextMouleMock.getTypedDataFilters.mockResolvedValueOnce({ @@ -207,7 +205,6 @@ describe("BuildEIP712ContextTask", () => { apiMock.getDeviceSessionState.mockReturnValueOnce({ sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel, deviceStatus: DeviceStatus.CONNECTED, - installedApps: [], currentApp: "Ethereum", }); contextMouleMock.getTypedDataFilters.mockResolvedValueOnce(